2016-05-29 28 views
1

Ich verwende derzeit eine leicht modifizierte Version der common example of the systemtray.Ereignisbehandlung der Ballonspitze mit win32gui

#!/usr/bin/env python 
# Module  : SysTrayIcon.py 
# Synopsis : Windows System tray icon. 
# Programmer : Simon Brunning - [email protected] 
# Date  : 11 April 2005 
# Notes  : Based on (i.e. ripped off from) Mark Hammond's 
#    win32gui_taskbar.py and win32gui_menu.py demos from PyWin32 
'''TODO 

For now, the demo at the bottom shows how to use it...''' 

import os 
import win32api 

import win32con 
import win32gui_struct 

try: 
    import winxpgui as win32gui 
except ImportError: 
    import win32gui 


class SysTrayIcon(object): 
    '''TODO''' 
    QUIT = 'QUIT' 
    SPECIAL_ACTIONS = [QUIT] 

    FIRST_ID = 1023 

    def __init__(self, 
       icon, 
       hover_text, 
       menu_options, 
       on_quit=None, 
       default_menu_index=None, 
       window_class_name=None,): 

     self.icon = icon 
     self.hover_text = hover_text 
     self.on_quit = on_quit 

     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 = (default_menu_index or 0) 
     self.window_class_name = window_class_name or "SysTrayIconPy" 

     message_map = {win32gui.RegisterWindowMessage("TaskbarCreated"): self.restart, 
         win32con.WM_DESTROY: self.destroy, 
         win32con.WM_COMMAND: self.command, 
         win32con.WM_USER + 20: self.notify,} 
     # 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 = message_map # could also specify a wndproc. 
     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() 

     win32gui.PumpMessages() 

    def _add_ids_to_menu_options(self, menu_options): 
     result = [] 
     for menu_option in menu_options: 
      option_text, option_icon, option_action = menu_option 
      if callable(option_action) or option_action in self.SPECIAL_ACTIONS: 
       self.menu_actions_by_id.add((self._next_action_id, option_action)) 
       result.append(menu_option + (self._next_action_id,)) 
      elif non_string_iterable(option_action): 
       result.append((option_text, 
           option_icon, 
           self._add_ids_to_menu_options(option_action), 
           self._next_action_id)) 
      else: 
       print 'Unknown item', option_text, option_icon, option_action 
      self._next_action_id += 1 
     return result 

    def refresh_icon(self): 
     # Try and find a custom icon 
     hinst = win32gui.GetModuleHandle(None) 
     if os.path.isfile(self.icon): 
      icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE 
      self.hicon = win32gui.LoadImage(hinst, 
             self.icon, 
             win32con.IMAGE_ICON, 
             0, 
             0, 
             icon_flags) 
     else: 
      print "Can't find icon file - using default." 
      self.hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION) 

     if self.notify_id: 
      message = win32gui.NIM_MODIFY 
     else: 
      message = win32gui.NIM_ADD 
     self.notify_id = (self.hwnd, 
          0, 
          win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, 
          win32con.WM_USER + 20, 
          self.hicon, 
          self.hover_text) 
     win32gui.Shell_NotifyIcon(message, self.notify_id) 

    def restart(self, hwnd, msg, wparam, lparam): 
     self.refresh_icon() 

    def destroy(self, hwnd, msg, wparam, lparam): 
     if self.on_quit: self.on_quit(self) 
     nid = (self.hwnd, 0) 
     win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid) 
     win32gui.PostQuitMessage(0) # Terminate the app. 

    def notify(self, hwnd, msg, wparam, lparam): 
     if lparam == win32con.WM_LBUTTONDBLCLK: 
      self.execute_menu_option(self.default_menu_index + self.FIRST_ID) 
     elif lparam == win32con.WM_RBUTTONUP: 
      self.show_menu() 
     elif lparam == win32con.WM_LBUTTONUP: 
      pass 
     return True 

    def show_menu(self): 
     menu = win32gui.CreatePopupMenu() 
     self.create_menu(menu, self.menu_options) 
     # win32gui.SetMenuDefaultItem(menu, 1000, 0) 

     pos = win32gui.GetCursorPos() 
     # See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/menus_0hdi.asp 
     win32gui.SetForegroundWindow(self.hwnd) 
     win32gui.TrackPopupMenu(menu, 
           win32con.TPM_LEFTALIGN, 
           pos[0], 
           pos[1], 
           0, 
           self.hwnd, 
           None) 
     win32gui.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0) 

    def create_menu(self, menu, menu_options): 
     for option_text, option_icon, option_action, option_id in menu_options[::-1]: 
      if option_icon: 
       option_icon = self.prep_menu_icon(option_icon) 

      if option_id in self.menu_actions_by_id: 
       item, extras = win32gui_struct.PackMENUITEMINFO(text=option_text, 
                   hbmpItem=option_icon, 
                   wID=option_id) 
       win32gui.InsertMenuItem(menu, 0, 1, item) 
      else: 
       submenu = win32gui.CreatePopupMenu() 
       self.create_menu(submenu, option_action) 
       item, extras = win32gui_struct.PackMENUITEMINFO(text=option_text, 
                   hbmpItem=option_icon, 
                   hSubMenu=submenu) 
       win32gui.InsertMenuItem(menu, 0, 1, item) 

    def prep_menu_icon(self, icon): 
     # First load the icon. 
     ico_x = win32api.GetSystemMetrics(win32con.SM_CXSMICON) 
     ico_y = win32api.GetSystemMetrics(win32con.SM_CYSMICON) 
     hicon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON, ico_x, ico_y, win32con.LR_LOADFROMFILE) 

     hdcBitmap = win32gui.CreateCompatibleDC(0) 
     hdcScreen = win32gui.GetDC(0) 
     hbm = win32gui.CreateCompatibleBitmap(hdcScreen, ico_x, ico_y) 
     hbmOld = win32gui.SelectObject(hdcBitmap, hbm) 
     # Fill the background. 
     brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU) 
     win32gui.FillRect(hdcBitmap, (0, 0, 16, 16), brush) 
     # unclear if brush needs to be feed. Best clue I can find is: 
     # "GetSysColorBrush returns a cached brush instead of allocating a new 
     # one." - implies no DeleteObject 
     # draw the icon 
     win32gui.DrawIconEx(hdcBitmap, 0, 0, hicon, ico_x, ico_y, 0, 0, win32con.DI_NORMAL) 
     win32gui.SelectObject(hdcBitmap, hbmOld) 
     win32gui.DeleteDC(hdcBitmap) 

     return hbm 

    def command(self, hwnd, msg, wparam, lparam): 
     id = win32gui.LOWORD(wparam) 
     self.execute_menu_option(id) 

    def execute_menu_option(self, id): 
     menu_action = self.menu_actions_by_id[id] 
     if menu_action == self.QUIT: 
      win32gui.DestroyWindow(self.hwnd) 
     else: 
      menu_action(self) 

    def show_notification(self, title, msg): 
     """ 
     show the shell notification with the title and message 

     :param title: 
     :param msg: 
     :return: 
     """ 
     win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, 
            (self.hwnd, 0, win32gui.NIF_INFO, win32con.WM_USER + 20, 
            self.hicon, "", msg, 200, title)) 

