简体   繁体   中英

How to add system tray to my tkinter application and avoid using lots of pywin32 code(Just on windows)?

I find three ways to make system tray:

  1. Tkinter: How to make a system tray application? This answer was a little abstract, especially for we didn't study about tcl.
  2. How to build a SystemTray app for Windows? The accepted answer used lots of pywin32 code which looked a little tedious.
  3. Another answer mentioned 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?

The solution with Winico :

An example GIF image: https://imgur.com/a/vulO6Mo

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM