Skip to content

Commit 1eeaa7b

Browse files
使得GUI更紧凑
1 parent db56663 commit 1eeaa7b

3 files changed

Lines changed: 67 additions & 64 deletions

File tree

‎src/gui/main_window.py‎

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def __init__(self, service=None, script_service=None):
4444
# MainWindow 构造耗时打点基准:__init__ 入口归零,阶段差值为真实耗时
4545
self._init_t0 = time.perf_counter()
4646
self.setWindowTitle("OneDragon")
47-
self.setMinimumSize(350, 800)
47+
self.setMinimumSize(315, 800)
4848

4949
self.script_items = []
5050
self.all_config_data = None
@@ -85,7 +85,7 @@ def _init_ui(self):
8585
self.scroll_content.setStyleSheet("background-color: transparent;")
8686
self.scroll_layout = QVBoxLayout(self.scroll_content)
8787
self.scroll_layout.setContentsMargins(2, 4, 14, 4)
88-
self.scroll_layout.setSpacing(12)
88+
self.scroll_layout.setSpacing(5)
8989
self.scroll_layout.addStretch()
9090
# 挂载延迟到 _load_scripts 末尾(卡片插满后再 setWidget+addWidget):
9191
# widgetResizable=True 时,若先挂载再逐卡插入,每次插入都触发 viewport
@@ -102,6 +102,8 @@ def _init_ui(self):
102102
accent=theme.BLUE,
103103
pressed_bg=theme.BG_CHIP,
104104
font_size=theme.FONT_SIZE_BODY,
105+
padding="0 10px",
106+
min_width=0,
105107
)
106108
self.select_all_btn.clicked.connect(self._select_all)
107109
action_layout.addWidget(self.select_all_btn)
@@ -111,6 +113,8 @@ def _init_ui(self):
111113
accent=theme.CRIMSON,
112114
pressed_bg=theme.BG_DANGER_SOFT,
113115
font_size=theme.FONT_SIZE_BODY,
116+
padding="0 10px",
117+
min_width=0,
114118
)
115119
self.deselect_all_btn.clicked.connect(self._deselect_all)
116120
action_layout.addWidget(self.deselect_all_btn)
@@ -121,6 +125,8 @@ def _init_ui(self):
121125
hover_color=theme.DARK_BLUE,
122126
pressed_bg=theme.BG_CHIP,
123127
font_size=theme.FONT_SIZE_BODY,
128+
padding="0 10px",
129+
min_width=0,
124130
)
125131
self.add_script_btn.clicked.connect(self._add_script)
126132
action_layout.addWidget(self.add_script_btn)
@@ -132,6 +138,8 @@ def _init_ui(self):
132138
hover_color=theme.BLUE,
133139
pressed_bg=theme.BG_CHIP,
134140
font_size=theme.FONT_SIZE_BODY,
141+
padding="0 10px",
142+
min_width=0,
135143
)
136144
self.open_config_btn.clicked.connect(self._open_config_yml)
137145
action_layout.addWidget(self.open_config_btn)
@@ -193,29 +201,13 @@ def _load_scripts(self):
193201
self._sync_dungeon_btn_widths()
194202

