wxpython systray icon menu - python

I'm designing an application that I want to run in the background. There isn't any user interaction necessary, so I want the app to run invisibly save for a systray icon. I want that icon to have a menu that just opens the config/help files in notepad. Could someone point me in the right direction or provide an example?

You can probably do this more cleanly but I have using some samples a while back I was able to create myself a class to handle the basic contruction of a taskbar icon.
TaskBarIcon.py
import wx
ID_SHOW_OPTION = wx.NewId()
ID_EDIT_OPTION = wx.NewId()
class Icon(wx.TaskBarIcon):
def __init__(self, parent, icon, tooltip):
wx.TaskBarIcon.__init__(self)
self.SetIcon(icon, tooltip)
self.parent = parent
self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.OnLeftDClick)
self.CreateMenu()
def CreateMenu(self):
self.Bind(wx.EVT_TASKBAR_RIGHT_UP, self.OnPopup)
self.menu = wx.Menu()
self.menu.Append(ID_SHOW_OPTION, '&Show Option 1')
self.menu.Append(ID_EDIT_OPTION, '&Edit Option 2')
self.menu.AppendSeparator()
self.menu.Append(wx.ID_EXIT, 'E&xit')
def OnPopup(self, event):
self.PopupMenu(self.menu)
def OnLeftDClick(self, event):
if self.parent.IsIconized():
self.parent.Iconize(False)
if not self.parent.IsShown():
self.parent.Show(True)
self.parent.Raise()
Within your Frame's init(), add the two lines below:
self.TrayIcon = tbi.Icon(self, wx.Icon("C:\\YourIcon.png", wx.BITMAP_TYPE_PNG), "ToolTip Help Text Here")
self.Bind(wx.EVT_ICONIZE, self.OnIconify)
And now just add this function to your frame and you should be set:
def OnIconify(self, event):
self.Hide()
Just remember to edit the items in the Icon class to suit your needs.

Have you considered running this application as a windows service? Many users will consider a system tray icon with little to no functionality a nuisance. You could still provide links to help/config files as a start menu entry.
The python win32 extensions package should have support for python services.
Of course, there are still reasons why you may want to run this as a system tray icon. I'm sorry that I don't have any experience with that.

You want the wx.TaskBarIcon:
http://docs.wxwidgets.org/stable/wx_wxtaskbaricon.html
The wxPython Demo has example code you can look at.

Related

How to use QSignalMapper with QActions created dynamically?

I'd like to create a dynamic menu which enumerates all QDockWidget from my QMainWindow and allows to show/hide the QDockWidgets, so far I got this code:
class PluginActionsViewDocks():
def __init__(self, main_window):
self.main_window = main_window
mapper = QSignalMapper(self.main_window)
self.actions = []
for dock in main_window.findChildren(QtWidgets.QDockWidget):
action = create_action(
main_window, dock.windowTitle(),
slot=mapper.map,
tooltip='Show {0} dock'.format(dock.windowTitle())
)
mapper.setMapping(action, dock)
self.actions.append(action)
mapper.mapped.connect(self.toggle_dock_widget)
help_menu = main_window.menuBar().addMenu('&View')
setattr(help_menu, "no_toolbar_policy", True)
add_actions(help_menu, tuple(self.actions))
def toggle_dock_widget(self, dock_widget):
print("toggle_dock_widget")
The menu is populated with all QDockWidget windowTitles but when i press each of them the slot toggle_dock_widget is not called. create_action is a helper which creates the QAction and connect the triggered signal to slot.
The thing is, I don't really understand quite well how QSignalMapper works but my intuition tells me it's the right choice for this particular problem.
What could I be missing here?
There's aleady a built-in dock-widget menu. Just right-click any dock title-bar, or any tool-bar or menu-bar. See: QMainWindow::createPopupMenu.
PS:
The reason why your QSignalMapper code doesn't work is probably because you are connecting to the wrong overload of the mapped signal. Try this instead:
mapper.mapped[QtWidgets.QWidget].connect(self.toggle_dock_widget)

How to open a popup window with a spinner in python + Gtk

I have a python Gtk application, with the GUI designed in Glade. It has a "scan" feature which will scan the network for a few seconds and then report its results to the user. During the scanning I want a popup window to appear stealing the focus from the parent until scanning is done.
I use a threading.Lock to synchronize the GUI and the scan thread, which makes the popup to last exactly the right time I want (see scanLock.acquire() ). It seems straightforward to me to implement something like a show() and hide() call before and after the scanLock.acquire(). I did use waitPopupShow and waitPopupHide instead of just calling the window.show() and window.hide() because I also may want to set the Label in the popup or start/stop the GtkSpinner. Here is some code from the GUI class:
def scan(self):
sT = scannerThread(self,self.STagList)
self.dataShare.threadsList.append(sT)
sT.start() # start scanning
self.waitPopupShow('Scanning... Please Wait')
self.scanLock.acquire() # blocks here until scan is finished
self.waitPopupHide()
def waitPopupShow(self, msg): # shows a GtkSpinner until the semaphore is cleared
self.waitDialogLabel.set_text(msg)
self.waitDialogBox.show_all()
self.waitDialog.show()
self.waitDialogSpinner.start()
def waitPopupHide(self):
# how to get the handle to the spinner and stop it?
self.waitDialogSpinner.stop()
self.waitDialog.hide()
def getAll(self):
# GUI
self.builder = Gtk.Builder()
self.builder.add_from_file(path to main GUI)
# ... getting stuff from a first glade file
# getting stuff from the waitDialog glade file
self.builder.add_from_file(path to waitDialog GUI)
self.waitDialog = self.builder.get_object("waitDialog") # GtkWindow
self.waitDialogBox = self.builder.get_object("waitDialogBox") # GtkBox
self.waitDialogLabel = self.builder.get_object("waitDialogLabel") # GtkLabel
self.waitDialogSpinner = self.builder.get_object("waitDialogSpinner") # GtkSpinner
self.waitDialog.hide()
I'm trying hardly since a couple of days to show a dialog with a label and a Gtk.Spinner. The best I obtain at the moment is to have the window showing up with no content. Please note that the self.waitDialog.hide() right after getting it with self.builder.get_object is needed because I set the property of the waitDialog Gtkwindow to Visibile. If I stop with the debugger before .hide() the waitDialog shows up perfectly. Afterwards its broken.
This is the waitDialog GUI file: http://pastebin.com/5enDQg3g
So my best guess is that I'm dooing something wrong, and I could find nothing on creating a new Gtk window over the main one, only basic examples and dialogs. A pointer to the documentation saying a bit about this would be a good starting point...

