当前位置:网站首页>tkinter minimize to tray

tkinter minimize to tray

2022-09-23 08:36:53Weapon Master 72

背景

I usually use it myselftkinter开发一些小工具,Some tools need to work consistently,This kind of program is better hidden in the tray.于是上网搜索,Learn how to developtkinterThe minimize to tray program.这里做下记录,方便使用.

前提

The prerequisite is to have the following code,Just copy it and save it.for use,Not much to explain the implementation code.

SysTrayIcon.py:Renders the right-click menu of the tray icon based on the incoming menu and callback,设置菜单回调;Also encapsulatedWindow消息,and exit callback.It is convenient to realize the function you want.

test.py:测试Demo主程序

If the program continues to run in the background,需要将 activation The function is used in a separate thread.

完整代码

Both files can be placed in the same folder
test.py


import os, tkinter as tk
from SysTrayIcon import SysTrayIcon

class _Main:  # 调用SysTrayIcon的Demo窗口
    def __init__(s):
        s.SysTrayIcon = None  # Determine whether to open the system tray icon

    def main(s):
        # tk窗口
        s.root = tk.Tk()
        s.root.bind("<Unmap>", # UnmapTriggered when it is hidden
                    lambda event: s.Hidden_window() if s.root.state() == 'iconic' else False)  # Window minimization judgment,Arguably the most important step in calling
        s.root.protocol('WM_DELETE_WINDOW', s.exit)  # 点击TkCalled directly when the window is closeds.exit,Do not use default off
        s.root.resizable(0, 0)  # The lock window size cannot be changed
        s.root.geometry("800x500")
        s.root.title("Mobile phone remote debugging toolV1.0_By:Qi Le")
        s.root.mainloop()


    def switch_icon(s, _sysTrayIcon, icon='D:\\2.ico'):
        # 点击右键菜单项目会传递SysTrayIcon自身给引用的函数,所以这里的_sysTrayIcon = s.sysTrayIcon
        # Just an example of changing the icon,You can delete this function if you don't need it
        _sysTrayIcon.icon = icon
        _sysTrayIcon.refresh()

        # Example of a bubble prompt
        s.show_msg(title='图标更换', msg='The icon was replaced successfully!', time=500)

    def show_msg(s, title='标题', msg='内容', time=500):
        s.SysTrayIcon.refresh(title=title, msg=msg, time=time)

    def Hidden_window(s, icon='D:\\1.ico', hover_text="SysTrayIcon.py Demo"):
        '''Hide the window to the tray area,调用SysTrayIcon的重要函数'''

        # Tray icon right-click menu, 格式: ('name', None, callback),Below is also an example of a secondary menu
        # 24Rows are added automatically‘退出’,Unnecessary can be deleted
        menu_options = (('一级 菜单', None, s.switch_icon),
                        ('二级 菜单', None, (('更改 图标', None, s.switch_icon),)))

        s.root.withdraw()  # 隐藏tk窗口
        if not s.SysTrayIcon: s.SysTrayIcon = SysTrayIcon(
            icon,  # 图标
            hover_text,  # The cursor stays on to display the text
            menu_options,  # 右键菜单
            on_quit=s.exit,  # 退出调用
            tk_window=s.root,  # Tk窗口
        )
        s.SysTrayIcon.activation()

    def exit(s, _sysTrayIcon=None):
        s.root.destroy()
        print('exit...')


if __name__ == '__main__':
    Main = _Main()



    Main.main()

SysTrayIcon.py


