跳转至

更新日志

tkdeft 的所有版本变更都记录在这里,按时间倒序排列——最新一版在最上面。

每一篇都写清楚:这一版新增了什么、修掉了什么、升级要注意什么。 想直接看"当前版本与上一版有什么不同",看下面最新那两篇即可; 想顺手上手,去 快速上手。

只有版本更新会发在这里吗

不限于此。引擎对比、性能数据、某个坑的来龙去脉这类"值得单独成篇"的内容 也会发在这里;日常使用说明仍然在指南里。

发布 0.4.0:新增 tkdeft.extra 与 Material 涟漪效果

0.4.0 是纯加法:核心包一行没改,旧代码原样能跑。新增的是一个可选的子包, 以及里面第一个可以直接拿去用的效果。

tkdeft.extra

核心包继续只提供零件(画布、绘制后端、配置容器);"用零件做出来的东西"以后放 tkdeft.extra,按需加载——import tkdeft 不会顺带把 Pillow 的 ImageTk 拉起来。

from tkdeft import extra          # 懒加载整个子包
from tkdeft.extra.ripple import Ripple

涟漪效果:tkdeft.extra.ripple

它不提供按钮,只提供涟漪本身,所以任何控件都能用:

from tkdeft.extra.ripple import Ripple

self.ripple = Ripple(self, on_frame=self._paint)   # ① 建控制器
self.ripple.start(event.x, event.y)                # ② 点击处起涟漪
self.ripple.paint(color="#000000", opacity=0.12,   # ③ 在重绘里贴一层
                  corner_radius=8)

导出内容:

  • Ripple(widget, on_frame=None, *, duration=500, frame_ms=16) —— 管理状态与计时, 有 start() / paint() / stop() / active / states;
  • ripple_layer() —— 算出某一帧的 RGBA 图层(圆角裁剪 + 抗锯齿 + 半透明);
  • ripple_alpha() / ripple_radius() / ease_out_cubic() —— 浓度、半径、缓动三条曲线。

控制器不碰 canvas item、也不存颜色:贴在哪一层、跟不跟主题换色,都由宿主决定。 不依赖 DDrawWidget 也行,任何有 create_image / winfo_width 的控件都能接。

示例与文档

examples/ripple_button.py 里有一个完整按钮作为接法参照(圆角底色、悬停 / 按压 / 焦点 / 禁用、空格触发、T 换主题),并带非交互自检:

python examples/ripple_button.py --check

新文档页 涟漪效果:给任意控件加一层扩散动画 先讲三步接上,再拆遮罩、曲线、计时与图片保活,最后是三个真实踩过的坑。

升级

无需改代码。想用新功能:

pip install -U tkdeft

0.3.x 的 API 全部保留,tkdeft.extra 只是新目录。

新增功能:Material 涟漪效果

tkdeft 本身不带组件,这次也没有加组件——加的是一个可以接到任何控件上的效果: tkdeft.extra.ripple。你的按钮照旧自己画,只要在点击处记一笔、在重绘里贴一层。

from tkdeft.extra.ripple import Ripple

self.ripple = Ripple(self, on_frame=self._paint)   # 建控制器,每帧回调重绘
self.ripple.start(event.x, event.y)                # 点击处起涟漪
self.ripple.paint(color="#000000", opacity=0.12,   # 在合适层级贴一层
                  corner_radius=8)

tkdeft.extra 是新开的子包:核心包继续只提供零件,可选功能单独放、按需加载 (import tkdeft 不会顺带把 Pillow 的 ImageTk 拉起来)。

examples/ripple_button.py 里有一个完整的按钮示范:圆角底色、悬停 / 按压 / 焦点 / 禁用、空格触发、T 换主题都由示例自己画,涟漪只借这三行。也带自检:

python examples/ripple_button.py                # 默认自动挑栅格引擎
python examples/ripple_button.py --engine tksvg # 换引擎看每帧开销
python examples/ripple_button.py --check        # 自检,不开窗口

