如何用Python案例实现系统托盘?

wen python案例 3

本文目录导读:

如何用Python案例实现系统托盘?

  1. 方案一:使用 pystray(推荐)
  2. 方案二:使用 Infi.systray(跨平台)
  3. 方案三:使用 PyQt5 实现
  4. 方案四:使用 wxPython
  5. 安装依赖
  6. 完整示例:带有后台线程的系统托盘
  7. 注意事项

我来推荐几个实现系统托盘的Python方案。

使用 pystray(推荐)

import pystray
from pystray import MenuItem as item
from PIL import Image, ImageDraw
def create_image():
    """创建一个简单的图标"""
    # 创建一个 64x64 的图像
    image = Image.new('RGBA', (64, 64), (255, 255, 255, 0))
    draw = ImageDraw.Draw(image)
    # 画一个蓝色圆形
    draw.ellipse([8, 8, 56, 56], fill='blue')
    # 画一个白色字母 "P"
    draw.text((20, 15), "P", fill='white', font=None)
    return image
def on_quit(icon, item):
    """退出程序"""
    icon.stop()
def on_show(icon, item):
    """显示信息"""
    print("系统托盘示例运行中...")
def on_settings(icon, item):
    """设置菜单"""
    print("打开设置...")
# 创建系统托盘图标
icon = pystray.Icon(
    "test_icon",
    create_image(),
    "系统托盘示例",
    menu=pystray.Menu(
        item("显示信息", on_show),
        item("设置", on_settings),
        item("退出", on_quit)
    )
)
# 运行系统托盘
if __name__ == "__main__":
    print("系统托盘已启动")
    icon.run()

使用 Infi.systray(跨平台)

from infi.systray import SysTrayIcon
import os
def on_quit_callback(systray):
    """退出回调"""
    print("程序退出")
def on_show_info(systray):
    """显示信息"""
    print("系统托盘正在运行")
def on_open_folder(systray):
    """打开文件夹"""
    os.startfile('.')  # Windows
    # os.system('open .')  # macOS
    # os.system('xdg-open .')  # Linux
# 设置菜单项
menu_options = (
    ("显示信息", None, on_show_info),
    ("打开文件夹", None, on_open_folder),
)
# 创建系统托盘
systray = SysTrayIcon(
    "icon.ico",  # 图标文件路径
    "系统托盘示例",
    menu_options,
    on_quit_callback
)
# 启动
if __name__ == "__main__":
    systray.start()

使用 PyQt5 实现

import sys
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QAction
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QTimer
class SystemTrayDemo:
    def __init__(self):
        self.app = QApplication(sys.argv)
        # 设置应用图标
        self.app.setWindowIcon(QIcon.fromTheme("applications-system"))
        # 创建系统托盘
        self.tray_icon = QSystemTrayIcon()
        self.tray_icon.setIcon(QIcon.fromTheme("applications-system"))
        self.tray_icon.setToolTip("系统托盘示例")
        # 创建右键菜单
        self.create_menu()
        # 连接信号
        self.tray_icon.activated.connect(self.on_activated)
        # 显示系统托盘
        self.tray_icon.show()
        # 定时器示例
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_status)
        self.timer.start(5000)  # 每5秒更新一次
    def create_menu(self):
        """创建右键菜单"""
        menu = QMenu()
        # 显示信息
        show_action = QAction("显示信息", self.tray_icon)
        show_action.triggered.connect(self.show_info)
        menu.addAction(show_action)
        # 设置
        settings_action = QAction("设置", self.tray_icon)
        settings_action.triggered.connect(self.open_settings)
        menu.addAction(settings_action)
        menu.addSeparator()
        # 退出
        quit_action = QAction("退出", self.tray_icon)
        quit_action.triggered.connect(self.quit_app)
        menu.addAction(quit_action)
        self.tray_icon.setContextMenu(menu)
    def on_activated(self, reason):
        """系统托盘被激活时的回调"""
        if reason == QSystemTrayIcon.DoubleClick:
            self.show_info()
    def show_info(self):
        """显示信息"""
        self.tray_icon.showMessage(
            "系统托盘",
            "这是一个系统托盘示例",
            QSystemTrayIcon.Information,
            3000  # 显示3秒
        )
    def open_settings(self):
        """打开设置"""
        print("打开设置界面")
    def update_status(self):
        """定时更新状态"""
        self.tray_icon.setToolTip(f"系统托盘 - {QtCore.QTime.currentTime().toString()}")
    def quit_app(self):
        """退出应用"""
        QApplication.quit()
    def run(self):
        """运行应用"""
        sys.exit(self.app.exec_())
# 运行
if __name__ == "__main__":
    demo = SystemTrayDemo()
    demo.run()

使用 wxPython