import win32api, win32con, win32gui_struct, win32gui
import os
class SysTrayIcon(object):
    '''SysTrayIconClass for displaying taskbar icons'''
    QUIT = 'QUIT'
    SPECIAL_ACTIONS = [QUIT]
    FIRST_ID = 5320

    def __init__(s, icon, hover_text, menu_options, on_quit, tk_window=None, default_menu_index=None,
                 window_class_name=None):
        ''' icon The path to the icon file that needs to be displayed hover_text Text displayed when the mouse hovers over the icon menu_options 右键菜单,格式: (('a', None, callback), ('b', None, (('b1', None, callback),))) on_quit Pass the exit function,Runs along with the execution exit tk_window 传递Tk窗口,s.root,Used to click the icon to display the window default_menu_index The right-click menu number that is not displayed window_class_name 窗口类名 '''
        s.icon = icon
        s.hover_text = hover_text
        s.on_quit = on_quit
        s.root = tk_window

        # Add exit to right-click menu
        menu_options = menu_options + (('退出', None, s.QUIT),)
        # Initialize the tray program for each optionID,后面的依次+1
        s._next_action_id = s.FIRST_ID
        s.menu_actions_by_id = set()
        print(menu_options,type(menu_options),'menu_options')
        s.menu_options = s._add_ids_to_menu_options(list(menu_options))
        s.menu_actions_by_id = dict(s.menu_actions_by_id)
        print(s.menu_actions_by_id,'menu_actions_by_id')
        del s._next_action_id

        s.default_menu_index = (default_menu_index or 0)
        s.window_class_name = window_class_name or "SysTrayIconPy"

        message_map = {
    win32gui.RegisterWindowMessage("TaskbarCreated"): s.restart,
                       win32con.WM_DESTROY: s.destroy,
                       win32con.WM_COMMAND: s.command,
                       win32con.WM_USER + 20: s.notify, }
        # 注册窗口类.
        wc = win32gui.WNDCLASS()
        wc.hInstance = win32gui.GetModuleHandle(None)
        wc.lpszClassName = s.window_class_name
        wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW;
        wc.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW)
        wc.hbrBackground = win32con.COLOR_WINDOW
        wc.lpfnWndProc = message_map  # 也可以指定wndproc.
        s.classAtom = win32gui.RegisterClass(wc)

    def activation(s):
        '''Activate the taskbar icon,No need to recreate a new tray icon every time'''
        hinst = win32gui.GetModuleHandle(None)  # 创建窗口.
        style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
        s.hwnd = win32gui.CreateWindow(s.classAtom,
                                       s.window_class_name,
                                       style,
                                       0, 0,
                                       win32con.CW_USEDEFAULT,
                                       win32con.CW_USEDEFAULT,
                                       0, 0, hinst, None)
        win32gui.UpdateWindow(s.hwnd)
        s.notify_id = None
        s.refresh(title='The software is in the background!', msg='点击重新打开', time=500)

        win32gui.PumpMessages()

    def refresh(s, title='', msg='', time=500):
        '''刷新托盘图标 title 标题 msg 内容,If it is empty, no prompt will be displayed time The prompt shows the time'''
        hinst = win32gui.GetModuleHandle(None)
        if os.path.isfile(s.icon):
            icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
            hicon = win32gui.LoadImage(hinst, s.icon, win32con.IMAGE_ICON,
                                       0, 0, icon_flags)
        else:  # 找不到图标文件 - 使用默认值
            hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)

        if s.notify_id:
            message = win32gui.NIM_MODIFY
        else:
            message = win32gui.NIM_ADD

        s.notify_id = (s.hwnd, 0,  # 句柄、托盘图标ID
                       win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP | win32gui.NIF_INFO,
                       # Identifiers of functions that can be used by the tray icon
                       win32con.WM_USER + 20, hicon, s.hover_text,  # 回调消息ID、托盘图标句柄、图标字符串
                       msg, time, title,  # 提示内容、The prompt shows the time、提示标题
                       win32gui.NIIF_INFO  # 提示用到的图标
                       )
        win32gui.Shell_NotifyIcon(message, s.notify_id)

    def show_menu(s):
        '''显示右键菜单'''
        menu = win32gui.CreatePopupMenu()
        s.create_menu(menu, s.menu_options)

        pos = win32gui.GetCursorPos()
        win32gui.SetForegroundWindow(s.hwnd)
        win32gui.TrackPopupMenu(menu,
                                win32con.TPM_LEFTALIGN,
                                pos[0],
                                pos[1],
                                0,
                                s.hwnd,
                                None)
        win32gui.PostMessage(s.hwnd, win32con.WM_NULL, 0, 0)

    # Option to organize menus,添加ID.递归添加,Add the secondary menu page to it
    def _add_ids_to_menu_options(s, menu_options):
        result = []
        for menu_option in menu_options:
            option_text, option_icon, option_action = menu_option
            if callable(option_action) or option_action in s.SPECIAL_ACTIONS:
                s.menu_actions_by_id.add((s._next_action_id, option_action))
                result.append(menu_option + (s._next_action_id,))
            else:
                result.append((option_text,
                               option_icon,
                               s._add_ids_to_menu_options(option_action),
                               s._next_action_id))
            s._next_action_id += 1
        print(result,'result')
        return result

    def restart(s, hwnd, msg, wparam, lparam):
        s.refresh()

    def destroy(s, hwnd=None, msg=None, wparam=None, lparam=None, exit=1):
        nid = (s.hwnd, 0)
        win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
        win32gui.PostQuitMessage(0)  # 终止应用程序.
        if exit and s.on_quit:
            s.on_quit()  # Need to pass itself in the past tense s.on_quit(s)
        else:
            s.root.deiconify()  # 显示tk窗口

    def notify(s, hwnd, msg, wparam, lparam):
        '''鼠标事件'''
        if lparam == win32con.WM_LBUTTONDBLCLK:  # 双击左键
            pass
        elif lparam == win32con.WM_RBUTTONUP:  # 右键弹起
            s.show_menu()
        elif lparam == win32con.WM_LBUTTONUP:  # 左键弹起
            s.destroy(exit=0)
        return True
        """ possible mouse events: WM_MOUSEMOVE #The cursor goes over the icon WM_LBUTTONDOWN #左键按下 WM_LBUTTONUP #左键弹起 WM_LBUTTONDBLCLK #双击左键 WM_RBUTTONDOWN #右键按下 WM_RBUTTONUP #右键弹起 WM_RBUTTONDBLCLK #双击右键 WM_MBUTTONDOWN #滚轮按下 WM_MBUTTONUP #Roller pops up WM_MBUTTONDBLCLK #Double-click the scroll wheel """

    def create_menu(s, menu, menu_options):
        for option_text, option_icon, option_action, option_id in menu_options[::-1]:
            if option_icon:
                option_icon = s.prep_menu_icon(option_icon)

            if option_id in s.menu_actions_by_id:
                item, extras = win32gui_struct.PackMENUITEMINFO(text=option_text,
                                                                hbmpItem=option_icon,
                                                                wID=option_id)
                win32gui.InsertMenuItem(menu, 0, 1, item)
            else:
                submenu = win32gui.CreatePopupMenu()
                s.create_menu(submenu, option_action)
                item, extras = win32gui_struct.PackMENUITEMINFO(text=option_text,
                                                                hbmpItem=option_icon,
                                                                hSubMenu=submenu)
                win32gui.InsertMenuItem(menu, 0, 1, item)

    def prep_menu_icon(s, icon):
        # 加载图标.
        ico_x = win32api.GetSystemMetrics(win32con.SM_CXSMICON)
        ico_y = win32api.GetSystemMetrics(win32con.SM_CYSMICON)
        hicon = win32gui.LoadImage(0, icon, win32con.IMAGE_ICON, ico_x, ico_y, win32con.LR_LOADFROMFILE)

        hdcBitmap = win32gui.CreateCompatibleDC(0)
        hdcScreen = win32gui.GetDC(0)
        hbm = win32gui.CreateCompatibleBitmap(hdcScreen, ico_x, ico_y)
        hbmOld = win32gui.SelectObject(hdcBitmap, hbm)
        brush = win32gui.GetSysColorBrush(win32con.COLOR_MENU)
        win32gui.FillRect(hdcBitmap, (0, 0, 16, 16), brush)
        win32gui.DrawIconEx(hdcBitmap, 0, 0, hicon, ico_x, ico_y, 0, 0, win32con.DI_NORMAL)
        win32gui.SelectObject(hdcBitmap, hbmOld)
        win32gui.DeleteDC(hdcBitmap)

        return hbm

    def command(s, hwnd, msg, wparam, lparam):
        id = win32gui.LOWORD(wparam)
        s.execute_menu_option(id)

    def execute_menu_option(s, id):
        menu_action = s.menu_actions_by_id[id]
        if menu_action == s.QUIT:
            win32gui.DestroyWindow(s.hwnd)
        else:
            menu_action(s)
原网站

版权声明
本文为[Weapon Master 72]所创,转载请带上原文链接,感谢
https://chowdera.com/2022/266/202209230812185253.html

随机推荐