TrayIcon context menu only showing once

Hi!

I building a small application that will live in the system tray and I’ve run into a small problem. Using the following code, the context menu will only ever show once every time I start the program. If i select the sub-menu item or click anywhere else on the screen there is now way to make it reappear. Is there any way to solve this?

Thx!
//T

Code:
import wx

class TrayIcon(wx.TaskBarIcon):

def __init__(self):
    self.simpleApp = wx.PySimpleApp()
    wx.TaskBarIcon.__init__(self)
    self.__cMenu = self.constructContextMenu()
    self.Bind(wx.EVT_MENU,  self.handleMenuEvent)
   
    #setup icon object
    icon = wx.Icon("my_icon.png", wx.BITMAP_TYPE_PNG)
    self.SetIcon(icon, "gammaMail")
    self.simpleApp.MainLoop()

def constructContextMenu(self):
    manageMenu = wx.Menu()
    manageMenu.Append(6001,  "Sub-menu item")
   
    contextMenu = wx.Menu()
    contextMenu.AppendSubMenu(manageMenu,  "Sub-menu")
    contextMenu.Append(wx.ID_EXIT,  "Exit")
    return contextMenu

def CreatePopupMenu(self):
    return self.__cMenu
   
def handleMenuEvent(self, evt):
    print "menu event"
    if evt.GetId() == wx.ID_EXIT:
        self.simpleApp.ExitMainLoop()

trayIcon = TrayIcon()

Hi Thomas,

Hi!

I building a small application that will live in the system tray and I've run into a small problem. Using the following code, the context menu will only ever show once every time I start the program. If i select the sub-menu item or click anywhere else on the screen there is now way to make it reappear. Is there any way to solve this?

Thx!
//T

Code:
import wx
   class TrayIcon(wx.TaskBarIcon):
       def __init__(self):
        self.simpleApp = wx.PySimpleApp()
        wx.TaskBarIcon.__init__(self)
        self.__cMenu = self.constructContextMenu()
        self.Bind(wx.EVT_MENU, self.handleMenuEvent)
               #setup icon object
        icon = wx.Icon("my_icon.png", wx.BITMAP_TYPE_PNG)
        self.SetIcon(icon, "gammaMail")
        self.simpleApp.MainLoop()
       def constructContextMenu(self):
        manageMenu = wx.Menu()
        manageMenu.Append(6001, "Sub-menu item")
               contextMenu = wx.Menu()
        contextMenu.AppendSubMenu(manageMenu, "Sub-menu")
        contextMenu.Append(wx.ID_EXIT, "Exit")
        return contextMenu

    def CreatePopupMenu(self):
        return self.__cMenu
           def handleMenuEvent(self, evt):
        print "menu event"
        if evt.GetId() == wx.ID_EXIT:
            self.simpleApp.ExitMainLoop()

trayIcon = TrayIcon()

I'm not completely certain, but I think having the MainLoop() and app instantiation within the TaskBarIcon __init__ is causing the issue. Not only that, but it's bad form and I'm surprised that that works at all.

Mike

Hi Mike!

Thanks for your reply!

Writing the code like that was just to condense my code into a working example that was as small as possible. In my original code I have another module instantiating a pythonSimpleApp and calling MainLoop(), with the same result.

I don’t know if it should matter but I’m in Ubuntu, using Gnome. I haven’t tested this using windows.

//T

···

On Thu, Oct 23, 2008 at 12:01 PM, Mike Driscoll mike@pythonlibrary.org wrote:

Hi Thomas,

Hi!

I building a small application that will live in the system tray and I’ve run into a small problem. Using the following code, the context menu will only ever show once every time I start the program. If i select the sub-menu item or click anywhere else on the screen there is now way to make it reappear. Is there any way to solve this?

Thx!

//T

Code:

import wx

