187 lines
7.3 KiB
Python
187 lines
7.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""会话模式弹窗 · 可视化调参工具
|
|
运行: C:\\Users\\14890\\miniconda3\\envs\\haocode\\python.exe tests\\tune_mode_popup.py
|
|
- 左侧:调参窗(拖动/修改参数 → 右侧弹窗实时重渲染)
|
|
- 右侧:真实的 SessionModePopup 本体(独立窗口,点调参窗不会消失)
|
|
- 满意后点「确定」→ 参数 JSON 写入 data/mode_popup_tune.json → 自动退出
|
|
"""
|
|
import os
|
|
import sys
|
|
import json
|
|
import traceback
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--disable-gpu"
|
|
os.environ.setdefault("HAOCODE_RENDER", "software")
|
|
|
|
from PyQt6.QtWidgets import ( # noqa: E402
|
|
QApplication, QWidget, QFormLayout, QHBoxLayout, QSpinBox,
|
|
QPushButton, QCheckBox, QMessageBox,
|
|
)
|
|
from PyQt6.QtCore import Qt # noqa: E402
|
|
from PyQt6.QtGui import QPixmap # noqa: E402
|
|
from ui.views.main_window import SessionModePopup, _ModeRow, _popup_svg_path # noqa: E402
|
|
|
|
DEFAULTS = {
|
|
"icon_size": 14, # 图标尺寸
|
|
"name_font": 12, # 名称字号
|
|
"desc_font": 12, # 描述字号
|
|
"row_margin_v": 8, # 行上下内边距
|
|
"row_margin_h": 10, # 行左右内边距
|
|
"icon_text_gap": 12, # 图标-文字间距
|
|
"name_desc_gap": 4, # 名称/描述行间距
|
|
"row_radius": 8, # 行圆角
|
|
"popup_width": 320, # 弹窗宽度
|
|
"container_margin": 4, # 容器边距
|
|
}
|
|
RANGES = {
|
|
"icon_size": (8, 48), "name_font": (10, 20), "desc_font": (9, 18),
|
|
"row_margin_v": (2, 20), "row_margin_h": (4, 24), "icon_text_gap": (4, 24),
|
|
"name_desc_gap": (0, 8), "row_radius": (0, 16), "popup_width": (240, 420),
|
|
"container_margin": (0, 16),
|
|
}
|
|
CN = {
|
|
"icon_size": "图标尺寸 (px)",
|
|
"name_font": "名称字号 (px)",
|
|
"desc_font": "描述字号 (px)",
|
|
"row_margin_v": "行上下内边距 (px)",
|
|
"row_margin_h": "行左右内边距 (px)",
|
|
"icon_text_gap": "图标-文字间距 (px)",
|
|
"name_desc_gap": "名称/描述行距 (px)",
|
|
"row_radius": "行圆角 (px)",
|
|
"popup_width": "弹窗宽度 (px)",
|
|
"container_margin": "容器边距 (px)",
|
|
}
|
|
FONT_STACK = '"HarmonyOS Sans SC", "Microsoft YaHei UI", "Microsoft YaHei", "微软雅黑", sans-serif'
|
|
|
|
|
|
class Tuner:
|
|
def __init__(self):
|
|
self.app = QApplication(sys.argv)
|
|
self.params = dict(DEFAULTS)
|
|
|
|
# ---- 弹窗本体(真实类):去掉 Popup 标志 → 独立普通无边框窗,不随失焦消失 ----
|
|
self.popup = SessionModePopup(None)
|
|
self.popup.setWindowFlags(Qt.WindowType.Window | Qt.WindowType.FramelessWindowHint)
|
|
self.rows = self.popup.container.findChildren(_ModeRow)
|
|
icon_by_mode = {m: _popup_svg_path(ic) for m, _, _, ic in SessionModePopup.MODES}
|
|
for r in self.rows:
|
|
r._tune_pix = QPixmap(icon_by_mode[r.mode]) # 原始分辨率,重缩不糊
|
|
|
|
screen = self.app.primaryScreen().availableGeometry()
|
|
self.px = screen.center().x() - 300
|
|
self.py = screen.center().y() - 200
|
|
self.popup.move(self.px, self.py)
|
|
self.popup.show()
|
|
|
|
# ---- 调参窗 ----
|
|
self.win = QWidget()
|
|
self.win.setWindowTitle("会话模式弹窗调参 —— 调好后点「确定」")
|
|
form = QFormLayout(self.win)
|
|
form.setSpacing(8)
|
|
self.spins = {}
|
|
for key, (lo, hi) in RANGES.items():
|
|
sp = QSpinBox()
|
|
sp.setRange(lo, hi)
|
|
sp.setValue(self.params[key])
|
|
sp.valueChanged.connect(lambda v, k=key: self.on_change(k, v))
|
|
self.spins[key] = sp
|
|
form.addRow(CN[key], sp)
|
|
|
|
self.chk_selected = QCheckBox("显示选中态(预览)")
|
|
self.chk_selected.toggled.connect(lambda _: self.apply_all())
|
|
form.addRow(self.chk_selected)
|
|
|
|
btns = QHBoxLayout()
|
|
b_reset = QPushButton("重置默认")
|
|
b_reset.clicked.connect(self.reset)
|
|
b_ok = QPushButton("✅ 确定(写入日志并退出)")
|
|
b_ok.setStyleSheet("font-weight: bold;")
|
|
b_ok.clicked.connect(self.finish)
|
|
btns.addWidget(b_reset)
|
|
btns.addWidget(b_ok)
|
|
form.addRow(btns)
|
|
|
|
self.win.resize(360, self.win.sizeHint().height())
|
|
self.win.move(self.px - 380, self.py)
|
|
self.win.show()
|
|
|
|
self.apply_all()
|
|
|
|
# ---------- 实时渲染 ----------
|
|
def on_change(self, k, v):
|
|
self.params[k] = v
|
|
self.apply_all()
|
|
|
|
def apply_all(self):
|
|
p = self.params
|
|
popup = self.popup
|
|
popup.setFixedWidth(p["popup_width"])
|
|
cl = popup.container.layout()
|
|
cl.setContentsMargins(p["container_margin"], p["container_margin"],
|
|
p["container_margin"], p["container_margin"])
|
|
sel = self.chk_selected.isChecked()
|
|
for r in self.rows:
|
|
r.lbl_icon.setFixedSize(p["icon_size"], p["icon_size"])
|
|
r.lbl_icon.setPixmap(r._tune_pix.scaled(
|
|
p["icon_size"], p["icon_size"],
|
|
Qt.AspectRatioMode.KeepAspectRatio,
|
|
Qt.TransformationMode.SmoothTransformation))
|
|
lay = r.layout()
|
|
lay.setContentsMargins(p["row_margin_h"], p["row_margin_v"],
|
|
p["row_margin_h"] + 2, p["row_margin_v"])
|
|
lay.setSpacing(p["icon_text_gap"])
|
|
txt = lay.itemAt(1).layout()
|
|
if txt is not None:
|
|
txt.setSpacing(p["name_desc_gap"])
|
|
name_color = "#1a73e8" if sel else "#333333"
|
|
r.lbl_name.setStyleSheet(
|
|
f"font-family: {FONT_STACK}; font-size: {p['name_font']}px;"
|
|
f"font-weight: bold; color: {name_color}; background: transparent;")
|
|
r.lbl_desc.setStyleSheet(
|
|
f"font-family: {FONT_STACK}; font-size: {p['desc_font']}px;"
|
|
f"color: #888888; background: transparent;")
|
|
bg = "#e8f0fe" if sel else "transparent"
|
|
r.setStyleSheet(f"background-color: {bg}; border-radius: {p['row_radius']}px;")
|
|
self.app.processEvents()
|
|
popup.adjust_popup_height()
|
|
popup.adjustSize()
|
|
|
|
def reset(self):
|
|
self.params = dict(DEFAULTS)
|
|
for k, sp in self.spins.items():
|
|
sp.blockSignals(True)
|
|
sp.setValue(self.params[k])
|
|
sp.blockSignals(False)
|
|
self.apply_all()
|
|
|
|
# ---------- 确定 → 写日志 ----------
|
|
def finish(self):
|
|
log = os.path.join(os.path.dirname(__file__), "..", "data", "mode_popup_tune.json")
|
|
os.makedirs(os.path.dirname(log), exist_ok=True)
|
|
with open(log, "w", encoding="utf-8") as f:
|
|
json.dump(self.params, f, ensure_ascii=False, indent=2)
|
|
print("TUNE_LOG=" + os.path.abspath(log), flush=True)
|
|
print(json.dumps(self.params, ensure_ascii=False), flush=True)
|
|
self.app.quit()
|
|
|
|
|
|
def main():
|
|
try:
|
|
Tuner()
|
|
QApplication.instance().exec()
|
|
except Exception:
|
|
err = traceback.format_exc()
|
|
try:
|
|
with open(os.path.join(os.path.dirname(__file__), "..", "data",
|
|
"tune_error.log"), "w", encoding="utf-8") as f:
|
|
f.write(err)
|
|
except OSError:
|
|
pass
|
|
QMessageBox.critical(None, "调参工具启动失败", err)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|