wxpython change message dialog font?

Python 2.7.3 x64
wxPython 2.8 x64
I'm having trouble changing the font of the wxpython message dialog box. I'd like to use a fixed-width font (I think wxFAMILY_MODERN is) to control the formatting of the output. Here's the code I'm using to test with ...
def infDialog (self, msg, title):
""" Display Info Dialog Message """
font = wx.Font(14, wx.MODERN, wx.NORMAL, wx.NORMAL)
style = wx.OK | wx.ICON_INFORMATION | wx.STAY_ON_TOP
dialog = wx.MessageDialog(self, msg, title, style)
dialog.CenterOnParent()
dialog.SetFont(font)
result = dialog.ShowModal()
if result == wx.ID_OK:
print dialog.GetFont().GetFaceName()
dialog.Destroy()
return
# End infDialog()
But the results when I click OK are always "Arial". Any thoughts? Perhaps I need to make a custom dialog class?
Thanks,
-RMWChaos
The wx.MessageDialog is a wrapper around the OS / system message dialog. I suspect each OS only allows so much editing or none at all. So yes, using a custom wx.Dialog or the generic message dialog widget (wx.lib.agw.genericmessagedialog) is the way to go if fonts are important.
I was looking all over the internet and found that no one actually took the time to answer the question, instead just telling users to create their own dialog class. There is actually an easy way in which you should be able to change the font of your message dialog. Grabbing the dialog's textCtrl object and then setting the font of that object should do the trick.
Simply replace:
dialog.SetFont(font)
with
txt_ctrl = dialog.text
txt_ctrl.SetFont(font)
Let me know if this works for you.

display a menu bar icon on osx

I'd like to have my application display an icon in OSX menu bar (top of screen where Growl sits). How would I do this using Python? (I understand this is not possible using wxPython but I am not after a wxPython specific solution).
Thanks!
An implementation of this may be found at:
https://web.archive.org/web/20080709014939/http://the.taoofmac.com/space/blog/2007/04/22/1745
http://the.taoofmac.com/space/blog/2007/04/22/1745
The API for displaying icons in the OS X menubar is called NSStatusItem. It's going to be difficult or impossible to use from a wxPython application, though -- you will probably have to write your application using PyObjC to use it effectively.
The rumps package makes this very easy. Here's an example from rumps's README:
import rumps
class AwesomeStatusBarApp(rumps.App):
#rumps.clicked("Preferences")
def prefs(self, _):
rumps.alert("jk! no preferences available!")
#rumps.clicked("Silly button")
def onoff(self, sender):
sender.state = not sender.state
#rumps.clicked("Say hi")
def sayhi(self, _):
rumps.notification("Awesome title", "amazing subtitle", "hi!!1")
if __name__ == "__main__":
AwesomeStatusBarApp("Awesome App").run()

Tkinter Label bound to StringVar is one click behind when updating

The problem I'm running into here is that, when I click on the different file names in the Listbox, the Label changes value one click behind whatever I'm currently clicking on.
What am I missing here?
import Tkinter as tk
class TkTest:
def __init__(self, master):
self.fraMain = tk.Frame(master)
self.fraMain.pack()
# Set up a list box containing all the paths to choose from
self.lstPaths = tk.Listbox(self.fraMain)
paths = [
'/path/file1',
'/path/file2',
'/path/file3',
]
for path in paths:
self.lstPaths.insert(tk.END, path)
self.lstPaths.bind('<Button-1>', self.update_label)
self.lstPaths.pack()
self.currentpath = tk.StringVar()
self.lblCurrentPath = tk.Label(self.fraMain, textvariable=self.currentpath)
self.lblCurrentPath.pack()
def update_label(self, event):
print self.lstPaths.get(tk.ACTIVE),
print self.lstPaths.curselection()
self.currentpath.set(self.lstPaths.get(tk.ACTIVE))
root = tk.Tk()
app = TkTest(root)
root.mainloop()
The problem has to do with the fundamental design of Tk. The short version is, bindings on specific widgets fire before the default class bindings for a widget. It is in the class bindings that the selection of a listbox is changed. This is exactly what you observe -- you are seeing the selection before the current click.
The best solution is to bind to the virtual event <<ListboxSelect>> which is fired after the selection has changed. Other solutions (unique to Tk and what gives it some of its incredible power and flexibility) is to modify the order that the bindings are applied. This involves either moving the widget bindtag after the class bindtag, or adding a new bindtag after the class bindtag and binding it to that.
Since binding to <<ListboxSelect>> is the better solution I won't go into details on how to modify the bindtags, though it's straight-forward and I think fairly well documented.

Categories

Resources