class TrayIcon(wx.TaskBarIcon):

  def __init__(self):

    self.simpleApp = wx.PySimpleApp()

    wx.TaskBarIcon.__init__(self)

    self.__cMenu = self.constructContextMenu()

    self.Bind(wx.EVT_MENU,  self.handleMenuEvent)

          #setup icon object

    icon = wx.Icon("my_icon.png", wx.BITMAP_TYPE_PNG)

    self.SetIcon(icon, "gammaMail")

    self.simpleApp.MainLoop()

  def constructContextMenu(self):

    manageMenu = wx.Menu()

    manageMenu.Append(6001,  "Sub-menu item")

          contextMenu = wx.Menu()

    contextMenu.AppendSubMenu(manageMenu,  "Sub-menu")

    contextMenu.Append(wx.ID_EXIT,  "Exit")

    return contextMenu



def CreatePopupMenu(self):

    return self.__cMenu

      def handleMenuEvent(self, evt):

    print "menu event"

    if evt.GetId() == wx.ID_EXIT:

        self.simpleApp.ExitMainLoop()

trayIcon = TrayIcon()

I’m not completely certain, but I think having the MainLoop() and app instantiation within the TaskBarIcon init is causing the issue. Not only that, but it’s bad form and I’m surprised that that works at all.

Mike


wxpython-users mailing list

wxpython-users@lists.wxwidgets.org

http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

Hi Thomas,

Hi Mike!

Thanks for your reply!

Writing the code like that was just to condense my code into a working example that was as small as possible. In my original code I have another module instantiating a pythonSimpleApp and calling MainLoop(), with the same result.

I don't know if it should matter but I'm in Ubuntu, using Gnome. I haven't tested this using windows.

//T

I just used the wxPython demo's code for it's TaskBarIcon and modified it for my needs. It looks something like this:

<code>

import wx
from icon import getIcon

class DemoTaskBarIcon(wx.TaskBarIcon):
    TBMENU_RESTORE = wx.NewId()
    TBMENU_CLOSE = wx.NewId()
    TBMENU_CHANGE = wx.NewId()
    TBMENU_REMOVE = wx.NewId()
       def __init__(self, frame, filename=None):
        wx.TaskBarIcon.__init__(self)
        self.frame = frame
        self.fName = filename

        # Set the image tbIcon = getIcon()
        self.SetIcon(tbIcon, "Zimbra Alerts Beta (Build 1023008)")
        self.imgidx = 1
               # bind some events
        self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
        self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
        self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
        self.Bind(wx.EVT_MENU, self.OnTaskBarList, id=self.TBMENU_CHANGE)

    def CreatePopupMenu(self):
        """
        This method is called by the base class when it needs to popup
        the menu for the default EVT_RIGHT_DOWN event. Just create
        the menu how you want it and return it from this function,
        the base class takes care of the rest.
        """
        menu = wx.Menu()
        menu.Append(self.TBMENU_RESTORE, "Open Program")
        menu.Append(self.TBMENU_CHANGE, "Do Something") menu.AppendSeparator()
        menu.Append(self.TBMENU_CLOSE, "Exit Program")
               menu.Append(self.TBMENU_REMOVE, "Remove the TB Icon")
        return menu

</code>

I left out the event handlers, for the most part. As far as I can tell, your code looks a lot like the above. Thus, it should "just work". Sprinkle "print" statements throughout your code and find which one prints last. This is one way I find goofy problems in my code. I usually stick a print statement at the top of my event handlers that just prints the name of the event handler so I know when it's called. Once I know where something is stuck, I can stick in additional print statements to help track it down (if it's not immediately obvious).

Mike

···

