dufte
dufte

Reputation: 249

How to communicate between my python frame and task bar icon

Situation

I am new to python and wxPython and currently working on a small (linux-only targeted) script which features a tray icon (task bar icon) and a main window (Frame).

I am realizing an issue right now which i am not sure how to handle.

I tried to slimdown the code for this post as good as i can, hope it helps.

Code

import wx

TRAY_TOOLTIP = 'demo'
TRAY_ICON = 'gfx/core/bt_appIcon_16.png'
appName = 'demo'
windowWidth=350
windowHeight=200

class MyFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title, size=(windowWidth,windowHeight), style=wx.NO_BORDER) # Frame without borders
        self.SetSizeHintsSz( wx.Size( windowWidth,windowHeight ), wx.Size( windowWidth,windowHeight ) ) # forcing min and max size to same values - prevents resizing option
        appIcon = wx.Icon('gfx/core/bt_appIcon_16.png', wx.BITMAP_TYPE_PNG)
        self.SetIcon(appIcon) # set an application icon
        # Preference button of main ui
        img_preferences = wx.Bitmap('gfx/core/bt_prefs_16.png', wx.BITMAP_TYPE_BMP)
        self.bt_preferences = wx.BitmapButton(self, id = wx.ID_ANY, style=wx.NO_BORDER, bitmap = img_preferences, size = (img_preferences.GetWidth()+10, img_preferences.GetHeight()+10))
        self.bt_preferences.SetLabel('Preferences')
        bSizer1 = wx.BoxSizer( wx.VERTICAL ) # define layout container
        bSizer1.Add( self.bt_preferences, 0, wx.ALIGN_RIGHT, 100) # preferences
        self.SetSizer( bSizer1 )
        self.Center() # open window centered
        self.bt_preferences.Bind(wx.EVT_BUTTON, self.OnClicked) 
        self.Show(True) # show main UI

    def OnClickedOptionButton(self, event): 
        self.launchExternalApplication()

    def OnClicked(self, event): 
        btn = event.GetEventObject().GetLabel() 
        if (btn =='Preferences'):
            print('clicked preference button in main-window/frame')

def create_menu_item(menu, label, func):
    item = wx.MenuItem(menu, -1, label)
    menu.Bind(wx.EVT_MENU, func, id=item.GetId())
    menu.AppendItem(item)
    return item

class TaskBarIcon(wx.TaskBarIcon, MyFrame):
    def __init__(self, frame):
        self.frame = frame
        super(TaskBarIcon, self).__init__()
        self.set_icon(TRAY_ICON)
        self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)

    def CreatePopupMenu(self):
        menu = wx.Menu()
        create_menu_item(menu, 'Preferences', self.openPrefsFromMenu)
        create_menu_item(menu, 'Exit', self.on_exit)
        return menu

    def set_icon(self, path):
        icon = wx.IconFromBitmap(wx.Bitmap(path))
        self.SetIcon(icon, TRAY_TOOLTIP)

    def on_left_down(self, event):
        print('on_left_down of tray icon')
        # NEED HELP HERE
        # should check if main-window is visible or not
        # then toggle it

    def openPrefsFromMenu(self, event):
        print('open prefs from menu')

    def on_exit(self, event):
        wx.CallAfter(self.Destroy) # close the tray icon
        self.frame.Close()
        # AND SOME MORE HELP HERE
        # should close the main UI as well
        #exit()

class App(wx.App):
    def OnInit(self):
        frame=wx.Frame(None)
        self.SetTopWindow(frame)
        TaskBarIcon(frame)
        return True

def main():
    app = App(False)
    frame = MyFrame(None, appName)
    app.MainLoop()

if __name__ == '__main__':
    main()

Upvotes: 0

Views: 327

Answers (1)

Selçuk Cihan
Selçuk Cihan

Reputation: 2034

Firstly, please check this example for a working sample of what you are trying to accomplish.

There are a few issues here,

  • You need to register for wx.EVT_CLOSE and handle that closing event properly.
  • For styling your main frame, you should apply wx.NO_BORDER on top of the default style. Otherwise, your window won't minimize at all.
  • Within on_exit of tray icon, you should signal the main frame to be closed by wx.CallAfter(self.frame.Close)

See the below code for an application of above tips:

class MyFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, title=title, size=(windowWidth,windowHeight), style=wx.DEFAULT_FRAME_STYLE | wx.NO_BORDER) # Frame without borders
        self.tbicon = TaskBarIcon(self)
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)

    def OnCloseWindow(self, evt):
        self.tbicon.RemoveIcon()
        self.tbicon.Destroy()

        self.Destroy()
        wx.GetApp().ExitMainLoop()

        evt.Skip()

The modifications for TaskBarIcon:

class TaskBarIcon(wx.TaskBarIcon):
    def on_left_down(self, event):
        print('on_left_down of tray icon')
        if self.frame.IsIconized():
            self.frame.Iconize(False)
        if not self.frame.IsShown():
            self.frame.Show(True)
        self.frame.Raise()

    def openPrefsFromMenu(self, event):
        print('open prefs from menu')

    def on_exit(self, event):
        wx.CallAfter(self.frame.Close)

Upvotes: 2

Related Questions