[pythonwin] popup menu problems (was: [Pythonwin] Help needed creating a Window)

Dave Kirby dkirby at bigfoot.com
Mon Jun 14 11:53:03 EDT 1999


Thanks to Mark Hammond's help I now have an app that displays an icon
in the system tray which I can process messages from. However when I
try to create a popup menu menu for it nothing happens. The call to
PyCMenu.TrackPopupMenu doesnt display anything and never returns.  I
have tried both using it to send a notify message to the window and
returning a command, but it didnt make any difference. I am unsure
whether it is a failure of my understanding of the way Windows does
these things, a bug in my code, or a problem with pythonwin. 


I also have the minor problem that when I destroy the icon it doesnt
disappear until I move the mouse pointer over it. Does anyone know how
to force the system tray to redraw?

Any help would be greatly appreciated.

   Dave K


Here is the relevant code:

#--------------- 8< --------------------------------------
class Icon:
    #constructor
    # param: dll - handle to the dll containing the icon resources
    # param: startIcon - the initial icon resource ID to display
    # param: tooltip = the string to display when the cursor is over
    # the icon
    def __init__( self, dll, startIcon, tooltip ):
        self.iconMap = {}
        self.msgID = win32con.WM_USER+20
        launchID = win32con.WM_USER+21
        quitID = win32con.WM_USER+22
        message_map = {
           win32con.WM_DESTROY: self.OnDestroy,
           self.msgID : self.OnTaskbarNotify,
           win32con.WM_CONTEXTMENU : self.OnContextMenu,
        }
        #the menu_map contains the information on the popup menu
        #entries -
        # message ID, callback function, text string
        menu_map = (
            ( launchID, self.OnLaunch, "Launch Browser" ),
            ( quitID, self.OnExit, "Exit program" )
        )
        
        self.dll = dll
        
        #add menu commands to the message_map dictionary
        for entry in menu_map:
            message_map[entry[0]] = entry[1]
            
        #define a window class for the icon handler
        wc = win32gui.WNDCLASS()
        hinst = wc.hInstance = win32gui.GetModuleHandle(None)
        wc.lpszClassName = "BaxterTaskbar"
        wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
        wc.hCursor = win32gui.LoadCursor( 0, win32con.IDC_ARROW )
        wc.hbrBackground = win32con.COLOR_WINDOW
        wc.lpfnWndProc = message_map
        classAtom = win32gui.RegisterClass(wc)
        
        #create the window
        style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
        self.hwnd = win32gui.CreateWindow( classAtom, "title", style,
0,0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, 0, 0, hinst,
None)
        win32gui.UpdateWindow(self.hwnd)
        self.cWnd = win32ui.CreateWindowFromHandle( self.hwnd )
        
        #initialise the icon display
        icon = win32gui.LoadIcon( 0, win32con.IDI_APPLICATION )
        flags =   win32gui.NIF_ICON \
		| win32gui.NIF_MESSAGE \
                | win32gui.NIF_TIP
        nid = (self.hwnd, 0, flags, self.msgID, icon, tooltip)
        win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, nid)
        self.changeIcon( startIcon )

        #initialise the popup menu
        self.cMenu = win32ui.CreateMenu()
        for entry in menu_map:
            self.cMenu.AppendMenu( win32con.MF_STRING, entry[0],
entry[2] )

    ####################################
    # this is the bit that actually calls the popup menu
    ####################################
    def OnTaskbarNotify(self, hwnd, msg, wparam, lparam):
        if lparam==win32con.WM_LBUTTONDBLCLK:
            print "double clicked"
        elif lparam==win32con.WM_RBUTTONUP:
            pos = win32api.GetCursorPos()
            print "opening menu at", pos
            self.cMenu.TrackPopupMenu( pos, owner=self.cWnd )

            #alternative attempt returning a command:
            #cmd = self.cMenu.TrackPopupMenu( pos,
            #       win32con.TPM_LEFTALIGN
            #       |win32.TPM_LEFTBUTTON
            #       |win32con.TPM_NONOTIFY
            #       |win32con.TPM_RETURNCMD, None )

            print "finished popup menu"
        return 1

    #the following methods are included for completeness, but are not
    # part of the problem
        
    #change to a new icon - if hasnt been loaded then do so, otherwise

    #use cached icon
    def changeIcon( self, iconResource ):
        if self.iconMap.has_key( iconResource ):
            icon = self.iconMap[iconResource]
        else:
            icon = win32gui.LoadIcon(self.dll, iconResource)
            self.iconMap[iconResource] = icon
            
        flags = win32gui.NIF_ICON
        nid = (self.hwnd, 0, flags, 0, icon)
        win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, nid)
        
    def changeTooltip( self, tip ):
        flags = win32gui.NIF_TIP
        nid = (self.hwnd, 0, flags, 0, 0, tip)
        win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, nid)
        
    
    #destructor - remove icon
    def __del__( self ):
        nid = (self.hwnd, 0, win32gui.NIF_MESSAGE, self.msgID )
        win32gui.Shell_NotifyIcon(NIM_DELETE, nid)
        win32gui.UpdateWindow(self.hwnd)

    def OnDestroy(self, hwnd, msg, wparam, lparam):
        nid = (self.hwnd, 0, win32gui.NIF_MESSAGE, self.msgID )
        win32gui.Shell_NotifyIcon(NIM_DELETE, nid)
        win32gui.UpdateWindow(self.hwnd)

    def OnContextMenu(self, *args):
        print "Icon.OnContextMenu", args

    def OnLaunch(self,  *args ):
        print "Icon.OnLaunch", args

    def OnExit(self, *args ):
        print "Icon.OnExit", args





More information about the Python-list mailing list