from pathlib import Path import ast,hashlib,json folder=Path(__file__).resolve().parent;target=Path('C:/wechat_rpa');payload=folder/'payload' doc=r'''# 高 DPI 与响应式界面修复(2026-09-16) ## 修复效果 - 「自动」跟随系统显示缩放,支持 100%、125%、200%、300% 等倍率。Qt 与内嵌网页各使用逻辑像素,不再把系统放大后的界面压回固定画布。 - 字体、SVG 图标、按钮、边距保持统一比例。长页面滚动;空间不足时卡片减少列数,导航区独立滚动,监听和设置入口保留在侧栏底部。 - 1080p、4K 和更高分辨率使用同一套逻辑布局。调整窗口高度和切换页面不会改变文字大小。 - 初始窗口按当前显示器可用区域设定;移动到另一显示器或改变显示设置后,窗口重新约束在可用区域内。已处理负坐标副屏和显示器移除事件。 - 修复原生 QBoxLayout、QGridLayout 的间距未跟随界面大小变化的问题。 ## 使用 退出后从本目录重新运行 `python wechat_gui_qt.py`,进入「系统设置 → 界面大小」选择「自动」。以后直接调整 Windows 的显示缩放即可。 需要额外放大时可选择 100%–300%,相对于系统缩放立即生效,无需重启。恢复「自动」即可跟随系统。此设置不再写入进程级 `QT_SCALE_FACTOR`;开发者显式设置的环境变量仍由 Qt 正常处理。 这次更新的是协议目录源码及 UI 资源,已有 EXE/安装包未重新打包;旧 EXE 不会自动读取新源码。原监听、账号、聊天和协议配置没有被本次更新覆盖。 ## 验证 - 138 项显示适配、Qt 控制台、悬浮胶囊、运行设置、模拟协议链路和打包路径测试通过。 - 8 个页面 × 7 种显示条件,共 56 组浏览器检查:无横向溢出、无页面脚本异常,含 125% 分数倍率、2 倍/3 倍和小窗口。 - 实际 Qt WebEngine 以 1、2、3 倍模拟 DPI 运行:960 逻辑像素宽的窗口分别输出 960、1920、2880 物理像素;Qt 与网页 DPR 一致,字体布局逻辑尺寸一致。 - 窗口换屏、负坐标屏幕、缩小可用区域及移除显示器通过模拟事件回归;本机未进行实体三显示器拖拽验收。 - 没有向真实客户发送测试消息。 ## 文件与备份 主要文件:`wechat_gui_qt.py`、`assets/ui/console-app.js`、`assets/ui/console-responsive.css`、`assets/ui/zhenyang-ai-console.html`。 备份:`backups/dpi-adaptation-20260916`。哈希清单与测试结果:`UI_DPI_MANIFEST_20260916.json`。现有打包配置已包含整个 `assets/ui` 目录,新样式会随下次打包进入安装包。 技术依据:Qt 使用设备无关像素并按显示器 DPR 绘制,详见 [Qt High DPI](https://doc.qt.io/qt-6.8/highdpi.html)。页面采用参与布局计算的缩放及响应式栅格,详见 [CSS zoom](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/zoom)。 ''' (target/'UI_DPI_20260916.md').write_text(doc,encoding='utf-8',newline='\n') record=json.loads((folder/'applied.json').read_text()) sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest() for row in record['manifest']: path=target/row['path'];assert sha(path)==sha(payload/row['path']),row['path'] if path.suffix=='.py':ast.parse(path.read_text(encoding='utf-8')) row['sha256']=sha(path) browser=json.loads((folder/'qa/results.json').read_text()) assert len(browser['results'])==56 and not browser['errors'] assert all(r['contentWidth']<=r['mainWidth']+2 for r in browser['results']) record.update(tests_passed=138,browser_checks=56,browser_errors=[],qt_dpi_checks=[json.loads((folder/'qa'/f'qt-{n}x.json').read_text()) for n in [1,2,3]], installer_rebuilt=False,artifacts=str(folder),guide='UI_DPI_20260916.md') (target/'UI_DPI_MANIFEST_20260916.json').write_text(json.dumps(record,ensure_ascii=False,indent=2),encoding='utf-8') (folder/'final.json').write_text(json.dumps(record,ensure_ascii=False,indent=2),encoding='utf-8') print(json.dumps({'files':len(record['manifest']),'tests':138,'browser_checks':56,'qt_dpi':[1,2,3],'backup':record['backup']},ensure_ascii=False))