How can I develop Windows PC background tray apps using Python? [duplicate] - python

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

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()

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()

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()

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.

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 me#gmail.com")
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

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/

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.

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/

For an example, refer to this thread -> wx question.
wxPython "classic" -> [new API] wxPython 'Phoenix' (Py3)

Related

Cannot get media to play in wxpython

I just want to add a simple video to my application and I cannot get wx.media.MediaCtrl to work.
It seems to fail when I call Load(), I have checked to make sure the path exists ect.
import wx
import wx.media
class TestPanel(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None)
self.testMedia = wx.media.MediaCtrl(self, style=wx.SIMPLE_BORDER, szBackend=wx.media.MEDIABACKEND_QUICKTIME)
self.media = '/Users/nicholasmaisel/Downloads/test.mp4'
self.testMedia.Bind(wx.media.EVT_MEDIA_LOADED, self.play)
self.testMedia.Bind(wx.media.EVT_MEDIA_FINISHED, self.quit)
print(self.testMedia.Load('/Users/nicholasmaisel/Downloads/test.mp4'))
if self.testMedia.Load(self.media):
pass
print("loaded")
else:
self.testMedia.Play()
wx.media.MediaCtrl()
print("Media not found")
self.quit(None)
def play(self, event):
self.testMedia.Play()
def quit(self, event):
self.Destroy()
if __name__ == '__main__':
app = wx.App()
Frame = TestPanel()
Frame.Show()
app.MainLoop()
I just want to add a simple video to a panel in wx.python, if anyone could please help it would be much appreciated.
Thanks
I suggest that you check that you can play the media file with something else before you start. It sounds obvious but it can save ridiculous amounts of wasted time.
Here is your own code, adapted a bit, to allow me to get a screen shot.
Let us know, if it is still giving you grief, after running it, with a valid, tested, video.
import wx
import wx.media
class TestPanel(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Media Player')
self.testMedia = wx.media.MediaCtrl(self, style=wx.SIMPLE_BORDER)
self.media = '/home/rolf/27343023_69.mp4'
self.testMedia.Bind(wx.media.EVT_MEDIA_LOADED, self.play)
self.testMedia.Bind(wx.media.EVT_MEDIA_FINISHED, self.quit)
self.Bind(wx.media.EVT_MEDIA_STOP, self.OnMediaStop, self.testMedia)
self.Bind(wx.EVT_CLOSE, self.quit)
if self.testMedia.Load(self.media):
pass
else:
print("Media not found")
self.quit(None)
self.Show()
def play(self, event):
self.testMedia.Play()
def quit(self, event):
self.testMedia.Stop()
self.Destroy()
# Sets the mp4 file in a loop for testing only
def OnMediaStop(self, event):
self.testMedia.Seek(0)
event.Veto()
if __name__ == '__main__':
app = wx.App()
Frame = TestPanel()
app.MainLoop()

windows system tray icon as a service

I have created a python application that runs as a service and has a system tray icon (for Windows). I am using wxPython as the wrapper program that calls the main program as a thread. The problem is that when I start the program as a service, the main program runs but the system tray icon is not created.
I have read that creating a system tray icon as a Windows service is not ideal but again have found conflicting information on that topic.
Here is my code:
import win32serviceutil
import win32service
import win32event
import servicemanager
import socket
import sys,os,time
#// Wx
import wx.adv
import wx #pip install wxpython
from wx.lib.embeddedimage import PyEmbeddedImage
from threading import Thread
#from pubsub import pub #pip install PyPubSub
TRAY_TOOLTIP = 'test5'
get_python_logo = PyEmbeddedImage(
"iVBORw0KGgoAAAANSUhEUgAAAA4AAAATCAIAAAAvYqvDAAAAA3NCSVQICAjb4U/gAAACjUlE"
"QVQokQXBW4/bRBgA0G++ufhux068abaQUvUmASovCHhC/HOe+oCQkFYVSKjbVQXsshuycXwb"
"ezw3ziH+6idvUJuAh/VvVzd5eRnnZZSEd7efpDx+9/3bqbnjTlL0CGb2oAk6QmxepFkeE2LP"
"5+N6U4QRs1NvnaKUEOoRqEXugWhtZLEKt0/K9ToLAnJRr7JEDGNjnSTcADUMGAWky2SHvmO8"
"IkkS8OiCI01FnKVSdWnIgFBvFvSGg4+8j8bJAY3BBoApj9bgwizbeh8xkQGJnAtRQ+BJyKKc"
"iixbbZ1DWKzSRC1OJOXiqYXAeO5pwJRDpeys7eSBRInUYLRarAGvs1SchplwSATEgrNOlvPs"
"hgnPHb3Ylf0EljBPnLMRE+Gxib0TJuEkT5jsv3z1w4+Q1Nfv3lPyYplkulpruwBZ5DC//erZ"
"k69fwPnhz19/YY4I1/QY7F5+8y3Eu+JlAMDAG6AWpkfABaax/fcxiHKG0c0fH2/Ebb17+gb6"
"CnnqgRFOEJbT6bbM+afr983hr/3TLbu8hA8f/0Yzns+2H3FxQVHVSs9q7gJhtVqiqGvxfrNZ"
"ITNtTOTr/Trmc5l4XJp1CqgbNx3rgk6nf54/r8vEE92wpW+dHP0sz3cd0NXcqhB8wvixeZxz"
"1h4OMG5xnuSpYVm8qUI7n2ZhuHeiCmOxsIt4M4SV792zej89DIUoC5oysF5Q1Mvw+RdvnKsN"
"xNp5RNx/9kqOD+vX9eHqXcgpsQYBVFZQzx/vTx/u207s9hNTPUw83RRVebj+edS/F5XFAhjo"
"AVCj0NapblTpeWjGkVOm1GRVi7QNMgU4AY0QnKEMw1AgopTzuem7VqrZ9d18/K9FEuZZ5ZwD"
"5/4H45tv6Xt4MTIAAAAASUVORK5CYII=")
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 ProgramThread(Thread):
"""Worker Thread Class."""
def __init__(self):
"""Init Worker Thread Class."""
Thread.__init__(self)
self.setDaemon(True)
self.start() # start the thread
def run(self):
"""Run Worker Thread."""
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(AppServerSvc)
servicemanager.StartServiceCtrlDispatcher()
class AppServerSvc (win32serviceutil.ServiceFramework):
_svc_name_ = "test5"
_svc_display_name_ = "test5"
_svc_description_name_ = "test5"
def __init__(self,args):
win32serviceutil.ServiceFramework.__init__(self,args)
self.hWaitStop = win32event.CreateEvent(None,0,0,None)
def SvcStop(self):
#wx.CallAfter(pub.sendMessage, "exit", event="exit")
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
win32event.SetEvent(self.hWaitStop)
def SvcDoRun(self):
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_,'Attemping to Start the Service.'))
self.main()
def main(self):
while True:
print 1
class TaskBarIcon(wx.adv.TaskBarIcon):
def __init__(self, frame):
self.frame = frame
super(TaskBarIcon, self).__init__()
self.set_icon(get_python_logo.GetIcon())
self.Bind(wx.adv.EVT_TASKBAR_LEFT_DOWN, self.on_left_down)
# create a pubsub receiver
#pub.subscribe(self.on_exit, "exit")
ProgramThread()
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):
print "set icon"
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
# Run the program
if __name__ == "__main__":
#// Whether called by user or service manager
if len(sys.argv) == 1:
app = App(False)
app.MainLoop()
else:
win32serviceutil.HandleCommandLine(AppServerSvc)