On Thu, Oct 23, 2008 at 12:01 PM, Mike Driscoll > <mike@pythonlibrary.org <mailto:mike@pythonlibrary.org>> wrote:

    Hi Thomas,

        Hi!

        I building a small application that will live in the system
        tray and I've run into a small problem. Using the following
        code, the context menu will only ever show once every time I
        start the program. If i select the sub-menu item or click
        anywhere else on the screen there is now way to make it
        reappear. Is there any way to solve this?

        Thx!
        //T

        Code:
        import wx
          class TrayIcon(wx.TaskBarIcon):
             def __init__(self):
               self.simpleApp = wx.PySimpleApp()
               wx.TaskBarIcon.__init__(self)
               self.__cMenu = self.constructContextMenu()
               self.Bind(wx.EVT_MENU, self.handleMenuEvent)
                     #setup icon object
               icon = wx.Icon("my_icon.png", wx.BITMAP_TYPE_PNG)
               self.SetIcon(icon, "gammaMail")
               self.simpleApp.MainLoop()
             def constructContextMenu(self):
               manageMenu = wx.Menu()
               manageMenu.Append(6001, "Sub-menu item")
                     contextMenu = wx.Menu()
               contextMenu.AppendSubMenu(manageMenu, "Sub-menu")
               contextMenu.Append(wx.ID_EXIT, "Exit")
               return contextMenu

           def CreatePopupMenu(self):
               return self.__cMenu
                 def handleMenuEvent(self, evt):
               print "menu event"
               if evt.GetId() == wx.ID_EXIT:
                   self.simpleApp.ExitMainLoop()

        trayIcon = TrayIcon()

    I'm not completely certain, but I think having the MainLoop() and
    app instantiation within the TaskBarIcon __init__ is causing the
    issue. Not only that, but it's bad form and I'm surprised that
    that works at all.

    Mike

Mike Driscoll wrote:

I just used the wxPython demo's code for it's TaskBarIcon and
modified
it for my needs. It looks something like this:

<code>

import wx
from icon import getIcon

class DemoTaskBarIcon(wx.TaskBarIcon):
    TBMENU_RESTORE = wx.NewId()
    TBMENU_CLOSE = wx.NewId()
    TBMENU_CHANGE = wx.NewId()
    TBMENU_REMOVE = wx.NewId()
   
    def __init__(self, frame, filename=None):
        wx.TaskBarIcon.__init__(self)
        self.frame = frame
        self.fName = filename

        # Set the image
        tbIcon = getIcon()
        self.SetIcon(tbIcon, "Zimbra Alerts Beta (Build 1023008)")
        self.imgidx = 1
       
        # bind some events
        self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
        self.Bind(wx.EVT_MENU, self.OnTaskBarActivate,
id=self.TBMENU_RESTORE)
        self.Bind(wx.EVT_MENU, self.OnTaskBarClose,
id=self.TBMENU_CLOSE)
        self.Bind(wx.EVT_MENU, self.OnTaskBarList,
id=self.TBMENU_CHANGE)

    def CreatePopupMenu(self):
        """
        This method is called by the base class when it needs to popup
        the menu for the default EVT_RIGHT_DOWN event. Just create
        the menu how you want it and return it from this function,
        the base class takes care of the rest.
        """
        menu = wx.Menu()
        menu.Append(self.TBMENU_RESTORE, "Open Program")
        menu.Append(self.TBMENU_CHANGE, "Do Something")
        menu.AppendSeparator()
        menu.Append(self.TBMENU_CLOSE, "Exit Program")
       
        menu.Append(self.TBMENU_REMOVE, "Remove the TB Icon")
        return menu

</code>

I left out the event handlers, for the most part. As far as I
can tell,
your code looks a lot like the above.

The obvious difference between this code and Thomas's version is that
his code only creates the menu once (it calls constructContextMenu from
__init__) and tries to reuse the reference, whereas Mike's creates a new
wx.Menu every time.

I don't know how wx.TaskBarIcon works, but it sounds like you're not
allowed to reuse the menu (perhaps wx destroys it after a menu item is
selected?), so Mike's version would be the only way to go.

Hope that helps,

Simon

King Simon-NFHD78 wrote:

Mike Driscoll wrote:
  

I just used the wxPython demo's code for it's TaskBarIcon and modified it for my needs. It looks something like this:

