From 3472cf707059ee5b58136bfd44deb2eebf32e353 Mon Sep 17 00:00:00 2001 From: sherry0429 Date: Mon, 14 Feb 2022 14:39:45 +0800 Subject: [PATCH 1/4] Update __init__.py fix bug when for i in range(0, 100): toaster.show_toast(...., duration=300) time.sleep(1) and program create window instance many times and remove destroy window after toast show. now window will be destroy after this: toast.destroy() or with CustomToastNotifier() as toaster: toaster.... text codes: import time count = 0 with CustomToastNotifier() as toaster: while True: time.sleep(3) toaster.show_toast("Hello World!!!", "Python is 10 seconds awsm!", icon_path="custom.ico", duration=100) count += 1 if count >= 5: break --- win10toast/__init__.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/win10toast/__init__.py b/win10toast/__init__.py index 8ed1bc5..54b54b1 100644 --- a/win10toast/__init__.py +++ b/win10toast/__init__.py @@ -57,6 +57,8 @@ class ToastNotifier(object): def __init__(self): """Initialize.""" + self.hwnd = None + self.nid = None self._thread = None def _show_toast(self, title, msg, @@ -80,10 +82,11 @@ def _show_toast(self, title, msg, except: pass #not sure of this style = WS_OVERLAPPED | WS_SYSMENU - self.hwnd = CreateWindow(self.classAtom, "Taskbar", style, - 0, 0, CW_USEDEFAULT, - CW_USEDEFAULT, - 0, 0, self.hinst, None) + if self.hwnd is None: + self.hwnd = CreateWindow(self.classAtom, "Taskbar", style, + 0, 0, CW_USEDEFAULT, + CW_USEDEFAULT, + 0, 0, self.hinst, None) UpdateWindow(self.hwnd) # icon @@ -102,16 +105,13 @@ def _show_toast(self, title, msg, # Taskbar icon flags = NIF_ICON | NIF_MESSAGE | NIF_TIP - nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "Tooltip") - Shell_NotifyIcon(NIM_ADD, nid) + if self.nid is None: + self.nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "Tooltip") + Shell_NotifyIcon(NIM_ADD, self.nid) Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, 0, NIF_INFO, WM_USER + 20, hicon, "Balloon Tooltip", msg, 200, title)) - # take a rest then destroy - sleep(duration) - DestroyWindow(self.hwnd) - UnregisterClass(self.wc.lpszClassName, None) return None def show_toast(self, title="Notification", msg="Here comes the message", @@ -140,6 +140,16 @@ def notification_active(self): # We have an active notification, let is finish we don't spam them return True return False + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.destroy() + + def destroy(self): + DestroyWindow(self.hwnd) + UnregisterClass(self.wc.lpszClassName, None) def on_destroy(self, hwnd, msg, wparam, lparam): """Clean after notification ended. From 9b89472dbc356d3a2a5ce3d850c9a6cdea14bca5 Mon Sep 17 00:00:00 2001 From: sherry0429 Date: Wed, 16 Feb 2022 10:57:36 +0800 Subject: [PATCH 2/4] Update __init__.py fix bug when run many times, LoadImage function will raise OOM exceptions --- win10toast/__init__.py | 72 +++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/win10toast/__init__.py b/win10toast/__init__.py index 54b54b1..cd6a22e 100644 --- a/win10toast/__init__.py +++ b/win10toast/__init__.py @@ -44,6 +44,7 @@ from win32gui import UpdateWindow from win32gui import WNDCLASS + # ############################################################################ # ########### Classes ############## # ################################## @@ -51,7 +52,6 @@ class ToastNotifier(object): """Create a Windows 10 toast notification. - from: https://github.com/jithurjacob/Windows-10-Toast-Notifications """ @@ -60,11 +60,12 @@ def __init__(self): self.hwnd = None self.nid = None self._thread = None + self.wc = None + self.hicon = None def _show_toast(self, title, msg, icon_path, duration): """Notification settings. - :title: notification title :msg: notification message :icon_path: path to the .ico file to custom notification @@ -73,51 +74,52 @@ def _show_toast(self, title, msg, message_map = {WM_DESTROY: self.on_destroy, } # Register the window class. - self.wc = WNDCLASS() - self.hinst = self.wc.hInstance = GetModuleHandle(None) - self.wc.lpszClassName = str("PythonTaskbar") # must be a string - self.wc.lpfnWndProc = message_map # could also specify a wndproc. - try: - self.classAtom = RegisterClass(self.wc) - except: - pass #not sure of this - style = WS_OVERLAPPED | WS_SYSMENU - if self.hwnd is None: - self.hwnd = CreateWindow(self.classAtom, "Taskbar", style, - 0, 0, CW_USEDEFAULT, - CW_USEDEFAULT, - 0, 0, self.hinst, None) + if self.wc is None: + self.wc = WNDCLASS() + self.hinst = self.wc.hInstance = GetModuleHandle(None) + self.wc.lpszClassName = str("PythonTaskbar") # must be a string + self.wc.lpfnWndProc = message_map # could also specify a wndproc. + try: + self.classAtom = RegisterClass(self.wc) + except: + pass # not sure of this + style = WS_OVERLAPPED | WS_SYSMENU + if self.hwnd is None: + self.hwnd = CreateWindow(self.classAtom, "Taskbar", style, + 0, 0, CW_USEDEFAULT, + CW_USEDEFAULT, + 0, 0, self.hinst, None) UpdateWindow(self.hwnd) # icon - if icon_path is not None: - icon_path = path.realpath(icon_path) - else: - icon_path = resource_filename(Requirement.parse("win10toast"), "win10toast/data/python.ico") - icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE - try: - hicon = LoadImage(self.hinst, icon_path, - IMAGE_ICON, 0, 0, icon_flags) - except Exception as e: - logging.error("Some trouble with the icon ({}): {}" - .format(icon_path, e)) - hicon = LoadIcon(0, IDI_APPLICATION) + if self.hicon is None: + if icon_path is not None: + icon_path = path.realpath(icon_path) + else: + icon_path = resource_filename(Requirement.parse("win10toast"), "win10toast/data/python.ico") + icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE + try: + self.hicon = LoadImage(self.hinst, icon_path, + IMAGE_ICON, 0, 0, icon_flags) + except Exception as e: + logging.error("Some trouble with the icon ({}): {}" + .format(icon_path, e)) + self.hicon = LoadIcon(0, IDI_APPLICATION) # Taskbar icon flags = NIF_ICON | NIF_MESSAGE | NIF_TIP if self.nid is None: - self.nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "Tooltip") + self.nid = (self.hwnd, 0, flags, WM_USER + 20, self.hicon, "Tooltip") Shell_NotifyIcon(NIM_ADD, self.nid) Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, 0, NIF_INFO, WM_USER + 20, - hicon, "Balloon Tooltip", msg, 200, + self.hicon, "Balloon Tooltip", msg, 200, title)) return None def show_toast(self, title="Notification", msg="Here comes the message", - icon_path=None, duration=5, threaded=False): + icon_path=None, duration=5, threaded=False): """Notification settings. - :title: notification title :msg: notification message :icon_path: path to the .ico file to custom notification @@ -140,20 +142,19 @@ def notification_active(self): # We have an active notification, let is finish we don't spam them return True return False - + def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.destroy() - + def destroy(self): DestroyWindow(self.hwnd) UnregisterClass(self.wc.lpszClassName, None) def on_destroy(self, hwnd, msg, wparam, lparam): """Clean after notification ended. - :hwnd: :msg: :wparam: @@ -164,4 +165,3 @@ def on_destroy(self, hwnd, msg, wparam, lparam): PostQuitMessage(0) return None - From e89b3432242b9e218929ecde08942fc55df81715 Mon Sep 17 00:00:00 2001 From: sherry0429 Date: Wed, 16 Feb 2022 11:01:56 +0800 Subject: [PATCH 3/4] Update __main__.py 1. remove duration params, now destory control by 'destroy' method or auto destory after with block. 2. add with block usage examples --- win10toast/__main__.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/win10toast/__main__.py b/win10toast/__main__.py index a962b7a..01cd6ff 100644 --- a/win10toast/__main__.py +++ b/win10toast/__main__.py @@ -5,18 +5,26 @@ # ###### Stand alone program ######## # ################################### if __name__ == "__main__": - # Example + # Example simple use toaster = ToastNotifier() toaster.show_toast( "Hello World!!!", - "Python is 10 seconds awsm!", - duration=10) + "Python is 10 seconds awsm!") + toaster.destroy() + + # Example with case + with toaster: + toaster.show_toast( + "Hello World!!!", + "Python is 10 seconds awsm!") + + # Example with threaded toaster.show_toast( "Example two", "This notification is in it's own thread!", icon_path=None, - duration=5, threaded=True ) # Wait for threaded notification to finish while toaster.notification_active(): time.sleep(0.1) + toaster.destroy() From f486aeaf746f768f0c913ddb27bba0c37d227b2d Mon Sep 17 00:00:00 2001 From: sherry0429 Date: Wed, 16 Feb 2022 11:03:10 +0800 Subject: [PATCH 4/4] Update __init__.py fix bug when call 'destory', and then call 'show_toast', toast now show correctly --- win10toast/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/win10toast/__init__.py b/win10toast/__init__.py index cd6a22e..5b0307f 100644 --- a/win10toast/__init__.py +++ b/win10toast/__init__.py @@ -152,6 +152,11 @@ def __exit__(self, exc_type, exc_val, exc_tb): def destroy(self): DestroyWindow(self.hwnd) UnregisterClass(self.wc.lpszClassName, None) + self.hwnd = None + self.nid = None + self._thread = None + self.wc = None + self.hicon = None def on_destroy(self, hwnd, msg, wparam, lparam): """Clean after notification ended.