How to create a TaskBarIcon-only application in wxpython?

I'm trying to create an application in wxpython that only uses a TaskBarIcon and no frames.
There is a question on this here, but that example doesn't work for me; it just exits with no error.
The code I've written below is a much simplified version of the code I'm working with:
import wx
class Systray_Icon(wx.TaskBarIcon):
def __init__(self):
icon = wx.Icon('yellow.ico', wx.BITMAP_TYPE_ICO)
self.SetIcon(icon, "Test")
self.Bind(wx.EVT_MENU, self.Destroy(), id=wx.ID_EXIT)
def CreatePopupMenu(self):
menu = wx.Menu()
menu.Append(wx.ID_EXIT, "Quit")
return menu
app = wx.App()
sysicon = Systray_Icon()
app.MainLoop()
I'm getting the following error:
$ python2 systray.py
Traceback (most recent call last):
File "systray.py", line 15, in <module>
sysicon = TaskBarIcon()
File "systray.py", line 6, in __init__
self.SetIcon(icon, "Test")
File "/usr/lib/python2.7/dist-packages/wx-3.0-gtk2/wx/_windows.py", line 2841, in SetIcon
return _windows_.TaskBarIcon_SetIcon(*args, **kwargs)
TypeError: in method 'TaskBarIcon_SetIcon', expected argument 1 of type 'wxPyTaskBarIcon *'
So, my questions:
1: Why won't SetIcon accept my class? I've tried moving the SetIcon call to a function like in the question I linked, but it still doesn't work. I can fiddle around with it and probably get something to work, but I'd like to know the reason it won't work.
2: The question I linked to runs, but exits immediately. Is that because a TaskBarIcon won't hold MainLoop() open? What can I do about this?
Here is a working sample on Linux with python 2.7 wxpython 2.8:
import wx
TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = '/usr/share/pixmaps/thunderbird.xpm'
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):
wx.TaskBarIcon.__init__(self)
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.App()
TaskBarIcon()
app.MainLoop()
if __name__ == '__main__':
main()
EDIT:
For anyone still getting grief this is a version that essentially uses a dummy frame.
Edit 2019: Updated for python3/wxpython 4+
import wx
import wx.adv
TRAY_TOOLTIP = 'System Tray Demo'
TRAY_ICON = '/usr/share/pixmaps/python.xpm'
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):
wx.adv.TaskBarIcon.__init__(self)
self.myapp_frame = frame
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, 'Say Hello', self.on_hello)
menu.AppendSeparator()
create_menu_item(menu, 'Exit', self.on_exit)
return menu
def set_icon(self, path):
icon = wx.Icon(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):
self.myapp_frame.Close()
class My_Application(wx.Frame):
#----------------------------------------------------------------------
def __init__(self):
wx.Frame.__init__(self, None, wx.ID_ANY, "", size=(1,1))
panel = wx.Panel(self)
self.myapp = TaskBarIcon(self)
self.Bind(wx.EVT_CLOSE, self.onClose)
#----------------------------------------------------------------------
def onClose(self, evt):
"""
Destroy the taskbar icon and the frame
"""
self.myapp.RemoveIcon()
self.myapp.Destroy()
self.Destroy()
if __name__ == "__main__":
MyApp = wx.App()
My_Application()
MyApp.MainLoop()
OK, figured it out the next day, after 30 minutes of messing about.
SetIcon wasn't accepting my class because I needed to add the following:
super(TaskBarIcon, self).__init__()
to __init__.
I suspect it's duck typing biting me here; without the wx.TaskBarIcon constructor running, python doesn't see it as a wx.TaskBarIcon object.
To keep the application open with only a TaskBarIcon, create a derived class from wx.App and override the OnInit function, like so:
class App(wx.App):
def OnInit(self):
self.SetTopWindow(wx.Frame(None, -1))
TaskBarIcon()
return True