值得单独说的几点

  • 半透明涟漪没有用 create_oval:Tk 的 canvas item 没有逐像素 alpha。 这里在 Pillow 里算「圆 ∩ 圆角矩形」的遮罩,超采样 3 倍再缩回,得到一张 与控件等大的 RGBA 图层,create_image 贴上去。底色画在它下面、文字画在上面, 层级由宿主决定。
  • 动画进度按 time.monotonic() 算,不是每帧累加 16 ms—— 主线程卡一下,下一帧直接跳到正确进度,整段动画的时长不会被拖长。
  • 每帧全量重绘在这个尺寸下完全够用:一个 200×48 的按钮, 中位耗时 skia 0.42 ms、pillow 0.38 ms、cairo 0.41 ms, 默认的 tksvg 也只要 4.66 ms(60 fps 的预算是 16.7 ms)。
  • 控制器不碰 canvas item、也不存颜色:paint() 每次现取颜色与圆角, 所以同一份状态能跟着主题换色,不需要重建对象。

顺手记下三个坑

写这个效果时踩到的、文档里原来没写清楚的:

  1. class MyButton(DCanvas, DDrawWidget) 会直接 MRO 报错—— DDrawWidget 已经继承 DCanvas,只写后者就行;
  2. draw_roundrect(..., tags=...) 在SVG 回退路径上会抛 Invalid attribute 'tags' for svg-element <rect>,打标签要用 addtag_withtag(返回的 item id);
  3. 先 pack() 再 mainloop() 时,<Configure> 往往在映射之前就发完了, 而 DDrawWidget._draw() 在未映射时直接返回——控件会一直空白, 直到鼠标划过。补一个 <Map> 绑定即可(示例里已经这么做)。

另外还发现:PrintWindow 抓不到 canvas 上的半透明叠加层(窗口里看得见、 截图里没有),所以文档插图改成"引擎渲染的底色 + ripple_layer() 逐帧合成"。 这一条已经写进 常见问题与排查 第 13 条。

文档

新增 涟漪效果:给任意控件加一层扩散动画, 先讲怎么接(三步、控制器与纯函数的 API),再按"为什么不用 create_oval → 遮罩怎么算 → 浓度与半径两条曲线 → 计时 → 图片保活 → 三个坑 → 性能"拆开讲。 插图 ripple-frames.png 由 docs/gen_figures.py --write --only ripple-frames 生成, 可重新跑。

发布 0.3.0:把接口补齐、讲清楚

这一版没有新组件,做的是把 tkdeft 的对外接口补齐、并让 tkfluent 用上它们; 绘制结果与 0.2.0 逐项一致(24 个按钮状态 × 5 引擎的像素差没有变化)。

绘制引擎层

  • 引擎注册表补充 available_engines() / engine_names() / engine_index() / engine_from_index() / describe_engines() / reset_engine() / unregister_engine()
  • 引擎基类新增 render(spec)(按规格类型自动分发)、supports(kind)、info()、index
  • 拼错引擎名不再静默退回慢路径,而是抛 UnknownEngineError (ValueError 子类,带"你是不是想用 …"的提示)
  • 渲染入口新增 render(spec) 与 RENDERERS; 渲染失败可用 last_engine_error() / clear_engine_error() 查询
  • 三种规格补齐 from_box()(按坐标对构造)、pixels、to_dict()、describe()、kind

画布层

  • DCanvas 新增统一绘制入口 draw_roundrect() / draw_track() / draw_thumb(): 有栅格引擎就走位图快速路径,否则自动回退 SVG,返回值一定是 item id
  • 新增可覆盖的 SVG 钩子 draw_roundrect_svg() / draw_track_svg() / draw_thumb_svg() 与 draw_svg_item()——组件不必再抄一遍回退逻辑
  • raster_enabled 属性 / raster=False 参数可以强制走 SVG(做引擎对照时很方便)

绘制后端

  • DSvgDraw 补齐通用图元 create_roundrect() / create_track() / create_thumb()
  • create_svg_image(..., way=None) 默认按当前引擎自动挑 tksvg / Wand
  • tkdeft.svg 新增 svg_paint() / gradient_stop(): None 与 "transparent" 不再让 svgwrite 抛 TypeError—— 以前同一份 RoundRectSpec(outline=None) 在 skia 上正常、换 tksvg 就崩

