tkwin11 0.2.3__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.
- tkwin11-0.2.3/.gitignore +7 -0
- tkwin11-0.2.3/CHANGELOG.md +103 -0
- tkwin11-0.2.3/LICENSE +22 -0
- tkwin11-0.2.3/PKG-INFO +88 -0
- tkwin11-0.2.3/README.md +133 -0
- tkwin11-0.2.3/README.pypi.md +70 -0
- tkwin11-0.2.3/docs/images/gallery-home.png +0 -0
- tkwin11-0.2.3/docs/images/gallery-menu.png +0 -0
- tkwin11-0.2.3/docs/images/gallery-placeholder.png +0 -0
- tkwin11-0.2.3/docs/window-chrome-notes.md +142 -0
- tkwin11-0.2.3/gallery.py +1657 -0
- tkwin11-0.2.3/main.py +8 -0
- tkwin11-0.2.3/pyproject.toml +39 -0
- tkwin11-0.2.3/tests/check_window_chrome.py +99 -0
- tkwin11-0.2.3/tests/test_callback_cleanup.py +51 -0
- tkwin11-0.2.3/tests/test_menu_underline.py +125 -0
- tkwin11-0.2.3/tests/test_motion.py +23 -0
- tkwin11-0.2.3/tests/test_scale_dpi_dirty.py +158 -0
- tkwin11-0.2.3/tests/test_window_flicker_workaround.py +201 -0
- tkwin11-0.2.3/tkwin11/__init__.py +72 -0
- tkwin11-0.2.3/tkwin11/button.py +218 -0
- tkwin11-0.2.3/tkwin11/cards.py +130 -0
- tkwin11-0.2.3/tkwin11/charts.py +578 -0
- tkwin11-0.2.3/tkwin11/classic.py +115 -0
- tkwin11-0.2.3/tkwin11/core.py +1416 -0
- tkwin11-0.2.3/tkwin11/dataview.py +218 -0
- tkwin11-0.2.3/tkwin11/dpi.py +555 -0
- tkwin11-0.2.3/tkwin11/dropdowns.py +169 -0
- tkwin11-0.2.3/tkwin11/expansion.py +89 -0
- tkwin11-0.2.3/tkwin11/extras.py +487 -0
- tkwin11-0.2.3/tkwin11/feedback.py +280 -0
- tkwin11-0.2.3/tkwin11/fluent.py +409 -0
- tkwin11-0.2.3/tkwin11/flyouts.py +128 -0
- tkwin11-0.2.3/tkwin11/forms.py +527 -0
- tkwin11-0.2.3/tkwin11/inputs.py +154 -0
- tkwin11-0.2.3/tkwin11/layouts.py +60 -0
- tkwin11-0.2.3/tkwin11/media.py +412 -0
- tkwin11-0.2.3/tkwin11/menus.py +61 -0
- tkwin11-0.2.3/tkwin11/motion.py +126 -0
- tkwin11-0.2.3/tkwin11/navigation.py +248 -0
- tkwin11-0.2.3/tkwin11/placeholder.py +173 -0
- tkwin11-0.2.3/tkwin11/popups.py +219 -0
- tkwin11-0.2.3/tkwin11/render.py +198 -0
- tkwin11-0.2.3/tkwin11/restore_animation.py +189 -0
- tkwin11-0.2.3/tkwin11/scrolling.py +99 -0
- tkwin11-0.2.3/tkwin11/selection.py +157 -0
- tkwin11-0.2.3/tkwin11/slider.py +460 -0
- tkwin11-0.2.3/tkwin11/states.py +49 -0
- tkwin11-0.2.3/tkwin11/switches.py +284 -0
- tkwin11-0.2.3/tkwin11/table.py +400 -0
- tkwin11-0.2.3/tkwin11/tcl_bridge.py +137 -0
- tkwin11-0.2.3/tkwin11/titlebar.py +59 -0
- tkwin11-0.2.3/tkwin11/tooltip.py +116 -0
- tkwin11-0.2.3/tkwin11/widgets.py +303 -0
- tkwin11-0.2.3/tkwin11/window_chrome.py +801 -0
tkwin11-0.2.3/.gitignore
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# 更新记录
|
|
2
|
+
|
|
3
|
+
## 0.2.3 — 2026-11-08
|
|
4
|
+
|
|
5
|
+
- 添加 PyPI 发行配置,提供无第三方必需依赖的 wheel 和源码包;版本号统一读取库内 `__version__`。
|
|
6
|
+
|
|
7
|
+
### 窗口闪烁缓解
|
|
8
|
+
|
|
9
|
+
- 纳入 RonTang 在 #11 中提供的原生窗口过渡方案,并在 #12 中增加配置与修复:最大化时短暂保护背景,还原时使用临时位图层,保留原生任务栏、Snap 和 DWM 动画。
|
|
10
|
+
- `install()` 默认启用 `window_flicker_workaround=True`;可传 `False` 关闭。新建根窗口保存配置,所属 Toplevel 继承;不自动添加自定义标题栏,也不回写已创建根窗口。
|
|
11
|
+
- `WindowChrome`、`FluentWindowChrome`、`FluentTitleBar` 支持同名参数,`None` 继承,`True` / `False` 按窗口覆盖。不调用 `install()` 的手动初始化程序默认关闭;Gallery 主窗口显式启用。
|
|
12
|
+
|
|
13
|
+
### 修复
|
|
14
|
+
|
|
15
|
+
- 隐藏后再显示不再被旧截图持续覆盖;隐藏、销毁和窗口 wrapper 替换时清理覆盖层与相关资源。
|
|
16
|
+
- 截图前后检查遮挡、窗口可见性和屏幕范围;不适合截图时退回原生还原,避免把遮挡窗口画面复制进还原位图。
|
|
17
|
+
- 快速二次最小化不再复用旧位图;还原客户区尺寸改变时放弃旧图。
|
|
18
|
+
|
|
19
|
+
### 验证与限制
|
|
20
|
+
|
|
21
|
+
- Windows、Python 3.11、150% 缩放下,30 项单元测试及开关开启/关闭两种状态下的 20 组窗口回归通过,Gallery 启动、最大化和还原截图检查通过。
|
|
22
|
+
- 该方案仍是实验性时序规避,不是渲染问题的根治。80ms / 350ms 为经验值;遮挡时保守回退,不能保证所有硬件、电源模式或 Tk 版本上的效果。
|
|
23
|
+
|
|
24
|
+
## 0.2.2 — 2026-10-28
|
|
25
|
+
|
|
26
|
+
本版的修复与优化大多来自 [@RonTang](https://github.com/RonTang) 的 PR 与问题报告,感谢。
|
|
27
|
+
|
|
28
|
+
### 修复
|
|
29
|
+
|
|
30
|
+
- 菜单项的 `underline` 为空字符串时,打开菜单浮层不再抛出 `ValueError`(#3,#8)。绘制与键盘助记键统一使用规范化后的下划线位置:保留有效的索引 0,空值、无效值与越界值视为没有助记键。
|
|
31
|
+
- 菜单助记键:只有唯一匹配的明确助记键才执行菜单命令;按首字母回退时只移动高亮、不再直接执行;重复的助记键循环选择,跳过禁用项(#8)。
|
|
32
|
+
- 控件销毁时,开关、输入框、下拉框与滑块的类绑定回调改为通过登记它们的根窗口删除,与 tkinter 的清理记录保持一致(#2)。
|
|
33
|
+
- 缓动函数在起点和终点精确返回 0 与 1(#1)。
|
|
34
|
+
|
|
35
|
+
### 性能
|
|
36
|
+
|
|
37
|
+
- 开关:动画帧与已提交的是同一张缓存图像时跳过拷贝,PR 中的回归场景图片拷贝次数减少约 60%(#4)。
|
|
38
|
+
- 原生滑块 `ttk.Scale`:像素状态不变时跳过重复渲染,PR 中测得连续键盘操作的绘制耗时中位数约减半(#5)。判重同时覆盖所有已存在的 DPI 图像,跳过时仍记录最新数值,之后新建的 DPI 图像不会沿用旧位置(#7)。
|
|
39
|
+
- 透明图标缓存不再按背景色区分,像素相同的图标只生成一次;打开 Gallery 全部页面后缓存图片由 102 张降为 94 张(#6)。
|
|
40
|
+
|
|
41
|
+
### 测试
|
|
42
|
+
|
|
43
|
+
- 新增缓动、回调清理、菜单助记键与滑块 DPI 判重的回归测试,共 20 项,`python -m unittest discover -s tests` 运行。
|
|
44
|
+
|
|
45
|
+
## 0.2.1 — 2026-10-21
|
|
46
|
+
|
|
47
|
+
- 修复自定义标题栏销毁内置 controls 后移动窗口触发 TclError(上游 TkWebUI issue #5):跳过已销毁按钮的状态同步、绘制和原生轮询,清除最大化命中区域,同时继续更新拖动区域、排除区域及窗口缩放策略;FluentWindowChrome 同样支持此用法。
|
|
48
|
+
|
|
49
|
+
## 0.2.0 — 2026-10-21
|
|
50
|
+
|
|
51
|
+
首次打开页面的速度与流畅度,以及按钮尺寸的一致性。
|
|
52
|
+
|
|
53
|
+
### 性能
|
|
54
|
+
|
|
55
|
+
- `apply_theme` 启动耗时约减半(150% 缩放下 0.47 s → 0.23 s),视觉逐像素不变(上游 TkWebUI issue #4):
|
|
56
|
+
- 圆角 / 圆形位图直接生成一张 RGBA PNG,不再拼接十六进制颜色串、也不再逐像素调用 `transparency_set`;2×2 采样颜色一致的像素跳过混色。
|
|
57
|
+
- 开关样式只预先生成开 / 关与禁用 4 帧,中间动画帧在首次切换时生成并缓存(悬停帧原本就是如此)。
|
|
58
|
+
- 字体存在性检查改为直接解析该字体,不再调用 `tkfont.families()` 枚举全部系统字体(Windows 冷启动时最多约 1 s)。
|
|
59
|
+
- 打开页面时不再反复让整个程序重新布局,外观逐像素不变:
|
|
60
|
+
- 按钮、复选 / 单选、开关、滑块首次出现时会声明自己专属的 ttk 元素与布局,DPI 模块也会为新样式生成副本。这些只是新名字,现有控件都用不到,但 ttk 每轮写入都会向整个程序广播 `<<ThemeChanged>>`,让所有控件重新布局和重绘,每打开一页约 5 次。现在这类“只新增”的写入不再广播。
|
|
61
|
+
- 其他样式修改仍然立即广播给当前显示的控件;隐藏的缓存页面、未选中的标签页、已撤下的窗口改为在再次显示时补发,不再在后台逐个重排。
|
|
62
|
+
- 线条图标光栅化快约 6 倍(每条笔画只计算其覆盖范围内的像素),同一图标换颜色时复用覆盖率;输出逐像素不变。
|
|
63
|
+
- 各模块的 `<Map>` 挂载钩子合并为一个只传窗口路径的 Tcl 绑定(`tcl_bridge.on_map`),DPI 模块的 `<Configure>` 在 Tcl 层只放行顶层窗口。此前每个控件映射时要为 10 个处理器各构造一次完整的 tkinter 事件对象。
|
|
64
|
+
- 合计(Windows 11、150% 缩放、Gallery 37 个页面,3 次取中位数):页面首次打开平均 462 ms → 222 ms,最慢一页 1.27 s → 0.45 s;从启动到窗口就绪 1.72 s → 1.06 s。
|
|
65
|
+
|
|
66
|
+
### 外观
|
|
67
|
+
|
|
68
|
+
- 按钮、切换按钮、工具按钮的表面元素统一为 `padding=0`,内边距只由样式的 `padding` 决定。此前动画控制器替换上的元素没有指定 `padding`,ttk 默认取边框宽度,按钮挂载后四周各被撑大一个边框(150% 缩放下 45 → 59 px,比同一行的输入框、下拉框高 14 px)。现在按钮与输入框同高,挂载前后尺寸一致。
|
|
69
|
+
|
|
70
|
+
### 修复
|
|
71
|
+
|
|
72
|
+
- 动画在事件循环被阻塞(构建或首次绘制页面)后不再直接跳到终点:单帧最多计约两帧的进度。首次打开页面时左侧导航选中指示条、页面滑入此前几乎不播放。
|
|
73
|
+
- 导航在页面命令中再次 `select(notify=False)` 时不再把正在展开的指示条瞬间画成满高。
|
|
74
|
+
- 展开卡片内容尺寸变化(如首次按真实宽度布局)时直接对齐新高度,不再播放 250 ms 动画;此前首次显示设置卡片页时卡片会上下抖动 1 px,并在每帧重排整页。用户点击展开 / 折叠仍有动画。
|
|
75
|
+
- 多列表格(`configure_table` / `ResponsiveTable`)创建时就只让最后一列拉伸。此前要等表格显示后才设置,在屏幕外完成布局的表格会把多出的宽度平均分给所有列,之后也不会恢复。
|
|
76
|
+
- Gallery:窗口在 DWM 隐藏(cloak)状态下完成首次布局后再显示。此前原生窗口提前创建,撤下状态的窗口按 200×200 布局,显示后的头几帧能看到标题栏、侧栏和页面逐步变宽。
|
|
77
|
+
|
|
78
|
+
## 0.1.0 — 2026-10-18
|
|
79
|
+
|
|
80
|
+
首个公开版本。
|
|
81
|
+
|
|
82
|
+
### 新增
|
|
83
|
+
|
|
84
|
+
- `tkwin11.install()`:旧程序在创建 `tk.Tk()` 前加一行即可换肤,经典 tk 控件同样适配(`theme_classic`);显式设置的颜色保持不变,可锁定主题避免被 `theme_use()` 覆盖。
|
|
85
|
+
- `PlaceholderEntry` / `set_placeholder`:输入框为空时显示灰色提示;提示是覆盖在输入框上的标签,`get()` 与 `textvariable` 始终是真实内容。
|
|
86
|
+
- 仿 WinUI 3 Gallery 的演示程序:标题栏搜索、分组导航、示例卡片、选项面板与可展开源代码,页面按需构建。
|
|
87
|
+
|
|
88
|
+
### 性能
|
|
89
|
+
|
|
90
|
+
- 打开弹窗、菜单、悬停卡片时不再写入 ttk 样式。此前每次写入都会触发整个程序的 `<<ThemeChanged>>` 重排与重绘,窗口拖到副屏后下拉浮层需要 1.4 秒、菜单约 1.9 秒,现在约 40–50 ms。
|
|
91
|
+
- 缓存的隐藏页面在 DPI 变化后不再被重新映射和重绘(Tk 画布会在内嵌窗口尺寸变化时直接映射它)。
|
|
92
|
+
- 控件命令代理只转发需要处理的子命令;`instate`、`identify`、画布 `coords`、`font measure` 等直接走原生。
|
|
93
|
+
- 按钮动画帧位图复用;动画不再每帧查询系统动画设置。
|
|
94
|
+
|
|
95
|
+
### 修复
|
|
96
|
+
|
|
97
|
+
- 在 Python 3.13 以前,`widget.unbind(seq, id)` 会删除同一事件的全部绑定:关闭任意菜单、弹窗或通知后,窗口的 `<Configure>` 等处理器全部丢失。
|
|
98
|
+
- `ttk.Label` 等控件的样式字体(加粗、小字、标题)被选项数据库的 `*Font` 覆盖而全部失效。
|
|
99
|
+
- 每个提示框都绑定一次窗口 `<Configure>`,窗口内任意控件变化都要逐个回调。
|
|
100
|
+
- 滚动区域的晚到子控件钩子在弹窗中无效,且销毁一个滚动区域会删除其他区域的钩子。
|
|
101
|
+
- 库修改全局 `ctypes.windll.user32` 的函数原型,会破坏宿主程序自己的 ctypes 调用。
|
|
102
|
+
- 窗口尺寸:固定尺寸窗口拖动变大、最小化还原残缺(上游 TkWebUI issue #2);可调整窗口打开时比请求尺寸大约 22×56 px;运行中改变 `resizable()` 后用户调整过的尺寸被弹回。
|
|
103
|
+
- 下拉按钮 / `ttk.Menubutton` 的箭头被纵向平铺出多余的点。
|
tkwin11-0.2.3/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TkWin11 contributors
|
|
4
|
+
Copyright (c) 2026 TkWebUI contributors
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
tkwin11-0.2.3/PKG-INFO
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tkwin11
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: Windows 11 Fluent-style themes and widgets for tkinter and ttk
|
|
5
|
+
Project-URL: Homepage, https://github.com/bvb6417/tkwin11
|
|
6
|
+
Project-URL: Documentation, https://github.com/bvb6417/tkwin11#readme
|
|
7
|
+
Project-URL: Issues, https://github.com/bvb6417/tkwin11/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/bvb6417/tkwin11/blob/main/CHANGELOG.md
|
|
9
|
+
Author: bvb6417 and TkWin11 contributors
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: fluent,gui,theme,tkinter,ttk,windows11
|
|
13
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# TkWin11
|
|
20
|
+
|
|
21
|
+
Windows 11 Fluent-style themes and widgets for Python's `tkinter` / `ttk`.
|
|
22
|
+
纯 Tkinter / ttk 实现的 Windows 11 Fluent 风格主题与控件库。
|
|
23
|
+
|
|
24
|
+
## Installation / 安装
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
python -m pip install tkwin11
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Requires Python 3.8+ with Tkinter available. Developed and tested on Windows 11
|
|
31
|
+
with Python 3.11. Windows-specific title bars, Snap and per-monitor DPI features
|
|
32
|
+
are enabled only on Windows; other platforms use the ordinary system title bar
|
|
33
|
+
and have not been fully tested. On Linux, install your distribution's Tkinter
|
|
34
|
+
package (often `python3-tk`) if needed. Tkinter is not installed by pip.
|
|
35
|
+
|
|
36
|
+
Core widgets require no third-party packages. `ImageViewer` can optionally use
|
|
37
|
+
Pillow for JPEG, WebP and additional image formats.
|
|
38
|
+
|
|
39
|
+
## Quick start / 快速开始
|
|
40
|
+
|
|
41
|
+
For an existing program, call `install()` before creating its first Tk root:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import tkinter as tk
|
|
45
|
+
from tkinter import ttk
|
|
46
|
+
import tkwin11
|
|
47
|
+
|
|
48
|
+
tkwin11.install()
|
|
49
|
+
root = tk.Tk()
|
|
50
|
+
root.title("TkWin11 example")
|
|
51
|
+
ttk.Entry(root).pack(padx=24, pady=12)
|
|
52
|
+
ttk.Button(root, text="Save").pack(padx=24, pady=12)
|
|
53
|
+
root.mainloop()
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
New programs may instead call `enable_dpi_awareness()` before `tk.Tk()` and
|
|
57
|
+
`apply_theme(root)` before creating widgets.
|
|
58
|
+
|
|
59
|
+
The library includes Fluent buttons, switches, navigation, custom title bars,
|
|
60
|
+
menus, dialogs, tooltips, tables, spreadsheets, charts and form controls.
|
|
61
|
+
|
|
62
|
+
## Window transitions / 窗口过渡
|
|
63
|
+
|
|
64
|
+
`install()` defaults to `window_flicker_workaround=True`. This experimental
|
|
65
|
+
workaround only affects windows using the library's custom title bars; it does
|
|
66
|
+
not add a title bar to ordinary Tk windows. To disable it:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
tkwin11.install(window_flicker_workaround=False)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`WindowChrome`, `FluentWindowChrome` and `FluentTitleBar` also accept this option
|
|
73
|
+
per window. Applications using manual theme initialization do not enable it
|
|
74
|
+
unless requested. The workaround is timing-based, not a root-cause rendering
|
|
75
|
+
fix; occluded windows fall back to native restore behavior.
|
|
76
|
+
|
|
77
|
+
## Demo and documentation
|
|
78
|
+
|
|
79
|
+
Download the [GitHub source](https://github.com/bvb6417/tkwin11) or source
|
|
80
|
+
distribution, then run `python main.py` to open the Gallery. The wheel installs
|
|
81
|
+
the library; the Gallery stays in the source distribution.
|
|
82
|
+
|
|
83
|
+
- [Full documentation and examples](https://github.com/bvb6417/tkwin11#readme)
|
|
84
|
+
- [Changelog](https://github.com/bvb6417/tkwin11/blob/main/CHANGELOG.md)
|
|
85
|
+
- [Issue tracker](https://github.com/bvb6417/tkwin11/issues)
|
|
86
|
+
- [TkWebUI comparison](https://github.com/bvb6417/tkwin11#如何选择tkwin11-还是-tkwebui)
|
|
87
|
+
|
|
88
|
+
MIT license. Derived from TkWebUI; see the included LICENSE for attribution.
|
tkwin11-0.2.3/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# TkWin11
|
|
2
|
+
|
|
3
|
+
纯 tkinter / ttk 实现的 Windows 11 Fluent 风格主题与控件库。只需要 Python 标准库;现有的 tkinter 程序加一行代码就能换上同样的外观。
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
- **一行换肤**:旧程序在创建 `tk.Tk()` 前调用 `tkwin11.install()`,ttk 控件、`tk.Entry/Text/Listbox`、`tk.Menu` 弹出菜单和经典 Button / Label / Frame 等全部换上 Win11 外观,代码其余部分不用改。
|
|
8
|
+
- **Fluent 控件**:一体化标题栏、层级导航、设置卡片与展开卡片、圆角浮层菜单、下拉浮层、开关、滑块、输入提示、对话框、通知、表格、图表等,带悬停 / 按下 / 焦点 / 禁用状态和可中断的过渡动画。
|
|
9
|
+
- **多显示器**:每块显示器按自己的缩放比例适配字体、图片和尺寸,窗口拖到另一块屏幕后自动重新适配。
|
|
10
|
+
- **Gallery**:仿 WinUI 3 Gallery 的完整演示,每个示例都能查看源代码。
|
|
11
|
+
|
|
12
|
+
## 如何选择:TkWin11 还是 TkWebUI
|
|
13
|
+
|
|
14
|
+
TkWin11 由 [TkWebUI](https://github.com/bvb6417/tkwebui) 分支而来,两者的控件大体相同,取舍不同:
|
|
15
|
+
|
|
16
|
+
| | TkWin11 | TkWebUI |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| 外观 | Windows 11 Fluent | 现代 Web 应用风格 |
|
|
19
|
+
| 实现 | 运行时接管样式、图片、字体和控件命令,以支持动画、多显示器 DPI 和一键换肤 | 样式一次生成后固定,不改动 Tk 的内部命令;代码量约为 TkWin11 的一半 |
|
|
20
|
+
| 接入 | 创建窗口前调用 `tkwin11.install()`,`tk.Button`、`tk.Menu` 等经典控件也一起换肤 | 创建窗口后调用 `apply_theme(root)`,换肤 ttk 控件 |
|
|
21
|
+
| 动效 | 悬停、按下、开关、导航和页面切换都有过渡 | 无过渡动画 |
|
|
22
|
+
| 主题开销¹ | 启动约 +0.15 s,首次打开每页约 +70 ms | 启动约 +0.2 s,切页每页约 +16 ms |
|
|
23
|
+
| 平台 | 以 Windows 11 为目标,一体化标题栏和多显示器适配只在 Windows 生效 | 以 Windows 为主,其他平台使用纯 Python 回退(未测试) |
|
|
24
|
+
|
|
25
|
+
- **选 TkWin11**:做发给 Windows 用户的桌面程序,或想让老 tkinter 程序一行代码换上 Windows 11 的观感;愿意为动效和多显示器适配多付一些开销。
|
|
26
|
+
- **选 TkWebUI**:做科研、工程或内部工具,重点是单元格表格、数据表、图表、表单这类干活的组件;希望依赖少、行为可预期,出了问题能直接读懂源码。
|
|
27
|
+
|
|
28
|
+
¹ 同一窗口套用主题与不套用主题相比,Windows 11、150% 缩放下实测。两者的来历与接口差异见[与 TkWebUI 的关系](#与-tkwebui-的关系)。
|
|
29
|
+
|
|
30
|
+
## 快速开始
|
|
31
|
+
|
|
32
|
+
可以通过 PyPI 安装控件库,也可把 `tkwin11` 文件夹复制到自己的脚本旁边:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
python -m pip install tkwin11
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
需要 Python 3.8+ 和可用的 Tkinter,核心控件无需第三方依赖。wheel 安装控件库,Gallery 保留在 GitHub 源码和源码发行包中。
|
|
39
|
+
|
|
40
|
+
**运行 Gallery**:双击 `main.py`,或
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python main.py
|
|
44
|
+
python main.py --page menu # 直接打开某一页,例如 button、entry、dpi
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**旧程序一键替换**:在创建 `tk.Tk()` 之前加一行
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import tkwin11; tkwin11.install()
|
|
51
|
+
|
|
52
|
+
import tkinter as tk
|
|
53
|
+
from tkinter import ttk
|
|
54
|
+
root = tk.Tk()
|
|
55
|
+
tk.Button(root, text='经典按钮').pack()
|
|
56
|
+
ttk.Combobox(root, values=['A', 'B'], state='readonly').pack()
|
|
57
|
+
root.mainloop()
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
代码里显式设置的颜色(如 `bg='red'`)保持不变;之后调用 `ttk.Style().theme_use('clam')` 也不会覆盖主题。硬编码像素尺寸、依赖系统位图缩放的程序可以用 `install(dpi=False)`。已经创建好窗口的程序可直接调用 `apply_theme(root)` 与 `theme_classic(root)`。
|
|
61
|
+
|
|
62
|
+
**可配置的窗口闪烁缓解**(实验性,`install()` 默认启用):
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import tkwin11
|
|
66
|
+
tkwin11.install() # 在 tk.Tk() 之前调用,默认 window_flicker_workaround=True
|
|
67
|
+
# 如需关闭,改用:tkwin11.install(window_flicker_workaround=False)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
该设置由之后创建的根窗口保存,其 `Toplevel` 窗口继承;仅影响使用 `WindowChrome`、`FluentWindowChrome` 或 `FluentTitleBar` 的自定义标题栏,不会为普通 Tk 窗口自动添加标题栏。重复调用 `install()` 或 `uninstall()` 只影响之后创建的根窗口。
|
|
71
|
+
|
|
72
|
+
手动调用 `apply_theme()` 的程序,未调用 `install()` 时默认不启用,也可以在创建上述标题栏/窗口控制器时传入 `window_flicker_workaround=True`。单个窗口显式传 `False` 可覆盖 `install()` 的设置,省略或传 `None` 则继承。Gallery 保留手动初始化方式,并在主窗口的 `FluentTitleBar` 中显式启用。
|
|
73
|
+
|
|
74
|
+
这是一组时序规避措施(最大化时短暂保护背景、还原时显示临时位图、缩放时 1px inset),不是渲染问题的根治。截图发现窗口被遮挡、超出屏幕,或在交接期间再次最小化时,会跳过位图层并退回原生还原;隐藏后重新显示不使用位图层。不同设备、电源模式和 Tk 版本的效果可能不同,详见[实现说明](docs/window-chrome-notes.md)。
|
|
75
|
+
|
|
76
|
+
**新程序**:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import tkinter as tk
|
|
80
|
+
import tkwin11 as ui
|
|
81
|
+
|
|
82
|
+
ui.enable_dpi_awareness() # 必须在 Tk() 之前
|
|
83
|
+
root = tk.Tk()
|
|
84
|
+
ui.apply_theme(root)
|
|
85
|
+
ui.configure_fluent(root)
|
|
86
|
+
|
|
87
|
+
page = ui.FluentPage(root)
|
|
88
|
+
page.pack(fill='both', expand=True)
|
|
89
|
+
card = ui.SettingsCard(page.body, '自动保存', '退出时保存当前设置。', symbol='folder')
|
|
90
|
+
card.pack(fill='x')
|
|
91
|
+
ui.FluentSwitch(card.actions, variable=tk.BooleanVar(root, value=True)).pack()
|
|
92
|
+
ui.PlaceholderEntry(page.body, placeholder='请输入名称').pack(fill='x', pady=12)
|
|
93
|
+
root.mainloop()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## 截图
|
|
97
|
+
|
|
98
|
+
| 菜单浮层与级联子菜单 | 输入提示(占位文字) |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
|  |  |
|
|
101
|
+
|
|
102
|
+
## 组件一览
|
|
103
|
+
|
|
104
|
+
| 类别 | 组件 |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| 原生 ttk / tk 控件(自动套用) | Button、Entry、Combobox、Spinbox、Checkbutton、Radiobutton、Scale、Progressbar、Notebook、Panedwindow、Treeview、LabelFrame、Separator、Sizegrip;tk.Entry / Text / Listbox / Menu 及经典 Button、Label 等 |
|
|
107
|
+
| 基础输入 | FluentButton(主要 / 轻量 / 语义色)、ToggleButton、ToolButton、SegmentedControl、FluentSwitch、FluentSlider、ValueSlider、ThemedCombobox、Dropdown、MenuButton、OptionMenu |
|
|
108
|
+
| 文本 | PlaceholderEntry / set_placeholder、ValidatedEntry、RoundedText、set_readonly、KeyChord |
|
|
109
|
+
| 集合 | RoundedListbox、SortableTreeview、DataTable(搜索 / 筛选 / 分页 / 导出)、Spreadsheet(Excel 式单元格表格)、CardList |
|
|
110
|
+
| 日期与时间 | DateEntry、CalendarPopup |
|
|
111
|
+
| 对话框与浮层 | Modal、Dialogs(提示 / 确认 / 输入)、FormFlyout、ThemedMenu、bind_context_menu、Tooltip、notify |
|
|
112
|
+
| 布局与导航 | FluentPage、SettingsCard、ExpanderCard、Accordion、SettingRow、Card、SectionCard、NavigationView、Sidebar、ScrollArea |
|
|
113
|
+
| 状态与图表 | TextProgress、Meter、Badge、LineChart、ColumnChart、ScatterChart、DonutChart、PieChart、BarChart |
|
|
114
|
+
| 媒体与外观 | ImageViewer、ColorPicker / ColorDialog、FontPicker / FontDialog、icon |
|
|
115
|
+
| 窗口 | FluentTitleBar、FluentWindowChrome(原生拖动、Win11 贴靠、边缘缩放、最大化)、enable_dpi_awareness |
|
|
116
|
+
|
|
117
|
+
每个组件的用法都能在 Gallery 对应页面的“源代码”里找到。
|
|
118
|
+
|
|
119
|
+
## 环境与限制
|
|
120
|
+
|
|
121
|
+
- 在 Windows 11、Python 3.11 上开发和测试,包括 150% 与 125% 双显示器。一体化标题栏与每显示器 DPI 适配只在 Windows 上启用,其他平台保留系统标题栏。
|
|
122
|
+
- 只依赖 Python 标准库(tkinter、ctypes 等)。
|
|
123
|
+
- 目前只有浅色主题;Tk 无法提供 WinUI 的 Mica / Acrylic 材质,浮层与动画由 Tk 自行绘制,不能保证逐帧达到原生 WinUI 的流畅度。
|
|
124
|
+
|
|
125
|
+
## 与 TkWebUI 的关系
|
|
126
|
+
|
|
127
|
+
TkWin11 由 [TkWebUI](https://github.com/bvb6417/tkwebui) 分支而来,改为 Windows 11 / Fluent 外观,并增加了一体化标题栏、导航、设置卡片、每显示器 DPI、一键替换等功能。原有的 `Spreadsheet`、`DataTable`、`ThemedCombobox`、`LineChart` 等接口保留,导入名从 `tkwebui` 改为 `tkwin11`。同一个 Tk 解释器里不要混用两个库。
|
|
128
|
+
|
|
129
|
+
更新记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
130
|
+
|
|
131
|
+
## 许可证
|
|
132
|
+
|
|
133
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# TkWin11
|
|
2
|
+
|
|
3
|
+
Windows 11 Fluent-style themes and widgets for Python's `tkinter` / `ttk`.
|
|
4
|
+
纯 Tkinter / ttk 实现的 Windows 11 Fluent 风格主题与控件库。
|
|
5
|
+
|
|
6
|
+
## Installation / 安装
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
python -m pip install tkwin11
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Requires Python 3.8+ with Tkinter available. Developed and tested on Windows 11
|
|
13
|
+
with Python 3.11. Windows-specific title bars, Snap and per-monitor DPI features
|
|
14
|
+
are enabled only on Windows; other platforms use the ordinary system title bar
|
|
15
|
+
and have not been fully tested. On Linux, install your distribution's Tkinter
|
|
16
|
+
package (often `python3-tk`) if needed. Tkinter is not installed by pip.
|
|
17
|
+
|
|
18
|
+
Core widgets require no third-party packages. `ImageViewer` can optionally use
|
|
19
|
+
Pillow for JPEG, WebP and additional image formats.
|
|
20
|
+
|
|
21
|
+
## Quick start / 快速开始
|
|
22
|
+
|
|
23
|
+
For an existing program, call `install()` before creating its first Tk root:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import tkinter as tk
|
|
27
|
+
from tkinter import ttk
|
|
28
|
+
import tkwin11
|
|
29
|
+
|
|
30
|
+
tkwin11.install()
|
|
31
|
+
root = tk.Tk()
|
|
32
|
+
root.title("TkWin11 example")
|
|
33
|
+
ttk.Entry(root).pack(padx=24, pady=12)
|
|
34
|
+
ttk.Button(root, text="Save").pack(padx=24, pady=12)
|
|
35
|
+
root.mainloop()
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
New programs may instead call `enable_dpi_awareness()` before `tk.Tk()` and
|
|
39
|
+
`apply_theme(root)` before creating widgets.
|
|
40
|
+
|
|
41
|
+
The library includes Fluent buttons, switches, navigation, custom title bars,
|
|
42
|
+
menus, dialogs, tooltips, tables, spreadsheets, charts and form controls.
|
|
43
|
+
|
|
44
|
+
## Window transitions / 窗口过渡
|
|
45
|
+
|
|
46
|
+
`install()` defaults to `window_flicker_workaround=True`. This experimental
|
|
47
|
+
workaround only affects windows using the library's custom title bars; it does
|
|
48
|
+
not add a title bar to ordinary Tk windows. To disable it:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
tkwin11.install(window_flicker_workaround=False)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`WindowChrome`, `FluentWindowChrome` and `FluentTitleBar` also accept this option
|
|
55
|
+
per window. Applications using manual theme initialization do not enable it
|
|
56
|
+
unless requested. The workaround is timing-based, not a root-cause rendering
|
|
57
|
+
fix; occluded windows fall back to native restore behavior.
|
|
58
|
+
|
|
59
|
+
## Demo and documentation
|
|
60
|
+
|
|
61
|
+
Download the [GitHub source](https://github.com/bvb6417/tkwin11) or source
|
|
62
|
+
distribution, then run `python main.py` to open the Gallery. The wheel installs
|
|
63
|
+
the library; the Gallery stays in the source distribution.
|
|
64
|
+
|
|
65
|
+
- [Full documentation and examples](https://github.com/bvb6417/tkwin11#readme)
|
|
66
|
+
- [Changelog](https://github.com/bvb6417/tkwin11/blob/main/CHANGELOG.md)
|
|
67
|
+
- [Issue tracker](https://github.com/bvb6417/tkwin11/issues)
|
|
68
|
+
- [TkWebUI comparison](https://github.com/bvb6417/tkwin11#如何选择tkwin11-还是-tkwebui)
|
|
69
|
+
|
|
70
|
+
MIT license. Derived from TkWebUI; see the included LICENSE for attribution.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# WindowChrome 闪烁问题与窗口状态切换修复记录
|
|
2
|
+
|
|
3
|
+
更新日期:2026-11-08
|
|
4
|
+
|
|
5
|
+
## 当前方案
|
|
6
|
+
|
|
7
|
+
本方案是实验性的时序规避。`install()` 默认以 `window_flicker_workaround=True` 为之后创建的根窗口启用;其 Toplevel 自动继承。传入 `window_flicker_workaround=False` 可关闭。它不会给普通 Tk 窗口添加自定义标题栏,也不改变已创建根窗口的设置。
|
|
8
|
+
|
|
9
|
+
`WindowChrome`、`FluentWindowChrome` 和 `FluentTitleBar` 同样接受 `window_flicker_workaround`:`None`(默认)继承所属 Tk 根窗口设置,`True` / `False` 显式覆盖。不经过 `install()` 的程序默认关闭,可直接在创建这些组件时启用。Gallery 保留原有 `enable_dpi_awareness()` 和手动主题初始化,在主窗口 `FluentTitleBar` 中显式传 `True`。
|
|
10
|
+
|
|
11
|
+
关闭时不创建位图控制器、不接管背景擦除,也不使用 1px inset、拖动时缓存/激活延迟;DWM frame、非客户区绘制和最大化路径保持上游 v0.2.2 行为。
|
|
12
|
+
|
|
13
|
+
正常最大化和最小化还原是两条不同的绘制路径,不共用静态层,也不再对正常最大化强制同步重绘。
|
|
14
|
+
|
|
15
|
+
### 正常窗口到最大化
|
|
16
|
+
|
|
17
|
+
保留原生 DWM 动画,在 `toggle_maximize()` 和原生 `SC_MAXIMIZE` 入口开启**全窗口 80ms 擦除保护**。保护只覆盖动画初期新暴露的客户区,随后通过同一个窗口计时器自动撤除,避免长期接管子窗口背景擦除。
|
|
18
|
+
|
|
19
|
+
当前组合:
|
|
20
|
+
|
|
21
|
+
- `begin_erase_guard(duration=80)`,范围为全窗口;
|
|
22
|
+
- DWM `MARGINS` 为 `(0,0,0,0)`,不保留顶部 1px frame;
|
|
23
|
+
- 不再执行 `update_idletasks() + RedrawWindow()` 抢画;
|
|
24
|
+
- `WM_NCCALCSIZE` 的最大化工作区处理保持不变。
|
|
25
|
+
|
|
26
|
+
原 PR 的真实 Gallery 实验报告,全窗口 80ms 保护改善了此前的黑闪;随后分别移除同步重绘和顶部 frame,未观察到效果回退。电池模式下仍可能短暂看到接近背景色的白板,不保证所有硬件上的效果。
|
|
27
|
+
|
|
28
|
+
长期全窗口擦除保护曾引起右侧频闪和还原回归。80ms 版本有效不代表可以恢复原来的 1000ms 全量接管。
|
|
29
|
+
|
|
30
|
+
### 最小化到普通或最大化状态
|
|
31
|
+
|
|
32
|
+
保留原生任务栏、DWM 还原动画和现有 Tk 控件。核心是由父窗口内部的静态子 HWND 提供动画期间的完整画面,等待真实 Tk 控件在窗口重新可见后完成绘制。
|
|
33
|
+
|
|
34
|
+
流程:
|
|
35
|
+
|
|
36
|
+
1. `minimize()` 或原生 `SC_MINIMIZE` 入口,使用屏幕 `BitBlt` 保存客户区画面。复制前后检查目标可见、未最小化、在虚拟桌面范围内,且 Z 序上方没有与截图区域相交的可见窗口;被遮挡或检查失败时不创建位图,退回原生还原。透明遮挡也保守地跳过,不猜测其像素内容。
|
|
37
|
+
2. 有有效截图、iconic 状态下收到 `SC_RESTORE` 时,窗口过程只设置请求标志,不直接调用 Tk。
|
|
38
|
+
3. `poll_native()` 在 Tk 主线程调用精简后的 `prepare_restore()`,只执行 `ShowWindow(SW_RESTORE)` 或 `ShowWindow(SW_MAXIMIZE)`。
|
|
39
|
+
4. `Static` 子 HWND 显示保存的位图,跟随父窗口参与 DWM 动画,不是独立置顶覆盖窗口。
|
|
40
|
+
5. 还原后保留静态层 350ms;交接时先 `DwmFlush()`,再销毁子 HWND 并释放 DC 和位图。
|
|
41
|
+
6. `withdraw()` 不捕获截图;隐藏时立即移除覆盖层,Tk 主线程随后取消定时任务并释放位图。恢复交接不再依赖轮询是否观察到过 iconic 状态。
|
|
42
|
+
7. 交接期间再次最小化时放弃旧位图,该次退回原生还原。窗口 wrapper 被替换、主动切换最大化和 `detach()` 同样清理资源;还原客户区尺寸与截图不同时不显示旧图。
|
|
43
|
+
|
|
44
|
+
这是一种保守回退,不是遮挡窗口的离屏渲染。屏幕截图与窗口移动并非原子操作;复制前后的检查缩小了竞争窗口,但不能保证所有并发桌面变化。没有采用同步 [PrintWindow](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-printwindow),因为该调用发送绘制消息,而原生回调路径不应重入 Tk/Tcl。
|
|
45
|
+
|
|
46
|
+
相关文件:
|
|
47
|
+
|
|
48
|
+
- `tkwin11/window_chrome.py`:原生状态桥接、80ms 擦除保护和还原入口。
|
|
49
|
+
- `tkwin11/restore_animation.py`:截图、静态子 HWND、350ms 交接和资源释放。
|
|
50
|
+
- `tests/check_window_chrome.py`:普通还原、快速状态和最大化状态回归。
|
|
51
|
+
|
|
52
|
+
## iconic 预绘制对比结论
|
|
53
|
+
|
|
54
|
+
以下连续帧数据为原 PR 的历史实验记录,本次修改没有重新测量这些时间。
|
|
55
|
+
|
|
56
|
+
此前 `prepare_restore()` 还在 `ShowWindow` 前执行 `SetWindowPos + update_idletasks + RedrawWindow`,并将其描述为“预绘制”。真实 Gallery 的三组隔离实验推翻了这一描述:
|
|
57
|
+
|
|
58
|
+
- 完整版本:保留上述三个调用;
|
|
59
|
+
- `no-redraw`:去掉 `RedrawWindow`;
|
|
60
|
+
- `show-only`:只执行 `ShowWindow`。
|
|
61
|
+
|
|
62
|
+
三组均固定使用同一个 350ms 静态层。结果:
|
|
63
|
+
|
|
64
|
+
- `ShowWindow` 前所有 Tk 控件的 `winfo_ismapped()` 都为 0;
|
|
65
|
+
- 对 359 个原生窗口的观察中,三组 iconic 阶段均为 0 次 `WM_PAINT`;
|
|
66
|
+
- 三组都在 `ShowWindow` 后出现 188 次绘制事件,真实绘制约在 245–255ms 完成;
|
|
67
|
+
- 静态层约 358–363ms 交接,覆盖了真实绘制窗口期;
|
|
68
|
+
- 三种版本的交接前截图彼此完全相同,交接后截图也彼此完全相同,SHA-256 一致。
|
|
69
|
+
|
|
70
|
+
因此,iconic 中的 `RedrawWindow` 没有实际绘制效果;本轮实验中提前设置尺寸和处理 idle 也没有可观察收益。这些冗余调用以及不再使用的 `_restore_work_area` 已删除。静态层不是预绘制的替代品之外的可选优化,而是当前还原方案稳定视觉的主要保障。
|
|
71
|
+
|
|
72
|
+
实验数据保存在 `perf/results/restore-predraw-full/`、`restore-predraw-no-redraw/`、`restore-predraw-show-only/`。这些结果来自当前 Tk/Windows 环境,不外推为所有 Tk 版本的源码级结论。
|
|
73
|
+
|
|
74
|
+
## 最大化截图的黑边问题
|
|
75
|
+
|
|
76
|
+
最大化还原曾出现左侧和顶部黑边,随后跳到正确画面。根因不是目标位置延迟,而是截图把最大化窗口的不可见边框也包含进去了。
|
|
77
|
+
|
|
78
|
+
同一时刻实测:
|
|
79
|
+
|
|
80
|
+
- `GetWindowRect`:`(-9,-9,1929,1149)`,包含不可见调整边框;
|
|
81
|
+
- DWM extended frame bounds:`(0,0,1920,1140)`,为实际可见工作区;
|
|
82
|
+
- 旧截图左侧前 9 列和顶部前 9 行为纯黑,静态层把这些屏幕外像素显示到了窗口内部。
|
|
83
|
+
|
|
84
|
+
原 PR 在 `IsZoomed(hwnd)` 为真时,改用 `DwmGetWindowAttribute(..., DWMWA_EXTENDED_FRAME_BOUNDS, ...)`。当前版本统一使用 `GetClientRect + ClientToScreen` 定位自定义标题栏窗口的客户区,与原生子覆盖层的 `(0, 0)` 对齐;不再截图不可见窗口边框。多显示器和不同 DPI 下不能假定不可见边框固定为 9px。
|
|
85
|
+
|
|
86
|
+
## 正常最大化诊断摘要
|
|
87
|
+
|
|
88
|
+
早期探针记录了 `WM_ERASEBKGND`、`WM_PAINT`、wrapper surface 和屏幕采样:
|
|
89
|
+
|
|
90
|
+
- 1418 次背景擦除涉及的 HWND,其类背景刷 `hbrBackground` 都为 0,黑块不是默认黑色刷子填出的;
|
|
91
|
+
- 修复前 wrapper 采样已出现黑色区域,而内容区、toolbar 和搜索框首绘明显更晚;
|
|
92
|
+
- 一轮 `update_idletasks() + RedrawWindow()` 曾将关键控件首绘提前到约 32–50ms,减少黑闪范围和时间,但不能消除顶部细条;
|
|
93
|
+
- 第二轮 idle 没有收益;
|
|
94
|
+
- 在当时没有全窗口 80ms 保护的组合下,`top=1` 优于 `top=0` 和 `top=2`;加入 80ms 保护后,用户复测移除同步重绘和顶部 frame 均不影响最终效果,因此二者均已删除;
|
|
95
|
+
- `WM_NCPAINT` 只在最大化初期到达一次,测试色补画未在最终合成采样中出现;
|
|
96
|
+
- `DWMWA_BORDER_COLOR` 调用成功,但测试色未命中顶部采样区域。
|
|
97
|
+
|
|
98
|
+
连续帧和绘制数据保存在 `perf/results/maximize-diagnostic/`、`maximize-sync/`、`maximize-timeline/`。采样本身有开销,不能把采样时刻当作无扰动的 DWM 每帧时序。
|
|
99
|
+
|
|
100
|
+
## 保留的拖动兼容性处理
|
|
101
|
+
|
|
102
|
+
- 手动拖动期间,`WM_NCCALCSIZE` 使用 `rect.bottom -= 1`,规避 DWM 原生子窗口缩放瑕疵。
|
|
103
|
+
- 拖动期间推迟 `refresh_cache()`,退出拖动后统一刷新。
|
|
104
|
+
- Fluent 标题激活颜色动画在 `_native_moving` 期间暂停,结束后同步。
|
|
105
|
+
- `gallery.py` 没有为这些窗口状态修复而修改。
|
|
106
|
+
|
|
107
|
+
## 不要重复的失败方案
|
|
108
|
+
|
|
109
|
+
以下方案已经实测失败或引入回归,不能因编译、自动测试通过就重新保留:
|
|
110
|
+
|
|
111
|
+
- `WVR_REDRAW`、`WVR_VALIDRECTS`、`SWP_NOCOPYBITS`。
|
|
112
|
+
- `WS_CLIPCHILDREN`,包括后补样式并调用 `SWP_FRAMECHANGED`。
|
|
113
|
+
- DWM 垂直刷新时序同步和 `DCompositionWaitForCompositorClock` 类计时补丁。
|
|
114
|
+
- `ShowWindow` 与过渡计数挂起。
|
|
115
|
+
- 临时切换原生标题栏,动画结束后再切回。
|
|
116
|
+
- 禁用 DWM 动画、冻结文字或整个子窗口树。
|
|
117
|
+
- 长期全窗口擦除保护、第二轮 idle、正常最大化后的强制同步重绘。
|
|
118
|
+
- 最大化前 cloak、目标尺寸预布局后缩回;同步 Tk 原生重入曾触发 GIL fatal error。
|
|
119
|
+
- 独立置顶 BitBlt 覆盖窗口;当前有效的是父窗口内部静态子 HWND。
|
|
120
|
+
- DirectComposition 原型:具体实现未通过 Gallery 黑闪目测。
|
|
121
|
+
- 在还原期间忽略左侧子菜单高度回写:未通过并已撤回。
|
|
122
|
+
- 依靠 iconic 中的 `RedrawWindow` 代替静态层:实测没有发生预绘制。
|
|
123
|
+
|
|
124
|
+
## 验证与维护边界
|
|
125
|
+
|
|
126
|
+
标准检查:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
python -m compileall -q tkwin11 gallery.py tests/check_window_chrome.py
|
|
130
|
+
python -m unittest discover -s tests -p "test_*.py"
|
|
131
|
+
python tests/check_window_chrome.py
|
|
132
|
+
git diff --check
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
WindowChrome 与 FluentWindowChrome 的 normal、destroy、single、override、before_attach 在开关关闭/开启两种状态下共 20 组测试必须全部通过。`test_window_flicker_workaround.py` 另覆盖 install 默认启用、显式关闭、继承与覆盖、隐藏/还原、遮挡回退、快速最小化、原生系统命令和交接资源清理。自动状态回归不能替代真实 Gallery 的连续帧和目测验证。
|
|
136
|
+
|
|
137
|
+
后续修改时:
|
|
138
|
+
|
|
139
|
+
- 正常最大化的 80ms 保护不能被扩大为长期全窗口接管。
|
|
140
|
+
- 若修改静态层交接时序,必须重新检查静态层残留、首帧空窗、真实控件提前露出和资源释放。
|
|
141
|
+
- 窗口过程只传递数据,Tk 工作回到主线程;不要在原生回调中直接调用 Tcl。
|
|
142
|
+
- 电池模式仍可能出现短暂背景板,不把当前结果宣称为所有性能环境下的数学保证。
|