我用滴答清单管日常已经好几年了,但有个毛病一直没改:app 得先想起来去打开它。 手机上的通知会被刷掉,电脑上的网页版要切标签页,桌面客户端又是个大窗口——打开了就挡事,不开就看不见。结果是「今天该做什么」这件最基本的事,反而总要靠脑子记着「去查一下」。
真正让我下定决心的是孩子上学这件事。早上送完孩子回来,脑子里全是「下午三点接、周五交材料、晚上八点线上家长会」这类具体到时刻的碎事,散在清单里、标签里、不同列表里。我需要一个一抬眼就能看见、不抢焦点、不用打开任何 app 的东西。
于是有了这个小工具:一个常驻桌面右上角的无边框悬浮窗,从滴答清单同步「今天的任务」,按标签分好组,点一下就完成、右键就能改期。它不替代滴答清单,只是把「今天」那一屏拎出来,钉在你眼前。
整个东西只有一个 Python 脚本,只用标准库——不装任何第三方包,双击即用。源码已放上 GitHub:xuruizhi/TodoList,欢迎自取。下面先讲它长什么样、能做什么,再讲怎么装、有什么坑,最后交代 AI 在这中间到底干了哪些活。

悬浮窗全貌:顶部是日期与刷新 / 关闭 / 置顶三个按钮,一行状态条写着「已同步 · 待办 N 项 · 已完成 N 项」;任务按标签分组(这里是孩子 / 家务 / 工作 / 无标签),行首方块点一下即完成,小圆点按优先级着色(红高、黄中、蓝低);今天的定时任务右侧标时刻(15:00),全天任务标「今天」;已完成的沉到底部「已完成」区并划线。图中的任务是为演示编造的示例数据。
一、它解决什么问题
先看现状。滴答清单本身功能很全,问题不在「缺功能」,而在「开箱路径太长」:
想看一眼今天要做什么
│
├─ 手机 → 解锁 → 找到 app → 等同步 → 点「今天」 (一系列动作)
├─ 网页 → 打开浏览器 → 切到滴答标签页 → 登录态 → 点「今天」
└─ 桌面端 → 启动大窗口 → 点「今天」 (窗口挡住后面的事)
这条链路的麻烦不在任何一步很难,而在它每天要重复几十次,每一次都有「要不要打开它」的心理成本。悬浮窗的思路是把这条链路压平成一件事:
开机 / 常驻
│
▼
桌面右上角一行一行的任务 ← 每 5 分钟自动同步一次
│
├─ 点一下 → 标记完成(落到「今日已完成」区,可撤销)
├─ 右键 → 改期 / 打开滴答清单 / 删除
└─ 拖标题栏 / 拖边缘 → 挪位置、调大小,自动记住
核心价值就一句话:把「今天」从需要主动打开的 app 里,变成一直都在的一块桌面角落。 没有主界面、不进任务栏、不抢焦点,什么时候抬眼都在。
二、功能
2.1 任务口径:和滴答清单网页版「今天」对齐
最容易被做歪的是「今天的任务」到底指什么。这里的口径是:
| 类别 | 是否显示 |
|---|---|
| 今天到期(含定时、全天) | ✅ 显示为待办 |
| 逾期未完成 | ✅ 显示为待办 |
| 今天已完成 | ✅ 归入下方「今日已完成」区,可点击撤销 |
| 明天及以后到期 | ❌ 不显示(那是「未来」清单的事) |
也就是 今天到期 + 逾期未完成 + 今天已完成,和滴答清单「今天」智能清单的口径一致。时间标注也刻意和网页版拉齐:
- 今天的定时任务:显示具体时间(
16:00),用强调色; - 今天的全天任务:显示「今天」;
- 其他日期的任务:直接标日期(
9月23日、2025年6月30日),不显示「逾期」两个字——逾期本身已经通过「出现在这里」传达,再刷一个红字只会制造焦虑。
2.2 按标签分组,一眼分清场合
任务按第一个标签分组显示,无标签的归到最后一组「无标签」。这个设计的用意很实际:大多数人的标签本身就代表场合(工作、家务、孩子、学习),分组之后就变成「这一屏告诉我上午处理工作、中午前搞定家务、晚上看孩子的材料」。组头左侧一条强调色竖条 + 组名 + 计数,扫一眼就知道哪组还有活。
排序规则也照顾了「手头最要紧的」:定时任务排在前面(按时间先后),全天的往后放;同组内再按优先级、再按标题。这样最上面那几行,基本就是现在该看的东西。
2.3 完成、改期、删除、跳转——都在鼠标一下之内
| 操作 | 入口 | 说明 |
|---|---|---|
| 标记完成 | 点任务行 / 点复选框 | 完成项移到下方「今日已完成」,可点击撤销 |
| 撤销完成 | 点已完成项 / 右键 | 中国版无独立撤销接口,用更新接口把状态改回未完成 |
| 快速改期 | 右键 → 改期 → 今天 / 明天 / 后天 | 全天任务保留全天,定时任务按原时分平移天数 |
| 自定义改期 | 右键 → 改期 → 自定义 | 见下方格式表 |
| 删除任务 | 右键 → 删除任务 | 有二次确认 |
| 在滴答清单中打开 | 右键 → 在滴答清单中打开 | 优先唤起 ticktick://,失败退回网页 |
自定义改期接受这几种输入,回车即生效:
| 输入 | 含义 |
|---|---|
2026-10-01 |
该日全天 |
2026-10-01 14:30 |
该日定时 14:30 |
14:30 |
今天 14:30 |
10-02 09:00 |
今年 10月2日 09:00 |
今天 / 明天 / 后天 |
对应日期(全天) |
顺手的地方在于:改期时如果任务的开始时间和到期是同一天,开始时间会跟着一起挪;不是同一天就保留原样——这样「持续三天的事」不会被一次改期压扁成一天。
2.4 界面细节:无边框、可拖、可调、能记住
这是最耗时间、也最容易被忽略的一层。悬浮窗要真的「住得舒服」,细节比功能本身更磨人:
- 无边框 + 半透明(0.97):
overrideredirect(True)去掉系统标题栏,自己画一条矮标题栏,视觉上像贴在桌面上的一块便签。 - 窗口置顶默认关闭:这点是刻意的。默认不置顶,就不会挡住你正在写的东西;想要它一直在最上层,点标题栏的 📌 或右键勾选「窗口置顶」即可,状态会记住。
- 可自由拖动大小:拖右侧边缘调宽度、拖底部边缘调高度、拖右下角手柄同时调长宽,松手即保存。双击右下角手柄恢复「按内容自适应高度」。
- 高度自适应:默认让窗口刚好容纳全部条目(含今日已完成),只有任务超过屏幕高度时才开始滚动——底部不会留一大片空白。
- 滚动条按需出现:内容不满一屏时自动隐藏,需要滚动时才显示,避免右侧常年挂着一条灰杠。
- 悬停高亮 + 优先级圆点:鼠标划过整行变色;行首一个小圆点按优先级着色(高红、中黄、低蓝),无优先级则不显眼。
- 字体、字号、文字色、强调色可调:设置窗口里改完即时重绘。强调色会同时作用于「今天的时间」「复选框」「分组竖条」「置顶按钮」四处,换一套配色,整个悬浮窗的气质就变了。
2.5 自动刷新与开机自启
- 定时自动刷新:默认每 5 分钟自动同步一次(状态条会写「每 N 分钟自动刷新」)。可以改成 1 / 15 / 30 / 60 分钟或关闭。刷新时保持当前滚动位置不跳顶——这个细节很重要,否则每刷一次你就得重新往下翻。
- 开机自启:右键标题栏勾选「开机自启」,就往 Windows 启动文件夹写一个
dida-widget.bat;取消勾选即删除。打包版指向 exe,源码版指向pythonw+ 脚本。 - 状态条:一行显示「已同步 20:18 · 待办 7 项 · 已完成 2 项 · 每5分钟自动刷新」,出问题时也会在这里变红提示(口令失效、网络错误、操作失败)。
三、部署 / 使用
3.1 环境要求
| 项目 | 要求 | 说明 |
|---|---|---|
| 操作系统 | Windows | 用了 os.startfile、启动文件夹等 Windows 特性 |
| Python(源码版) | ≥ 3.10 | 本机实测 3.13;只用标准库,无需 pip 安装 |
| 打包版 | 无 | DidaWidget.exe 独立运行,不依赖 Python 环境 |
| 账号 | 滴答清单账号 + API 口令 | 官方开放接口,Bearer Token 认证 |
3.2 目录结构
dida365-widget/
├─ DidaWidget.exe # 推荐:独立打包版,双击即用
├─ dida_widget.py # 主程序源码(仅标准库)
├─ 启动桌面任务.bat # 用系统 Python 启动(无控制台窗口)
├─ config.json # 首次运行生成:口令 / 窗口位置 / 刷新间隔 / 主题
└─ widget.log # 运行日志,出问题先看这里
3.3 三步跑起来
第零步 · 拿口令。 打开 dida365.com,登录后点左上角头像 → 设置 → 账户与安全 → API 口令 → 创建并复制。
第一步 · 启动。 双击 DidaWidget.exe(或 启动桌面任务.bat)。首次运行会自动弹出设置窗口。
第二步 · 粘贴口令。 把口令粘进设置窗口,点「保存并连接」。悬浮窗出现在桌面右上角,自动拉取今天的任务。完成。
3.4 第一次验证
装完别急着把它拖到角落,先确认这几件事都对:
- 状态条显示「已同步 HH:MM · 待办 N 项」,不是红色的「口令无效」;
- 点一个任务行,它移到下方「今日已完成」,再点一下能撤销;
- 右键一个任务 → 改到明天,它从列表里消失(因为「明天」不在此窗口口径内);
- 关掉程序再开,窗口位置、大小、字号、配色都还在(
config.json记住了); - 打开
widget.log,没有大段红色报错。
3.5 关于「同步失败」
这是这个工具最容易撞上的现实问题,日志里就记着不少:
[2026-09-29 14:29:38] 刷新失败: 网络错误: [WinError 10061] 由于目标计算机积极拒绝,无法连接。
[2026-09-30 07:04:36] 刷新失败: 网络错误: [Errno 11001] getaddrinfo failed
WinError 10061 / getaddrinfo failed 基本都是本机网络环境的问题——代理软件没开、开着全局代理但程序没走代理、断网、DNS 抽风。程序不会崩,只在状态条变红,并在下次自动刷新时重试;恢复网络后点 ⟳ 手动刷新即可。真正需要动配置的只有一种:「API 口令无效或过期」,那就重拿口令。
四、已知限制
不好看但必须写清楚的部分:
| 限制 | 说明 |
|---|---|
| 只支持 Windows | overrideredirect 的窗口行为、开机自启路径、ticktick:// 唤起都贴着 Windows 走,换 Linux / macOS 要改不少地方 |
| 口令明文存储 | config.json 里 access_token 是明文,别把这个文件发给别人、别上传到仓库 |
| 依赖第三方 API | 接口变了、限流了、官方收紧开放平台,工具就得跟着改;这不是能自己掌控的一环 |
| 「中国版」没有撤销接口 | 撤销完成只能「查回任务 + 用更新接口改状态」,多一次往返,理论上存在极小的时间窗竞争 |
| 无边框窗口的固有麻烦 | 没有系统标题栏,屏幕缩放(DPI)变化、多显示器拔插时,窗口位置可能需要手动再拖一次 |
| 分组只看第一个标签 | 一个任务挂了多个标签,只按第一个分组,做不到「一处出现在多组」 |
| 定时刷新的精度 | 最细 1 分钟一档,且刷新时若正在请求会跳过,不是严格的实时推送 |
五、它是怎么被写出来的(简)
这个工具的定位很清楚:人定方向和验收,AI 负责查证、写代码、改错。 具体落到几个点上:
第一,接口全靠查证,不靠记忆。 滴答清单的开放接口细节(任务列表要 POST /task/filter 而不是 GET /task、时间字段是 UTC、完成时间要单独查 /task/completed、中国版没有撤销接口)都不是拍脑袋写出来的——是让 AI 去读官方文档、拿真实请求去试、撞到错误再翻日志对齐出来的。这类「查准一条事实省掉几小时试错」的活,是 AI 最实在的用处。
第二,磨的是界面长尾,不是主干。 主干功能(拉数据、渲染列表)很快就有雏形;真正花时间的是那堆「不住进去不知道难受」的细节——无边框怎么留 1px 描边、滚动条不超一屏时怎么藏、自适应高度怎么扣掉标题栏和状态条的实测高度、右键菜单点在子控件上为什么不冒泡、自动刷新怎么保住滚动位置。这些是我描述「别扭在哪」、AI 去改代码、我再验收一轮轮磨出来的。人的价值在「知道什么叫别扭」,AI 的价值在「改得动」。
第三,连限制也是被撞出来的。 上面那张「已知限制」表,每一条背后基本都有一段日志或一次实际失败:网络错误刷满日志、DPI 变化窗口跑位、多标签任务只进一个组。写文章时照实列出来,比藏起来有用得多。
说到底,从「会不会写这个工具」降到了「知不知道为什么这么写」——接口为什么这么调、窗口为什么要这么画、哪个细节是真正在解决痛点。这是 AI 参与构建之后,我最想留下来的那部分。
六、一些数字
| 维度 | 数字 |
|---|---|
| 主程序 | 单个 dida_widget.py,约 1300 行 |
| 第三方依赖 | 0(仅 Python 标准库:tkinter + urllib) |
| 自动刷新档位 | 1 / 5 / 15 / 30 / 60 分钟 + 关闭 |
| 快捷键 / 鼠标操作 | 完成、撤销、改期(3 档快捷 + 自定义)、打开、删除 |
| 可调界面项 | 字体、字号(7–18)、文字色、强调色,共 4 项 |
| 部署方式 | 打包版双击即用 / 源码版 bat 启动,二选一 |
本项目已开源:xuruizhi/TodoList。仓库里放的是源码、启动脚本与说明,不含任何个人配置文件或口令;欢迎提 issue 或 PR。
这个工具解决的是很小的一件事:让「今天」一直在你眼角余光里。小到不值得单开一个 app,却又天天用得上——大概正是这类工具适合自己动手写的理由。