<code>

import wx
from icon import getIcon

class DemoTaskBarIcon(wx.TaskBarIcon):
    TBMENU_RESTORE = wx.NewId()
    TBMENU_CLOSE = wx.NewId()
    TBMENU_CHANGE = wx.NewId()
    TBMENU_REMOVE = wx.NewId()
       def __init__(self, frame, filename=None):
        wx.TaskBarIcon.__init__(self)
        self.frame = frame
        self.fName = filename

        # Set the image tbIcon = getIcon()
        self.SetIcon(tbIcon, "Zimbra Alerts Beta (Build 1023008)")
        self.imgidx = 1
               # bind some events
        self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)
        self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)
        self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)
        self.Bind(wx.EVT_MENU, self.OnTaskBarList, id=self.TBMENU_CHANGE)

    def CreatePopupMenu(self):
        """
        This method is called by the base class when it needs to popup
        the menu for the default EVT_RIGHT_DOWN event. Just create
        the menu how you want it and return it from this function,
        the base class takes care of the rest.
        """
        menu = wx.Menu()
        menu.Append(self.TBMENU_RESTORE, "Open Program")
        menu.Append(self.TBMENU_CHANGE, "Do Something") menu.AppendSeparator()
        menu.Append(self.TBMENU_CLOSE, "Exit Program")
               menu.Append(self.TBMENU_REMOVE, "Remove the TB Icon")
        return menu

</code>

I left out the event handlers, for the most part. As far as I can tell, your code looks a lot like the above.
    
The obvious difference between this code and Thomas's version is that
his code only creates the menu once (it calls constructContextMenu from
__init__) and tries to reuse the reference, whereas Mike's creates a new
wx.Menu every time.

I don't know how wx.TaskBarIcon works, but it sounds like you're not
allowed to reuse the menu (perhaps wx destroys it after a menu item is
selected?), so Mike's version would be the only way to go.

Hope that helps,

Simon

Oh...duh! I didn't realize he was trying to return his own instance of the menu. Yeah, that's probably why it doesn't work. I'm pretty sure Robin said that the pop-up menu creates itself on command and destroys itself after a selection is made just a few weeks ago.

Sorry I didn't catch that. I'll blame it on being distracted by work...

Mike

Yes, that was it! Thanks a lot!

That is really weird though, shouldn’t it be up to the application whether to regenerate the context menu, what if for example one submenu took a lot of work/communication to build up? I know there are (not so difficult) ways to work around it, but it’s still kind of weird default behavior, no?

//T

···

On Thu, Oct 23, 2008 at 12:17 PM, Mike Driscoll mike@pythonlibrary.org wrote:

Hi Thomas,

Hi Mike!

Thanks for your reply!

Writing the code like that was just to condense my code into a working example that was as small as possible. In my original code I have another module instantiating a pythonSimpleApp and calling MainLoop(), with the same result.

I don’t know if it should matter but I’m in Ubuntu, using Gnome. I haven’t tested this using windows.

//T

I just used the wxPython demo’s code for it’s TaskBarIcon and modified it for my needs. It looks something like this:

import wx

from icon import getIcon

class DemoTaskBarIcon(wx.TaskBarIcon):

TBMENU_RESTORE = wx.NewId()

TBMENU_CLOSE = wx.NewId()

TBMENU_CHANGE = wx.NewId()

TBMENU_REMOVE = wx.NewId()

 def __init__(self, frame, filename=None):

   wx.TaskBarIcon.__init__(self)

   self.frame = frame

   self.fName = filename



   # Set the image           tbIcon = getIcon()

   self.SetIcon(tbIcon, "Zimbra Alerts Beta (Build 1023008)")

   self.imgidx = 1

         # bind some events

   self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnTaskBarActivate)

   self.Bind(wx.EVT_MENU, self.OnTaskBarActivate, id=self.TBMENU_RESTORE)

   self.Bind(wx.EVT_MENU, self.OnTaskBarClose, id=self.TBMENU_CLOSE)

   self.Bind(wx.EVT_MENU, self.OnTaskBarList, id=self.TBMENU_CHANGE)