def non_string_iterable(obj): 
    try: 
     iter(obj) 
    except TypeError: 
     return False 
    else: 
     return not isinstance(obj, basestring) 


# Minimal self test. You'll need a bunch of ICO files in the current working 
# directory in order for this to work... 
if __name__ == '__main__': 
    import itertools, glob 

    icons = itertools.cycle(glob.glob('*.ico')) 
    hover_text = "SysTrayIcon.py Demo" 


    def hello(sysTrayIcon): print "Hello World." 


    def simon(sysTrayIcon): print "Hello Simon." 


    def switch_icon(sysTrayIcon): 
     sysTrayIcon.icon = icons.next() 
     sysTrayIcon.refresh_icon() 



    menu_options = (('Say Hello', icons.next(), hello), 
        ('Show balloon tip', None, lambda x: x.show_notification("title", "message")), 
        ('A sub-menu', icons.next(), (('Say Hello to Simon', icons.next(), simon), 
                ('Switch Icon', icons.next(), switch_icon), 
               )) 
        ) 


    def bye(sysTrayIcon): print 'Bye, then.' 


    SysTrayIcon(icons.next(), hover_text, menu_options, on_quit=bye, default_menu_index=1) 

Ich machte die hicon-Variable für die gesamte Klasse zugänglich und fügte die Funktion show_notification hinzu. Ich versuche gerade, eine Funktion aufzurufen, wenn auf die Benachrichtigung geklickt wird. Ich fand in der win32gui perl documentation (die im Wesentlichen die gleiche von der Funktionalität sein sollte), dass es bestimmte häufige Ereignisse wie Click oder MouseEvent gibt, aber bis jetzt konnte ich keine Möglichkeit finden, einen Ereignishandler zu dem Shell_NotifyIcon hinzuzufügen.

Ich habe die NOTIFYICONDATA structure from Microsoft geprüft und habe diesen interessanten Teil:

Wenn das uVersion Mitglied NOTIFYICON_VERSION_4 ist, Anwendungen weiterhin Benachrichtigungsereignisse in Form von anwendungsdefinierte Nachrichten über das Mitglied uCallbackMessage erhalten

Aber ich konnte keinen Weg finden, dies zu irgendeinem Gebrauch zu setzen. Schließlich habe ich versucht, einen Eventhandler mit win32com.client.DispatchWithEvents hinzuzufügen, aber ich versage hier mit dem Clsid bereits.

Antwort

0

Ich endete mit der versuchen, außer Block mit einem win32gui Fehler Ausnahme. Überall, wo ich win32gui anrief, wickelte ich den Ausnahmeblock win32gui.error ab. AFAIK, das ist die einzige Methode, um die Ausnahme zu erhalten.

try: win32gui.<something> except win32gui.error as error: logging.error(error)

Zusätzlich können Sie die Traceback-Bibliothek verwenden, um ein Stack-Trace zu erhalten. Das half mir, einen Großteil der Python-Wrapped-Version der Windows-API zu debuggen.

import traceback 
try: 
    hicon = win32gui.LoadImage(self.hinst, self.icon, win32con.IMAGE_ICON, 0, 0, icon_flags) 

except win32gui.error as error: 
    logging.error(self.hinst) 
    logging.error(self.icon) 
    logging.error(icon_flags) 
    logging.error(error) 
    logging.error(traceback.format_exc())