Have Win32 MessageBox appear over other programs - python

I've recently started learning Python and wrote a little script that informs me when a certain website changes content. I then added it as a scheduled task to Windows so it can run every 10 minutes. I'd like to be informed of the website changing right away so I added a win32ui MessageBox that pops up if the script detects that the website has changed. Here's the little code snippet I'm using for the MessageBox (imaginative text, I know):
win32ui.MessageBox("The website has changed.", "Website Change", 0)
My issue is this, I spend most of my time using remote desktop so when the MessageBox does pop up it sits behind the remote desktop session, is there any way to force the MessageBox to appear on top of it?
On a similar note when the script runs the command line opens up very briefly over the remote desktop session which I don't want, is there any way of stopping this behaviour?
I'm happy with Windows specific solutions as I'm aware it might mean dealing with the windowing manager or possibly an alternative way to inform me rather than using a MessageBox.

When you start anything from Task Scheduler, Windows blocks any "easy" ways to bring your windows or dialogs to top.
First way - use MB_SYSTEMMODAL (4096 value) flag. In my experience, it makes Msg dialog "Always on top".
win32ui.MessageBox("The website has changed.", "Website Change", MB_SYSTEMMODAL)
Second way - try to bring your console/window/dialog to the front with Following calls. Of course, if you use MessageBox you must do that (for your own created window) before calling MessageBox.
SetForegroundWindow(Wnd);
BringWindowToTop(Wnd);
SetForegroundWindow(Wnd);
As for flickering of the console window, you may try to start Python in a hidden state. For example, use ConEmu, ‘HidCon’ or cmdow. Refer to their parameters, something like:
ConEmu -basic -MinTSA -cmd C:\Python27\python.exe C:\pythonScript.py
or
CMDOW /RUN /MIN C:\Python27\python.exe C:\pythonScript.py

Avoiding the command window flash is done by naming the script with a pyw extension instead of simply py. You might also use pythonw.exe instead of python.exe, it really depends on your requirements.
See http://onlamp.com/pub/a/python/excerpts/chpt20/index.html?page=2

Use ctypes it displays an windows error message box very easy to use,
import ctypes
if condition:
ctypes.windll.user32.MessageBoxW(0, u"Error", u"Error", 0)

This works for me:
from ctypes import *
def MessageBox(title, text, style):
sty = int(style) + 4096
return windll.user32.MessageBoxW(0, text, title, sty) #MB_SYSTEMMODAL==4096
## Button Styles:
### 0:OK -- 1:OK|Cancel -- 2:Abort|Retry|Ignore -- 3:Yes|No|Cancel -- 4:Yes|No -- 5:Retry|No -- 6:Cancel|Try Again|Continue
## To also change icon, add these values to previous number
### 16 Stop-sign ### 32 Question-mark ### 48 Exclamation-point ### 64 Information-sign ('i' in a circle)
Usage:
MessageBox('Here is my Title', 'Message to be displayed', 64)

Making the message box system modal will cause it to pop up over every application, but none can be interacted with until it is dismissed. Consider either creating a custom dialog box window that you can bring to the front or using a notification bubble instead.

Windows tries to make it hard to pop a window over the active application. Users find it annoying, especially since the interrupting window generally steals keyboard focus.
The Windows way to give a notification like this is with a balloon in the notification area rather than a message box. Notification balloons don't steal focus and are (supposedly) less distracting.
I'm not sure if the python Windows UI library offers wrappers for notification balloons.

