# coding: utf-8 """主题切换回归自检(无窗口, 离屏渲染)。 背景: fluent 控件直接 setStyleSheet 的样式会在 setTheme 时被库重刷的自带 qss 冲掉 (v0.4.0 RGB swatch 按钮切主题丢底色的根因)。swatch 改走 setCustomStyleSheet、 标题字号走 QFont 后,本测试保证: 1. Dark -> Light -> Dark -> Light 往返后 9 个 swatch 按钮样式表仍带底色; 2. Light 下实际渲染出的按钮中心像素 = swatch 色值(不只样式表字符串存在); 3. 标题 24px 加粗(QFont)在往返后不变。 运行: python selftest_theme.py (依赖 PySide6-Fluent-Widgets) """ import os import sys os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from PySide6.QtGui import QColor from PySide6.QtWidgets import QApplication from qfluentwidgets import PushButton, StrongBodyLabel, Theme, setTheme import app_fluent import patterns def check_style_sheet(win, app): swatches = [b for b in win.findChildren(PushButton) if b.text() in {"55", "AA", "FF"}] assert len(swatches) == 9, f"expect 9 swatch buttons, got {len(swatches)}" title = [l for l in win.findChildren(StrongBodyLabel) if l.text() == "DLPilot"] assert len(title) == 1, "title label not found" colored = sum(1 for b in swatches if "background-color: #" in b.styleSheet()) f = title[0].font() print(f" swatch 样式表带底色: {colored}/9, 标题 24px 粗体: " f"{f.pixelSize() == 24 and f.bold()}") return colored == 9 and f.pixelSize() == 24 and f.bold() def check_rendered_pixel(win, app): """Light 主题下逐个渲染 swatch,与目标色一致的像素须占明显多数(背景为主, 中心处是文字字形,不能只采单点)。""" ok = True for channel, items in patterns.SOLID_ROWS: for label, color in items: button = next(b for b in win.findChildren(PushButton) if b.text() == label and f"background-color: {color}" in b.styleSheet()) image = button.grab().toImage() target = QColor(color) total, matched = image.width() * image.height(), 0 for y in range(image.height()): for x in range(image.width()): if image.pixelColor(x, y) == target: matched += 1 ratio = matched / total same = ratio >= 0.2 ok &= same if not same: print(f" 渲染不符: {channel}/{label} 期望 {color} 仅占 {ratio:.0%}") print(f" Light 下渲染像素比对: {'一致' if ok else '存在偏差'}") return ok def main(): app = QApplication.instance() or QApplication(sys.argv) setTheme(Theme.AUTO) win = app_fluent.App(0) passed = True for step, theme in (("初始 Dark", Theme.DARK), ("切 Light", Theme.LIGHT), ("切回 Dark", Theme.DARK), ("再切 Light", Theme.LIGHT)): setTheme(theme) app.processEvents() print(f"[{step}]") passed &= check_style_sheet(win, app) if theme == Theme.LIGHT: passed &= check_rendered_pixel(win, app) win.close() print("ALL PASS" if passed else "FAILED") return 0 if passed else 1 if __name__ == "__main__": sys.exit(main())