把今天的任务钉在桌面上:滴答清单悬浮窗的功能与 AI 构建手记

一个只用 Python 标准库、双击即用的本地桌面小工具

我用滴答清单管日常已经好几年了,但有个毛病一直没改: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 第一次验证

装完别急着把它拖到角落,先确认这几件事都对:

  1. 状态条显示「已同步 HH:MM · 待办 N 项」,不是红色的「口令无效」;
  2. 点一个任务行,它移到下方「今日已完成」,再点一下能撤销;
  3. 右键一个任务 → 改到明天,它从列表里消失(因为「明天」不在此窗口口径内);
  4. 关掉程序再开,窗口位置、大小、字号、配色都还在(config.json 记住了);
  5. 打开 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,却又天天用得上——大概正是这类工具适合自己动手写的理由。