tkdeft 0.1.2__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tkdeft-0.2.0/PKG-INFO +150 -0
- tkdeft-0.2.0/README.md +120 -0
- tkdeft-0.2.0/pyproject.toml +33 -0
- tkdeft-0.2.0/tkdeft/__init__.py +32 -0
- tkdeft-0.2.0/tkdeft/engines/__init__.py +168 -0
- tkdeft-0.2.0/tkdeft/engines/base.py +241 -0
- tkdeft-0.2.0/tkdeft/engines/cache.py +203 -0
- tkdeft-0.2.0/tkdeft/engines/cairo_engine.py +219 -0
- tkdeft-0.2.0/tkdeft/engines/colors.py +135 -0
- tkdeft-0.2.0/tkdeft/engines/pillow_engine.py +233 -0
- tkdeft-0.2.0/tkdeft/engines/skia_engine.py +239 -0
- tkdeft-0.2.0/tkdeft/svg.py +135 -0
- tkdeft-0.2.0/tkdeft/windows/canvas.py +242 -0
- tkdeft-0.2.0/tkdeft/windows/draw.py +160 -0
- {tkdeft-0.1.2 → tkdeft-0.2.0}/tkdeft/windows/drawwidget.py +120 -84
- tkdeft-0.1.2/PKG-INFO +0 -84
- tkdeft-0.1.2/README.md +0 -61
- tkdeft-0.1.2/pyproject.toml +0 -19
- tkdeft-0.1.2/tkdeft/__init__.py +0 -3
- tkdeft-0.1.2/tkdeft/windows/canvas.py +0 -26
- tkdeft-0.1.2/tkdeft/windows/draw.py +0 -48
- {tkdeft-0.1.2 → tkdeft-0.2.0}/tkdeft/object.py +0 -0
- {tkdeft-0.1.2 → tkdeft-0.2.0}/tkdeft/utility/__init__.py +0 -0
- {tkdeft-0.1.2 → tkdeft-0.2.0}/tkdeft/utility/fonts.py +0 -0
- {tkdeft-0.1.2 → tkdeft-0.2.0}/tkdeft/utility/segoe_font.ttf +0 -0
- {tkdeft-0.1.2 → tkdeft-0.2.0}/tkdeft/windows/__init__.py +0 -0
tkdeft-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tkdeft
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: 使用tkinter+tksvg开发的现代化界面库
|
|
5
|
+
Author: XiangQinxi
|
|
6
|
+
Author-email: xiangqinxi@outlook.com
|
|
7
|
+
Requires-Python: >=3.7,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Provides-Extra: cairo
|
|
18
|
+
Provides-Extra: raster
|
|
19
|
+
Provides-Extra: skia
|
|
20
|
+
Requires-Dist: easydict (>=1.13,<2.0)
|
|
21
|
+
Requires-Dist: numpy
|
|
22
|
+
Requires-Dist: pillow (>=10.2)
|
|
23
|
+
Requires-Dist: pycairo (>=1.29,<2.0) ; extra == "cairo" or extra == "raster"
|
|
24
|
+
Requires-Dist: skia-python (>=138.0,<139.0) ; extra == "skia" or extra == "raster"
|
|
25
|
+
Requires-Dist: svgwrite (>=1.4.3,<2.0.0)
|
|
26
|
+
Requires-Dist: tkextrafont (>=0.6.3,<0.7.0)
|
|
27
|
+
Requires-Dist: tksvg (>=0.7.4,<0.8.0)
|
|
28
|
+
Project-URL: Documentation, https://tkdeft.netlify.app
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
<div align="center">
|
|
32
|
+
|
|
33
|
+
# tkdeft
|
|
34
|
+
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
[](https://app.netlify.com/sites/tkdeft/deploys)
|
|
38
|
+
|
|
39
|
+
意为`灵巧`,灵活轻巧好用
|
|
40
|
+
|
|
41
|
+
继`tkadw`之后的`tkinter`的现代化界面库
|
|
42
|
+
|
|
43
|
+
> 开发中
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
## 注意
|
|
47
|
+
> [!NOTE]
|
|
48
|
+
> 这里呢,已经成为[tkfluent](https://pypi.org/project/tkfluent)的基础库了,不含[tkfluent](https://pypi.org/project/tkfluent)的组件。
|
|
49
|
+
> 如果你想体验扩展界面库的效果,请去[tkfluent](https://pypi.org/project/tkfluent)查阅。
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## 原理
|
|
53
|
+
先使用`svgwrite`进行矢量作图(默认会将其存储到临时文件夹中),再用`tksvg`打开图片,将图片利用`Canvas`或`Label`显示出来
|
|
54
|
+
[index.md](docs/docs/index.md)
|
|
55
|
+
> 这其中还是有些坑的,比如图片不显示等,导致我以前想做,却放弃了,现在知道错误所在了
|
|
56
|
+
|
|
57
|
+
## 绘制引擎
|
|
58
|
+
`tkdeft` 现在把"**画什么**"和"**用什么画**"分开了。组件只需要描述一个
|
|
59
|
+
`RoundRectSpec`(尺寸、圆角、填充、描边……),具体由哪个引擎栅格化是可切换的。
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from tkdeft.engines import set_engine, list_engines, cache_stats
|
|
63
|
+
|
|
64
|
+
print(list_engines()) # {'tksvg': True, 'wand': True, 'skia': True, 'pillow': True, 'cairo': True}
|
|
65
|
+
set_engine("skia") # 或 set_engine(2)
|
|
66
|
+
print(cache_stats()) # 缓存命中率,便于诊断
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| 引擎 | 类型 | 说明 | 额外依赖 |
|
|
70
|
+
| --- | --- | --- | --- |
|
|
71
|
+
| `tksvg` | SVG | 默认,保持既有行为 | 无 |
|
|
72
|
+
| `wand` | SVG | 经 Wand(ImageMagick) 转 PNG | `Wand` |
|
|
73
|
+
| `skia` | 栅格 | skia-python,进程内出图,画质与速度最好 | `pip install tkdeft[skia]` |
|
|
74
|
+
| `pillow` | 栅格 | Pillow 超采样,**永远可用** | 无(Pillow 是硬依赖) |
|
|
75
|
+
| `cairo` | 栅格 | pycairo | `pip install tkdeft[cairo]` |
|
|
76
|
+
|
|
77
|
+
栅格引擎不写临时文件、不解析 SVG、不碰磁盘;渲染结果按规格缓存,
|
|
78
|
+
**同尺寸同配色的一组控件共用同一张图片**。
|
|
79
|
+
|
|
80
|
+
### 实测(120×32 圆角矩形,本机)
|
|
81
|
+
|
|
82
|
+
| 场景 | 优化前 | 仅修基础设施 | + skia 引擎 |
|
|
83
|
+
| --- | --- | --- | --- |
|
|
84
|
+
| 圆角矩形(尺寸各异) | 13.81 ms | 4.94 ms | **1.26 ms** |
|
|
85
|
+
| 圆角矩形(参数相同) | 10.80 ms | 4.33 ms | **0.02 ms** |
|
|
86
|
+
| 按钮重绘 | 5.95 ms | 2.86 ms | **0.15 ms** |
|
|
87
|
+
| 按钮 hover 往返 | 13.65 ms | 6.08 ms | **0.26 ms** |
|
|
88
|
+
| 20 个按钮批量重绘 | 139.9 ms | 110.4 ms | **2.93 ms** |
|
|
89
|
+
|
|
90
|
+
复现:`python benchmarks/run_all.py`
|
|
91
|
+
|
|
92
|
+
## 已修复的缺陷
|
|
93
|
+
这一轮审查中定位并修掉的真实问题(都不是"风格问题"):
|
|
94
|
+
|
|
95
|
+
| 问题 | 影响 |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| `DSvgDraw.temppath()` 每次调用都 `mkstemp()` 且**从不关闭返回的 fd** | 每绘制一帧泄漏一个句柄,并在临时目录留下一个残留文件 |
|
|
98
|
+
| `DDrawWidget.__init__` 一次性 `mkstemp()` 四个临时文件 | 每个控件创建即泄漏 4 个句柄(20 个按钮 = 80 个) |
|
|
99
|
+
| `DCanvas.create_round_rectangle` 只把图片存进 `self._img` / `self._tkimg` | 同一画布上多张图片时,除最后一张外都会被 GC,**画布 item 变空白** |
|
|
100
|
+
| `RenderManager._render_all` 调用 `widget.winfo_zorder()` | `tkinter` **没有**这个方法,只要有脏控件就抛 `AttributeError` |
|
|
101
|
+
| `RenderManager._schedule_render` 里 `tk._default_root or list(...)[0]` | 两者都不可用时 `IndexError` |
|
|
102
|
+
| `_draw` 结尾又 `mark_dirty(self)` | 集中式调度下同一控件被重绘两遍 |
|
|
103
|
+
| svgwrite 生成的矩形用 `translate(0.5,0.5)` + 整宽整高 | 描边中心线压在画布边界上,**下边框和右边框被完全裁掉** |
|
|
104
|
+
| `create_svg_image` 对 `way` 未做兜底 | 回退路径遇到非 0/1 的渲染器编号会直接失败 |
|
|
105
|
+
|
|
106
|
+
## 计划
|
|
107
|
+
未来我打算先制作出`SunValley`设计的库然后就去做别的项目,[tkfluent](https://pypi.org/project/tkfluent)
|
|
108
|
+
|
|
109
|
+
至于完整文档,我后面会加紧制作的。
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
设计来源: https://pixso.cn/community/file/ItC5JH1TOwj15EeOPcY7LQ?from_share
|
|
113
|
+
|
|
114
|
+
### 为什么不像tkadw一样做跟易用的主题?
|
|
115
|
+
因为`svg`能实现很多漂亮的组件,而我套的模板可能不对其它设计其太大的作用
|
|
116
|
+
|
|
117
|
+
所以我将这个设计库放在这里当做模板,供其它设计者参考使用。
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
## 更新日志
|
|
121
|
+
### 2024-01-22
|
|
122
|
+
发布`0.0.1`版本,模板组件包括`DButton`
|
|
123
|
+
|
|
124
|
+
### 2023-01-23
|
|
125
|
+
发布`0.0.2`版本,补充模板组件`DEntry`、`DFrame`、`DText`, `DBadge`
|
|
126
|
+
|
|
127
|
+
### 2023-01-25
|
|
128
|
+
发布`0.0.3` `0.0.4`版本,粗心了,两次补充依赖
|
|
129
|
+
发布`0.0.5`版本,模板组件主题由`theme(mode=..., style=...)`设置,不再使用如`DDarkButton`这样的,添加`DWindow.wincustom`自定义窗口(仅限Windows)
|
|
130
|
+
发布`0.0.6`版本,模板组件`DBadge`补充样式`style=accent`,并对自定义窗口进行稍微调整
|
|
131
|
+
|
|
132
|
+
### 2023-01-26
|
|
133
|
+
发布`0.0.7`版本,模板库`Fluent`已移至`tkfluent`库
|
|
134
|
+
|
|
135
|
+
### 2024-09-16
|
|
136
|
+
发布`0.0.9`版本,一些小修改
|
|
137
|
+
|
|
138
|
+
### 2025-06-26
|
|
139
|
+
发布`0.1.0`版本,完善功能
|
|
140
|
+
|
|
141
|
+
### 2026-09-12
|
|
142
|
+
发布`0.2.0`版本:
|
|
143
|
+
|
|
144
|
+
* 新增 `tkdeft.engines` 绘制引擎层,可插拔切换 `tksvg` / `wand` / `skia` / `pillow` / `cairo`
|
|
145
|
+
* 新增进程内栅格引擎(skia-python / Pillow / pycairo),完全不落盘
|
|
146
|
+
* 新增按规格缓存的图片缓存,参数相同的绘制结果直接复用
|
|
147
|
+
* 新增 SVG 形状助手 `tkdeft.svg`,统一并修正圆角矩形几何
|
|
148
|
+
* 修复临时文件 / fd 泄漏、`PhotoImage` 被 GC 导致画面空白、
|
|
149
|
+
`RenderManager` 的 `winfo_zorder` 崩溃等问题
|
|
150
|
+
* 详细缺陷清单与性能数据见 [benchmarks/](benchmarks/)
|
tkdeft-0.2.0/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# tkdeft
|
|
4
|
+
|
|
5
|
+
</div>
|
|
6
|
+
|
|
7
|
+
[](https://app.netlify.com/sites/tkdeft/deploys)
|
|
8
|
+
|
|
9
|
+
意为`灵巧`,灵活轻巧好用
|
|
10
|
+
|
|
11
|
+
继`tkadw`之后的`tkinter`的现代化界面库
|
|
12
|
+
|
|
13
|
+
> 开发中
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
## 注意
|
|
17
|
+
> [!NOTE]
|
|
18
|
+
> 这里呢,已经成为[tkfluent](https://pypi.org/project/tkfluent)的基础库了,不含[tkfluent](https://pypi.org/project/tkfluent)的组件。
|
|
19
|
+
> 如果你想体验扩展界面库的效果,请去[tkfluent](https://pypi.org/project/tkfluent)查阅。
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## 原理
|
|
23
|
+
先使用`svgwrite`进行矢量作图(默认会将其存储到临时文件夹中),再用`tksvg`打开图片,将图片利用`Canvas`或`Label`显示出来
|
|
24
|
+
[index.md](docs/docs/index.md)
|
|
25
|
+
> 这其中还是有些坑的,比如图片不显示等,导致我以前想做,却放弃了,现在知道错误所在了
|
|
26
|
+
|
|
27
|
+
## 绘制引擎
|
|
28
|
+
`tkdeft` 现在把"**画什么**"和"**用什么画**"分开了。组件只需要描述一个
|
|
29
|
+
`RoundRectSpec`(尺寸、圆角、填充、描边……),具体由哪个引擎栅格化是可切换的。
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from tkdeft.engines import set_engine, list_engines, cache_stats
|
|
33
|
+
|
|
34
|
+
print(list_engines()) # {'tksvg': True, 'wand': True, 'skia': True, 'pillow': True, 'cairo': True}
|
|
35
|
+
set_engine("skia") # 或 set_engine(2)
|
|
36
|
+
print(cache_stats()) # 缓存命中率,便于诊断
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
| 引擎 | 类型 | 说明 | 额外依赖 |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| `tksvg` | SVG | 默认,保持既有行为 | 无 |
|
|
42
|
+
| `wand` | SVG | 经 Wand(ImageMagick) 转 PNG | `Wand` |
|
|
43
|
+
| `skia` | 栅格 | skia-python,进程内出图,画质与速度最好 | `pip install tkdeft[skia]` |
|
|
44
|
+
| `pillow` | 栅格 | Pillow 超采样,**永远可用** | 无(Pillow 是硬依赖) |
|
|
45
|
+
| `cairo` | 栅格 | pycairo | `pip install tkdeft[cairo]` |
|
|
46
|
+
|
|
47
|
+
栅格引擎不写临时文件、不解析 SVG、不碰磁盘;渲染结果按规格缓存,
|
|
48
|
+
**同尺寸同配色的一组控件共用同一张图片**。
|
|
49
|
+
|
|
50
|
+
### 实测(120×32 圆角矩形,本机)
|
|
51
|
+
|
|
52
|
+
| 场景 | 优化前 | 仅修基础设施 | + skia 引擎 |
|
|
53
|
+
| --- | --- | --- | --- |
|
|
54
|
+
| 圆角矩形(尺寸各异) | 13.81 ms | 4.94 ms | **1.26 ms** |
|
|
55
|
+
| 圆角矩形(参数相同) | 10.80 ms | 4.33 ms | **0.02 ms** |
|
|
56
|
+
| 按钮重绘 | 5.95 ms | 2.86 ms | **0.15 ms** |
|
|
57
|
+
| 按钮 hover 往返 | 13.65 ms | 6.08 ms | **0.26 ms** |
|
|
58
|
+
| 20 个按钮批量重绘 | 139.9 ms | 110.4 ms | **2.93 ms** |
|
|
59
|
+
|
|
60
|
+
复现:`python benchmarks/run_all.py`
|
|
61
|
+
|
|
62
|
+
## 已修复的缺陷
|
|
63
|
+
这一轮审查中定位并修掉的真实问题(都不是"风格问题"):
|
|
64
|
+
|
|
65
|
+
| 问题 | 影响 |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| `DSvgDraw.temppath()` 每次调用都 `mkstemp()` 且**从不关闭返回的 fd** | 每绘制一帧泄漏一个句柄,并在临时目录留下一个残留文件 |
|
|
68
|
+
| `DDrawWidget.__init__` 一次性 `mkstemp()` 四个临时文件 | 每个控件创建即泄漏 4 个句柄(20 个按钮 = 80 个) |
|
|
69
|
+
| `DCanvas.create_round_rectangle` 只把图片存进 `self._img` / `self._tkimg` | 同一画布上多张图片时,除最后一张外都会被 GC,**画布 item 变空白** |
|
|
70
|
+
| `RenderManager._render_all` 调用 `widget.winfo_zorder()` | `tkinter` **没有**这个方法,只要有脏控件就抛 `AttributeError` |
|
|
71
|
+
| `RenderManager._schedule_render` 里 `tk._default_root or list(...)[0]` | 两者都不可用时 `IndexError` |
|
|
72
|
+
| `_draw` 结尾又 `mark_dirty(self)` | 集中式调度下同一控件被重绘两遍 |
|
|
73
|
+
| svgwrite 生成的矩形用 `translate(0.5,0.5)` + 整宽整高 | 描边中心线压在画布边界上,**下边框和右边框被完全裁掉** |
|
|
74
|
+
| `create_svg_image` 对 `way` 未做兜底 | 回退路径遇到非 0/1 的渲染器编号会直接失败 |
|
|
75
|
+
|
|
76
|
+
## 计划
|
|
77
|
+
未来我打算先制作出`SunValley`设计的库然后就去做别的项目,[tkfluent](https://pypi.org/project/tkfluent)
|
|
78
|
+
|
|
79
|
+
至于完整文档,我后面会加紧制作的。
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
设计来源: https://pixso.cn/community/file/ItC5JH1TOwj15EeOPcY7LQ?from_share
|
|
83
|
+
|
|
84
|
+
### 为什么不像tkadw一样做跟易用的主题?
|
|
85
|
+
因为`svg`能实现很多漂亮的组件,而我套的模板可能不对其它设计其太大的作用
|
|
86
|
+
|
|
87
|
+
所以我将这个设计库放在这里当做模板,供其它设计者参考使用。
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
## 更新日志
|
|
91
|
+
### 2024-01-22
|
|
92
|
+
发布`0.0.1`版本,模板组件包括`DButton`
|
|
93
|
+
|
|
94
|
+
### 2023-01-23
|
|
95
|
+
发布`0.0.2`版本,补充模板组件`DEntry`、`DFrame`、`DText`, `DBadge`
|
|
96
|
+
|
|
97
|
+
### 2023-01-25
|
|
98
|
+
发布`0.0.3` `0.0.4`版本,粗心了,两次补充依赖
|
|
99
|
+
发布`0.0.5`版本,模板组件主题由`theme(mode=..., style=...)`设置,不再使用如`DDarkButton`这样的,添加`DWindow.wincustom`自定义窗口(仅限Windows)
|
|
100
|
+
发布`0.0.6`版本,模板组件`DBadge`补充样式`style=accent`,并对自定义窗口进行稍微调整
|
|
101
|
+
|
|
102
|
+
### 2023-01-26
|
|
103
|
+
发布`0.0.7`版本,模板库`Fluent`已移至`tkfluent`库
|
|
104
|
+
|
|
105
|
+
### 2024-09-16
|
|
106
|
+
发布`0.0.9`版本,一些小修改
|
|
107
|
+
|
|
108
|
+
### 2025-06-26
|
|
109
|
+
发布`0.1.0`版本,完善功能
|
|
110
|
+
|
|
111
|
+
### 2026-09-12
|
|
112
|
+
发布`0.2.0`版本:
|
|
113
|
+
|
|
114
|
+
* 新增 `tkdeft.engines` 绘制引擎层,可插拔切换 `tksvg` / `wand` / `skia` / `pillow` / `cairo`
|
|
115
|
+
* 新增进程内栅格引擎(skia-python / Pillow / pycairo),完全不落盘
|
|
116
|
+
* 新增按规格缓存的图片缓存,参数相同的绘制结果直接复用
|
|
117
|
+
* 新增 SVG 形状助手 `tkdeft.svg`,统一并修正圆角矩形几何
|
|
118
|
+
* 修复临时文件 / fd 泄漏、`PhotoImage` 被 GC 导致画面空白、
|
|
119
|
+
`RenderManager` 的 `winfo_zorder` 崩溃等问题
|
|
120
|
+
* 详细缺陷清单与性能数据见 [benchmarks/](benchmarks/)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "tkdeft"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "使用tkinter+tksvg开发的现代化界面库"
|
|
5
|
+
authors = ["XiangQinxi <xiangqinxi@outlook.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
documentation = "https://tkdeft.netlify.app"
|
|
8
|
+
|
|
9
|
+
[tool.poetry.dependencies]
|
|
10
|
+
easydict = "^1.13"
|
|
11
|
+
python = "^3.7"
|
|
12
|
+
tksvg = "^0.7.4"
|
|
13
|
+
tkextrafont = "^0.6.3"
|
|
14
|
+
svgwrite = "^1.4.3"
|
|
15
|
+
# 不设上界:Pillow 10 / 11 / 12 都实测可用(^10.2.0 会逼 pip 把已经装好的
|
|
16
|
+
# 新版 Pillow 降级,影响同一环境里的其它项目)。用到的最低特性是
|
|
17
|
+
# ImageDraw.rounded_rectangle(Pillow 8.2 引入)。
|
|
18
|
+
pillow = ">=10.2"
|
|
19
|
+
numpy = "*"
|
|
20
|
+
|
|
21
|
+
# 可选的进程内栅格绘制引擎。不装也能用(会退回 tksvg 或 Pillow 引擎)。
|
|
22
|
+
skia-python = { version = "^138.0", optional = true }
|
|
23
|
+
pycairo = { version = "^1.29", optional = true }
|
|
24
|
+
|
|
25
|
+
[tool.poetry.extras]
|
|
26
|
+
# 画质与速度最好的一组后端,推荐安装
|
|
27
|
+
skia = ["skia-python"]
|
|
28
|
+
cairo = ["pycairo"]
|
|
29
|
+
raster = ["skia-python", "pycairo"]
|
|
30
|
+
|
|
31
|
+
[build-system]
|
|
32
|
+
requires = ["poetry-core"]
|
|
33
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""tkdeft —— 把矢量绘图变成 Tkinter 组件的底座。
|
|
2
|
+
|
|
3
|
+
本包不提供具体组件,只提供零件:
|
|
4
|
+
|
|
5
|
+
* :mod:`tkdeft.engines` —— 可插拔的绘制引擎(tksvg / wand / skia / pillow / cairo)
|
|
6
|
+
* :mod:`tkdeft.svg` —— SVG 形状助手(统一的圆角矩形几何)
|
|
7
|
+
* :mod:`tkdeft.windows` —— ``DCanvas`` / ``DDraw`` / ``DDrawWidget``
|
|
8
|
+
* :mod:`tkdeft.object` —— ``DObject`` 配置容器
|
|
9
|
+
|
|
10
|
+
快速上手::
|
|
11
|
+
|
|
12
|
+
from tkdeft.engines import RoundRectSpec, render_roundrect, set_engine
|
|
13
|
+
|
|
14
|
+
set_engine("skia")
|
|
15
|
+
photo = render_roundrect(
|
|
16
|
+
RoundRectSpec(width=120, height=32, rx=6, fill="#ffffff"),
|
|
17
|
+
master=my_canvas,
|
|
18
|
+
)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from . import engines
|
|
22
|
+
from .utility import *
|
|
23
|
+
from .windows import *
|
|
24
|
+
from .object import DObject
|
|
25
|
+
|
|
26
|
+
#: 版本号。tkfluent 会据此判断环境里的 tkdeft 是否够新。
|
|
27
|
+
#: 注意:需要与 pyproject.toml 里的 version 保持一致。
|
|
28
|
+
__version__ = "0.2.0"
|
|
29
|
+
|
|
30
|
+
#: 是否提供绘制引擎层(tkfluent >= 0.2.0 依赖它)。
|
|
31
|
+
#: 旧版本没有这个子包,用它做能力探测比解析版本号更直接。
|
|
32
|
+
HAS_ENGINES = True
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""tkdeft 绘制引擎层。
|
|
2
|
+
|
|
3
|
+
对外主要接口
|
|
4
|
+
------------
|
|
5
|
+
::
|
|
6
|
+
|
|
7
|
+
from tkdeft.engines import set_engine, get_engine, render_roundrect, cache_stats
|
|
8
|
+
|
|
9
|
+
set_engine("skia") # 或 set_engine(2)
|
|
10
|
+
photo = render_roundrect(spec, master=canvas) # -> PhotoImage | None
|
|
11
|
+
|
|
12
|
+
``render_*`` 返回 ``None`` 表示"当前引擎不参与这条路径",调用方应回退到
|
|
13
|
+
既有的 svgwrite + tksvg 实现。这样 SVG 引擎与栅格引擎可以共存于同一套组件代码。
|
|
14
|
+
|
|
15
|
+
内置引擎
|
|
16
|
+
--------
|
|
17
|
+
====== ========== ==================================================
|
|
18
|
+
名字 类型 说明
|
|
19
|
+
====== ========== ==================================================
|
|
20
|
+
tksvg SVG(文件) 默认,保持既有行为完全不变
|
|
21
|
+
wand SVG(文件) 经 Wand 转 PNG,兼容旧 ``renderer=1``
|
|
22
|
+
skia 栅格 skia-python,质量与速度最好
|
|
23
|
+
pillow 栅格 Pillow 超采样,零额外依赖,永远可用
|
|
24
|
+
cairo 栅格 pycairo
|
|
25
|
+
====== ========== ==================================================
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import warnings
|
|
31
|
+
from typing import Optional
|
|
32
|
+
|
|
33
|
+
from .base import (
|
|
34
|
+
DrawEngine,
|
|
35
|
+
RoundRectSpec,
|
|
36
|
+
SvgEngine,
|
|
37
|
+
ThumbSpec,
|
|
38
|
+
TrackSpec,
|
|
39
|
+
get_engine,
|
|
40
|
+
get_engine_name,
|
|
41
|
+
list_engines,
|
|
42
|
+
register_engine,
|
|
43
|
+
set_engine,
|
|
44
|
+
)
|
|
45
|
+
from .cache import (
|
|
46
|
+
cache_stats,
|
|
47
|
+
clear_cache,
|
|
48
|
+
get_cache,
|
|
49
|
+
set_cache_budget,
|
|
50
|
+
)
|
|
51
|
+
from .cairo_engine import CairoEngine
|
|
52
|
+
from .colors import parse_color, parse_opacity
|
|
53
|
+
from .pillow_engine import PillowEngine
|
|
54
|
+
from .skia_engine import SkiaEngine
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
"DrawEngine",
|
|
58
|
+
"RoundRectSpec",
|
|
59
|
+
"TrackSpec",
|
|
60
|
+
"ThumbSpec",
|
|
61
|
+
"SvgEngine",
|
|
62
|
+
"register_engine",
|
|
63
|
+
"get_engine",
|
|
64
|
+
"get_engine_name",
|
|
65
|
+
"set_engine",
|
|
66
|
+
"list_engines",
|
|
67
|
+
"render_roundrect",
|
|
68
|
+
"render_track",
|
|
69
|
+
"render_thumb",
|
|
70
|
+
"to_photoimage",
|
|
71
|
+
"clear_cache",
|
|
72
|
+
"cache_stats",
|
|
73
|
+
"set_cache_budget",
|
|
74
|
+
"parse_color",
|
|
75
|
+
"parse_opacity",
|
|
76
|
+
"engine_error",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
# --------------------------------------------------------------------------
|
|
80
|
+
# 引擎注册
|
|
81
|
+
# --------------------------------------------------------------------------
|
|
82
|
+
class TksvgEngine(SvgEngine):
|
|
83
|
+
name = "tksvg"
|
|
84
|
+
requires = ("tksvg",)
|
|
85
|
+
description = "svgwrite + tksvg(默认,保持既有行为)"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class WandEngine(SvgEngine):
|
|
89
|
+
name = "wand"
|
|
90
|
+
requires = ("wand",)
|
|
91
|
+
description = "svgwrite + Wand(ImageMagick)"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
#: 与历史 ``set_renderer(int)`` API 的对应关系
|
|
95
|
+
RENDERER_INDEX = {0: "tksvg", 1: "wand", 2: "skia", 3: "pillow", 4: "cairo"}
|
|
96
|
+
|
|
97
|
+
register_engine(TksvgEngine(), aliases=("0", "svg", "default"), default=True)
|
|
98
|
+
register_engine(WandEngine(), aliases=("1", "1.0"))
|
|
99
|
+
register_engine(SkiaEngine(), aliases=("2",))
|
|
100
|
+
register_engine(PillowEngine(), aliases=("3", "pil"))
|
|
101
|
+
register_engine(CairoEngine(), aliases=("4", "pycairo"))
|
|
102
|
+
|
|
103
|
+
#: 最近一次栅格化失败的原因,便于诊断(``None`` 表示正常)
|
|
104
|
+
engine_error: Optional[str] = None
|
|
105
|
+
|
|
106
|
+
_WARNED: "set[str]" = set()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _warn_once(engine_name: str, exc: BaseException) -> None:
|
|
110
|
+
global engine_error
|
|
111
|
+
engine_error = f"{engine_name}: {type(exc).__name__}: {exc}"
|
|
112
|
+
if engine_name in _WARNED:
|
|
113
|
+
return
|
|
114
|
+
_WARNED.add(engine_name)
|
|
115
|
+
warnings.warn(
|
|
116
|
+
f"绘制引擎 {engine_name!r} 渲染失败,已回退到 SVG 路径。"
|
|
117
|
+
f"原始错误:{engine_error}",
|
|
118
|
+
RuntimeWarning,
|
|
119
|
+
stacklevel=3,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def to_photoimage(image, master):
|
|
124
|
+
"""``PIL.Image`` → ``tkinter`` 的 ``PhotoImage``。"""
|
|
125
|
+
from PIL import ImageTk
|
|
126
|
+
|
|
127
|
+
return ImageTk.PhotoImage(image, master=master)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _render(kind: str, spec, master):
|
|
131
|
+
"""三种图元共用的"查缓存 → 渲染 → 转 PhotoImage → 回填缓存"流程。"""
|
|
132
|
+
engine = get_engine()
|
|
133
|
+
if not engine.is_raster:
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
key = (engine.name, kind, spec)
|
|
137
|
+
cache = get_cache(master)
|
|
138
|
+
cached = cache.get(key)
|
|
139
|
+
if cached is not None:
|
|
140
|
+
return cached
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
image = getattr(engine, f"render_{kind}")(spec)
|
|
144
|
+
except Exception as exc: # 单个引擎出错不应让整个界面挂掉
|
|
145
|
+
_warn_once(engine.name, exc)
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
if image is None:
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
photo = to_photoimage(image, master)
|
|
152
|
+
cache.put(key, photo, max(1, int(spec.width) * int(spec.height)))
|
|
153
|
+
return photo
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def render_roundrect(spec: RoundRectSpec, master):
|
|
157
|
+
"""渲染圆角矩形;当前引擎不支持时返回 ``None``。"""
|
|
158
|
+
return _render("roundrect", spec, master)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def render_track(spec: TrackSpec, master):
|
|
162
|
+
"""渲染滑块进度条槽;当前引擎不支持时返回 ``None``。"""
|
|
163
|
+
return _render("track", spec, master)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def render_thumb(spec: ThumbSpec, master):
|
|
167
|
+
"""渲染滑块把手;当前引擎不支持时返回 ``None``。"""
|
|
168
|
+
return _render("thumb", spec, master)
|