Quick and easy: trayicon with python?
Asked Answered
M

9

46

I'd just need a quick example on how to easily put an icon with python on my systray. This means: I run the program, no window shows up, just a tray icon (I've got a png file) shows up in the systray and when I right-click on it a menu appears with some options (and when I click on an option, a function is run). Is that possible? I don't need any window at all...

Examples / code snippets are REALLY appreciated! :D

Masquerade answered 17/6, 2011 at 17:38 Comment(2)
What OS? If linux, I'd look into say the source code of gmail-notify.py. See gmail-notify.sourceforge.net/download.phpRowdy
I guess it didn't really matter as fogglebird's wx code works fine in linux (specifically gnome system panel).Rowdy
D
63

For Windows & Gnome

Here ya go! wxPython is the bomb. Adapted from the source of my Feed Notifier application.

import wx

TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = 'icon.png'


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):
    def __init__(self):
        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, 'Say Hello', self.on_hello)
        menu.AppendSeparator()
        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 'Tray icon was left-clicked.'

    def on_hello(self, event):
        print 'Hello, world!'

    def on_exit(self, event):
        wx.CallAfter(self.Destroy)


def main():
    app = wx.PySimpleApp()
    TaskBarIcon()
    app.MainLoop()


if __name__ == '__main__':
    main()
Dorena answered 17/6, 2011 at 17:50 Comment(9)
No problem. Use item.SetBitmap(wx.Bitmap(path)) in create_menu_item if you want icons on your menu items too. (Just to make it look nice.)Dorena
I'd also look into using PyEmbeddedImage: wxpython.org/docs/api/…Wilmerwilmette
The icon doesn't appear on the Ubuntu 12.10 Unity tray.. Got no error, just can't see it..Bisutun
Unity uses appindicator instead of the freedesktop.org System Tray Protocol which wxWidgets requires. docs.wxwidgets.org/trunk/classwx_task_bar_icon.htmlTish
Hey I'm getting the following output : "Segmentation fault 11" Any idea how to fix this ? I'm running a Mac OS XPinon
I made an ubuntu post as well, belowPsychogenic
I got AttributeError: 'module' object has no attribute 'TaskBarIcon'. My python version is 2.7Jermayne
wxPython is a huge, heavy library. I am not sure this response can validly address the part of the question that says "... to easily put an icon with python on my systray."Woven
SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?Feinstein
E
19

2018 version

import wx.adv
import wx
TRAY_TOOLTIP = 'Name' 
TRAY_ICON = 'icon.png' 


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


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

    def CreatePopupMenu(self):
        menu = wx.Menu()
        create_menu_item(menu, 'Site', self.on_hello)
        menu.AppendSeparator()
        create_menu_item(menu, 'Exit', self.on_exit)
        return menu

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

    def on_left_down(self, event):      
        print ('Tray icon was left-clicked.')

    def on_hello(self, event):
        print ('Hello, world!')

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

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

def main():
    app = App(False)
    app.MainLoop()


if __name__ == '__main__':
    main()
Emanation answered 23/1, 2018 at 12:35 Comment(1)
Why is the frame needed? Why self.Destroy() instead of self.Close() as in the wxPython basic tutorial?Remy
X
16

wx.PySimpleApp deprecated, here's how to use wx.App instead

Took me while to figure this out so I thought I'd share. wx.PySimpleApp is deprecated in wxPython 2.9 and beyond. Here's FogleBird's original script using wx.App instead.

import wx

TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = 'icon.png'

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):
    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, 'Say Hello', self.on_hello)
        menu.AppendSeparator()
        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 'Tray icon was left-clicked.'

    def on_hello(self, event):
        print 'Hello, world!'

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

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

def main():
    app = App(False)
    app.MainLoop()


if __name__ == '__main__':
    main()
Xylograph answered 11/10, 2015 at 19:54 Comment(3)
Running this code returns: AttributeError: 'module' object has no attribute 'TaskBarIcon'Ventriculus
@Ventriculus Same here, but I discovered that some classes and constants have been moved to a subpackage called wx.adv so instead of wx.TaskBarIcon you should use wx.adv.TaskBarIcon. Same for EVT_TASKBAR_LEFT_DOWNOolite
This solution works on Gnome 3.26 (Ubuntu 18.04) and + if the extension TopIcon is enabled. The system tray is gone in modern Gnome and application indicators replace them. Anybody knows how to create application indicators with WX? This link shows how to do one, without WX though.Thereby
U
6

If you can guarantee windows and you do not want to introduce the heavy dependencies of wx, you can do this with the pywin32 extensions.

Also see this question.