def CreatePopupMenu(self):

   """

   This method is called by the base class when it needs to popup

   the menu for the default EVT_RIGHT_DOWN event.  Just create

   the menu how you want it and return it from this function,

   the base class takes care of the rest.

   """

   menu = wx.Menu()

   menu.Append(self.TBMENU_RESTORE, "Open Program")

   menu.Append(self.TBMENU_CHANGE, "Do Something")             menu.AppendSeparator()

   menu.Append(self.TBMENU_CLOSE,   "Exit Program")

         menu.Append(self.TBMENU_REMOVE, "Remove the TB Icon")

   return menu

I left out the event handlers, for the most part. As far as I can tell, your code looks a lot like the above. Thus, it should “just work”. Sprinkle “print” statements throughout your code and find which one prints last. This is one way I find goofy problems in my code. I usually stick a print statement at the top of my event handlers that just prints the name of the event handler so I know when it’s called. Once I know where something is stuck, I can stick in additional print statements to help track it down (if it’s not immediately obvious).

Mike

On Thu, Oct 23, 2008 at 12:01 PM, Mike Driscoll <mike@pythonlibrary.org mailto:mike@pythonlibrary.org> wrote:

Hi Thomas,





    Hi!



    I building a small application that will live in the system

    tray and I've run into a small problem. Using the following

    code, the context menu will only ever show once every time I

    start the program. If i select the sub-menu item or click

    anywhere else on the screen there is now way to make it

    reappear. Is there any way to solve this?



    Thx!

    //T



    Code:

    import wx

      class TrayIcon(wx.TaskBarIcon):

         def __init__(self):

           self.simpleApp = wx.PySimpleApp()

           wx.TaskBarIcon.__init__(self)

           self.__cMenu = self.constructContextMenu()

           self.Bind(wx.EVT_MENU,  self.handleMenuEvent)

                 #setup icon object

           icon = wx.Icon("my_icon.png", wx.BITMAP_TYPE_PNG)

           self.SetIcon(icon, "gammaMail")

           self.simpleApp.MainLoop()

         def constructContextMenu(self):

           manageMenu = wx.Menu()

           manageMenu.Append(6001,  "Sub-menu item")

                 contextMenu = wx.Menu()

           contextMenu.AppendSubMenu(manageMenu,  "Sub-menu")

           contextMenu.Append(wx.ID_EXIT,  "Exit")

           return contextMenu



       def CreatePopupMenu(self):

           return self.__cMenu

             def handleMenuEvent(self, evt):

           print "menu event"

           if evt.GetId() == wx.ID_EXIT:

               self.simpleApp.ExitMainLoop()



    trayIcon = TrayIcon()





I'm not completely certain, but I think having the MainLoop() and

app instantiation within the TaskBarIcon __init__ is causing the

issue. Not only that, but it's bad form and I'm surprised that

that works at all.



Mike

wxpython-users mailing list

wxpython-users@lists.wxwidgets.org

http://lists.wxwidgets.org/mailman/listinfo/wxpython-users

Thomas Järvstrand wrote:

Yes, that was it! Thanks a lot!

That is really weird though, shouldn't it be up to the application whether to regenerate the context menu, what if for example one submenu took a lot of work/communication to build up? I know there are (not so difficult) ways to work around it, but it's still kind of weird default behavior, no?

It had to be done this way because of how wx.TaskBarIcon is implemented for Mac. There the system asks for the menu when it needs it and then completely manages the menu from that point forward. So the same model was implemented for the other platforms too. If you don't care about supporting Mac then you can simply not implement CreatePopupMenu and instead bind a handler for EVT_TASKBAR_CLICK, and in that handler call PopupMenu yourself.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!