import wx
class TaskBarIcon(wx.TaskBarIcon):
    def __init__(self, frame):
        super().__init__()
        self.frame = frame
        # 设置图标
        icon = wx.Icon()
        icon.LoadFile("icon.ico", wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon, "系统托盘示例")
        # 绑定事件
        self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, self.on_double_click)
        self.Bind(wx.EVT_TASKBAR_RIGHT_UP, self.on_right_click)
    def CreatePopupMenu(self):
        """创建右键菜单"""
        menu = wx.Menu()
        # 显示窗口
        show_item = menu.Append(wx.ID_ANY, "显示窗口")
        self.Bind(wx.EVT_MENU, self.on_show, show_item)
        # 设置
        settings_item = menu.Append(wx.ID_ANY, "设置")
        self.Bind(wx.EVT_MENU, self.on_settings, settings_item)
        menu.AppendSeparator()
        # 退出
        quit_item = menu.Append(wx.ID_ANY, "退出")
        self.Bind(wx.EVT_MENU, self.on_quit, quit_item)
        return menu
    def on_double_click(self, event):
        """双击事件"""
        self.frame.Show()
        self.frame.Raise()
    def on_right_click(self, event):
        """右键事件"""
        self.PopupMenu(self.CreatePopupMenu())
    def on_show(self, event):
        """显示窗口"""
        self.frame.Show()
        self.frame.Raise()
    def on_settings(self, event):
        """打开设置"""
        print("打开设置")
    def on_quit(self, event):
        """退出程序"""
        self.frame.Close()
class MyFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title="系统托盘示例")
        # 创建系统托盘
        self.tray_icon = TaskBarIcon(self)
        # 创建主界面
        panel = wx.Panel(self)
        text = wx.StaticText(panel, label="双击系统托盘图标显示窗口", pos=(50, 50))
        # 最小化到系统托盘
        self.Bind(wx.EVT_ICONIZE, self.on_minimize)
        self.Show()
    def on_minimize(self, event):
        """最小化时隐藏窗口"""
        self.Hide()
# 运行
if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame()
    app.MainLoop()

安装依赖

根据选择的方案安装相应库:

# 方案一
pip install pystray pillow
# 方案二
pip install infi.systray
# 方案三
pip install PyQt5
# 方案四
pip install wxPython

完整示例:带有后台线程的系统托盘

import pystray
from pystray import MenuItem as item
from PIL import Image, ImageDraw
import threading
import time
import random
class SystemTrayApp:
    def __init__(self):
        self.running = True
        self.counter = 0
        # 创建图标
        self.icon = pystray.Icon(
            "demo_icon",
            self.create_image(),
            "系统托盘演示",
            self.create_menu()
        )
    def create_image(self):
        """创建动态图标"""
        image = Image.new('RGBA', (64, 64), (255, 255, 255, 0))
        draw = ImageDraw.Draw(image)
        # 随机颜色
        r, g, b = random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
        draw.ellipse([8, 8, 56, 56], fill=(r, g, b))
        draw.text((20, 15), str(self.counter), fill='white')
        return image
    def create_menu(self):
        """创建菜单"""
        return pystray.Menu(
            item("状态", self.show_status),
            item("更新图标", self.update_icon),
            item("重置计数", self.reset_counter),
            item("退出", self.quit_app)
        )
    def show_status(self, icon, item):
        """显示状态"""
        icon.notify(f"计数器: {self.counter}", "系统托盘状态")
    def update_icon(self, icon, item):
        """更新图标"""
        icon.icon = self.create_image()
    def reset_counter(self, icon, item):
        """重置计数器"""
        self.counter = 0
        self.update_icon(icon, item)
    def quit_app(self, icon, item):
        """退出应用"""
        self.running = False
        icon.stop()
    def background_task(self):
        """后台任务"""
        while self.running:
            time.sleep(2)
            self.counter += 1
            print(f"计数器: {self.counter}")
    def run(self):
        """运行应用"""
        # 启动后台线程
        bg_thread = threading.Thread(target=self.background_task)
        bg_thread.daemon = True
        bg_thread.start()
        # 运行系统托盘
        self.icon.run()
# 运行应用
if __name__ == "__main__":
    app = SystemTrayApp()
    app.run()

注意事项

  1. 图标文件:确保图标文件存在,或使用 PIL/Pillow 动态生成
  2. 平台兼容性:pystray 支持 Windows、macOS 和 Linux
  3. 线程安全:在后台线程中更新图标时要注意线程安全
  4. 资源释放:程序退出时正确清理资源

这些方案各有特点:

  • pystray:简单易用,功能完整
  • Infi.systray:轻量级,适合简单的系统托盘
  • PyQt5/wxPython:适合需要复杂 GUI 的应用

选择哪种方案取决于你的具体需求和项目规模。

抱歉,评论功能暂时关闭!