第一个托盘图标:从零开始,让图标出现在系统托盘中

说实话,系统托盘通知这个功能,我第一次接触是在做一个内部运维工具的时候。当时老板说:“你就弄个小图标,在右下角待着,能点一下弹出菜单就行。”我心想,这还不简单?结果一查资料,发现 Python 里做托盘图标,最靠谱的库就是 pystray。今天我们就从最基础的开始——让一个图标老老实实出现在系统托盘里。

准备工作:安装 pystray

在开始之前,你得先装上 pystray。我个人习惯用 pip 安装,干净利落:

pip install pystray

嗯,这里要注意一点:pystray 依赖 PIL(Pillow)来处理图标图片。如果你没装 Pillow,它会自动帮你装上。但如果你遇到奇怪的问题,可以手动装一下:

pip install Pillow

最简单的托盘图标代码

我们先写一个最精简的版本。说白了,就是创建一个图标对象,然后让它跑起来。来看代码:

import pystray
from PIL import Image

# 1. 创建一个简单的图标图片
# 这里我用一个 64x64 的纯色方块做演示
image = Image.new('RGB', (64, 64), color='blue')

# 2. 定义点击图标时的回调函数
def on_clicked(icon, item):
    print("图标被点击了!")

# 3. 创建托盘图标
icon = pystray.Icon(
    "test_icon",          # 图标的内部名称
    image,                # 图标图片
    "我的第一个托盘图标",  # 鼠标悬停时显示的提示文字
    menu=pystray.Menu(    # 右键菜单(这里先留空)
        pystray.MenuItem("退出", lambda: icon.stop())
    )
)

# 4. 启动图标(这会阻塞主线程)
icon.run()

这段代码跑起来后,你会在系统托盘里看到一个蓝色的小方块。鼠标放上去会显示“我的第一个托盘图标”。右键点击会有一个“退出”菜单项。

核心要点:

  • pystray.Icon() 的第一个参数是内部标识名,随便起,但别重复
  • 第二个参数是图片对象,必须是 PIL.Image 类型
  • icon.run() 会阻塞当前线程,直到你调用 icon.stop()

避坑指南:为什么图标没显示?

我曾经遇到过一个问题:代码跑起来没有任何报错,但托盘里就是看不到图标。排查了半天,发现是图片尺寸太小。Windows 系统托盘对图标尺寸有要求,建议至少 32x32 像素,64x64 更稳妥。

另外,如果你在 macOS 上跑,可能会发现图标显示为纯白色方块。这是因为 macOS 对图标格式有特殊要求。我建议你直接用 PNG 格式的图片,别用 JPEG。

让图标更“像样”:使用真实图片

纯色方块太丑了,对吧?我们来换一个真正的图标文件。假设你有一个 icon.png 文件放在脚本同目录下:

import pystray
from PIL import Image

# 加载真实的图标文件
image = Image.open("icon.png")

# 确保图片尺寸合适
image = image.resize((64, 64))

def on_clicked(icon, item):
    print("图标被点击了")

icon = pystray.Icon(
    "my_app_icon",
    image,
    "我的应用",
    menu=pystray.Menu(
        pystray.MenuItem("退出", lambda: icon.stop())
    )
)

icon.run()

小技巧:如果你没有现成的图标文件,可以用在线工具生成一个 64x64 的 PNG 图标。我个人习惯用 ICO 格式,因为 Windows 原生支持。但 pystray 对 PNG 兼容性更好。

理解图标生命周期

你可能会问:为什么 icon.run() 会阻塞?其实 pystray 内部启动了一个事件循环,专门监听系统托盘的消息。如果你想让程序在后台运行,同时做其他事情,可以用线程:

import threading
import pystray
from PIL import Image

def run_tray():
    image = Image.new('RGB', (64, 64), 'green')
    icon = pystray.Icon("background_icon", image, "后台图标")
    icon.run()

# 在后台线程中启动托盘图标
tray_thread = threading.Thread(target=run_tray, daemon=True)
tray_thread.start()

# 主线程继续做其他事情
print("托盘图标已在后台运行...")
input("按回车键退出\n")

这样,你的程序既能显示托盘图标,又能继续执行其他逻辑。嗯,这里要注意:daemon=True 表示主线程退出时,后台线程会自动结束,避免程序无法退出。

常见问题速查表

问题 原因 解决方法
图标不显示 图片尺寸太小或格式不对 使用 64x64 PNG 图片
点击无反应 回调函数未正确绑定 检查 on_clicked 参数
程序无法退出 icon.run() 阻塞了主线程 使用线程或调用 icon.stop()
macOS 上图标变白 系统对图标格式要求严格 使用 PNG 格式,避免 JPEG

总结一下

第一个托盘图标其实就三步:准备图片、创建 Icon 对象、调用 run()。你想想看,是不是比想象中简单?我个人觉得 pystray 的设计很优雅,API 简洁,文档也清晰。

下一章我们会给这个图标加上真正的交互——右键菜单和左键点击事件。到时候你就可以做出一个“能干活”的托盘应用了。

警告:不要在 icon.run() 之后写任何代码,除非你用了线程。因为 run() 会一直阻塞,后面的代码永远不会执行。我曾经在这个坑里浪费了半小时...

好了,去试试吧。让你的第一个托盘图标亮起来!