最近这两天,在优化「todolist」应用的消息提醒功能。ps:todolist应用如果有同学感兴趣,可以查看这篇文章进行安装:为了不付费,我硬生生用ai开发了一个跨平台待办应用
桌面端应用消息提醒实现起来真是大有学问,看着简单的弹窗消息点击和消息驻留,开发起来挺费劲的。

*说明:消息的图标还没弄好,所以相对看着比较模糊~
前言提要
之前的桌面消息提醒是直接采用 python 调用指令的方式,让 powershell 运行脚本来实现弹窗,具体可以看这期的文章:才知道python还可以这样发消息提醒的
不过这种方式可定制性太差了,单纯使用 powershell 很难实现:
- 消息可以驻留在通知中,
- 消息点击后可以跳转到应用
- 支持跨平台
- 消息含应用图标
所以,最终还是要采用第三方组件来实现上述功能。
ai 辅助
我先尝试 ai 辅助,操作下来,感觉 ai 的差异还是蛮大的。
首先是 ai 智能体实现,试了多个应用,几轮对话下来,都达不到预期。

咨询 ai 相关组件,结果发现 ai 间的回答有偏差,甚至可以说是误导。
问了四个 ai 应用,发现推荐的第三方依赖包的功能描述都不太准确。比如:
- 豆包推荐
notify-py,但是这个依赖包做不了点击跳转功能; deepseek推荐desktop-notifier最终采用其提供的代码没有效果;- 千问说
desktop-notifier消息点击是完全 “伪回调”,而其他的ai应用诸如deepseek和gemini是反驳的……

最终根据 deepseek 和 gemini 的推荐,我采用的是 desktop-notifier。

代码核心实现
这里我以 gemini 的参考代码进行介绍。
初始化
这里需要初始化 asyncio 环境,同时启动两个线程,一个维护 asyncio 环境,一个维护消息队列。
def __init__(self): self.running = true self.notification_queue = queue() self.loop = asyncio.new_event_loop() # 1. 先启动一个纯粹运行事件循环的后台线程 self.loop_thread = threading.thread(target=self._run_event_loop, daemon=true) self.loop_thread.start() # 2. 启动你的队列监听线程 self.process_thread = threading.thread(target=self._process_notifications, daemon=true) self.process_thread.start()
asyncio 线程维护
这里 根据 gemini 的建议:通过专门的 loop_thread 维持一个活跃的 loop 是最稳健的做法。
def _run_event_loop(self): """专门负责运行 asyncio 循环的线程""" asyncio.set_event_loop(self.loop) self.loop.run_forever()
消息处理和发送
这里是消息发送,省略了消息回调,在下个章节会专门介绍。
def _process_notifications(self):
"""处理队列,将任务提交给已经运行的 loop"""
while self.running:
try:
# 获取队列中的数据(同步阻塞)
notification = self.notification_queue.get(timeout=1)
# 关键:将协程任务提交给正在运行的 self.loop
asyncio.run_coroutine_threadsafe(
self._show_notification(notification),
self.loop
)
except exception: # 队列为空超时
continue
async def _show_notification(self, data):
"""真正的异步发送逻辑"""
# 这里调用 desktop_notifier.send(...)
print(f"正在发送通知: {data}")
消息点击回调
这个专门起一个章节是因为这个功能是最影响组件选型的,也是费了我最多时间的。
这是我最终消息发送的实现:
from desktop_notifier.common import button
# 尝试使用desktop-notifier显示通知
await self.notifier.send(
title=title,
message=message,
urgency=urgency.critical if priority == 'high' else urgency.normal,
on_clicked=lambda: on_click(),
timeout=0 # 0表示通知常驻
)
on_click() 方法的回调效果是在应用隐藏时,唤醒应用,因为我使用的是 pywebview 框架,所以用的是 show等函数,具体代码实现是这样的:
def on_click():
print("用户点击了通知,准备唤醒窗口...")
# 关键判断:确保窗口实例存在且未被销毁
if backend.globals.window:
try:
# 1. 从最小化恢复
backend.globals.window.restore
# 2. 提到最前显示(如果之前是 hide 状态)
backend.globals.window.show
# 3. 某些平台下可能需要额外聚焦
backend.globals.window.focus
# windows部分系统需要强制处理下
hwnd = win32gui.findwindow(none, "todolist")
if hwnd:
# 恢复窗口(如果最小化)
win32gui.showwindow(hwnd, win32con.sw_restore)
# 强制推至前台
win32gui.setforegroundwindow(hwnd)
except exception as e:
print(f"唤醒失败,窗口可能已关闭: {e}")
else:
print("错误:找不到窗口实例!")
不过很遗憾,调试了一下午,最终我实现的仅仅是弹窗消息点击能触发回调,而通知中心的依然触发不了。

根据 ai 的分析,是 python 进程与 windows 通知系统的 rpc 链接(远程过程调用) 在某种环境下断开了。
尝试过注册 aumid、创建开始菜单快捷方式、使用 url protocol 等方式改进,结果都不奏效……
补充:组件测验效果
| 组件名称 | 组件大小 | 组件说明 |
|---|---|---|
desktop_notifier | 30kb | 基本能达到预期效果,除了通知中心的消息点击依然触发不了回调 |
plyer | 5mb | 体积较大,且 deepseek 说无法点击消息跳转 |
win10toast-click | 39.5kb | windows 专用,无法跨平台,而且版本过老,实测有报错,需要改源码 |
notify-py | 0.3 mb | 很轻量化,实测支持消息驻留,但是无法实现点击跳转 |
py-notifier | 0.4 mb | 实测过的给忘了特性啦,千问说不支持消息驻留和点击跳转 |
windows-toasts | 36kb | win10toast-click的 2.0 版本吧,是后面问 gemini 才知道有这个版本的,不过也是通知中心点击跳转不生效 |
以上就是python实现桌面端应用消息提醒功能的详细内容,更多关于python桌面端应用消息提醒的资料请关注代码网其它相关文章!
发表评论