基础件

  • DObject 补全 dget / dhas / dkeys / dvalues / ditems / dcopy / dreset, dcget(key, default) 可给默认值;实例现在拥有自己的属性字典,不再共享类级默认值
  • 顶层 tkdeft 再导出常用名字;新增 __version_info__

文档

tkfluent 同步

9 个组件里"栅格优先 + SVG 回退"的样板代码收敛到 tkdeft (button / badge / togglebutton / entry / text / listbox / frame / scrollbar / slider, 净删约 400 行)。同时修掉两个被回归漏掉的缺陷:

  • FluListBox.theme() 用参数 style 判断分支 → 点"切换主题"直接崩;
  • --check 自检用 except AttributeError: continue 跳过"没有钩子"的组件, 把钩子内部的 AttributeError 一起吞了——这也是上面那个 bug 一直没被发现的原因。

升级建议

0.3.0 是纯加法:旧代码原样能跑。想顺手改的地方见 从 0.2 升级到 0.3。

发布 0.2.0:可插拔的绘制引擎层

这一版把"画什么"和"用什么画"彻底分开了,并修掉一批长期存在的真实缺陷。

新东西

  • 新增 tkdeft.engines 绘制引擎层,可插拔切换 tksvg / wand / skia / pillow / cairo
  • 新增三个进程内栅格引擎(skia-python / Pillow / pycairo),完全不落盘; 其中 Pillow 引擎零额外依赖、永远可用
  • 新增按规格的图片缓存:RoundRectSpec 是不可变的,同尺寸同配色的一组控件 共用同一张 PhotoImage
  • 新增 SVG 形状助手 tkdeft.svg,统一并修正圆角矩形几何

修掉的缺陷

问题 影响
DSvgDraw.temppath() 每帧 mkstemp() 且从不关闭 fd 每绘制一帧泄漏一个句柄 + 一个残留文件
DDrawWidget.__init__ 一次性建 4 个临时文件 每个控件创建即泄漏 4 个句柄
只把图片存进 self._tkimg 同一画布上多张图片时,除最后一张外都会被 GC,画布 item 变空白
RenderManager 调用 widget.winfo_zorder() tkinter 没有这个方法,有脏控件就抛 AttributeError
svgwrite 矩形用 translate(0.5,0.5) + 整宽整高 描边中心线压在画布边界上,外侧半个线宽被裁掉

细节见常见问题与排查。

性能

栅格引擎配合规格缓存后,按钮重绘快约 39 倍、hover 往返约 52 倍 (本机 120×32 圆角矩形实测)。复现:python benchmarks/run_all.py。

发布 0.1.0

完善功能。这一版之后,tkdeft 的定位开始明确为"tkfluent 的底层零件库": 只提供把矢量图变成 Tkinter 控件所需的零件,不含具体组件。

发布 0.0.9

一些小修改。

早期版本回顾(0.0.1 – 0.0.7)

这几版是 tkdeft 起步阶段的记录,日期沿用最早那份更新日志。

版本 内容
0.0.7 模板库 Fluent 拆出去,成为独立的 tkfluent
0.0.6 模板组件 DBadge 增加 style=accent,调整自定义窗口
0.0.5 主题改为 theme(mode=..., style=...),不再需要 DDarkButton 这类成对的组件;新增 DWindow.wincustom 自定义窗口(仅 Windows)
0.0.3 / 0.0.4 补依赖(两次)
0.0.2 补充模板组件 DEntry、DFrame、DText、DBadge(详见 2024-01-25)
0.0.1 首个版本,模板组件 DButton

从这里开始,tkdeft 的方向就定下来了:用矢量绘图生成图片,再交给 Canvas / Label 显示, 具体走 SVG 还是进程内栅格化,交给可插拔的引擎决定。

发布0.0.2 0.0.3 0.0.4版本

补充设计模板Fluent的组件DEntry、DText、DFrame、DBadge(相对应会有深色设计,如DDarkEntry等)