Uncork answered 17/6, 2011 at 19:58 Comment(1)
@kbec, thanks for the heads up. I updated my answer with some newer links. It also looks like I duplicated an older answer, so I'm included to delete mine, but I'll leave it here for Google's sake.Uncork
P
6

For Ubuntu

class TrayIcon:
    def init():


iconPath = {"Windows":os.path.expandvars("%PROGRAMFILES%/MyProgram/icon.png"),
                  "Linux":"/usr/share/icons/myprogramicon.png"}        
    if platform.system()=="Linux":
        import gtk
        import appindicator # Ubuntu apt-get install python-appindicator 

    # Create an application indicator
    try:
        gtk.gdk.threads_init()
        gtk.threads_enter()
        icon = iconPath[platform.system()]
        indicator = appindicator.Indicator("example-simple-client", "indicator-messages", appindicator.CATEGORY_APPLICATION_STATUS)
        indicator.set_icon(icon)
        indicator.set_status (appindicator.STATUS_ACTIVE)
        indicator.set_attention_icon ("indicator-messages-new")
        menu = gtk.Menu()

        menuTitle = "Quit"   
        menu_items = gtk.MenuItem(menuTitle)
        menu.append(menu_items)
        menu_items.connect("activate", TrayIcon.QuitApp, menuTitle)
        menu_items.show()

        menuTitle = "About My Program"
        menu_items = gtk.MenuItem(menuTitle)
        menu.append(menu_items)
        menu_items.connect("activate", TrayIcon.AboutApp, menuTitle)
        menu_items.show()   

        indicator.set_menu(menu)    
    except:
        pass

    # Run the app indicator on the main thread.
    try:

        t = threading.Thread(target=gtk.main)
        t.daemon = True # this means it'll die when the program dies.
        t.start()
        #gtk.main()

    except:
        pass
    finally:
        gtk.threads_leave()     

@staticmethod
def AboutApp(a1,a2):
    gtk.threads_enter()
    dialog = gtk.Dialog("About",
                        None,
                        gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                        (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
    label = gtk.Label("My Program v0.0.1, (C)opyright ME 2015. All rights reserved.")
    dialog.vbox.pack_start(label)
    label.show()
    label2 = gtk.Label("example.com\n\nFor more support contact [email protected]")
    label2.show()
    dialog.action_area.pack_end(label2)
    response = dialog.run()
    dialog.destroy()
    gtk.threads_leave()

@staticmethod
def QuitApp(a1, a2):
    sys.exit(0)

Cross-Platform

See PyQt: Show menu in a system tray application

Psychogenic answered 4/8, 2015 at 22:2 Comment(1)
In Ubuntu 16.04 there is a python-appindicator apt package but no python3-appindicator package. So for "modern" Ubuntu 22.04 a different solution may be needed.Bergstrom
L
4

There is a package called pystray (bad name, just say it out loud) but works like a charm and is more lightweight than wx or Qt. These are the links:

https://pystray.readthedocs.io/en/latest/index.html
https://pypi.org/project/pystray/

Lenalenard answered 5/10, 2020 at 17:13 Comment(0)
S
2

Yes. There is a cross-platform example on wiki.wxpython.org that I've tested with python 2.7 (minconda install) on macOS High Sierra (10.13.3), Windows 7, and gnome 3/centos7. It is here (ignore the page title): https://wiki.wxpython.org/Custom%20Mac%20OsX%20Dock%20Bar%20Icon

Small mods are needed for python 3.6:

  • you must import wx.adv
  • wx.TaskBarIcon becomes wx.adv.TaskBarIcon
  • wx.IconFromBitmap becomes wx.Icon

Gnome 3 required installation of TopIcons Plus.

Since you don't want to have the window display (" no window shows up, just a tray icon"), simply comment out the following line (though you still want to keep the wx.Frame parent):

frame.Show(True)

And since you want to use your own .png icon, remove the WXPdemo image and embeddedimage stuff and replace

icon = self.MakeIcon(WXPdemo.GetImage())

with, for example

icon = wx.Icon('icon.png')

In my experience, this will provide a good start for adapting or extending further.

Seaden answered 15/3, 2018 at 19:26 Comment(0)
A
1

An alternative if you are trying to run a python based program in the background you can run it as a service. Check out this active state recipe its pretty useful. I believe one of the options is to convert your application to exe with py2exe or pyinstall.

http://code.activestate.com/recipes/551780/

Adolfoadolph answered 14/11, 2013 at 17:13 Comment(2)
is it possible to have a GUI run as a service?Alisonalissa
@Isquare1 my python knowledge is a bit dated but this SO question seems to answer it.Adolfoadolph
M
1

For an example, refer to this thread -> wx question.

wxPython "classic" -> [new API] wxPython 'Phoenix' (Py3)

Magnificent answered 7/3, 2018 at 21:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.