Advertisement
Guest User

Untitled

a guest
May 29th, 2016
80
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.19 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # Module : SysTrayIcon.py
  3. # Synopsis : Windows System tray icon.
  4. # Programmer : Simon Brunning - simon@brunningonline.net
  5. # Date : 11 April 2005
  6. # Notes : Based on (i.e. ripped off from) Mark Hammond's
  7. # win32gui_taskbar.py and win32gui_menu.py demos from PyWin32
  8. '''TODO
  9.  
  10. For now, the demo at the bottom shows how to use it...'''
  11.  
  12. import os
  13. import win32api
  14.  
  15. import win32con
  16. import win32gui_struct
  17.  
  18. try:
  19. import winxpgui as win32gui
  20. except ImportError:
  21. import win32gui
  22.  
  23.  
  24. class SysTrayIcon(object):
  25. '''TODO'''
  26. QUIT = 'QUIT'
  27. SPECIAL_ACTIONS = [QUIT]
  28.  
  29. FIRST_ID = 1023
  30.  
  31. def __init__(self,
  32. icon,
  33. hover_text,
  34. menu_options,
  35. on_quit=None,
  36. default_menu_index=None,
  37. window_class_name=None, ):
  38.  
  39. self.icon = icon
  40. self.hover_text = hover_text
  41. self.on_quit = on_quit
  42.  
  43. menu_options = menu_options + (('Quit', None, self.QUIT),)
  44. self._next_action_id = self.FIRST_ID
  45. self.menu_actions_by_id = set()
  46. self.menu_options = self._add_ids_to_menu_options(list(menu_options))
  47. self.menu_actions_by_id = dict(self.menu_actions_by_id)
  48. del self._next_action_id
  49.  
  50. self.default_menu_index = (default_menu_index or 0)
  51. self.window_class_name = window_class_name or "SysTrayIconPy"
  52.  
  53. message_map = {win32gui.RegisterWindowMessage("TaskbarCreated"): self.restart,
  54. win32con.WM_DESTROY: self.destroy,
  55. win32con.WM_COMMAND: self.command,
  56. win32con.WM_USER + 20: self.notify,}
  57. # Register the Window class.
  58. window_class = win32gui.WNDCLASS()
  59. hinst = window_class.hInstance = win32gui.GetModuleHandle(None)
  60. window_class.lpszClassName = self.window_class_name
  61. window_class.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW;
  62. window_class.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW)
  63. window_class.hbrBackground = win32con.COLOR_WINDOW
  64. window_class.lpfnWndProc = message_map # could also specify a wndproc.
  65. classAtom = win32gui.RegisterClass(window_class)
  66. # Create the Window.
  67. style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
  68. self.hwnd = win32gui.CreateWindow(classAtom,
  69. self.window_class_name,
  70. style,
  71. 0,
  72. 0,
  73. win32con.CW_USEDEFAULT,
  74. win32con.CW_USEDEFAULT,
  75. 0,
  76. 0,
  77. hinst,
  78. None)
  79. win32gui.UpdateWindow(self.hwnd)
  80. self.notify_id = None
  81. self.refresh_icon()
  82.  
  83. win32gui.PumpMessages()
  84.  
  85. def _add_ids_to_menu_options(self, menu_options):
  86. result = []
  87. for menu_option in menu_options:
  88. option_text, option_icon, option_action = menu_option
  89. if callable(option_action) or option_action in self.SPECIAL_ACTIONS:
  90. self.menu_actions_by_id.add((self._next_action_id, option_action))
  91. result.append(menu_option + (self._next_action_id,))
  92. elif non_string_iterable(option_action):
  93. result.append((option_text,
  94. option_icon,
  95. self._add_ids_to_menu_options(option_action),
  96. self._next_action_id))
  97. else:
  98. print 'Unknown item', option_text, option_icon, option_action
  99. self._next_action_id += 1
  100. return result
  101.  
  102. def refresh_icon(self):
  103. # Try and find a custom icon
  104. hinst = win32gui.GetModuleHandle(None)
  105. if os.path.isfile(self.icon):
  106. icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
  107. self.hicon = win32gui.LoadImage(hinst,
  108. self.icon,
  109. win32con.IMAGE_ICON,
  110. 0,
  111. 0,
  112. icon_flags)
  113. else:
  114. print "Can't find icon file - using default."
  115. self.hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
  116.  
  117. if self.notify_id:
  118. message = win32gui.NIM_MODIFY
  119. else:
  120. message = win32gui.NIM_ADD
  121. self.notify_id = (self.hwnd,
  122. 0,
  123. win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP,
  124. win32con.WM_USER + 20,
  125. self.hicon,
  126. self.hover_text)
  127. win32gui.Shell_NotifyIcon(message, self.notify_id)
  128.  
  129. def restart(self, hwnd, msg, wparam, lparam):
  130. self.refresh_icon()
  131.  
  132. def destroy(self, hwnd, msg, wparam, lparam):
  133. if self.on_quit: self.on_quit(self)
  134. nid = (self.hwnd, 0)
  135. win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
  136. win32gui.PostQuitMessage(0) # Terminate the app.
  137.  
  138. def notify(self, hwnd, msg, wparam, lparam):
  139. if lparam == win32con.WM_LBUTTONDBLCLK:
  140. self.execute_menu_option(self.default_menu_index + self.FIRST_ID)
  141. elif lparam == win32con.WM_RBUTTONUP:
  142. self.show_menu()
  143. elif lparam == win32con.WM_LBUTTONUP:
  144. pass
  145. return True
  146.  
  147. def show_menu(self):
  148. menu = win32gui.CreatePopupMenu()
  149. self.create_menu(menu, self.menu_options)
  150. # win32gui.SetMenuDefaultItem(menu, 1000, 0)
  151.  
  152. pos = win32gui.GetCursorPos()
  153. # See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/menus_0hdi.asp
  154. win32gui.SetForegroundWindow(self.hwnd)
  155. win32gui.TrackPopupMenu(menu,
  156. win32con.TPM_LEFTALIGN,
  157. pos[0],
  158. pos[1],
  159. 0,
  160. self.hwnd,
  161. None)
  162. win32gui.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)
  163.  
  164. def create_menu(self, menu, menu_options):
  165. for option_text, option_icon, option_action, option_id in menu_options[::-1]:
  166. if option_icon:
  167. option_icon = self.prep_menu_icon(option_icon)
  168.  
  169. if option_id in self.menu_actions_by_id:
  170. item, extras = win32gui_struct.PackMENUITEMINFO(text=option_text,
  171. hbmpItem=option_icon,
  172. wID=option_id)
  173. win32gui.InsertMenuItem(menu, 0, 1, item)
  174. else:
  175. submenu = win32gui.CreatePopupMenu()
  176. self.create_menu(submenu, option_action)
  177. item, extras = win32gui_struct.PackMENUITEMINFO(text=option_text,
  178. hbmpItem=option_icon,
  179. hSubMenu=submenu)
  180. win32gui.InsertMenuItem(menu, 0, 1, item)
  181.  
  182. def prep_menu_icon(self, icon):
  183. # First load the icon.
  184. ico_x = win32api.GetSystemMetrics(win32con.SM_CXSMICON)
  185. ico_y = win32api.GetSystemMetrics(win32con.SM_CYSMICON)
  186. hicon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON, ico_x, ico_y, win32con.LR_LOADFROMFILE)
  187.  
  188. hdcBitmap = win32gui.CreateCompatibleDC(0)
  189. hdcScreen = win32gui.GetDC(0)
  190. hbm = win32gui.CreateCompatibleBitmap(hdcScreen, ico_x, ico_y)
  191. hbmOld = win32gui.SelectObject(hdcBitmap, hbm)
  192. # Fill the background.
  193. brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU)
  194. win32gui.FillRect(hdcBitmap, (0, 0, 16, 16), brush)
  195. # unclear if brush needs to be feed. Best clue I can find is:
  196. # "GetSysColorBrush returns a cached brush instead of allocating a new
  197. # one." - implies no DeleteObject
  198. # draw the icon
  199. win32gui.DrawIconEx(hdcBitmap, 0, 0, hicon, ico_x, ico_y, 0, 0, win32con.DI_NORMAL)
  200. win32gui.SelectObject(hdcBitmap, hbmOld)
  201. win32gui.DeleteDC(hdcBitmap)
  202.  
  203. return hbm
  204.  
  205. def command(self, hwnd, msg, wparam, lparam):
  206. id = win32gui.LOWORD(wparam)
  207. self.execute_menu_option(id)
  208.  
  209. def execute_menu_option(self, id):
  210. menu_action = self.menu_actions_by_id[id]
  211. if menu_action == self.QUIT:
  212. win32gui.DestroyWindow(self.hwnd)
  213. else:
  214. menu_action(self)
  215.  
  216. def show_notification(self, title, msg):
  217. """
  218. show the shell notification with the title and message
  219.  
  220. :param title:
  221. :param msg:
  222. :return:
  223. """
  224. win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY,
  225. (self.hwnd, 0, win32gui.NIF_INFO, win32con.WM_USER + 20,
  226. self.hicon, "", msg, 200, title))
  227.  
  228. def non_string_iterable(obj):
  229. try:
  230. iter(obj)
  231. except TypeError:
  232. return False
  233. else:
  234. return not isinstance(obj, basestring)
  235.  
  236.  
  237. # Minimal self test. You'll need a bunch of ICO files in the current working
  238. # directory in order for this to work...
  239. if __name__ == '__main__':
  240. import itertools, glob
  241.  
  242. icons = itertools.cycle(glob.glob('*.ico'))
  243. hover_text = "SysTrayIcon.py Demo"
  244.  
  245.  
  246. def hello(sysTrayIcon): print "Hello World."
  247.  
  248.  
  249. def simon(sysTrayIcon): print "Hello Simon."
  250.  
  251.  
  252. def switch_icon(sysTrayIcon):
  253. sysTrayIcon.icon = icons.next()
  254. sysTrayIcon.refresh_icon()
  255.  
  256.  
  257.  
  258. menu_options = (('Say Hello', icons.next(), hello),
  259. ('Show balloon tip', None, lambda x: x.show_notification("title", "message")),
  260. ('A sub-menu', icons.next(), (('Say Hello to Simon', icons.next(), simon),
  261. ('Switch Icon', icons.next(), switch_icon),
  262. ))
  263. )
  264.  
  265.  
  266. def bye(sysTrayIcon): print 'Bye, then.'
  267.  
  268.  
  269. SysTrayIcon(icons.next(), hover_text, menu_options, on_quit=bye, default_menu_index=1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement