I am using a module for balloon tip notifications in the system tray, whenever the user receives a new message. (using Python 3.3 and Kivy 1.8. I am deploying to windows 7.)
I believe the original code comes from this github.
This is module as I have altered it::
from win32api import *
from win32gui import *
import win32con
import sys, os
import struct
import threading
import time
print ("Importing pypops")
class WindowsBalloonTip:
def __init__(self, title, msg):
message_map = {
win32con.WM_DESTROY: self.OnDestroy,
# win32con.WM_CLOSE: self.onClose
}
# Register the Window class.
wc = WNDCLASS()
hinst = wc.hInstance = GetModuleHandle(None)
wc.lpszClassName = "PythonTaskbar"
wc.lpfnWndProc = message_map # could also specify a wndproc.
classAtom = RegisterClass(wc)
# Create the Window.
style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
self.hwnd = CreateWindow( classAtom, "Taskbar", style, \
0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, \
0, 0, hinst, None)
UpdateWindow(self.hwnd)
iconPathName = os.path.abspath(os.path.join( sys.path[0], "balloontip.ico" ))
icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
try:
hicon = LoadImage(hinst, iconPathName, \
win32con.IMAGE_ICON, 0, 0, icon_flags)
except:
hicon = LoadIcon(0, win32con.IDI_APPLICATION)
flags = NIF_ICON | NIF_MESSAGE | NIF_TIP
nid = (self.hwnd, 0, flags, win32con.WM_USER+20, hicon, "tooltip")
#Notify
Shell_NotifyIcon(NIM_ADD, nid)
Shell_NotifyIcon(NIM_MODIFY, \
(self.hwnd, 0, NIF_INFO, win32con.WM_USER+20,\
hicon, "Balloon tooltip",msg,400,title))
### from original code
# time.sleep(10) #cannot use this. it kills the whole app
# DestroyWindow(self.hwnd)
# classAtom = UnregisterClass(classAtom, hinst)
def onClose(self, hwnd): #to be called from thread as a method of the class instance
DestroyWindow(hwnd)
classAtom = UnregisterClass(classAtom, hinst)
return True
def OnDestroy(self, hwnd, msg, wparam, lparam):
nid = (self.hwnd, 0)
Shell_NotifyIcon(NIM_DELETE, nid)
PostQuitMessage(0) # Terminate the app.
I am instantiating the WindowsBalloonTip
class in a thread, if and only if a new message comes in. After the class is instantiated, a if a subsequent thread detects another new message, I was trying to call onClose
as a method of the pypops class,and getting
pywintypes.error: (1400, 'DestroyWindow', 'Invalid window handle.').
I know from other sources that this is because the thread that creates the window must be used to destroy it. What is the way around this?
The time.sleep
solution, commented out in my code, is unfeasible for at least these reasons:
- I need the window to stay alive until a) user clicks or b) a new message comes in
time.sleep()
kills my whole app, prevents user input, redraws, etc
I have researched: Handling invalid window handle and many others.
How can I destroy the window and unregister the class dynamically from a thread or update the window Text
?
When I try to DestroyWindow
as a sequential --init--
step, it works fine. Logging 'hwnd'
yields an integer. However, when I try to call onClose
, I am getting the 1400 error, even though logging 'hwnd'
in this case yields the same exact value as the other 'hwnd'
: in other words, the parameter I am passing to DestroyWindow
is the same value whether DestroyWindow
happens in the --init--
stage, or as part of a method I call later. What is tipping off the error in the latter case?
I am very familiar with Python, and getting better with Kivy. However, this is my first foray into any windows gui/api intercompatability.
Thanks for any answers, suggestions, criticisms, leads!
"Leads? Leads, yeah sure. I'll uh, just check with the boys down at the Crime Lab. They uh, got uh, four more detectives working on the case. They've got us working in shifts."