Skip to content

Commit 8484fee

Browse files
committed
refactor: 优化下布局
1 parent aae1502 commit 8484fee

2 files changed

Lines changed: 180 additions & 116 deletions

File tree

aao/ui/farm_page.py

Lines changed: 178 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
QCheckBox,
1717
QComboBox,
1818
QFormLayout,
19+
QGridLayout,
1920
QGroupBox,
2021
QHBoxLayout,
2122
QLabel,
@@ -24,17 +25,21 @@
2425
QMessageBox,
2526
QPushButton,
2627
QRadioButton,
28+
QSpinBox,
2729
QSplitter,
2830
QTextEdit,
2931
QVBoxLayout,
3032
QWidget,
3133
)
3234

35+
from aao import config
3336
from aao.core.timing.time_source import format_timer
3437
from aao.ui import floating_state
38+
from aao.ui.collapsible_box import CollapsibleBox
3539
from aao.ui.farm_worker import FarmWorker
3640
from aao.ui.floating_log_window import FloatingLogWindow
3741
from aao.ui.scrollbar_style import apply_themed_scrollbar
42+
from aao.ui.settings_page import load_settings, save_settings
3843
from aao.utils.runtime_paths import project_root
3944

4045
if TYPE_CHECKING:
@@ -110,6 +115,16 @@ def _build_ui(self) -> None:
110115
btn_row.addStretch()
111116
form.addRow(btn_row)
112117

118+
# 悬浮日志偏好(紧挨按钮行)
119+
chk_row = QHBoxLayout()
120+
self.chk_float_log_auto = QCheckBox("开始凹图时自动显示悬浮日志")
121+
self.chk_float_log_top = QCheckBox("悬浮日志默认置顶")
122+
self.chk_float_log_top.setChecked(True)
123+
chk_row.addWidget(self.chk_float_log_auto)
124+
chk_row.addWidget(self.chk_float_log_top)
125+
chk_row.addStretch()
126+
form.addRow(chk_row)
127+
113128
root.addWidget(top)
114129

115130
# 状态区
@@ -124,14 +139,19 @@ def _build_ui(self) -> None:
124139
s.addStretch()
125140
root.addWidget(status_box)
126141

142+
# 高级执行参数(默认折叠)
143+
self._build_advanced_params(root)
144+
127145
# 结果历史 + 日志(左右分栏)
128146
splitter = QSplitter(Qt.Orientation.Horizontal)
147+
splitter.setChildrenCollapsible(False)
129148

130149
hist_box = QGroupBox("结果历史")
131150
hist_box.setStyleSheet("QGroupBox { background: transparent; }")
132151
hl = QVBoxLayout(hist_box)
133152
self.list_history = QListWidget()
134153
apply_themed_scrollbar(self.list_history, "QListWidget { background: transparent; }")
154+
self.list_history.setMinimumWidth(160)
135155
hl.addWidget(self.list_history)
136156
splitter.addWidget(hist_box)
137157

@@ -140,6 +160,7 @@ def _build_ui(self) -> None:
140160
ll = QVBoxLayout(log_box)
141161
self.txt_log = QTextEdit()
142162
self.txt_log.setReadOnly(True)
163+
self.txt_log.setMinimumWidth(220)
143164
apply_themed_scrollbar(self.txt_log, "QTextEdit { background: transparent; }")
144165
self.txt_log.document().setMaximumBlockCount(2000)
145166
ll.addWidget(self.txt_log)
@@ -154,6 +175,11 @@ def _build_ui(self) -> None:
154175
self.btn_stop.clicked.connect(self._on_stop)
155176
self.rb_sand.toggled.connect(self._update_practice_visible)
156177
self.btn_float_log.clicked.connect(self._show_floating_log)
178+
self.chk_float_log_auto.toggled.connect(self._on_float_log_pref_changed)
179+
self.chk_float_log_top.toggled.connect(self._on_float_log_pref_changed)
180+
self._advanced_box.toggled.connect(self._on_advanced_collapsed)
181+
for spin in self._advanced_spins:
182+
spin.valueChanged.connect(self._on_advanced_param_changed)
157183
self._update_practice_visible()
158184

159185
def _update_practice_visible(self) -> None:
@@ -162,6 +188,154 @@ def _update_practice_visible(self) -> None:
162188
if is_sand:
163189
self.chk_practice.setChecked(False)
164190

