python --windos系统托盘pystray/自实现
pip install pystray
示例
import os
import signal
import subprocess
import tempfile
def open_chromium():
'''系统托盘重启界面'''
temp_profile_dir = os.path.join(tempfile.gettempdir(), "flaskwebgui")
launch_options = [f"--window-size=1300,800"]
options = [
get_default_chrome_path(),
f"--user-data-dir={temp_profile_dir}",
"--new-window",
"--no-first-run",
] + launch_options + [f'--app=http://127.0.0.1:8001']
subprocess.Popen(options, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
def _kill():
ThreadPid.update(query={'arrange_id': 1}, data={'status': 0})
logger.error('调用用户退出')
handle = os.getpid()
logger.error(f'pid={handle}')
os.kill(handle, signal.SIGABRT)
image = Image.open('logo.ico')
icon = pystray.Icon('Neural', image, menu=pystray.Menu(
pystray.MenuItem('打开主界面', open_chromium),
pystray.MenuItem('退出', _kill)))
icon.run_detached() #【注】 后台运行(必须有父进程)
# ThreadPid.update(query={'arrange_id': 1}, data={'status': 1})
# icon.run() #【注】 阻塞运行;
from PIL import Image
import pystray
image = Image.open('logo.ico')
icon = pystray.Icon('Neural', image, title='11', menu=pystray.Menu(
pystray.MenuItem('打开主界面', lambda x: print(1), default=True), # 鼠标被单击事件
pystray.Menu.SEPARATOR,
pystray.MenuItem('退出', lambda x: print(2)),
pystray.Menu.SEPARATOR,
))
icon.run() # 【注】 此方式为阻塞
import win32con
import win32gui
import time
import abc
from threading import Thread
class Pystray(object):
'''windows添加系统托盘'''
def __init__(self, ico_path: str, ico_class_name: str, window_name: str='mywindowclass'):
'''
@params ico_path --> 系统托盘图标路径;
@params ico_class_name --> 图标名;
@params window_name --> 窗口类(非必要);
'''
self._ico_path: str = ico_path
self._ico_class_name = ico_class_name
self.__window_name = window_name
self._menu = {} # {1024: ('菜单名', fun1), 1025: ('菜单名1', fun2)}
@property
def menu(self): return self._menu
@menu.setter
def menu(self, value: dict):
if isinstance(value, dict):
self._menu.update(value)
else:
raise TypeError('参数必须为字典;{1024: (菜单名, fun1), 1025: (菜单名1, fun2)}')
@abc.abstractmethod
def left_doubleclick(self):
'''鼠标左键双击事件'''
print("鼠标左键被双击了")
@abc.abstractmethod
def left_click(self):
'''鼠标左键单击事件'''
print("鼠标左键被单击了")
@abc.abstractmethod
def right_doubleclick(self):
'''鼠标右键双击事件'''
print("鼠标右键被双击了!")
def _create_window(self):
'''创建window对象'''
wc = win32gui.WNDCLASS()
self.hinst = wc.hInstance = win32gui.GetModuleHandle(None)
wc.lpszClassName = self.__window_name
wc.lpfnWndProc = self.wndProc
self.classAtom = win32gui.RegisterClass(wc)
def create_tray_icon(self):
'''创建图标'''
self._create_window()
self.hwnd = win32gui.CreateWindow(self.classAtom, self.__window_name, win32con.WS_OVERLAPPED | win32con.WS_SYSMENU, 0, 0,
win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, 0, 0, self.hinst, None)
icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE
hicon = win32gui.LoadImage(self.hinst, self._ico_path, win32con.IMAGE_ICON, 0, 0, icon_flags)
# 设置系统托盘图标的属性
flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP
nid = (self.hwnd, 0, flags, win32con.WM_USER + 20, hicon, self._ico_class_name)
win32gui.Shell_NotifyIcon(win32gui.NIM_ADD, nid)
@abc.abstractmethod
def wndProc(self, hwnd, msg, wparam, lparam):
''' 定义系统托盘图标的消息处理函数'''
if msg == win32con.WM_USER + 20: # 监听系统托盘图标的消息
if lparam == win32con.WM_LBUTTONDBLCLK: # 监听鼠标左键双击事件
self.left_doubleclick()
elif lparam == win32con.WM_RBUTTONDOWN: # 监听右键单击事件
# 显示右键菜单
menu = win32gui.CreatePopupMenu()
# for k, v in self._menu.items():
# ids, name = k, v[0]
# win32gui.AppendMenu(menu, win32con.MF_STRING, ids, name)
win32gui.AppendMenu(menu, win32con.MF_STRING, 1023, "菜单项1")
win32gui.AppendMenu(menu, win32con.MF_STRING, 1024, "菜单项2")
win32gui.AppendMenu(menu, win32con.MF_STRING, 1025, "退出")
pos = win32gui.GetCursorPos()
win32gui.SetForegroundWindow(hwnd)
win32gui.TrackPopupMenu(menu, win32con.TPM_LEFTALIGN, pos[0], pos[1], 0, hwnd, None)
win32gui.PostMessage(hwnd, win32con.WM_NULL, 0, 0)
elif lparam == win32con.WM_LBUTTONDOWN: # 监听鼠标左键单击事件
self.left_click()
elif lparam == win32con.WM_RBUTTONDBLCLK: # 监听右键双击事件
self.right_doubleclick()
elif msg == win32con.WM_COMMAND:
id = win32gui.LOWORD(wparam)
if id == 1023:
print("执行菜单项1的操作")
elif id == 1024:
print("执行菜单项2的操作")
elif id == 1025:
win32gui.DestroyWindow(hwnd)
return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)
def run(self):
'''消息循环(正常启动)'''
self.create_tray_icon()
while True:
time.sleep(0.1)
win32gui.PumpWaitingMessages()
def run_detached(self):
'''非阻塞(须有父进程)'''
t = Thread(target=a.run)
t.daemon = True
t.start()
if __name__ == "__main__":
a = Pystray('logo.ico', 'edg')
# a.run()
a.run_detached()