def __init__(self, icon_path, msg_q): """Initialize.""" self.visible = 0 self.log = [] self._thread = None self.msg_q = msg_q self.message_box = Mbox() message_map = { WM_DESTROY: self.onDestroy, WM_USER + 20: self.onTaskbarNotify, } wc = WNDCLASS() hinst = wc.hInstance = GetModuleHandle(None) icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE try: self.hicon = LoadImage(hinst, os.path.realpath(icon_path), IMAGE_ICON, 0, 0, icon_flags) except Exception as e: print(str(e)) self.hicon = LoadIcon(0, IDI_APPLICATION) wc.lpszClassName = str("Trustbase_notification") #lol wc.style = CS_VREDRAW | CS_HREDRAW #wc.hCursor = win32gui.LoadCursor( 0, win32con.IDC_ARROW ) wc.hbrBackground = COLOR_WINDOW wc.lpfnWndProc = message_map # could also specify a wndproc. classAtom = RegisterClass(wc) # Create the Window. style = WS_OVERLAPPED | WS_SYSMENU self.hwnd = CreateWindow( classAtom, "MITM_alert", style, \ 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, \ 0, 0, hinst, None) UpdateWindow(self.hwnd) self.notification_id = 1 self.show()
def _show_toast(self, title, msg, duration): message_map = { WM_DESTROY: self.on_destroy, } self.wc = WNDCLASS() self.hinst = self.wc.hInstance = GetModuleHandle(None) self.wc.lpszClassName = str("PythonTaskbar") self.wc.lpfnWndProc = message_map try: self.classAtom = RegisterClass(self.wc) except: pass style = WS_OVERLAPPED | WS_SYSMENU self.hwnd = CreateWindow(self.classAtom, "Taskbar", style, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, self.hinst, None) UpdateWindow(self.hwnd) hicon = LoadIcon(0, IDI_APPLICATION) flags = NIF_ICON | NIF_MESSAGE | NIF_TIP nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "Tooltip") Shell_NotifyIcon(NIM_ADD, nid) Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, 0, NIF_INFO, WM_USER + 20, hicon, "Balloon Tooltip", msg, 200, title)) sleep(duration) DestroyWindow(self.hwnd) UnregisterClass(self.wc.lpszClassName, None) return None
def __init__(self, logger, caption_key, message_key, current_locale, caption_args=(), message_args=()): self.logger = logger self.handler = CreateWindow(_wnd_class_instance, "Taskbar", WS_OVERLAPPED | WS_SYSMENU, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, _wnd_class.hInstance, None) self.icon_handler = LoadImage(_wnd_class.hInstance, 'icon.ico', IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE) UpdateWindow(self.handler) Shell_NotifyIcon( NIM_ADD, (self.handler, 0, NIF_ICON | NIF_MESSAGE | NIF_TIP, WM_USER + 20, self.icon_handler, 'Railway Station Simulator')) Shell_NotifyIcon( NIM_MODIFY, (self.handler, 0, NIF_INFO, WM_USER + 20, self.icon_handler, 'Railway Station Simulator', I18N_RESOURCES[message_key][current_locale].format(*message_args), 200, I18N_RESOURCES[caption_key][current_locale].format( *caption_args)))
def __init__(self): # List of all notifications. Each notification is represented by its ID (key) and thread (value). self._notifications = {} # Notification ID counter self._current_id = 0 self.logger = logging.getLogger("zroya") # Event mapping. Keys is event type and value callback function. self._event_map = { WM_DESTROY: self.quit, } # Create window self._window_class = WNDCLASS() self._window_class.lpszClassName = str("Notification") self._window_class.lpfnWndProc = self._eventLoop self._class = RegisterClass(self._window_class) self._handle = GetModuleHandle(None) self._window = CreateWindow(self._class, "Notification", WS_OVERLAPPED | WS_SYSMENU, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, self._handle, None ) # Draw window UpdateWindow(self._window) self.logger.info("NotificationCenter created.")
def show_toast(self, title="Notification", msg="Here comes the message", icon_path=None, duration=5): """Notification settings. :title: notification title :msg: notification message :icon_path: path to the .ico file to custom notification :duration: delay in seconds before notification self-destruction """ 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) 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) # Taskbar icon flags = NIF_ICON | NIF_MESSAGE | NIF_TIP nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "Tooltip") Shell_NotifyIcon(NIM_ADD, 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_windows_notification(title, msg, icon_path): global tool_window, tool_icon from win32api import GetModuleHandle from win32con import ( CW_USEDEFAULT, IMAGE_ICON, LR_DEFAULTSIZE, LR_LOADFROMFILE, WM_USER, WS_OVERLAPPED, WS_SYSMENU, ) from win32gui import ( NIF_ICON, NIF_INFO, NIF_MESSAGE, NIF_TIP, NIM_ADD, NIM_MODIFY, WNDCLASS, CreateWindow, LoadImage, RegisterClass, Shell_NotifyIcon, UpdateWindow, ) wc = WNDCLASS() hinst = wc.hInstance = GetModuleHandle(None) wc.lpszClassName = "FastFlix" if not tool_window: tool_window = CreateWindow( RegisterClass(wc), "Taskbar", WS_OVERLAPPED | WS_SYSMENU, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hinst, None, ) UpdateWindow(tool_window) icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE tool_icon = LoadImage(hinst, icon_path, IMAGE_ICON, 0, 0, icon_flags) flags = NIF_ICON | NIF_MESSAGE | NIF_TIP nid = (tool_window, 0, flags, WM_USER + 20, tool_icon, "FastFlix Notifications") Shell_NotifyIcon(NIM_ADD, nid) Shell_NotifyIcon(NIM_MODIFY, (tool_window, 0, NIF_INFO, WM_USER + 20, tool_icon, "Balloon Tooltip", msg, 200, title, 4))
def _show_toast(self, title, msg, icon_path, duration, callback_on_click): """Notification settings. :title: notification title :msg: notification message :icon_path: path to the .ico file to custom notification :duration: delay in seconds before notification self-destruction, None for no-self-destruction """ # Register the window class. self.wc = WNDCLASS() self.hinst = self.wc.hInstance = GetModuleHandle(None) self.wc.lpszClassName = str(self) # must be a string # callback_on_click = self._destroy_wrap(callback_on_click) self.wc.lpfnWndProc = self._decorator( self.wnd_proc, callback_on_click) # could instead specify simple mapping try: self.classAtom = RegisterClass(self.wc) except Exception as e: logging.error("Some trouble with classAtom ({})".format(e)) style = WS_OVERLAPPED | WS_SYSMENU 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) # Taskbar icon flags = NIF_ICON | NIF_MESSAGE | NIF_TIP nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "Tooltip") Shell_NotifyIcon(NIM_ADD, nid) Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, 0, NIF_INFO, WM_USER + 20, hicon, "Balloon Tooltip", msg, 200, title)) PumpMessages() # take a rest then destroy if duration is not None and duration > 0: sleep(duration) self._destroy_window() return None
def __init__(self, message, title): message_map = {win32con.WM_DESTROY: self.OnDestroy} # 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") Shell_NotifyIcon(NIM_ADD, nid) Shell_NotifyIcon( NIM_MODIFY, ( self.hwnd, 0, NIF_INFO, win32con.WM_USER + 20, hicon, "Balloon tooltip", message, 200, title, ), ) time.sleep(10) DestroyWindow(self.hwnd) UnregisterClass(classAtom, hinst)
def windows_notify(self, title, message, config): config = self.prepare_config(config) try: from win32api import GetModuleHandle, PostQuitMessage from win32con import (CW_USEDEFAULT, IMAGE_ICON, IDI_APPLICATION, LR_DEFAULTSIZE, LR_LOADFROMFILE, WM_DESTROY, WS_OVERLAPPED, WS_SYSMENU, WM_USER) from win32gui import (CreateWindow, DestroyWindow, LoadIcon, LoadImage, NIF_ICON, NIF_INFO, NIF_MESSAGE, NIF_TIP, NIM_ADD, NIM_DELETE, NIM_MODIFY, RegisterClass, Shell_NotifyIcon, UpdateWindow, WNDCLASS) except ImportError: raise DependencyError( __name__, 'pypiwin32', 'pywin32 module is required for desktop notifications on ' 'windows. You can install it with `pip install pypiwin32`') # Register the window class. wc = WNDCLASS() hinst = wc.hInstance = GetModuleHandle(None) wc.lpszClassName = "FlexGetTaskbar" if not self.windows_classAtom: self.windows_classAtom = RegisterClass(wc) style = WS_OVERLAPPED | WS_SYSMENU hwnd = CreateWindow(self.windows_classAtom, "Taskbar", style, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hinst, None) UpdateWindow(hwnd) hicon = LoadIcon(0, IDI_APPLICATION) # Hackily grab the icon from the webui if possible icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE try: import flexget.ui icon_path = os.path.join(flexget.ui.__path__[0], 'src', 'favicon.ico') hicon = LoadImage(hinst, icon_path, IMAGE_ICON, 0, 0, icon_flags) except Exception as e: log.debug('Error trying to get flexget icon from webui folder: %s', e) # Taskbar icon flags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO nid = (hwnd, 0, flags, WM_USER + 20, hicon, "FlexGet Notification", message, config['timeout'] * 1000, title) Shell_NotifyIcon(NIM_ADD, nid)
def balloon_tip(self, title, msg): style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU hwnd = CreateWindow(self.classAtom, "Taskbar", style, 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, 0, 0, self.hinst, None) UpdateWindow(hwnd) hicon = LoadIcon(0, win32con.IDI_APPLICATION) nid = (hwnd, 0, NIF_ICON | NIF_MESSAGE | NIF_TIP, win32con.WM_USER + 20, hicon, 'Tooltip') Shell_NotifyIcon(NIM_ADD, nid) nid = (hwnd, 0, NIF_INFO, win32con.WM_USER + 20, hicon, 'Balloon Tooltip', msg, 200, title, NIIF_INFO) Shell_NotifyIcon(NIM_MODIFY, nid) DestroyWindow(hwnd)
def show_toast(self, title="Notification", msg="Here comes the message", icon_path=None, duration=5): """Notification settings. :title: notification title :msg: notification message :icon_path: path to the .ico file to custom notification :duration: delay in seconds before notification self-destruction """ style = WS_OVERLAPPED | WS_SYSMENU 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) # Taskbar icon flags = NIF_ICON | NIF_MESSAGE | NIF_TIP nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "Tooltip") Shell_NotifyIcon(NIM_ADD, 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) return 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 :duration: delay in seconds before notification self-destruction """ 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.style = self.wc.cbWndExtra = 0 # 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 = CreateWindowEx(0, "PythonTaskbar", "555", style, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, self.hinst, None) UpdateWindow(self.hwnd) hicon = LoadIcon(0, IDI_APPLICATION) # Taskbar icon flags = NIF_ICON | NIF_TIP nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "66666") Shell_NotifyIcon(NIM_ADD, nid) Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, 0, NIF_INFO, WM_USER + 20, hicon, "289347293874", msg, 0, title)) # # take a rest then destroy sleep(duration) DestroyWindow(self.hwnd) UnregisterClass(self.wc.lpszClassName, None) return None
def _show_toast(self, title, msg, icon_path, duration, callback_on_click): '''Notification settings. :param title: notification title :param msg: notification message :param icon_path: path to the .ico file to custom notification :para mduration: delay in seconds before notification self-destruction, None for no-self-destruction ''' # Register the window class. self.wc = WNDCLASS() self.hinst = self.wc.hInstance = GetModuleHandle(None) # must be a string self.wc.lpszClassName = str(f"PythonTaskbar{title}") # could instead specify simple mapping self.wc.lpfnWndProc = self._decorator(self.wnd_proc, callback_on_click) try: self.classAtom = RegisterClass(self.wc) except Exception as e: #logging.error('Some trouble with classAtom (%s)', e) self.classAtom = self.wc style = WS_OVERLAPPED | WS_SYSMENU 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) converted = False if Image is not None and icon_path.split('.')[-1] != '.ico': img = Image.open(icon_path) new_name = icon_path.split('.')[:-1] + '.ico' img.save(new_name) icon_path = new_name converted = True else: icon_path = resource_filename(Requirement.parse('win10toast'), 'win10toast/data/python.ico') converted = False icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE try: hicon = LoadImage(self.hinst, icon_path, IMAGE_ICON, 0, 0, icon_flags) if Image and path.exists(new_name and converted): remove(new_name) except Exception as e: logging.error('Some trouble with the icon (%s): %s', icon_path, e) 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) Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, 0, NIF_INFO, WM_USER + 20, hicon, 'Balloon Tooltip', msg, 200, title)) PumpMessages() # take a rest then destroy if duration is not None: sleep(duration) DestroyWindow(self.hwnd) UnregisterClass(self.wc.lpszClassName, None) return None
def _show_toast(self): """Internal function called by Toast#display to show the toast""" self.active = True self.destroy_window = self.toast_data["kill_without_click"] # Register the window class self.toast_data["wnd_class"] = WNDCLASS() self.toast_data["hinst"] = self.toast_data[ "wnd_class"].hInstance = GetModuleHandle(None) self.toast_data[ "wnd_class"].lpszClassName = f"PythonTaskbar{uuid4().hex}" self.toast_data["wnd_class"].lpfnWndProc = self._decorator( self._wnd_proc, self.toast_data["callback_on_click"]) self.toast_data["class_atom"] = RegisterClass( self.toast_data["wnd_class"]) style = WS_OVERLAPPED | WS_SYSMENU self.toast_data["hwnd"] = CreateWindow( self.toast_data["class_atom"], self.toast_data["wnd_class"].lpszClassName, style, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, self.toast_data["hinst"], None, ) UpdateWindow(self.toast_data["hwnd"]) icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE hicon = LoadImage(self.toast_data["hinst"], self.toast_data['icon_path'], IMAGE_ICON, 0, 0, icon_flags) # Set the duration buff = create_unicode_buffer(10) SystemParametersInfoW(SPI_GETMESSAGEDURATION, 0, buff, 0) try: oldlength = int( buff.value.encode("unicode_escape").decode().replace( "\\", "0"), 16) except ValueError: oldlength = 5 # Default notification length SystemParametersInfoW(SPI_SETMESSAGEDURATION, 0, self.toast_data["duration"], SPIF_SENDCHANGE) # Taskbar icon flags = NIF_ICON | NIF_MESSAGE | NIF_TIP nid = ( self.toast_data["hwnd"], 0, flags, WM_USER + 20, hicon, self.toast_data["tooltip"], ) # Make it so that it won't replace another # notification with the same title and message title = self.toast_data["title"] + " " * randint( 0, 63 - len(self.toast_data["title"])) message = self.toast_data["msg"] + " " * randint( 0, 128 - len(self.toast_data["msg"])) # Add tray icon and queue message Shell_NotifyIcon(NIM_ADD, nid) Shell_NotifyIcon( NIM_MODIFY, ( self.toast_data["hwnd"], 0, NIF_INFO, WM_USER + 20, hicon, self.toast_data["tooltip"], message, 0, title, NIIF_NOSOUND if self.toast_data["sound_path"] else 0, ), ) # Play the custom sound if self.toast_data["sound_path"] is not None: sound_path = path.realpath(self.toast_data["sound_path"]) PlaySound(sound_path, SND_FILENAME) # Show the message PumpMessages() # Put the notification duration back to normal SystemParametersInfoW(SPI_SETMESSAGEDURATION, 0, oldlength, SPIF_SENDCHANGE) # Take a rest then destroy if not self.toast_data["keep_alive"] and self.destroy_window: self.destroy()
def __call__(self, summary, message="", timeout=2000, **kwargs): tip = kwargs.get("tip", "Balloon tooltip") # Register the Window class. wc = WNDCLASS() hinst = wc.hInstance = GetModuleHandle(None) wc.lpszClassName = "PythonTaskbar" wc.lpfnWndProc = self.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) # Icons managment 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 Exception: 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, tip, message, timeout, summary, ), ) # Destroy DestroyWindow(self.hwnd) classAtom = UnregisterClass(classAtom, hinst) return True
def _show_toast( self, title: str, msg: str = "No msg", icon_path: Path = None, duration: float = None, sound_path=None, callback_on_click: callable = None, tooltip: Optional[str] = None, ) -> None: """Notification settings. :param title: notification title :param msg: notification message :param icon_path: path to the .ico file to custom notification :param duration: delay in seconds before notification self-destruction, None for no-self-destruction :param sound_path: path to the .wav file to custom notification """ self.duration = duration def callback(): """ """ self.duration = 0 if callback_on_click is not None: callback_on_click() if tooltip is None: tooltip = PROJECT_NAME # Register the window class. self.window_class = WNDCLASS() self.instance_handle = self.window_class.hInstance = GetModuleHandle( None) self.window_class.lpszClassName = f"{PROJECT_NAME}-{title}" # must be a string self.window_class.lpfnWndProc = self._decorator( self.wnd_proc, callback) # could instead specify simple mapping try: self.classAtom = RegisterClass(self.window_class) except Exception as e: logging.error("Some trouble with classAtom (%s)", e) style = WS_OVERLAPPED | WS_SYSMENU button_style = WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON # TODO: Unused for now self.window_handle = CreateWindow( self.classAtom, "Taskbar", style, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, self.instance_handle, None, ) UpdateWindow(self.window_handle) # icon new_name = "" if icon_path is not None: icon_path = path.realpath(icon_path) converted = False if Image is not None and icon_path.split(".")[-1] != ".ico": img = Image.open(icon_path) new_name = f'{str(icon_path.split(".")[:-1])}.ico' img.save(new_name) icon_path = new_name converted = True else: icon_path = resource_filename( Requirement.parse(PROJECT_NAME), str(Path(PROJECT_NAME) / "data" / "python.ico"), ) converted = False try: hicon = LoadImage( self.instance_handle, icon_path, IMAGE_ICON, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE, ) if Image and path.exists(new_name and converted): remove(new_name) except Exception as e: logging.error("Some trouble with the icon (%s): %s", icon_path, e) hicon = LoadIcon(0, IDI_APPLICATION) # Set the duration """ buff = create_unicode_buffer(10) windll.user32.SystemParametersInfoW(SPI_GETMESSAGEDURATION, 0, buff, 0) try: oldlength = int(buff.value.encode("unicode_escape").decode().replace("\\", "0"), 16) except ValueError: oldlength = 5 # Default notification length duration_output = windll.user32.SystemParametersInfoW(SPI_SETMESSAGEDURATION, 0, self.duration, SPIF_SENDCHANGE) windll.user32.SystemParametersInfoW(SPI_GETMESSAGEDURATION, 0, buff, 0) duration_error = False try: int(buff.value.encode("unicode_escape").decode().replace("\\", "0"), 16) except ValueError: duration_error = True if duration_output == 0 or self.duration > 255 or duration_error: windll.user32.SystemParametersInfoW(SPI_SETMESSAGEDURATION, 0, oldlength, SPIF_SENDCHANGE) self.active = False raise RuntimeError(f"Some trouble with the duration ({self.duration})" ": Invalid duration length") """ title += " " * randint(0, 63 - len(title)) msg += " " * randint(0, 128 - len(msg)) Shell_NotifyIcon( NIM_ADD, ( self.window_handle, 0, NIF_ICON | NIF_MESSAGE | NIF_TIP, WM_USER + 20, hicon, tooltip, ), ) Shell_NotifyIcon( NIM_MODIFY, ( self.window_handle, 0, NIF_INFO, WM_USER + 20, hicon, tooltip, msg, 200, title, 0 if sound_path is None else NIIF_NOSOUND, ), ) if sound_path is not None: # play the custom sound sound_path = path.realpath(sound_path) if not path.exists(sound_path): logging.error( f"Some trouble with the sound file ({sound_path}): [Errno 2] No such file" ) try: PlaySound(sound_path, SND_FILENAME) except Exception as e: logging.error( f"Some trouble with the sound file ({sound_path}): {e}") PumpMessages() """ # Put the notification duration back to normal SystemParametersInfoW(SPI_SETMESSAGEDURATION, 0, oldlength, SPIF_SENDCHANGE) """ if duration is not None: # take a rest then destroy # sleep(duration) while self.duration > 0: sleep(0.1) self.duration -= 0.1 DestroyWindow(self.window_handle) UnregisterClass(self.window_class.lpszClassName, self.instance_handle) try: # Sometimes the try icon sticks around until you click it - this should stop that Shell_NotifyIcon(NIM_DELETE, (self.window_handle, 0)) except WinTypesException: pass self.active = False
def _show_toast(self, title, msg, icon_path, duration, cbFunc=None, cbArgs=None): """Notification settings. :title: notification title :msg: notification message :icon_path: path to the .ico file to custom notification :duration: delay in seconds before notification self-destruction """ self.user_cbFunc = cbFunc self.user_cbArgs = cbArgs # Register the window class. self.wc = WNDCLASS() self.hinst = self.wc.hInstance = GetModuleHandle(None) self.wc.lpszClassName = str("PythonTaskbar" + os.urandom(24).hex()) # must be a string self.wc.lpfnWndProc = self._decorator( self.wnd_proc, self.user_cbFunc) # 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) UpdateWindow(self.hwnd) # icon hicon = None if icon_path is not None: icon_path = path.realpath(icon_path) icon_flags = LR_LOADFROMFILE 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) # Taskbar icon flags = NIF_ICON | NIF_MESSAGE | NIF_TIP nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, "Tooltip") Shell_NotifyIcon(NIM_ADD, nid) Shell_NotifyIcon(NIM_MODIFY, (self.hwnd, 0, NIF_INFO, WM_USER + 20, hicon, "Balloon Tooltip", msg, 200, title)) PumpMessages() # take a rest then destroy sleep(duration) DestroyWindow(self.hwnd) UnregisterClass(self.wc.lpszClassName, None) return None
def _show_toast(self, title, msg, icon_path, delay, sound_path, tooltip, duration, callback_on_click, kill_without_click): """Notification settings. :title: notification title :msg: notification message :icon_path: path to the .ico file to custom notification :delay: delay in seconds before notification self-destruction, None for no-self-destruction :sound_path: path to the .wav file to custom notification :duration: how long the notification stays on the screen in seconds :callback_on_click: function to run on click :kill_without_click: Kill the tray icon after the notification goes away, even if it wasn't clicked """ self.delay = delay self.destroy_window = kill_without_click # Register the window class. self.wc = WNDCLASS() self.hinst = self.wc.hInstance = GetModuleHandle(None) self.wc.lpszClassName = str( f"PythonTaskbar - {uuid4().hex}") # must be a string self.wc.lpfnWndProc = self._decorator( self.wnd_proc, callback_on_click) # could instead specify simple mapping try: self.classAtom = RegisterClass(self.wc) except Exception as e: raise type(e)(f"Some trouble with classAtom:\n{e}") from None style = WS_OVERLAPPED | WS_SYSMENU self.hwnd = CreateWindow(self.classAtom, "Python 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: raise type(e)( f"Some trouble with the icon ({icon_path}):\n{e}") from None hicon = LoadIcon(0, IDI_APPLICATION) # set the duration buff = create_unicode_buffer(10) SystemParametersInfoW(SPI_GETMESSAGEDURATION, 0, buff, 0) try: oldlength = int( buff.value.encode('unicode_escape').decode().replace( "\\", "0"), 16) except ValueError: oldlength = 5 # default notification length durationOutput = SystemParametersInfoW(SPI_SETMESSAGEDURATION, 0, duration, SPIF_SENDCHANGE) SystemParametersInfoW(SPI_GETMESSAGEDURATION, 0, buff, 0) durationError = False try: int( buff.value.encode('unicode_escape').decode().replace( "\\", "0"), 16) except ValueError: durationError = True if durationOutput == 0 or duration > 255 or durationError: SystemParametersInfoW(SPI_SETMESSAGEDURATION, 0, oldlength, SPIF_SENDCHANGE) raise RuntimeError( f"Some trouble with the duration ({duration}): Invalid duration length" ) # Taskbar icon flags = NIF_ICON | NIF_MESSAGE | NIF_TIP nid = (self.hwnd, 0, flags, WM_USER + 20, hicon, tooltip) Shell_NotifyIcon(NIM_ADD, nid) Shell_NotifyIcon( NIM_MODIFY, (self.hwnd, 0, NIF_INFO, WM_USER + 20, hicon, tooltip, msg, 0, title, 0 if sound_path == None else NIIF_NOSOUND)) # play the custom sound if sound_path is not None: sound_path = path.realpath(sound_path) if not path.exists(sound_path): raise IOError( "Some trouble with the sound file ({sound_path}): [Errno 2] No such file" ) try: PlaySound(sound_path, SND_FILENAME) except Exception as e: raise type( e)(f"Some trouble with the sound file ({sound_path}): {e}" ) from None PumpMessages() # put the notification duration back to normal SystemParametersInfoW(SPI_SETMESSAGEDURATION, 0, oldlength, SPIF_SENDCHANGE) # take a rest then destroy if self.delay is not None and self.destroy_window: while self.delay > 0: sleep(0.1) self.delay -= 0.1 DestroyWindow(self.hwnd) UnregisterClass(self.wc.lpszClassName, self.hinst) try: # sometimes the tray icon sticks around until you click it - this should stop it self.remove_window(self.hwnd) except WinTypesException: pass return