195203
def _sync_title_widths(self) -> None:
196-
"""统一所有 ScriptItem 标题列宽度 = 最长内容 + padding,带上下限。
204+
"""display_name 无框大字:宽度自适应内容,仅刷新超长截断。
197205
198-
流程化设计:每个 ScriptItem 构造时只设 minimum + 默认占位宽度;本方法
199-
在 ``_load_scripts`` 末尾 / 改名后调用,按实际内容动态算宽度,刷新
200-
``_title_width`` 并触发 ``_refresh_title`` 重新截断。这样无论新增脚本
201-
名字多长,都不会撑爆布局,也不会因硬编码 60/110 漂移而漏字。
206+
无框后不再需要 chip 时代的等宽对齐(那时固定宽度是为了所有 chip 右边
207+
框整齐);每个标题按内容自适应,超长(> TITLE_MAX_WIDTH)由
208+
``_refresh_title`` 内部 elide。改名后调用本方法重新按新文本截断。
202209
"""
203-
items = self.script_items
204-
if not items:
205-
return
206-
fm = QFontMetrics(items[0].title_label.font())
207-
# chip 水平 padding 之和(widgets.py title_label padding 10+10=20)
208-
# 同步固定宽度 = 文字宽 + 20
209-
padding = 20
210-
max_w = TITLE_MIN_WIDTH
211-
for it in items:
212-
w = fm.horizontalAdvance(it.display_name) + padding
213-
if w > max_w:
214-
max_w = w
215-
max_w = max(TITLE_MIN_WIDTH, min(TITLE_MAX_WIDTH, int(max_w)))
216-
for it in items:
217-
it._title_width = max_w
218-
it.title_label.setFixedWidth(max_w)
210+
for it in self.script_items:
219211
it._refresh_title()
220212

221213
def _sync_dungeon_btn_widths(self) -> None:

‎src/gui/theme.py‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,10 @@
4242

4343
# ── 字号 token(统一所有控件,避免散在多处) ─────────────────────────────────
4444
FONT_SIZE_BODY = 11 # 正文:脚本名 chip / 副本按钮 / 输入框 / 下拉框 / 表单标签
45+
FONT_SIZE_SMALL = 10 # 小字号:副本按钮(次级操作,弱于标题)
4546
FONT_SIZE_BTN = 12 # 按钮:次级 / 药丸 / 危险
4647
FONT_SIZE_HERO = 13 # 主要操作:运行按钮
48+
FONT_SIZE_TITLE = 13 # 标题:脚本名 display_name(无框大字,识别角色)
4749

4850

4951
# ── 文本 / 布局常量 ──────────────────────────────────────────────────────────
@@ -107,6 +109,29 @@ def outlined_qss(
107109
"""
108110

109111

112+
def title_qss(
113+
*,
114+
color: str = DARK_BLUE,
115+
hover_color: str = BLUE,
116+
font_size: int = FONT_SIZE_TITLE,
117+
) -> str:
118+
"""脚本名标题(display_name)无框大字模板:无边框无背景,hover 仅文字变色。
119+
120+
与 ``outlined_qss`` 的 chip 形态区分:标题是「识别」角色(无框大字),
121+
副本按钮等 chip 是「操作」角色(有边框)。避免两个 chip 视觉重复。
122+
"""
123+
return f"""
124+
QLabel {{
125+
border: none;
126+
background: transparent;
127+
font-family: {FONT_FAMILY};
128+
font-size: {font_size}px;
129+
color: {color};
130+
}}
131+
QLabel:hover {{ color: {hover_color}; }}
132+
"""
133+
134+
110135
def pill_button_qss(
111136
*,
112137
accent: str = BLUE,

‎src/gui/widgets.py‎

Lines changed: 27 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
f" border: {theme.BORDER_WIDTH} solid {theme.BORDER}; border-radius: 8px;"
4545
f" padding: 0 10px; background: transparent;"
4646
f" color: {theme.DARK_BLUE}; font-family: {theme.FONT_FAMILY};"
47-
f" font-size: {theme.FONT_SIZE_BODY}px; text-align: center;"
47+
f" font-size: {theme.FONT_SIZE_SMALL}px; text-align: center;"
4848
f"}}"
4949
f"QPushButton:hover {{"
5050
f" border-color: {theme.BLUE}; background: transparent;"
@@ -56,8 +56,8 @@
5656
f"}}"
5757
)
5858