191+
# --- 高级执行参数 ---
192+
193+
def _build_advanced_params(self, root: QVBoxLayout) -> None:
194+
"""折叠区:10 个帧级执行参数 SpinBox,2×5 网格(左帧阈值 / 右等待时间)。
195+
196+
用单个 QGridLayout(6 列:label|spin|间隔|label|spin|弹簧),关键点:
197+
- setColumnMinimumWidth 给两个 label 列设相同最小宽度,标签列视觉等宽
198+
- label 行右对齐,spin 列不拉伸(FieldsStayAtSizeHint 效果),两侧弹簧居中
199+
- setHorizontalSpacing/setVerticalSpacing 控制格内留白
200+
"""
201+
box = CollapsibleBox("高级执行参数")
202+
box.set_summary("帧阈值 / 等待时间")
203+
self._advanced_box = box
204+
205+
adv_group = QGroupBox("高级执行参数")
206+
grid = QGridLayout(adv_group)
207+
grid.setHorizontalSpacing(12)
208+
grid.setVerticalSpacing(8)
209+
# 列:0=label 1=spin 2=间隔 3=label 4=spin
210+
# 不给 label 列设最小宽度——让其收缩到最长标签的自然宽度,
211+
# 标签左对齐使空白落在标签右侧(标签↔spinner 之间,自然间距),
212+
# 避免 AlignRight + forced min-width 导致的左侧强迫空白。
213+
grid.setColumnMinimumWidth(2, 24) # 中间分隔列
214+
215+
# 左列:帧阈值
216+
self.spin_bullet = self._make_spin(0, 30, config.BULLET_THRESHOLD)
217+
self.spin_speedup = self._make_spin(10, 600, config.SPEED_UP_THRESHOLD)
218+
self.spin_big_step = self._make_spin(4, 30, config.BIG_STEP_THRESHOLD)
219+
self.spin_big_step.setToolTip("剩余帧数≥此值时用 T(快),否则用 R(慢)")
220+
self.spin_accept_early = self._make_spin(0, 3, config.ACCEPT_EARLY_FRAMES)
221+
self.spin_accept_early.setToolTip("允许动作提前触发的帧数(0=严格同帧)")
222+
self.spin_accept_late = self._make_spin(0, 5, config.ACCEPT_LATE_FRAMES)
223+
self.spin_accept_late.setToolTip("动作晚于目标多少帧仍可接受")
224+
225+
# 右列:等待时间
226+
self.spin_general_wait = self._make_spin(10, 2000, config.GENERAL_WAIT_MS, step=10)
227+
self.spin_mouse_wait = self._make_spin(10, 1000, config.MOUSE_WAIT_MS, step=10)
228+
self.spin_min_wait = self._make_spin(1, 500, config.MINIMUM_WAIT_MS, step=5)
229+
self.spin_pause_wait = self._make_spin(20, 500, config.PAUSE_WAIT_MS, step=10)
230+
self.spin_step_wait = self._make_spin(20, 500, config.STEP_WAIT_MS, step=10)
231+
232+
rows = [
233+
# (左标签, 左 spin, 右标签, 右 spin)
234+
("步进阈值(帧)", self.spin_bullet, "通用等待(ms)", self.spin_general_wait),
235+
("加速阈值(帧)", self.spin_speedup, "鼠标等待(ms)", self.spin_mouse_wait),
236+
("T 步进阈值(帧)", self.spin_big_step, "最小等待(ms)", self.spin_min_wait),
237+
("提前容忍(帧)", self.spin_accept_early, "暂停等待(ms)", self.spin_pause_wait),
238+
("延后容忍(帧)", self.spin_accept_late, "步进等待(ms)", self.spin_step_wait),
239+
]
240+
align_r = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter
241+
for r, (ll, lw, rl, rw) in enumerate(rows):
242+
grid.addWidget(self._mk_lbl(ll, align_r), r, 0)
243+
grid.addWidget(lw, r, 1)
244+
grid.addWidget(self._mk_lbl(rl, align_r), r, 3)
245+
grid.addWidget(rw, r, 4)
246+
247+
self._advanced_spins = [
248+
self.spin_bullet, self.spin_speedup, self.spin_big_step,
249+
self.spin_accept_early, self.spin_accept_late,
250+
self.spin_general_wait, self.spin_mouse_wait, self.spin_min_wait,
251+
self.spin_pause_wait, self.spin_step_wait,
252+
]
253+
254+
box.add_widget(adv_group)
255+
box.set_expanded(False)
256+
root.addWidget(box)
257+
258+
@staticmethod
259+
def _make_spin(
260+
lo: int, hi: int, val: int, suffix: str = "", step: int = 1
261+
) -> QSpinBox:
262+
s = QSpinBox()
263+
s.setRange(lo, hi)
264+
s.setSingleStep(step)
265+
s.setValue(val)
266+
if suffix:
267+
s.setSuffix(suffix)
268+
s.setMaximumWidth(120)
269+
return s
270+
271+
@staticmethod
272+
def _mk_lbl(text: str, alignment: Qt.AlignmentFlag) -> QLabel:
273+
lbl = QLabel(text)
274+
lbl.setAlignment(alignment)
275+
return lbl
276+
277+
def _load_advanced_settings(self) -> None:
278+
s = load_settings()
279+
self.chk_float_log_auto.setChecked(s.get("floating_log_auto_show", False))
280+
self.chk_float_log_top.setChecked(s.get("floating_log_topmost", True))
281+
self.spin_bullet.setValue(s.get("bullet_threshold", config.BULLET_THRESHOLD))
282+
self.spin_speedup.setValue(s.get("speed_up_threshold", config.SPEED_UP_THRESHOLD))
283+
self.spin_general_wait.setValue(s.get("general_wait_ms", config.GENERAL_WAIT_MS))
284+
self.spin_mouse_wait.setValue(s.get("mouse_wait_ms", config.MOUSE_WAIT_MS))
285+
self.spin_min_wait.setValue(s.get("minimum_wait_ms", config.MINIMUM_WAIT_MS))
286+
self.spin_pause_wait.setValue(s.get("pause_wait_ms", config.PAUSE_WAIT_MS))
287+
self.spin_step_wait.setValue(s.get("step_wait_ms", config.STEP_WAIT_MS))
288+
self.spin_big_step.setValue(s.get("big_step_threshold", config.BIG_STEP_THRESHOLD))
289+
self.spin_accept_early.setValue(s.get("accept_early_frames", config.ACCEPT_EARLY_FRAMES))
290+
self.spin_accept_late.setValue(s.get("accept_late_frames", config.ACCEPT_LATE_FRAMES))
291+
states = s.get("collapsible_sections", {})
292+
if isinstance(states, dict) and "farm_advanced_params" in states:
293+
self._advanced_box.set_expanded(bool(states["farm_advanced_params"]))
294+
295+
def _on_advanced_param_changed(self) -> None:
296+
"""即时保存到 settings.json + 覆盖 config(下次凹图立即生效)。"""
297+
data = load_settings()
298+
data.update(
299+
{
300+
"bullet_threshold": self.spin_bullet.value(),
301+
"speed_up_threshold": self.spin_speedup.value(),
302+
"general_wait_ms": self.spin_general_wait.value(),
303+
"mouse_wait_ms": self.spin_mouse_wait.value(),
304+
"minimum_wait_ms": self.spin_min_wait.value(),
305+
"pause_wait_ms": self.spin_pause_wait.value(),
306+
"step_wait_ms": self.spin_step_wait.value(),
307+
"big_step_threshold": self.spin_big_step.value(),
308+
"accept_early_frames": self.spin_accept_early.value(),
309+
"accept_late_frames": self.spin_accept_late.value(),
310+
}
311+
)
312+
save_settings(data)
313+
config.BULLET_THRESHOLD = self.spin_bullet.value()
314+
config.SPEED_UP_THRESHOLD = self.spin_speedup.value()
315+
config.GENERAL_WAIT_MS = self.spin_general_wait.value()
316+
config.MOUSE_WAIT_MS = self.spin_mouse_wait.value()
317+
config.MINIMUM_WAIT_MS = self.spin_min_wait.value()
318+
config.PAUSE_WAIT_MS = self.spin_pause_wait.value()
319+
config.STEP_WAIT_MS = self.spin_step_wait.value()
320+
config.BIG_STEP_THRESHOLD = self.spin_big_step.value()
321+
config.ACCEPT_EARLY_FRAMES = self.spin_accept_early.value()
322+
config.ACCEPT_LATE_FRAMES = self.spin_accept_late.value()
323+
324+
def _on_float_log_pref_changed(self) -> None:
325+
data = load_settings()
326+
data["floating_log_auto_show"] = self.chk_float_log_auto.isChecked()
327+
data["floating_log_topmost"] = self.chk_float_log_top.isChecked()
328+
save_settings(data)
329+
330+
def _on_advanced_collapsed(self, expanded: bool) -> None:
331+
data = load_settings()
332+
states = data.get("collapsible_sections", {})
333+
if not isinstance(states, dict):
334+
states = {}
335+
states["farm_advanced_params"] = expanded
336+
data["collapsible_sections"] = states
337+
save_settings(data)
338+
165339
def _load_timelines(self) -> None:
166340
cur = self.cb_timeline.currentText()
167341
self.cb_timeline.clear()
@@ -184,6 +358,7 @@ def showEvent(self, event: QShowEvent) -> None: # noqa: D401
184358
# 切到凹图页时刷新下拉(新加的 timeline/profile 立即可见)
185359
self._load_timelines()
186360
self._load_profiles()
361+
self._load_advanced_settings()
187362
super().showEvent(event)
188363

189364
# --- 注入(由 MainWindow 调用)---
@@ -212,8 +387,6 @@ def _ensure_floating_log(self) -> FloatingLogWindow:
212387

213388
def _show_floating_log(self) -> None:
214389
log_win = self._ensure_floating_log()
215-
from aao.ui.settings_page import load_settings
216-
217390
log_win.set_always_on_top(load_settings().get("floating_log_topmost", True))
218391
log_win.show()
219392
log_win.raise_()
@@ -261,8 +434,6 @@ def _on_start(self) -> None:
261434
self.list_history.clear()
262435
self._round_start_time = None
263436
self.busy_changed.emit(True)
264-
from aao.ui.settings_page import load_settings
265-
266437
s = load_settings()
267438
if s.get("floating_log_auto_show", False):
268439
self._show_floating_log()
@@ -359,6 +530,9 @@ def _set_inputs_enabled(self, enabled: bool) -> None:
359530
self.chk_practice,
360531
self.edit_retries,
361532
self.cb_profile,
533+
self.chk_float_log_auto,
534+
self.chk_float_log_top,
535+
*self._advanced_spins,
362536
):
363537
w.setEnabled(enabled)
364538

0 commit comments

Comments
 (0)