wxPython on system tray draw

I got a little issue concerning wxPython and the system tray.
Well, all I use is the system tray and I want to have it call a function right after the system tray got drawn, but EVT_PAINT seems to have no effect on the tray for me.
Anyways, here is my code:
#!/usr/bin/python2
import wxversion
wxversion.select('2.8')
import pynotify,subprocess,wx,os,json
#config
TRAY_TOOLTIP = 'Backups'
PROG_DIR = os.path.dirname(os.path.realpath(__file__))
class Config:
def __init__(self):
self.readFile()
def readFile(self):
jsons = ''
searchingJson = True
f = open(PROG_DIR+'/config.json')
lines = f.readlines()
f.close()
self.json = json.loads(lines)
pynotify.init("Backups")
def backup():
sysTray.set_icon(PROG_DIR+'/loading.png')
pynotify.Notification("Backups","Starting Backup",PROG_DIR+'/backup.png').show()
if subprocess.call(["rsync","-a",config.json['backup_dir'],"--exclude",".*",config.json['remote_dir']]):
pynotify.Notification("Backups","Backup Failed",PROG_DIR+'/backup.png').show()
else:
pynotify.Notification("Backups","Backup complete!",PROG_DIR+'/backup.png').show()
sysTray.set_icon(PROG_DIR+'/backup.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(PROG_DIR+'/backup.png')
self.Bind(wx.EVT_PAINT,self.initial_backup)
def CreatePopupMenu(self):
menu = wx.Menu()
create_menu_item(menu,'Start Backup',self.on_backup)
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_exit(self,event):
wx.CallAfter(self.Destroy)
def on_backup(self,event):
backup()
def initial_backup(self,event):
print 'test'
def main():
app = wx.App(False)
sysTray = TaskBarIcon()
app.MainLoop()
if __name__ == '__main__':
main()
Nothing gets printed to the terminal.
Thanks for help!

How to add OSX menu bar icon with wxPython

I would like to add an icon to the OSX menu bar at the top of the screen using wxPython. I have tried wx.TaskBarIcon, which adds a System Tray icon in Windows, but this doesn't work - it changes the Dock icon for the app instead. Does anyone know how to do this?
It seems that with wxPython2.9-osx-cocoa-py2.7 you can in fact put up a menubar icon. It looks like you can also call PopupMenu() on TaskBarIcon to attach a menu, which you should be able to use to create a full blown OSX menu bar application.
import wx
class TaskBarFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, style=wx.FRAME_NO_TASKBAR |
wx.NO_FULL_REPAINT_ON_RESIZE)
self.tbicon = wx.TaskBarIcon()
icon = wx.Icon('myicon.ico', wx.BITMAP_TYPE_ICO)
self.tbicon.SetIcon(icon, '')
app = wx.App(False)
frame = TaskBarFrame(None)
frame.Show(False)
app.MainLoop()
Answer provided here on Google Groups - in summary, you can't do it.
It seems now you can:
http://wiki.wxpython.org/Custom%20Mac%20OsX%20Dock%20Bar%20Icon?highlight=%28wx%5C.TaskBarIcon%29
just copy and paste the code and run it ;)
#!/usr/bin/env pythonw
import wx
import wx.lib.embeddedimage
WXPdemo = wx.lib.embeddedimage.PyEmbeddedImage(
"iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAWlJ"
"REFUWIW1V1sSwjAIBMebeBU9db2KZ8EPmxbCI4TUnXGskWaXDQktwhjErjERP4XRhER08iPi"
"5SKiyQR5JyI7xxB3j7wn5GI6V2hFxM0gJtjYANFBiIjQu7L/1lYlwR0QxLDZhE0II1+CtwRC"
"RI8riBva7DL7CC9VAwDbbxwKtdDXwBi7K+1zCP99T1vDFedd8FBwYd6BCAUXuACEF7QsbET/"
"FaHs+gDQw4vOLNHkMojAnTw8nlNipIiwmR0DCXJbjCXkFCAL23BnpQgRWt1EMbyujCK9AZzZ"
"f+b3sX0oSqJQ6EorFeT4NiL6Wtj0+LXnQAzThYoAAsN6ehqR3sHExmcEqGeFApQLcTvm5Kt9"
"wkHGgb+RZwSkyc1dwOcpCtCoNKSz6FRCUQ3o7Nn+5Y+Lg+y5CIXlcyAk99ziiQS32+svz/UY"
"vClJoLpIC8gi+VwwfDecEiEtT/WZTJDf94uk1Ru8vbz0cvoF7S2DnpeVL9UAAAAASUVORK5C"
"YII=")
class DemoTaskBarIcon(wx.TaskBarIcon):
TBMENU_RESTORE = wx.NewId()
TBMENU_CLOSE = wx.NewId()
TBMENU_CHANGE = wx.NewId()
TBMENU_REMOVE = wx.NewId()
def __init__(self, frame):
wx.TaskBarIcon.__init__(self)
self.frame = frame
# Set the image
icon = self.MakeIcon(WXPdemo.GetImage())
self.SetIcon(icon, "wxPython Demo")
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)
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, "Restore wxPython Demo")
menu.Append(self.TBMENU_CLOSE, "Close wxPython Demo")
return menu
def MakeIcon(self, img):
"""
The various platforms have different requirements for the
icon size...
"""
if "wxMSW" in wx.PlatformInfo:
img = img.Scale(16, 16)
elif "wxGTK" in wx.PlatformInfo:
img = img.Scale(22, 22)
# wxMac can be any size upto 128x128, so leave the source img alone....
icon = wx.IconFromBitmap(img.ConvertToBitmap() )
return icon
def OnTaskBarActivate(self, evt):
if self.frame.IsIconized():
self.frame.Iconize(False)
if not self.frame.IsShown():
self.frame.Show(True)
self.frame.Raise()
def OnTaskBarClose(self, evt):
wx.CallAfter(self.frame.Close)
class MainFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="Hello World")
self.tbicon = DemoTaskBarIcon(self)
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
def OnCloseWindow(self, evt):
self.tbicon.Destroy()
evt.Skip()
app = wx.App(redirect=False)
frame = MainFrame(None)
frame.Show(True)
app.MainLoop()

Categories