59-
# 标题列宽度常量(详见 MainWindow._sync_title_widths)
60-
TITLE_DEFAULT_WIDTH = 110 # 构造期占位宽度;加载完成后会被 sync 流程覆盖
59+
# 标题/副本按钮宽度常量:标题无框大字自适应内容(仅最大宽度上限防超长);
60+
# TITLE_MIN_WIDTH 供副本按钮等宽对齐流程使用(有框 chip 仍需等宽)
6161
TITLE_MIN_WIDTH = 60
6262
TITLE_MAX_WIDTH = 180
6363

@@ -158,7 +158,7 @@ def __init__(
158158
self._apply_card_style()
159159

160160
layout = QHBoxLayout(self)
161-
layout.setContentsMargins(16, 14, 16, 14)
161+
layout.setContentsMargins(12, 5, 5, 5)
162162
layout.setSpacing(10)
163163

164164
# 脚本图标:external 脚本用 exe 自带图标,其余(如 python)用默认图标
@@ -173,45 +173,31 @@ def __init__(
173173
self._refresh_icon(script_data)
174174
layout.addWidget(self.icon_label)
175175

176-
# 脚本名称:宽度由 MainWindow._sync_title_widths() 流程统一决定(避免硬编码漂移)。
177-
# 构造期先按默认 ``TITLE_DEFAULT_WIDTH`` 占位,load_scripts 末尾会刷新。
176+
# 脚本名称:无框大字,宽度自适应内容;仅设最大宽度上限防止超长名字撑爆布局。
177+
# 构造期直接显示全文,load_scripts 末尾的 sync 会按需重新截断。
178178
self.title_label = QLabel(self.display_name)
179-
# 脚本名 chip:字号用像素单位(与 QSS font-size 一致渲染),见 theme.make_font
180-
title_font = theme.make_font(size=theme.FONT_SIZE_BODY)
179+
# 脚本名标题:无框大字(识别角色),与副本按钮的 chip(操作角色)形态区分
180+
title_font = theme.make_font(size=theme.FONT_SIZE_TITLE)
181181
self.title_label.setFont(title_font)
182-
# 脚本名:透明底 + 雾蓝边框 + 圆角 8(hover 时由 enter/leaveEvent 变色,与次级按钮统一)
183-
self._STYLE_TITLE_NORMAL = theme.outlined_qss(
184-
selector="QLabel", color=theme.DARK_BLUE, border=theme.BORDER
185-
)
186-
self._STYLE_TITLE_HOVER = theme.outlined_qss(
187-
selector="QLabel", color=theme.BLUE, border=theme.BLUE, accent=theme.BLUE
188-
)
189-
self._STYLE_TITLE_DISABLED = theme.outlined_qss(
190-
selector="QLabel",
191-
color=theme.TEXT_FAINT,
192-
border=theme.BORDER_SOFT,
193-
accent=theme.BLUE,
194-
)
182+
# 无框大字:正常深空蓝 / hover 钢蓝(仅文字变色,不画边框)/ 停用弱色
183+
self._STYLE_TITLE_NORMAL = theme.title_qss(color=theme.DARK_BLUE)
184+
self._STYLE_TITLE_HOVER = theme.title_qss(color=theme.BLUE)
185+
self._STYLE_TITLE_DISABLED = theme.title_qss(color=theme.TEXT_FAINT)
195186
self.title_label.setStyleSheet(self._STYLE_TITLE_NORMAL)
196-
# 脚本名 hover 变色:与副本按钮 hover 效果一致(边框+文字变钢蓝)
187+
# 脚本名 hover 变色:仅文字变色(与次级按钮 chip 的边框变色区分)
197188
self.title_label.enterEvent = self._title_enter
198189
self.title_label.leaveEvent = self._title_leave
199-
self.title_label.setAlignment(Qt.AlignCenter)
200-
self.title_label.setMinimumWidth(TITLE_MIN_WIDTH)
201-
self._title_width = TITLE_DEFAULT_WIDTH
202-
# QLabel 无 setElideMode,用 QFontMetrics 手动截断超长名字
203-
self.title_label.setText(
204-
QFontMetrics(title_font).elidedText(
205-
self.display_name, Qt.TextElideMode.ElideRight, self._title_width
206-
)
207-
)
190+
# 左对齐(QLabel 默认水平左对齐,显式声明避免依赖隐式行为)
191+
self.title_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
192+
# 最大宽度上限:超长名字 elide 截断,防撑爆布局(不再固定等宽)
193+
self.title_label.setMaximumWidth(TITLE_MAX_WIDTH)
208194
self.title_label.setCursor(Qt.PointingHandCursor)
209195
self.title_label.mousePressEvent = self._title_mouse_press
210196
layout.addWidget(self.title_label)
211197

212-
# 左侧弹性空间 + 副本按钮(居中) + 右侧弹性空间
213-
# 无副本按钮时两块 stretch 合并为一段,脚本名靠左、开关靠右
214-
layout.addStretch(1) # 左 spacer(index 2)
198+
# 左侧弹性空间:标题自适应宽度后,剩余空间全部由左侧 stretch 吸收;
199+
# 副本按钮紧跟开关之前(右边缘对齐开关),不再随标题宽度漂移。
200+
layout.addStretch(1) # 左 spacer(吸收标题自适应后的剩余空间)
215201

216202
# 副本选择按钮(点击弹出级联菜单:一级 → 二级从右侧弹出)
217203
self.dungeon_btn = None
@@ -225,9 +211,8 @@ def __init__(
225211
if saved_state.get("sequence"): # optional: 保存状态可能没有选择过序列
226212
self._selected_sequence = saved_state["sequence"]
227213
self._ensure_dungeon_button()
228-
layout.addStretch(1) # 右 spacer(副本按钮在两个等宽 stretch 之间 = 居中)
229214

230-
# 开关(自定义滑动开关)
215+
# 开关(自定义滑动开关):最后加入,固定右端,副本按钮右边缘对齐它
231216
self.toggle = ToggleSwitch(checked=self.enabled)
232217
self.toggle.toggled.connect(self._on_toggle_changed)
233218
self._update_switch_style()
@@ -264,15 +249,16 @@ def _show_config_dialog(self):
264249
self._config_saved_callback(changes)
265250

266251
def _refresh_title(self) -> None:
267-
"""按当前 display_name + _title_width 刷新卡片标题(含超长截断)。
252+
"""按当前 display_name + 最大宽度上限刷新卡片标题(超长自动截断)。
268253
269-
``_title_width`` 由 :meth:`MainWindow._sync_title_widths` 同步设置,
270-
所有 ScriptItem 一致;改名后调用本方法可立即以新宽度重新截断。
254+
无框大字自适应宽度:短名字按内容显示,超长(超过 maximumWidth)elide。
255+
改名后调用本方法可立即以新文本重新截断。
271256
"""
272257
title_font = self.title_label.font()
258+
max_w = self.title_label.maximumWidth()
273259
self.title_label.setText(
274260
QFontMetrics(title_font).elidedText(
275-
self.display_name, Qt.TextElideMode.ElideRight, self._title_width
261+
self.display_name, Qt.TextElideMode.ElideRight, max_w
276262
)
277263
)
278264

@@ -523,7 +509,7 @@ def _ensure_dungeon_button(self):
523509
# 按内容自适应宽度:有二级时仅显示二级 display(较短),固定宽度会浪费空间;
524510
# QPushButton 默认按 minimumSizeHint(内容宽 + padding)调整,无须固定宽度。
525511
self.dungeon_btn.clicked.connect(self._show_dungeon_menu)
526-
# 追加到左 spacer 之后、右 spacer(__init__ 随后添加)之前 → 居中
512+
# 追加到左 spacer 之后、开关(__init__ 随后添加)之前 → 右边缘对齐开关
527513
self.layout().insertWidget(self.layout().count(), self.dungeon_btn)
528514
self.dungeon_btn.setVisible(True)
529515
if self._selected_dungeon:

0 commit comments

Comments
 (0)