#!/usr/bin/env python # Module : trayicon.py # Synopsis : Windows System tray icon. # Programmer : Takayuki Shimizukawa # Date : 18 November 2008 # Python ver : 2.4 tested. # win32 ver : # Notes : Based on Simon Brunning (simon@brunningonline.net)'s # SysTrayIcon.py # Notes : Based on (i.e. ripped off from) Mark Hammond's # win32gui_taskbar.py and win32gui_menu.py demos from PyWin32 import logging logger = logging.getLogger('trayicon') from ctypes import windll from contribs.SysTrayIcon import win32api, win32con, win32gui, win32gui_struct from contribs.SysTrayIcon import SysTrayIcon as SysTrayIconBase import log # for logger setup. import utils NIIF_NONE = win32gui.NIIF_NONE NIIF_INFO = win32gui.NIIF_INFO NIIF_WARNING = win32gui.NIIF_WARNING NIIF_ERROR = win32gui.NIIF_ERROR class SysTrayIcon(SysTrayIconBase): def __init__(self, icon, hover_text="trayicon", menu_options=(), window_class_name=None,): self.icon = icon self.hover_text = hover_text self.on_quit = None self.set_menus(menu_options) self.window_class_name = window_class_name or "SysTrayIconPy" # init message self.MSG_TASKBAR_CREATED = win32gui.RegisterWindowMessage("TaskbarCreated") message_map = {self.MSG_TASKBAR_CREATED: self.restart, win32con.WM_DESTROY: self.destroy, win32con.WM_COMMAND: self.command, win32con.WM_USER+20 : self.notify,} self._message_map = message_map # Register the Window class. window_class = win32gui.WNDCLASS() hinst = window_class.hInstance = win32gui.GetModuleHandle(None) window_class.lpszClassName = self.window_class_name window_class.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW window_class.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW) window_class.hbrBackground = win32con.COLOR_WINDOW window_class.lpfnWndProc = self.message_proc classAtom = win32gui.RegisterClass(window_class) # Create the Window. style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU self.hwnd = win32gui.CreateWindow(classAtom, self.window_class_name, style, 0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, 0, 0, hinst, None) win32gui.UpdateWindow(self.hwnd) self.notify_id = None self.refresh_icon() def mainloop(self): logger.debug("start mainloop.") win32gui.PumpMessages() logger.debug("exit mainloop.") def message_proc(self, hwnd, msg, wparam, lparam): logger.debug('msg: %s', str(utils.WM_MAP.get(msg, msg))) if msg in self._message_map: return self._message_map[msg](self, msg, wparam, lparam) return win32gui.DefWindowProc(hwnd, msg, wparam, lparam) def notify(self, hwnd, msg, wparam, lparam): logger.debug('Notify msg: %s', str(utils.WM_MAP.get(lparam, lparam))) if lparam==win32con.WM_RBUTTONUP: self.show_menu() elif lparam in self._message_map: return self._message_map[lparam](self, lparam, wparam, 0) return True def set_menus(self, menu_options): menu_options = menu_options + (('Quit', None, self.QUIT),) self._next_action_id = self.FIRST_ID self.menu_actions_by_id = set() self.menu_options = self._add_ids_to_menu_options(list(menu_options)) self.menu_actions_by_id = dict(self.menu_actions_by_id) del self._next_action_id self.default_menu_index = 0 def get_menus(self): return self.menu_options def show_balloon(self, title, body_text, balloon_type=NIIF_INFO, timeout=1000): """show TrayIcon balloon. (only Win2K or later)""" # TODO: check os version. Win2K or later. nid = utils.PyNOTIFYICONDATA() nid.hWnd = self.hwnd nid.uFlags = win32gui.NIF_INFO nid.dwInfoFlags = balloon_type nid.szInfo = body_text[:255] nid.szInfoTitle = title # Call the Windows function, not the wrapped one Shell_NotifyIcon = windll.shell32.Shell_NotifyIconA Shell_NotifyIcon(win32gui.NIM_MODIFY, nid.pack()) def set_hovertext(self, text): """Update TrayIcon hover text.""" nid = (self.hwnd, 0, win32gui.NIF_TIP, 0, 0, text) win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, nid) def set_icon(self, filepath, *args, **kw): """Update TrayIcon ICON image.""" self.icon = filepath self.refresh_icon() def set_menu(self, *args, **kw): """Update TrayIcon context menu.""" logger.warn("Not implemented 'SysTrayIcon.set_menu'") def subscribe(self, msg, handler): """set event subscriber""" self._message_map[msg] = handler def unsubscribe(self, msg): if msg in self._message_map: del self._message_map[msg]