From f93dbeb8484733dfb2b9b62cc393298016d9188b Mon Sep 17 00:00:00 2001 From: DXC Date: Thu, 18 Jun 2026 13:59:20 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Step=207=20=E8=B7=AF=E7=94=B1=20+=20?= =?UTF-8?q?=E5=8F=91=E9=80=81=E7=AB=AF=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=AF=B9?= =?UTF-8?q?=E9=BD=90=EF=BC=88=E6=B6=88=E9=99=A4=20KeyError=EF=BC=89+=20?= =?UTF-8?q?=E9=85=8D=E5=A5=97=E9=98=B2=E5=BE=A1=E6=80=A7=E8=A1=A5=E5=BC=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 核心修复 - panel_registry: step7_index class_ref 换绑 Step7View(最小侵入式对位,保持 step_id 保护全项目 18 处下游引用) - step7_view._on_run_single_clicked: 'step7' → 'step7_index'(wrapped_config key + step_name 同步对齐,消除 PipelineScheduler 抛 KeyError "未注册的步骤: 'step7'") 配套防御性补强 - pipeline_executor.run_single_step_handler: 后台 is_running 时改 QMessageBox 警告 + LogMessage,防多次点击死锁 - worker_thread.run_single_step: 兼容嵌套/扁平 config 格式,嵌套子 dict 为空时回退扁平读取 - 公式 ListWidget layout 修复:setUniformItemSizes + stretch=1 + update(),消除 step7_view 加载后坍塌/不刷新 --- src/gui/core/panel_registry.py | 4 ++-- src/gui/core/pipeline_executor.py | 14 ++++++++++++++ src/gui/core/worker_thread.py | 14 ++++++++++++-- src/gui/panels/step7_index_panel.py | 1 + src/new/views/step7_view.py | 19 +++++++++++++++---- 5 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/gui/core/panel_registry.py b/src/gui/core/panel_registry.py index 6e80dec..dfa6ed6 100644 --- a/src/gui/core/panel_registry.py +++ b/src/gui/core/panel_registry.py @@ -18,7 +18,7 @@ from src.gui.panels.step3_panel import Step3Panel from src.gui.panels.step4_sampling_panel import Step4SamplingPanel from src.gui.panels.step5_clean_panel import Step5CleanPanel from src.gui.panels.step6_feature_panel import Step6FeaturePanel -from src.gui.panels.step7_index_panel import Step7IndexPanel +from src.new.views.step7_view import Step7View from src.gui.panels.step8_ml_train_panel import Step8MlTrainPanel from src.gui.panels.step9_ml_predict_panel import Step9MlPredictPanel from src.gui.panels.step10_watercolor_panel import Step10WatercolorPanel @@ -112,7 +112,7 @@ PANEL_REGISTRY = [ }, { 'step_id': 'step7_index', - 'class_ref': Step7IndexPanel, + 'class_ref': Step7View, 'title': '水质光谱指数计算', 'icon': '7.png', 'stage': '阶段二:样本数据准备', diff --git a/src/gui/core/pipeline_executor.py b/src/gui/core/pipeline_executor.py index 26f3884..dc44477 100644 --- a/src/gui/core/pipeline_executor.py +++ b/src/gui/core/pipeline_executor.py @@ -409,6 +409,8 @@ class PipelineExecutor(QObject): step_name = data.get('step_name') config = data.get('config') + print(f"==== Executor 收到单步请求: {step_name} ====", flush=True) + if not step_name: global_event_bus.publish('LogMessage', { 'message': '[单步执行] 请求缺少 step_name,忽略', @@ -416,6 +418,18 @@ class PipelineExecutor(QObject): }) return + # ★ 防死锁:若已有 Worker 在运行,不静默吞掉,而是通知用户 + if self.is_running: + global_event_bus.publish('LogMessage', { + 'message': f'[单步执行] 后台正在运行中,无法启动 {step_name}。请等待当前任务完成或手动停止后再试。', + 'level': 'warning', + }) + QMessageBox.warning( + self.parent(), "后台忙碌", + f"后台正在运行中,无法启动 {step_name}。\n\n请等待当前任务完成,或点击「停止」按钮后再试。" + ) + return + global_event_bus.publish('LogMessage', { 'message': f'[单步执行] 收到 {step_name} 的执行请求', 'level': 'info', diff --git a/src/gui/core/worker_thread.py b/src/gui/core/worker_thread.py index aaef421..5a4dfa6 100644 --- a/src/gui/core/worker_thread.py +++ b/src/gui/core/worker_thread.py @@ -338,8 +338,18 @@ class WorkerThread(QThread): pass def run_single_step(self, scheduler, step_name, config): - """使用新调度器运行单个步骤。""" - step_config = dict(config.get(step_name, {})) + """使用新调度器运行单个步骤。 + + config 兼容两种格式: + 嵌套格式:{'step7': {'training_csv_path': ..., ...}} + 扁平格式:{'training_csv_path': ..., 'formula_names': [...], ...} + 优先检测嵌套格式;若 step_name 对应的子 dict 为空,则回退到扁平格式。 + """ + nested = config.get(step_name) + if nested and isinstance(nested, dict) and len(nested) > 0: + step_config = dict(nested) + else: + step_config = dict(config) # 透传外部预训练模型(非空才覆盖) for key in ('_external_model', '_external_model_path', diff --git a/src/gui/panels/step7_index_panel.py b/src/gui/panels/step7_index_panel.py index 8bec72b..2807034 100644 --- a/src/gui/panels/step7_index_panel.py +++ b/src/gui/panels/step7_index_panel.py @@ -123,6 +123,7 @@ class Step7IndexPanel(QWidget): self.formula_list = QListWidget() self.formula_list.setSelectionMode(QAbstractItemView.MultiSelection) + self.formula_list.setMinimumHeight(300) self.formula_list.itemChanged.connect(self._on_item_changed) self.formula_layout.addWidget(self.formula_list) diff --git a/src/new/views/step7_view.py b/src/new/views/step7_view.py index fa8adf3..8a407a0 100644 --- a/src/new/views/step7_view.py +++ b/src/new/views/step7_view.py @@ -129,9 +129,10 @@ class Step7View(BaseView): self.formula_list.setSelectionMode(QListWidget.MultiSelection) self.formula_list.setMinimumHeight(300) self.formula_list.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self.formula_list.setUniformItemSizes(True) # view 层不需要 itemChanged 副作用;service 接管时再启用 self.formula_list.blockSignals(True) - formula_outer_layout.addWidget(self.formula_list) + formula_outer_layout.addWidget(self.formula_list, stretch=1) self.formula_group.setLayout(formula_outer_layout) layout.addWidget(self.formula_group) @@ -233,6 +234,8 @@ class Step7View(BaseView): self.index_checkboxes[name] = item self.formula_list.blockSignals(False) self.formula_list.adjustSize() + self.formula_list.updateGeometry() + self.update() for btn in (self.select_all_btn, self.deselect_all_btn, self.select_ratio_btn, self.select_conc_btn, self.refresh_button): @@ -319,6 +322,14 @@ class Step7View(BaseView): # 执行入口 # ------------------------------------------------------------------ def _on_run_single_clicked(self): - from src.gui.core.event_bus import global_event_bus - config = self.get_config() - global_event_bus.publish('RequestRunSingleStep', {'step_name': 'step7', 'config': config}) + import traceback + print("==== Step 7 计算按钮物理按下 ====", flush=True) + try: + from src.gui.core.event_bus import global_event_bus + config = self.get_config() + wrapped_config = {'step7_index': config} + global_event_bus.publish('RequestRunSingleStep', {'step_name': 'step7_index', 'config': wrapped_config}) + except Exception as e: + traceback.print_exc() + from PyQt5.QtWidgets import QMessageBox + QMessageBox.critical(self, "Step7 发射失败", f"收集配置或发送请求时异常:\n\n{e}")