Very easy modal async message box with help of Python and MSG command (working on Win10):
# In CMD (you may use Username logged on target machine instead of * to send message to particular user):
msg /server:IP_or_ComputerName * /v /time:appearance_in_secs Message_up_to_255_chars
# I.e. send "Hello everybody!" to all users on 192.168.0.110 disappearing after 5 mins
msg /server:192.168.0.110 * /v /time:300 "Hello everybody!"
In Python I've been using subprocess to send CMD commands, allows me to read and process output, find error etc.
import subprocess as sp
name = 'Lucas'
message = f'Express yourself {name} in 255 characters ;)'
command = f'msg /server:192.168.0.110 * /v /time:360 "{message}"'
output = str(sp.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
if 'returncode=0' in output:
pint('Message sent')
else:
print('Error occurred. Details:\n')
print(output[output.index('stdout=b'):])

Related

Python Windows Explorer Refresh PostMessage

I'm writing a Python script to toggle the "Hidden Items" status of the Windows Explorer. It changes the value of Computer\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Hidden, but for the change to take effect all instances of the Explorer have to be refreshed.
I implemented the same idea a few months ago with AutoHotkey, there I could solve the refresh problem with the following commands I found on the AutoHotkey Forum:
WinGetClass, CabinetWClass
PostMessage, 0x111, 28931, , , A
PostMessage, 0x111, 41504, , , A
I tried different approaches to translate it, but wasn't able to get it working with Python. While searching for an answer I also found a solution with C# (Refresh Windows Explorer in Win7), which is far more complicated than the AutoHotkey version. I could let the Python script call the C# script, but I would much prefer a solution without auxiliary files.
How can I implement such behavior with Python?
Using pywin module:
import win32con
import win32gui
# List for handles:
handles = []
def collect_handles(h, _):
' Get window class name and add it to list '
if win32gui.GetClassName(h) == 'CabinetWClass':
handles.append(h)
def refresh_window(h):
' Send messages to window '
win32gui.PostMessage(h, win32con.WM_COMMAND, 28931, None)
win32gui.PostMessage(h, win32con.WM_COMMAND, 41504, None)
# Fill our list:
win32gui.EnumWindows(collect_handles, None)
# Perform action on our handles:
list(map(refresh_window, handles))

How to create an alert box without using Tkinter?

I need to create a message box in python without using python Tkinter library so that I can use that before using exit() function this will display the message and answer as soon as user presses okay, user gets out of program.
Here's one way to do it with Windows' msg command. The code is based on #ErykSun's comment under the question Can't execute msg (and other) Windows commands via subprocess.
import os
import subprocess
sysroot = os.environ['SystemRoot']
sysnative = (os.path.join(sysroot, 'SysNative')
if os.path.exists(os.path.join(sysroot, 'SysNative'))
else
os.path.join(sysroot, 'System32'))
msgexe_path = os.path.join(sysnative, 'msg.exe')
subprocess.run([msgexe_path, '*', 'ALL YOUR BASE ARE WHERE BELONG TO US.'])

Is there a way to directly tell my python program to use the mouse with os.system("import filename.png")?

I've been trying to write a little screenshot script for arch. It works, but the problem is when I try to assign it a keybinding in my i3-config it does nothing.
First I tried writing it fully in bash which worked fine but I stumbled onto the same problem with it not executing.
So I have redirected the output to a log-file to check it out and it welcomes me with this:
import: unable to grab mouse '': Datei oder Verzeichnis nicht gefunden # error/xwindow.c/XSelectWindow/9306.
import: unable to read X window image '': Erfolg # error/xwindow.c/XImportImage/4942.
import: unable to read X window image '': Erfolg # error/xwindow.c/XImportImage/5049.
import: `/home/lukas/Screenshot/20190419/scoot7.png' # error/import.c/ImportImageCommand/1288.
Translation first line: Couldn't find file or directory # error[...]
Translation Erfolg: success
I've tried googling it, which didn't lead me to anything really and I didn't really find any path resembling error/xwindow.c and so on.
Here is my code:
#!/usr/bin/env python
import os, os.path
import datetime
import sys
d = datetime.datetime.today()
directory="/home/lukas/Screenshot/%s"%d.strftime('%Y%m%d')
if not os.path.exists(directory):
os.mkdir(directory)
fileCount = 1
for file in os.listdir(directory):
if file.endswith('.png'):
fileCount+=1
filename = "%s/scr%d.png"%(directory,fileCount)
os.system("import %s"%filename)
and here is my entry to my i3 config:
bindsym $mod+Shift+F12 exec --no-startup-id scoot > /tmp/log.out 2>&1
The expected outcome of this is that when I press mod+shift+f12 it should transform my mouse-pointer to a "crosshair" so I can select something and takes a screenshot of that.
The actual result is that if I run it normaly it works, but if I try to use the Keyboard-Shortcut it just does nothing but output to my logfile.
I am pretty much a newbie to programming and linux, and I have no idea why it cannot find my mouse and I wanted to know if I can explicitly tell the program to use it or if there is another way to do this.
Thanks alot.
Hugenotte
I wrote a bash script a little while ago to do exactly what you are trying to do, using the very common utility ImageMagick that you can find here on Arch.
Here it is in case you are open to this alternative:
#!/bin/bash
# take screenshot using import from imagemagick
# allows to select the area by dragging across a rectangle
# or to select entire window by clicking inside it
set -e
# the date and time will be used as the file name
time=$(date +"%F_%H:%M:%S")
# naming the urxvt window "screenshot" so that the i3 "no_focus" option gets applied to it
urxvt -title "screenshot" -e bash -c "import $HOME/Screenshot/$time.png"
I then use it in i3 with:
no_focus [title="screenshot"]
bindsym $mod+Shift+F12 layout tabbed; exec --no-startup-id bash my_script.sh
Of course, you may want to change the date format to what you had in python. I like having the time in case I take several screenshots in a row. And you have to replace urxvt with the name of your terminal emulator and you might have to adapt the -title flag if your terminal emulator doesn't give windows a name in this way.
Note that naming the window is important: it took me a little while to figure out how to go around what happens without naming the window and using the no_focus on that window in i3:
The focus would jump to the screen capture window and thus out of the window of which I wanted to take a screenshot. It might be possible that the issue you are facing with your bash and python scripts may be related to this. You have to force i3 to keep your mouse on the old window and not jump to the screen capture window triggered by the script (by default, i3 will focus on a newly created window).

Create a MessageBox in Python for Mac?

Currently working through a free online class for Python from Cybrary (I'm coding in 3.6), but I use a Mac while the presenter uses Windows. So far, there have been very few differences if any.
The current section deals with learning and using Ctypes however, and the "assignment" says to Write a function which takes two arguments, title and body and creates a MessageBox with those arguments.
The code used in the video as an example of creating a Message Box:
from ctypes import *
windll.user32.MessageBoxA(0, "Click Yes or No\n", "This is a title\n", 4)
My code:
# 2.1 Ctypes: Write a function which takes two arguments, title and body
# and creates a MessageBox with those arguments
def python_message_box(title, body):
return windll.user32.MessageBoxA(0, body, title, 0)
Running this gives the error:
File ".../AdvancedActivities.py", line 9, in python_message_box
return windll.user32.MessageBoxA(0, body, title, 0)
NameError: name 'windll' is not defined
I don't believe I need to say that I get the same error trying to run
windll.user32.MessageBoxW(0, body, title, 0)
I haven't been able to find any examples anywhere of people creating Message Boxes on Mac computers. Is it a Windows-specific function? If so, what would be the Mac equivalent of this?
EDIT: Mark Setchell's solution is to have Python run terminal functions that accomplish windll tasks, so instead of windll.user32.MessageBoxA(0, body, title, 0), use:
command = "osascript -e 'Tell application \"System Events\" to
display dialog \""+body+"\"'"
system(command)
If you type this into a Terminal on any Mac, you'll get a dialog box:
osascript -e 'Tell application "System Events" to display dialog "Some Funky Message" with title "Hello Matey"'
See here for further examples.
So, just use a Python subprocess call to run that... subprocess documentation, or use system().
Nothing to install. No dependencies. You can also ask user for values, select files or directories and pick colours using the same technique. The dialog boxes are all native Mac ones - not some ugly imitation.
import os
body_Str="Body of Dialog"
title_Str="Title"
os.system("""osascript -e \'Tell application \"System Events\" to display dialog \""+body_Str+"\" with title \""+title_Str+"\"\'""")
this is much better

Dialog box unresponsive with Windows Script Host (Python)

I am trying to modify the Print Setup option found under File > Print Setup.
I am using Windows Script Host with Python. I use the Alt + F followed by S to open the appropriate dialogue box:
When I do those commands by hand, the Print Setup box is in focus, so I can press F to select "Print to File" then {ENTER} or O to accept the changes.
However, I neither ALT+F nor F is selecting the File option.
shell = win32com.client.Dispatch("WScript.Shell")
shell.AppActivate('Point of Sale')
shell.SendKeys("%fs") # I also tried "%fsf and "%fs%f" removing the other call to SendKeys"
time.sleep(0.1) # Removing this (or using a longer wait) makes no difference
shell.SendKeys("F")
You have to wait for the dialog to appear before sending "F".
Try this:
shell.SendKeys("%fs")
time.sleep(.25)
shell.SendKeys("f")
This problem had to do with incorrect implementing on part of the developer of this software. To solve the problem, I used SendKeys to send multiple TABS until File was selected.

Categories

Resources