diff --git a/win10toast/__init__.py b/win10toast/__init__.py index 8ed1bc5..5b0307f 100644 --- a/win10toast/__init__.py +++ b/win10toast/__init__.py @@ -44,6 +44,7 @@ from win32gui import UpdateWindow from win32gui import WNDCLASS + # ############################################################################ # ########### Classes ############## # ################################## @@ -51,18 +52,20 @@ class ToastNotifier(object): """Create a Windows 10 toast notification. - from: https://github.com/jithurjacob/Windows-10-Toast-Notifications """ def __init__(self): """Initialize.""" + 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 @@ -71,53 +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 - 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 - 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, 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)) - # 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", - 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 @@ -141,9 +143,23 @@ def notification_active(self): 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) + 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. - :hwnd: :msg: :wparam: @@ -154,4 +170,3 @@ def on_destroy(self, hwnd, msg, wparam, lparam): PostQuitMessage(0) return None - 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()