Reputation: 12672
I find three ways to make system tray:
infi.systray
and pystray
.I found them need to use some extra threads to create the system tray by looking up documentation.But refer to "All Tcl commands need to originate from the same thread",I think it is not a good practice to call .deiconify()
in other threads.Could someone show me a minimal example to use winico
or something else without using extra thread?
Upvotes: 3
Views: 3753
Reputation: 12672
The solution with Winico
:
An example GIF image: https://i.sstatic.net/LTX1D.jpg
You should download it firstly.
Download :32-bit, 64-bit(It is a little hard to find it,I finally find it in the deep of Google.Just download the pkg
in the website and rename it as Winico
is okay.)
After download it, you need to move it to yourPythonPath/tcl
How to use it to make a system tray with
Winico
?
A minimal example:
import tkinter as tk
from tkinter import messagebox
class App(tk.Tk):
def __init__(self):
super(App, self).__init__()
self.protocol("WM_DELETE_WINDOW", self.on_closing)
self.trayMenu = None
def on_closing(self):
if not self.trayMenu: # when system tray is not exists.
selection = messagebox.askyesnocancel("Tips", "Quit directly?\nYes : Quit.\nNo:Minimize to system tray.") # "Yes" will return True, "Cancel" will return None, "No" will return False.
if selection: # when select yes, quit the app directly.
self.destroy()
elif selection == False: # Minimize to system tray.
# make a system tray
self.withdraw()
# use bulitin tk.Menu
# The work about "Winico"
self.tk.call('package', 'require', 'Winico') # use the tcl "winico", make sure the folder of "winico" is in the same path.
icon = self.tk.call('winico', 'createfrom', '2.ico') # this is the icon on the system tray.
self.tk.call('winico', 'taskbar', 'add', icon, # set the icon
'-callback', (self.register(self.menu_func), '%m', '%x', '%y'), # refer to winico documentation.
'-pos', 0,
'-text', u'jizhihaoSAMA’s Tool') # the hover text of the system tray.
# About menu
self.trayMenu = tk.Menu(self, tearoff=False)
self.trayMenu.add_command(label="Show my app", command=self.deiconify)
# You could also add a cascade menu
cascadeMenu = tk.Menu(self, tearoff=False)
cascadeMenu.add_command(label="Casacde one", command=lambda :print("You could define it by yourself"))
cascadeMenu.add_command(label="Cascade two")
self.trayMenu.add_cascade(label="Other", menu=cascadeMenu)
self.trayMenu.add_separator() # you could add a separator
self.trayMenu.add_command(label="Quit", command=self.destroy)
# you could also add_command or add_checkbutton for what you want
else: # This is cancel operation
pass
else:
self.withdraw() # when system tray exists, hide the window directly.
def menu_func(self, event, x, y):
if event == 'WM_RBUTTONDOWN': # Mouse event, Right click on the tray.Mostly we will show it.
self.trayMenu.tk_popup(x, y) # pop it up on this postion
if event == 'WM_LBUTTONDOWN': # Mouse event, Left click on the tray,Mostly we will show the menu.
self.deiconify() # show it.
# All the Mouse event:
# WM_MOUSEMOVE
# WM_LBUTTONDOWN
# WM_LBUTTONUP
# WM_LBUTTONDBLCLK
# WM_RBUTTONDOWN
# WM_RBUTTONUP
# WM_RBUTTONDBLCLK
# WM_MBUTTONDOWN
# WM_MBUTTONUP
# WM_MBUTTONDBLCLK
app = App()
app.mainloop()
What should I do when I want to use
Pyinstaller
to pack it?
In the spec file:
a = Analysis(['script.py'],
pathex=['xxxx'],
binaries=[],
datas=[('Winico', 'winico')], # You need to revise this
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
Or just do:
pyinstaller -F --add-data "Winico;winico" script.py
Refer to a Chinese blog,I email the blog writer and get some help from him.
Not sure whether it could work on Linux or Unix.
Upvotes: 3