fix: stabilize Shopee cover replacement

This commit is contained in:
chengma
2026-07-01 08:35:46 +08:00
parent f7ea9ec0fa
commit 483bec427c
18 changed files with 782 additions and 330 deletions
+167 -114
View File
@@ -85,7 +85,7 @@ QTabBar::tab:hover:!selected {
if QT_IMPORT_ERROR is None:
from . import accounts, ai, appconfig, chrome, db, diagnostics, editor, excel, prompts
from . import accounts, ai, appconfig, chrome, db, diagnostics, editor, excel, image_paths, prompts
from . import config as account_config
@@ -1157,7 +1157,7 @@ if QT_IMPORT_ERROR is None:
filter_layout.addWidget(self.refresh_button)
self.summary_label = QLabel("任务 0 条")
self.risk_label = QLabel("点击「开始更新」后会先确认当前筛选范围;确认后才允许后续任务提交线上。")
self.risk_label = QLabel("可先点击「预览本轮更新」检查当前筛选范围;点击「开始更新」后会再次确认并按批提交线上。")
self.task_table = QTableView()
self.model = ApplyTaskTableModel(self.task_table)
self.task_table.setModel(self.model)
@@ -1172,6 +1172,8 @@ if QT_IMPORT_ERROR is None:
self.run_log_view.setMaximumHeight(128)
self.run_log_view.setPlaceholderText("运行日志")
self.preview_update_button = QPushButton("预览本轮更新")
self.preview_update_button.setObjectName("previewUpdateButton")
self.start_update_button = QPushButton("开始更新")
self.stop_update_button = QPushButton("停止")
self.reset_update_button = QPushButton("重置更新状态")
@@ -1181,6 +1183,7 @@ if QT_IMPORT_ERROR is None:
self.write_back_button.setEnabled(False)
action_layout = QHBoxLayout()
action_layout.addWidget(self.preview_update_button)
action_layout.addWidget(self.start_update_button)
action_layout.addWidget(self.stop_update_button)
action_layout.addWidget(self.reset_update_button)
@@ -1202,6 +1205,7 @@ if QT_IMPORT_ERROR is None:
self.item_filter.textChanged.connect(self.refresh_tasks)
self.status_filter.currentIndexChanged.connect(self.refresh_tasks)
self.refresh_button.clicked.connect(self.refresh_tasks)
self.preview_update_button.clicked.connect(self.preview_update)
self.start_update_button.clicked.connect(self.start_update)
self.stop_update_button.clicked.connect(self.stop_update)
self.reset_update_button.clicked.connect(self.reset_apply_status)
@@ -1249,6 +1253,12 @@ if QT_IMPORT_ERROR is None:
self._update_write_back_button()
def start_update(self, checked=False):
self._start_update(dry_run=False)
def preview_update(self, checked=False):
self._start_update(dry_run=True)
def _start_update(self, dry_run=False):
if self.apply_thread is not None:
self._set_status("更新正在进行...")
return
@@ -1260,7 +1270,7 @@ if QT_IMPORT_ERROR is None:
self._set_status("当前筛选结果没有可更新任务")
return
update_cfg = self._shopee_update_config()
dry_run = bool(update_cfg.get("dry_run", False))
dry_run = bool(dry_run)
safety_error = self._update_safety_error(tasks, dry_run=dry_run)
if safety_error:
QMessageBox.warning(self, "更新安全开关", safety_error)
@@ -1268,14 +1278,15 @@ if QT_IMPORT_ERROR is None:
return
answer = QMessageBox.question(
self,
"确认开始更新",
self._confirmation_message(tasks),
"确认预览本轮更新" if dry_run else "确认开始更新",
self._confirmation_message(tasks, dry_run=dry_run),
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No,
)
if answer != QMessageBox.Yes:
self._set_status("已取消开始更新")
self._set_status("已取消预览本轮更新" if dry_run else "已取消开始更新")
return
batch_size = max(1, int(update_cfg.get("max_items_per_run", 1) or 1))
worker = ApplyWorker(
tasks,
db_path=self.db_path,
@@ -1287,6 +1298,7 @@ if QT_IMPORT_ERROR is None:
1,
int(update_cfg.get("max_parallel_accounts", 1) or 1),
),
batch_size=batch_size,
)
worker.progress.connect(self._on_apply_progress)
worker.row_updated.connect(self._on_apply_row_updated)
@@ -1301,9 +1313,9 @@ if QT_IMPORT_ERROR is None:
self._set_apply_running(True)
self.run_log_view.clear()
if dry_run:
self._set_status(f"开始 dry-run 预览:{len(tasks)} 条")
self._set_status(f"开始预览本轮更新:{len(tasks)} 条")
else:
self._set_status(f"开始更新:{len(tasks)} 条")
self._set_status(f"开始更新:{len(tasks)} 条,按每批最多 {batch_size} 条执行")
thread.start()
def stop_update(self, checked=False):
@@ -1466,34 +1478,39 @@ if QT_IMPORT_ERROR is None:
return task.status == "skipped"
return True
def _confirmation_message(self, tasks):
def _confirmation_message(self, tasks, dry_run=False):
update_cfg = self._shopee_update_config()
cover_text = "允许" if update_cfg.get("allow_cover_update") else "不允许"
close_text = "是" if update_cfg.get("close_success_tab") else "否"
dry_run_text = "开启" if update_cfg.get("dry_run") else "关闭"
batch_size = max(1, int(update_cfg.get("max_items_per_run", 1) or 1))
batch_count = (len(tasks) + batch_size - 1) // batch_size if tasks else 0
parallel_text = (
f"开启,最多 {update_cfg.get('max_parallel_accounts', 1)} 个账号"
if update_cfg.get("parallel_accounts")
else "关闭"
)
intro = (
"即将预览当前筛选结果。\n\n"
if dry_run
else "即将按当前筛选结果分批更新 Shopee 线上商品。\n\n"
)
return (
"即将按当前筛选结果开始更新 Shopee 线上商品。\n\n"
f"批次:{self._batch_filter_label()}\n"
f"店铺:{self._shop_filter_label()}\n"
f"商品ID:{self._item_filter_label()}\n"
f"状态:{self._status_label()}\n"
f"任务数:{len(tasks)}\n\n"
"安全设置:"
f"测试商品ID={update_cfg.get('test_item_id') or '未配置'},"
f"封面更新={cover_text},"
f"最大条数={update_cfg.get('max_items_per_run', 1)},"
f"成功后关闭新页={close_text},"
f"dry-run={dry_run_text},"
f"多账号并行={parallel_text}\n\n"
intro
+ f"批次:{self._batch_filter_label()}\n"
+ f"店铺:{self._shop_filter_label()}\n"
+ f"商品ID:{self._item_filter_label()}\n"
+ f"状态:{self._status_label()}\n"
+ f"任务数:{len(tasks)}\n"
+ f"预计批次:{batch_count}\n\n"
+ "安全设置:"
+ f"封面更新={cover_text},"
+ f"每批最大更新条数={batch_size},"
+ f"成功后关闭新页={close_text},"
+ f"多账号并行={parallel_text}\n\n"
+ (
"dry-run 开启时只写运行日志和预览,不打开 Shopee、不点击「更新」、不改任务状态。"
if update_cfg.get("dry_run")
else "确认后后续执行会打开商品编辑页、替换标题/允许时替换封面,并点击「更新」提交线上。"
"预览只写运行日志,不打开 Shopee、不点击「更新」、不改任务状态。"
if dry_run
else f"确认后会打开商品编辑页、替换标题/允许时替换封面,并按每批最多 {batch_size} 条点击「更新」提交线上;点击停止后不再开始下一条或下一批。"
)
)
@@ -1503,20 +1520,6 @@ if QT_IMPORT_ERROR is None:
return None
if not update_cfg.get("allow_real_submit", False):
return "设置未开启「允许真实提交线上商品」,已阻止本次更新。"
max_items = max(1, int(update_cfg.get("max_items_per_run", 1) or 1))
if len(tasks) > max_items:
return f"当前筛选结果有 {len(tasks)} 条,超过单次最大更新条数 {max_items}。"
test_item_id = str(update_cfg.get("test_item_id", "")).strip()
if not test_item_id:
return "未配置测试商品ID,已阻止真实提交。"
mismatched = [
str(getattr(task, "item_id", ""))
for task in tasks
if str(getattr(task, "item_id", "")) != test_item_id
]
if mismatched:
shown = "、".join(mismatched[:5])
return f"当前任务包含非测试商品ID:{shown}。只允许更新测试商品 {test_item_id}。"
if not update_cfg.get("allow_cover_update", False):
cover_tasks = [
str(getattr(task, "item_id", ""))
@@ -1537,6 +1540,7 @@ if QT_IMPORT_ERROR is None:
return merged
def _set_apply_running(self, running):
self.preview_update_button.setEnabled(not running)
self.start_update_button.setEnabled(not running)
self.stop_update_button.setEnabled(running)
self.reset_update_button.setEnabled(not running)
@@ -1548,6 +1552,7 @@ if QT_IMPORT_ERROR is None:
self._update_write_back_button()
def _set_result_write_back_running(self, running):
self.preview_update_button.setEnabled(not running)
self.start_update_button.setEnabled(not running)
self.reset_update_button.setEnabled(not running)
self.refresh_button.setEnabled(not running)
@@ -1588,7 +1593,7 @@ if QT_IMPORT_ERROR is None:
self._show_apply_blocked(payload)
return
self.last_apply_summary = dict(payload)
prefix = "dry-run 预览完成:" if payload.get("dry_run") else "更新完成:"
prefix = "预览本轮更新完成:" if payload.get("dry_run") else "更新完成:"
message = prefix + self._apply_progress_text(payload)
batch_ids = payload.get("batch_ids") or self._active_batch_ids()
if (not payload.get("dry_run")) and payload.get("done", 0) > 0 and batch_ids:
@@ -1665,6 +1670,7 @@ if QT_IMPORT_ERROR is None:
def _show_account_guide(self, message):
full_message = (
f"{message}\n\n"
"本轮更新已中止,不会自动打开账号 Chrome,也不会提交任何商品。\n"
"请先到「④ 账号管理」配置账号、启动对应账号 Chrome,并确认已人工登录 Shopee。"
)
QMessageBox.warning(self, "账号未就绪", full_message)
@@ -1775,14 +1781,14 @@ if QT_IMPORT_ERROR is None:
def _show_apply_summary(self, apply_summary, write_back_payload=None):
QMessageBox.information(
self,
"dry-run 预览完成" if apply_summary.get("dry_run") else "更新完成",
"预览本轮更新完成" if apply_summary.get("dry_run") else "更新完成",
self._apply_summary_message(apply_summary, write_back_payload),
)
def _apply_summary_message(self, apply_summary, write_back_payload=None, error=None):
dry_run = bool(apply_summary.get("dry_run"))
lines = [
"dry-run 预览完成,未打开 Shopee、未提交线上、未改任务状态。"
"预览本轮更新完成,未打开 Shopee、未提交线上、未改任务状态。"
if dry_run
else "更新完成。",
"{success_label}:{applied},失败:{failed},略过:{skipped}".format(
@@ -1933,6 +1939,7 @@ if QT_IMPORT_ERROR is None:
def _show_account_guide(self, message):
full_message = (
f"{message}\n\n"
"本轮采集已中止,不会自动打开账号 Chrome。\n"
"请先到「④ 账号管理」配置账号、启动对应账号 Chrome,并确认已人工登录 Shopee。"
)
QMessageBox.warning(self, "账号未就绪", full_message)
@@ -2577,6 +2584,7 @@ if QT_IMPORT_ERROR is None:
dry_run=False,
parallel_accounts=False,
max_parallel_accounts=1,
batch_size=None,
):
super().__init__()
self.tasks = list(tasks)
@@ -2587,6 +2595,9 @@ if QT_IMPORT_ERROR is None:
self.dry_run = bool(dry_run)
self.parallel_accounts = bool(parallel_accounts)
self.max_parallel_accounts = max(1, int(max_parallel_accounts or 1))
self.batch_size = None if batch_size is None else max(1, int(batch_size or 1))
self._current_batch_size = None
self._batch_count = 0
self._progress_lock = threading.Lock()
self._run_id = None
@@ -2600,21 +2611,23 @@ if QT_IMPORT_ERROR is None:
eligible = [task for task in self.tasks if self._is_actionable_task(task)]
batch_ids = self._batch_ids(eligible)
total = len(eligible)
applied = 0
skipped = 0
failed = 0
done = 0
batch_size = self._effective_batch_size(total)
batches = self._task_batches(eligible, batch_size)
self._current_batch_size = batch_size
self._batch_count = len(batches)
counters = {
"done": done,
"applied": applied,
"skipped": skipped,
"failed": failed,
"done": 0,
"applied": 0,
"skipped": 0,
"failed": 0,
}
self._run_id = self._create_run_log(eligible, batch_ids)
self._log_run_event(
"运行开始:{mode},任务{total},{parallel}".format(
mode="dry-run 预览" if self.dry_run else "真实更新",
"运行开始:{mode},任务{total},每批最多{batch_size},批次{batch_count},{parallel}".format(
mode="预览本轮更新" if self.dry_run else "真实更新",
total=total,
batch_size=batch_size,
batch_count=len(batches),
parallel=(
f"多账号并行最多{self.max_parallel_accounts}"
if self.parallel_accounts
@@ -2637,20 +2650,24 @@ if QT_IMPORT_ERROR is None:
self._finish_run_log("blocked", summary)
return summary
if self.dry_run:
for task in eligible:
if self.should_cancel():
break
outcome = self._preview_task(task, account_by_alias)
self._record_outcome(counters, total, outcome)
elif self.parallel_accounts and self.max_parallel_accounts > 1:
self._run_parallel_by_account(eligible, account_by_alias, counters, total)
else:
for task in eligible:
if self.should_cancel():
break
outcome = self._apply_one_task(task, account_by_alias)
self._record_outcome(counters, total, outcome)
for batch_index, batch_tasks in enumerate(batches, start=1):
if self.should_cancel():
break
self._log_batch_start(batch_index, len(batches), batch_tasks, counters, total)
if self.dry_run:
for task in batch_tasks:
if self.should_cancel():
break
outcome = self._preview_task(task, account_by_alias)
self._record_outcome(counters, total, outcome)
elif self.parallel_accounts and self.max_parallel_accounts > 1:
self._run_parallel_by_account(batch_tasks, account_by_alias, counters, total)
else:
for task in batch_tasks:
if self.should_cancel():
break
outcome = self._apply_one_task(task, account_by_alias)
self._record_outcome(counters, total, outcome)
summary = self._summary(
ok=counters["failed"] == 0,
@@ -2729,6 +2746,27 @@ if QT_IMPORT_ERROR is None:
)
return duplicates
def _effective_batch_size(self, total):
if self.batch_size is None:
return max(1, int(total or 1))
return self.batch_size
def _task_batches(self, tasks, batch_size):
if not tasks:
return []
return [
tasks[index:index + batch_size]
for index in range(0, len(tasks), batch_size)
]
def _log_batch_start(self, batch_index, batch_count, batch_tasks, counters, total):
first = counters["done"] + 1
last = min(first + len(batch_tasks) - 1, total)
label = "预览批次" if self.dry_run else "更新批次"
self._log_run_event(
f"{label} {batch_index}/{batch_count} 开始:任务 {first}-{last}/{total}"
)
def _run_parallel_by_account(self, eligible, account_by_alias, counters, total):
groups = self._group_tasks_by_alias(eligible)
max_workers = min(self.max_parallel_accounts, len(groups))
@@ -2773,7 +2811,7 @@ if QT_IMPORT_ERROR is None:
if account is None:
reason = "别名未匹配账号"
self._log_run_event(
f"dry-run:任务 {task.id} 商品 {task.item_id} 将略过:{reason}",
f"预览:任务 {task.id} 商品 {task.item_id} 将略过:{reason}",
task=task,
level="warning",
)
@@ -2785,7 +2823,7 @@ if QT_IMPORT_ERROR is None:
action_parts.append("封面")
action_text = "+".join(action_parts) or "无变更"
self._log_run_event(
"dry-run:任务 {task_id} 商品 {item_id} 账号 {alias} 将更新 {action}".format(
"预览:任务 {task_id} 商品 {item_id} 账号 {alias} 将更新 {action}".format(
task_id=task.id,
item_id=task.item_id,
alias=account.alias,
@@ -2903,6 +2941,8 @@ if QT_IMPORT_ERROR is None:
"skipped": skipped,
"failed": failed,
"dry_run": self.dry_run,
"batch_size": self._current_batch_size,
"batch_count": self._batch_count,
}
)
@@ -2938,6 +2978,8 @@ if QT_IMPORT_ERROR is None:
"batch_ids": batch_ids,
"dry_run": self.dry_run,
"parallel_accounts": self.parallel_accounts,
"batch_size": self._current_batch_size,
"batch_count": self._batch_count,
"run_id": self._run_id,
}
if blocked:
@@ -2958,6 +3000,8 @@ if QT_IMPORT_ERROR is None:
"dry_run": self.dry_run,
"parallel_accounts": self.parallel_accounts,
"max_parallel_accounts": self.max_parallel_accounts,
"batch_size": self._current_batch_size,
"batch_count": self._batch_count,
},
path=self.db_path,
)
@@ -3284,13 +3328,7 @@ if QT_IMPORT_ERROR is None:
def _old_cover_path(self, account, task):
image_root = appconfig.image_dir(self.config)
return os.path.abspath(
os.path.join(
image_root,
account.slug,
f"{task.item_id}_old.jpg",
)
)
return image_paths.task_image_path(image_root, task, account, "old")
def _batch_ids(self, tasks):
batch_ids = []
@@ -3630,6 +3668,7 @@ if QT_IMPORT_ERROR is None:
self.save_config_button = QPushButton("保存设置")
self.test_item_id_edit = QLineEdit()
self.test_item_id_edit.setObjectName("testItemIdEdit")
self.test_item_id_edit.setVisible(False)
self.allow_real_submit_checkbox = QCheckBox("允许真实提交线上商品")
self.allow_real_submit_checkbox.setObjectName("allowRealSubmitCheckbox")
self.allow_cover_update_checkbox = QCheckBox("允许更新封面")
@@ -3637,15 +3676,27 @@ if QT_IMPORT_ERROR is None:
self.max_items_per_run_spin = QSpinBox()
self.max_items_per_run_spin.setObjectName("maxItemsPerRunSpin")
self.max_items_per_run_spin.setRange(1, 9999)
self.max_items_per_run_spin.setToolTip("作为每批最大更新条数;正式更新会分批处理当前筛选全部可更新记录。")
self.close_success_tab_checkbox = QCheckBox("成功后关闭本次新开编辑页")
self.close_success_tab_checkbox.setObjectName("closeSuccessTabCheckbox")
self.dry_run_checkbox = QCheckBox("dry-run 只预览不提交")
self.dry_run_checkbox = QCheckBox("预览本轮更新")
self.dry_run_checkbox.setObjectName("dryRunCheckbox")
self.dry_run_checkbox.setVisible(False)
self.parallel_accounts_checkbox = QCheckBox("多账号并行更新")
self.parallel_accounts_checkbox.setObjectName("parallelAccountsCheckbox")
self.max_parallel_accounts_spin = QSpinBox()
self.max_parallel_accounts_spin.setObjectName("maxParallelAccountsSpin")
self.max_parallel_accounts_spin.setRange(1, 16)
self.max_parallel_accounts_label = QLabel("最大并行账号数")
self.parallel_accounts_group = QWidget()
self.parallel_accounts_group.setObjectName("parallelAccountsGroup")
parallel_accounts_layout = QHBoxLayout(self.parallel_accounts_group)
parallel_accounts_layout.setContentsMargins(0, 0, 0, 0)
parallel_accounts_layout.setSpacing(12)
parallel_accounts_layout.addWidget(self.parallel_accounts_checkbox)
parallel_accounts_layout.addWidget(self.max_parallel_accounts_label)
parallel_accounts_layout.addWidget(self.max_parallel_accounts_spin)
parallel_accounts_layout.addStretch(1)
model_picker_layout = QHBoxLayout()
model_picker_layout.addWidget(self.model_combo, 1)
@@ -3657,7 +3708,7 @@ if QT_IMPORT_ERROR is None:
action_layout.addWidget(self.test_connection_button)
action_layout.addStretch(1)
form = self._two_column_form(
form = self._three_column_form(
[
("状态", self.enabled_checkbox),
("服务商名", self.name_edit),
@@ -3670,7 +3721,7 @@ if QT_IMPORT_ERROR is None:
]
)
ai_form = self._two_column_form(
ai_form = self._three_column_form(
[
("标题大模型", self.default_text_model_combo),
("图片大模型", self.default_image_model_combo),
@@ -3691,7 +3742,7 @@ if QT_IMPORT_ERROR is None:
port_range_widget = QWidget()
port_range_widget.setLayout(port_range_layout)
path_form = self._two_column_form(
path_form = self._three_column_form(
[
("Chrome路径", self.chrome_path_edit, True),
("账号数据根目录", self.user_data_root_edit),
@@ -3703,22 +3754,20 @@ if QT_IMPORT_ERROR is None:
]
)
update_form = self._two_column_form(
self.shopee_update_form_layout = self._three_column_form(
[
("测试商品ID", self.test_item_id_edit),
("单次最大更新条数", self.max_items_per_run_spin),
("每批最大更新条数", self.max_items_per_run_spin),
("", self.allow_real_submit_checkbox),
("", self.allow_cover_update_checkbox),
("", self.close_success_tab_checkbox),
("", self.dry_run_checkbox),
("", self.parallel_accounts_checkbox),
("最大并行账号数", self.max_parallel_accounts_spin),
("", self.allow_cover_update_checkbox),
("", self.parallel_accounts_group, 2),
]
)
panel = QWidget()
panel.setMaximumWidth(1800)
panel_layout = QVBoxLayout(panel)
panel_layout.setContentsMargins(0, 0, 0, 0)
panel_layout.setContentsMargins(13, 18, 13, 18)
panel_layout.addWidget(QLabel("AI 模型"))
panel_layout.addLayout(model_picker_layout)
panel_layout.addSpacing(14)
@@ -3734,13 +3783,19 @@ if QT_IMPORT_ERROR is None:
panel_layout.addLayout(path_form)
panel_layout.addSpacing(18)
panel_layout.addWidget(QLabel("Shopee 更新安全"))
panel_layout.addLayout(update_form)
panel_layout.addLayout(self.shopee_update_form_layout)
panel_layout.addWidget(self.save_config_button)
panel_layout.addStretch(1)
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setWidget(panel)
scroll_content = QWidget()
scroll_layout = QHBoxLayout(scroll_content)
scroll_layout.setContentsMargins(0, 0, 0, 0)
scroll_layout.addStretch(1)
scroll_layout.addWidget(panel)
scroll_layout.addStretch(1)
scroll.setWidget(scroll_content)
layout = QVBoxLayout(self)
layout.setContentsMargins(18, 18, 18, 18)
@@ -3759,46 +3814,43 @@ if QT_IMPORT_ERROR is None:
self.refresh_models()
self._populate_app_settings()
def _two_column_form(self, fields):
def _three_column_form(self, fields):
layout = QGridLayout()
layout.setHorizontalSpacing(18)
layout.setVerticalSpacing(8)
layout.setColumnStretch(1, 1)
layout.setColumnStretch(3, 1)
for column in (1, 3, 5):
layout.setColumnStretch(column, 1)
row = 0
column_pair = 0
for field in fields:
label = field[0]
widget = field[1]
full_width = len(field) > 2 and bool(field[2])
if full_width and column_pair:
span_pairs = self._form_field_span_pairs(field)
if span_pairs > 3 - column_pair:
row += 1
column_pair = 0
column = column_pair * 2
self._add_form_field(layout, row, column, label, widget, full_width)
if full_width:
row += 1
column_pair = 0
elif column_pair == 0:
column_pair = 1
else:
self._add_form_field(layout, row, column, label, widget, span_pairs)
column_pair += span_pairs
if column_pair >= 3:
row += 1
column_pair = 0
return layout
def _add_form_field(self, layout, row, column, label, widget, full_width):
if full_width:
if label:
layout.addWidget(QLabel(label), row, 0)
layout.addWidget(widget, row, 1, 1, 3)
else:
layout.addWidget(widget, row, 0, 1, 4)
return
def _form_field_span_pairs(self, field):
if len(field) <= 2:
return 1
span = field[2]
if isinstance(span, bool):
return 3 if span else 1
return max(1, min(3, int(span or 1)))
def _add_form_field(self, layout, row, column, label, widget, span_pairs):
if label:
layout.addWidget(QLabel(label), row, column)
layout.addWidget(widget, row, column + 1)
layout.addWidget(widget, row, column + 1, 1, span_pairs * 2 - 1)
else:
layout.addWidget(widget, row, column, 1, 2)
layout.addWidget(widget, row, column, 1, span_pairs * 2)
def _set_status(self, message):
if self.status_callback is not None:
@@ -3939,6 +3991,7 @@ if QT_IMPORT_ERROR is None:
self._replace_config(saved)
self._populate_app_settings()
self._set_status("设置已保存")
QMessageBox.information(self, "保存设置", "设置已保存")
def _app_settings_values(self):
start_port = self.debug_port_start_spin.value()
@@ -3991,7 +4044,7 @@ if QT_IMPORT_ERROR is None:
"allow_cover_update": self.allow_cover_update_checkbox.isChecked(),
"max_items_per_run": self.max_items_per_run_spin.value(),
"close_success_tab": self.close_success_tab_checkbox.isChecked(),
"dry_run": self.dry_run_checkbox.isChecked(),
"dry_run": False,
"parallel_accounts": self.parallel_accounts_checkbox.isChecked(),
"max_parallel_accounts": self.max_parallel_accounts_spin.value(),
},
@@ -4059,7 +4112,7 @@ if QT_IMPORT_ERROR is None:
self.close_success_tab_checkbox.setChecked(
bool(update_cfg.get("close_success_tab", False))
)
self.dry_run_checkbox.setChecked(bool(update_cfg.get("dry_run", False)))
self.dry_run_checkbox.setChecked(False)
self.parallel_accounts_checkbox.setChecked(
bool(update_cfg.get("parallel_accounts", False))
)