diff --git a/src/gui/core/config_manager.py b/src/gui/core/config_manager.py
new file mode 100644
index 0000000..a119eb0
--- /dev/null
+++ b/src/gui/core/config_manager.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+配置管理器
+
+接管主窗口中所有配置读写逻辑:
+- new_config() 清空所有面板配置
+- load_config(file_path) 从 JSON 文件加载配置并回填面板
+- save_config(file_path) 将当前配置保存为 JSON 文件
+- get_current_config() 遍历 PanelFactory 收集配置(懒加载安全)
+
+懒加载兼容原则:
+- get_current_config() 仅遍历已加载面板,未加载面板返回空字典 {}
+- 绝不为了拿配置而强行唤醒/渲染所有 Panel
+- 如需全量配置(如保存),调用方应先执行 panel_factory.preload_all()
+"""
+
+import json
+import os
+from typing import Dict, Optional
+
+from PyQt5.QtCore import QObject
+from PyQt5.QtWidgets import QMessageBox, QFileDialog
+
+from src.gui.core.event_bus import global_event_bus
+
+
+class ConfigManager(QObject):
+ """配置管理器。
+
+ 用法::
+
+ cfg_mgr = ConfigManager(panel_factory, parent=self)
+ cfg_mgr.new_config() # 清空配置
+ cfg_mgr.load_config(path) # 加载 JSON
+ cfg_mgr.save_config(path) # 保存 JSON
+ config = cfg_mgr.get_current_config() # 收集当前配置
+ """
+
+ def __init__(self, panel_factory, parent=None):
+ """
+ Args:
+ panel_factory: PanelFactory 实例
+ parent: 父 QObject(用于弹窗定位)
+ """
+ super().__init__(parent)
+ self._panel_factory = panel_factory
+
+ # ═══════════════════════════════════════════════════════════
+ # 公开 API
+ # ═══════════════════════════════════════════════════════════
+
+ def new_config(self):
+ """清空所有面板配置(需用户确认)。"""
+ reply = QMessageBox.question(
+ self.parent(), "新建配置", "是否清空当前配置?",
+ QMessageBox.Yes | QMessageBox.No
+ )
+ if reply != QMessageBox.Yes:
+ return
+
+ for panel in self._panel_factory.get_loaded_panels().values():
+ if hasattr(panel, 'clear_config'):
+ panel.clear_config()
+
+ global_event_bus.publish('LogMessage', {
+ 'message': '已清空配置',
+ 'level': 'info',
+ })
+
+ def load_config(self, file_path: str = None):
+ """从 JSON 文件加载配置并回填面板。
+
+ Args:
+ file_path: JSON 文件路径。若为 None,弹出文件选择对话框。
+ """
+ if file_path is None:
+ file_path, _ = QFileDialog.getOpenFileName(
+ self.parent(), "加载配置", "",
+ "JSON Files (*.json);;All Files (*.*)"
+ )
+ if not file_path:
+ return
+
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ config = json.load(f)
+ except Exception as e:
+ QMessageBox.critical(
+ self.parent(), "加载失败",
+ f"无法读取配置文件:\n{file_path}\n\n错误: {e}"
+ )
+ return
+
+ # 回填已加载面板
+ loaded_count = 0
+ for step_id, panel in self._panel_factory.get_loaded_panels().items():
+ if step_id in config and hasattr(panel, 'set_config'):
+ try:
+ panel.set_config(config[step_id])
+ loaded_count += 1
+ except Exception:
+ pass
+
+ global_event_bus.publish('LogMessage', {
+ 'message': f'已加载配置: {file_path}(回填 {loaded_count} 个面板)',
+ 'level': 'info',
+ })
+
+ def save_config(self, file_path: str = None):
+ """将当前配置保存为 JSON 文件。
+
+ 注意:保存前会强制加载所有面板(preload_all),确保配置完整。
+
+ Args:
+ file_path: 目标 JSON 文件路径。若为 None,弹出保存对话框。
+ """
+ if file_path is None:
+ file_path, _ = QFileDialog.getSaveFileName(
+ self.parent(), "保存配置", "config.json",
+ "JSON Files (*.json);;All Files (*.*)"
+ )
+ if not file_path:
+ return
+
+ # 保存前强制加载所有面板,确保配置完整
+ self._panel_factory.preload_all()
+ config = self.get_current_config()
+
+ try:
+ with open(file_path, 'w', encoding='utf-8') as f:
+ json.dump(config, f, indent=2, ensure_ascii=False)
+ except Exception as e:
+ QMessageBox.critical(
+ self.parent(), "保存失败",
+ f"无法保存配置文件:\n{file_path}\n\n错误: {e}"
+ )
+ return
+
+ global_event_bus.publish('LogMessage', {
+ 'message': f'已保存配置: {file_path}',
+ 'level': 'info',
+ })
+
+ def get_current_config(self) -> Dict[str, dict]:
+ """收集当前所有步骤的配置。
+
+ 懒加载安全:仅遍历已加载面板,未加载面板返回空字典 {}。
+ 绝不为了拿配置而强行唤醒/渲染所有 Panel。
+
+ Returns:
+ {step_id: panel_config_dict}
+ """
+ config = {}
+ for step_id, panel in self._panel_factory.get_loaded_panels().items():
+ if hasattr(panel, 'get_config'):
+ try:
+ config[step_id] = panel.get_config()
+ except Exception:
+ config[step_id] = {}
+ return config
diff --git a/src/gui/core/dialog_service.py b/src/gui/core/dialog_service.py
new file mode 100644
index 0000000..7cba483
--- /dev/null
+++ b/src/gui/core/dialog_service.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+对话框服务
+
+封装纯展示类弹窗,从主窗口中彻底剥离 UI 对话框逻辑。
+
+职责:
+- show_pipeline_status() 显示 Pipeline 模块加载状态
+- show_about() 显示"关于"对话框
+- show_ai_settings() 显示 AI 引擎配置对话框
+"""
+
+from PyQt5.QtCore import QObject
+from PyQt5.QtWidgets import QMessageBox
+
+
+class DialogService(QObject):
+ """对话框服务。
+
+ 用法::
+
+ dlg_svc = DialogService(parent=self)
+ dlg_svc.show_about()
+ dlg_svc.show_pipeline_status()
+ dlg_svc.show_ai_settings()
+ """
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+
+ # ═══════════════════════════════════════════════════════════
+ # 公开 API
+ # ═══════════════════════════════════════════════════════════
+
+ def show_pipeline_status(self):
+ """显示 Pipeline 模块加载状态。"""
+ from src.gui.core.worker_thread import PIPELINE_AVAILABLE, PIPELINE_ERROR_INFO
+
+ if PIPELINE_AVAILABLE:
+ QMessageBox.information(
+ self.parent(), "Pipeline状态",
+ "Pipeline模块: 正常加载"
+ )
+ else:
+ detail = "\n".join(PIPELINE_ERROR_INFO)
+ QMessageBox.warning(
+ self.parent(), "Pipeline状态",
+ f"Pipeline模块: 加载失败\n\n{detail}"
+ )
+
+ def show_about(self):
+ """显示"关于"对话框。"""
+ QMessageBox.about(
+ self.parent(), "关于",
+ "MegaCube-Water Quality V1.2\n\n"
+ "一个完整的水质参数反演工作流程工具\n\n"
+ "公司:北京依锐思遥感技术有限公司\n"
+ "地址:北京市海淀区清河安宁庄东路18号5号楼二层205\n"
+ "电话:010-51292601\n"
+ "邮箱:hanshanlong@iris-rs.cn"
+ )
+
+ def show_ai_settings(self):
+ """显示 AI 引擎配置对话框。"""
+ from src.gui.dialogs import AISettingsDialog
+ AISettingsDialog(self.parent()).exec_()
diff --git a/src/gui/core/log_manager.py b/src/gui/core/log_manager.py
new file mode 100644
index 0000000..18f5f67
--- /dev/null
+++ b/src/gui/core/log_manager.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+日志与进度管理器
+
+将主窗口中的日志区(QTextEdit)、进度条(QProgressBar)和"清空日志"按钮
+的 UI 创建与控制逻辑完全封装。
+
+职责:
+- create_log_panel() → 返回组装好的 QWidget(日志 + 进度条)
+- 内部订阅 LogMessage / ProgressUpdate 事件,自动更新 UI
+- 主窗口无需再关心日志/进度的状态同步
+
+订阅的事件:
+ LogMessage → {message, level} 写入日志区
+ ProgressUpdate → {percentage, message} 更新进度条
+"""
+
+from datetime import datetime
+
+from PyQt5.QtCore import QObject
+from PyQt5.QtGui import QTextCursor
+from PyQt5.QtWidgets import (
+ QWidget, QVBoxLayout, QHBoxLayout,
+ QGroupBox, QTextEdit, QProgressBar, QPushButton,
+)
+
+from src.gui.core.event_bus import global_event_bus
+
+
+class LogManager(QObject):
+ """日志与进度管理器。
+
+ 用法::
+
+ log_mgr = LogManager(parent=self)
+ log_panel = log_mgr.create_log_panel()
+ layout.addWidget(log_panel)
+ # 之后所有日志/进度更新由 EventBus 自动驱动,无需手动操作
+ """
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._log_text: QTextEdit = None
+ self._progress_bar: QProgressBar = None
+
+ # 订阅事件
+ global_event_bus.subscribe('LogMessage', self._on_log_message)
+ global_event_bus.subscribe('ProgressUpdate', self._on_progress_update)
+
+ # ═══════════════════════════════════════════════════════════
+ # 公开 API
+ # ═══════════════════════════════════════════════════════════
+
+ def create_log_panel(self) -> QWidget:
+ """创建并返回日志+进度面板的组装 Widget。
+
+ Returns:
+ QWidget: 包含日志区(QGroupBox)和进度条(QGroupBox)的垂直布局容器
+ """
+ from src.gui.styles import ModernStylesheet
+
+ container = QWidget()
+ layout = QVBoxLayout()
+ layout.setContentsMargins(0, 0, 0, 0)
+ layout.setSpacing(10)
+
+ # ── 日志区 ──
+ log_group = QGroupBox("执行日志")
+ log_group.setStyleSheet(f"""
+ QGroupBox {{
+ background-color: {ModernStylesheet.COLORS['panel_bg']};
+ border: 1px solid {ModernStylesheet.COLORS['border_light']};
+ border-radius: 5px; margin-top: 8px; padding-top: 15px;
+ padding-left: 9px; padding-right: 9px; padding-bottom: 9px;
+ }}
+ QGroupBox::title {{
+ subcontrol-origin: margin; subcontrol-position: top left;
+ padding: 0 5px; font-weight: bold;
+ color: {ModernStylesheet.COLORS['text_primary']};
+ }}
+ """)
+ log_layout = QVBoxLayout()
+ log_layout.setContentsMargins(5, 5, 5, 5)
+
+ self._log_text = QTextEdit()
+ self._log_text.setReadOnly(True)
+ self._log_text.setMaximumHeight(200)
+ self._log_text.setStyleSheet(f"""
+ QTextEdit {{
+ background-color: {ModernStylesheet.COLORS['panel_bg']};
+ color: {ModernStylesheet.COLORS['text_primary']};
+ border: 1px solid {ModernStylesheet.COLORS['border']};
+ border-radius: 4px; padding: 5px;
+ font-family: 'Courier New', monospace; font-size: 10px;
+ }}
+ """)
+ log_layout.addWidget(self._log_text)
+
+ clear_btn = QPushButton("清空日志")
+ clear_btn.setMaximumWidth(100)
+ clear_btn.setStyleSheet(ModernStylesheet.get_button_stylesheet('normal'))
+ clear_btn.clicked.connect(self.clear_log)
+
+ btn_row = QHBoxLayout()
+ btn_row.addWidget(clear_btn)
+ btn_row.addStretch()
+ log_layout.addLayout(btn_row)
+
+ log_group.setLayout(log_layout)
+ layout.addWidget(log_group, 1)
+
+ # ── 进度条 ──
+ progress_group = QGroupBox("执行进度")
+ progress_group.setStyleSheet(f"""
+ QGroupBox {{
+ background-color: {ModernStylesheet.COLORS['panel_bg']};
+ border: 1px solid {ModernStylesheet.COLORS['border_light']};
+ border-radius: 5px; margin-top: 8px; padding-top: 10px;
+ padding-left: 9px; padding-right: 9px; padding-bottom: 9px;
+ }}
+ QGroupBox::title {{
+ subcontrol-origin: margin; subcontrol-position: top left;
+ padding: 0 5px; font-weight: bold;
+ color: {ModernStylesheet.COLORS['text_primary']};
+ }}
+ """)
+ progress_layout = QVBoxLayout()
+ progress_layout.setContentsMargins(5, 5, 5, 5)
+
+ self._progress_bar = QProgressBar()
+ self._progress_bar.setValue(0)
+ self._progress_bar.setStyleSheet(f"""
+ QProgressBar {{
+ background-color: {ModernStylesheet.COLORS['panel_bg']};
+ border: 1px solid {ModernStylesheet.COLORS['border']};
+ border-radius: 4px; padding: 2px; text-align: center; height: 20px;
+ }}
+ QProgressBar::chunk {{
+ background-color: {ModernStylesheet.COLORS['success']}; border-radius: 3px;
+ }}
+ """)
+ progress_layout.addWidget(self._progress_bar)
+ progress_group.setLayout(progress_layout)
+ layout.addWidget(progress_group, 0)
+
+ container.setLayout(layout)
+ return container
+
+ def clear_log(self):
+ """清空日志区。"""
+ if self._log_text is not None:
+ self._log_text.clear()
+
+ @property
+ def progress_bar(self) -> QProgressBar:
+ return self._progress_bar
+
+ @property
+ def log_text(self) -> QTextEdit:
+ return self._log_text
+
+ # ═══════════════════════════════════════════════════════════
+ # EventBus 订阅回调
+ # ═══════════════════════════════════════════════════════════
+
+ def _on_log_message(self, data: dict):
+ """LogMessage 事件回调:写入日志区。"""
+ if self._log_text is None:
+ return
+ message = data.get('message', '')
+ level = data.get('level', 'info')
+ timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ color_map = {'error': 'red', 'warning': 'orange'}
+ color = color_map.get(level, 'black')
+ formatted = f'[{timestamp}] {message}'
+ self._log_text.append(formatted)
+ cursor = self._log_text.textCursor()
+ cursor.movePosition(QTextCursor.End)
+ self._log_text.setTextCursor(cursor)
+
+ def _on_progress_update(self, data: dict):
+ """ProgressUpdate 事件回调:更新进度条。"""
+ if self._progress_bar is None:
+ return
+ percentage = data.get('percentage', 0)
+ self._progress_bar.setValue(percentage)
diff --git a/src/gui/core/training_mode_manager.py b/src/gui/core/training_mode_manager.py
new file mode 100644
index 0000000..4905093
--- /dev/null
+++ b/src/gui/core/training_mode_manager.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+训练模式管理器
+
+管理"有训练数据模式"的勾选状态。
+切换状态时通过 global_event_bus 发布 TrainingModeChanged 事件,
+让需要响应的面板(如机器学习训练面板)自行订阅并刷新。
+
+发布的事件:
+ TrainingModeChanged → {training_mode: bool} 面板订阅:根据模式启用/禁用控件
+"""
+
+from PyQt5.QtCore import QObject
+
+from src.gui.core.event_bus import global_event_bus
+
+
+class TrainingModeManager(QObject):
+ """训练模式管理器。
+
+ 用法::
+
+ mode_mgr = TrainingModeManager(parent=self)
+ mode_mgr.toggle(True) # 切换到有训练数据模式
+ mode_mgr.toggle(False) # 切换到无训练数据模式
+ is_training = mode_mgr.is_training_mode
+ """
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._training_mode: bool = True # 默认:有训练数据
+
+ # ═══════════════════════════════════════════════════════════
+ # 公开 API
+ # ═══════════════════════════════════════════════════════════
+
+ @property
+ def is_training_mode(self) -> bool:
+ return self._training_mode
+
+ def toggle(self, checked: bool):
+ """切换训练模式。
+
+ Args:
+ checked: True=有训练数据模式, False=无训练数据模式
+ """
+ self._training_mode = checked
+
+ global_event_bus.publish('LogMessage', {
+ 'message': f'切换到{"有训练数据" if checked else "无训练数据"}模式',
+ 'level': 'info',
+ })
+
+ global_event_bus.publish('TrainingModeChanged', {
+ 'training_mode': checked,
+ })
+
+ def get_action_text(self, checked: bool) -> str:
+ """返回菜单 Action 的显示文本。
+
+ Args:
+ checked: 当前勾选状态
+
+ Returns:
+ "有训练数据模式" 或 "无训练数据模式"
+ """
+ return "有训练数据模式" if checked else "无训练数据模式"
diff --git a/src/gui/water_quality_gui_v2.py b/src/gui/water_quality_gui_v2.py
index 2ffc76b..5459c22 100644
--- a/src/gui/water_quality_gui_v2.py
+++ b/src/gui/water_quality_gui_v2.py
@@ -10,12 +10,10 @@ WaterQualityGUI 只负责窗口框架(标题栏、菜单栏、状态栏、QTab
- PanelFactory → 面板懒加载与生命周期
- PipelineExecutor → Pipeline 执行/停止/回调(通过 EventBus 发布状态)
- WorkspaceInitializer → 工作目录选择 + 自动回填(通过 EventBus 广播)
-
-待实现的 Manager(下一批):
-- ConfigManager → 配置读写
-- LogManager → 日志 + 进度条
-- TrainingModeManager → 训练模式切换
-- DialogService → 各类对话框
+- LogManager → 日志区 + 进度条(内部订阅 LogMessage/ProgressUpdate)
+- ConfigManager → 配置读写(new/load/save/get_current_config)
+- DialogService → 纯展示类弹窗(Pipeline状态/关于/AI设置)
+- TrainingModeManager → 训练模式切换(发布 TrainingModeChanged 事件)
"""
import osgeo # noqa: F401
@@ -118,6 +116,10 @@ class WaterQualityGUI(QMainWindow):
from src.gui.core.panel_registry import PANEL_REGISTRY
from src.gui.core.workspace_initializer import WorkspaceInitializer
from src.gui.core.pipeline_executor import PipelineExecutor
+ from src.gui.core.log_manager import LogManager
+ from src.gui.core.config_manager import ConfigManager
+ from src.gui.core.dialog_service import DialogService
+ from src.gui.core.training_mode_manager import TrainingModeManager
from src.gui.core.event_bus import global_event_bus
self._panel_factory = PanelFactory(
@@ -137,6 +139,17 @@ class WaterQualityGUI(QMainWindow):
parent=self,
)
+ self._log_manager = LogManager(parent=self)
+
+ self._config_manager = ConfigManager(
+ panel_factory=self._panel_factory,
+ parent=self,
+ )
+
+ self._dialog_service = DialogService(parent=self)
+
+ self._training_mode_manager = TrainingModeManager(parent=self)
+
self._event_bus = global_event_bus
# ================================================================
@@ -283,8 +296,7 @@ class WaterQualityGUI(QMainWindow):
self._tab_widget.currentChanged.connect(self._on_tab_changed)
right_layout.addWidget(self._tab_widget, 3)
- right_layout.addWidget(self._create_log_panel(), 1)
- right_layout.addWidget(self._create_progress_panel(), 0)
+ right_layout.addWidget(self._log_manager.create_log_panel(), 1)
right_widget.setLayout(right_layout)
main_layout.addWidget(right_widget, 4)
@@ -365,89 +377,6 @@ class WaterQualityGUI(QMainWindow):
)
return nav_widget
- def _create_log_panel(self):
- from src.gui.styles import ModernStylesheet
-
- log_group = QGroupBox("执行日志")
- log_group.setStyleSheet(f"""
- QGroupBox {{
- background-color: {ModernStylesheet.COLORS['panel_bg']};
- border: 1px solid {ModernStylesheet.COLORS['border_light']};
- border-radius: 5px; margin-top: 8px; padding-top: 15px;
- padding-left: 9px; padding-right: 9px; padding-bottom: 9px;
- }}
- QGroupBox::title {{
- subcontrol-origin: margin; subcontrol-position: top left;
- padding: 0 5px; font-weight: bold;
- color: {ModernStylesheet.COLORS['text_primary']};
- }}
- """)
- log_layout = QVBoxLayout()
- log_layout.setContentsMargins(5, 5, 5, 5)
-
- self._log_text = QTextEdit()
- self._log_text.setReadOnly(True)
- self._log_text.setMaximumHeight(200)
- self._log_text.setStyleSheet(f"""
- QTextEdit {{
- background-color: {ModernStylesheet.COLORS['panel_bg']};
- color: {ModernStylesheet.COLORS['text_primary']};
- border: 1px solid {ModernStylesheet.COLORS['border']};
- border-radius: 4px; padding: 5px;
- font-family: 'Courier New', monospace; font-size: 10px;
- }}
- """)
- log_layout.addWidget(self._log_text)
-
- clear_btn = QPushButton("清空日志")
- clear_btn.setMaximumWidth(100)
- clear_btn.setStyleSheet(ModernStylesheet.get_button_stylesheet('normal'))
- clear_btn.clicked.connect(self._log_text.clear)
-
- btn_row = QHBoxLayout()
- btn_row.addWidget(clear_btn)
- btn_row.addStretch()
- log_layout.addLayout(btn_row)
-
- log_group.setLayout(log_layout)
- return log_group
-
- def _create_progress_panel(self):
- from src.gui.styles import ModernStylesheet
-
- progress_group = QGroupBox("执行进度")
- progress_group.setStyleSheet(f"""
- QGroupBox {{
- background-color: {ModernStylesheet.COLORS['panel_bg']};
- border: 1px solid {ModernStylesheet.COLORS['border_light']};
- border-radius: 5px; margin-top: 8px; padding-top: 10px;
- padding-left: 9px; padding-right: 9px; padding-bottom: 9px;
- }}
- QGroupBox::title {{
- subcontrol-origin: margin; subcontrol-position: top left;
- padding: 0 5px; font-weight: bold;
- color: {ModernStylesheet.COLORS['text_primary']};
- }}
- """)
- progress_layout = QVBoxLayout()
- progress_layout.setContentsMargins(5, 5, 5, 5)
-
- self._progress_bar = QProgressBar()
- self._progress_bar.setValue(0)
- self._progress_bar.setStyleSheet(f"""
- QProgressBar {{
- background-color: {ModernStylesheet.COLORS['panel_bg']};
- border: 1px solid {ModernStylesheet.COLORS['border']};
- border-radius: 4px; padding: 2px; text-align: center; height: 20px;
- }}
- QProgressBar::chunk {{
- background-color: {ModernStylesheet.COLORS['success']}; border-radius: 3px;
- }}
- """)
- progress_layout.addWidget(self._progress_bar)
- progress_group.setLayout(progress_layout)
- return progress_group
-
# ================================================================
# EventBus 连线(UI 状态由事件驱动)
# ================================================================
@@ -456,15 +385,13 @@ class WaterQualityGUI(QMainWindow):
self._event_bus.subscribe('PipelineStarted', self._on_pipeline_started)
self._event_bus.subscribe('PipelineFinished', self._on_pipeline_finished)
self._event_bus.subscribe('PipelineStopped', self._on_pipeline_stopped)
- self._event_bus.subscribe('LogMessage', self._on_log_message)
- self._event_bus.subscribe('ProgressUpdate', self._on_progress_update)
self._event_bus.subscribe('NavigateToTab', self._on_navigate_to_tab)
self._event_bus.subscribe('WorkspaceChanged', self._on_workspace_changed)
def _on_pipeline_started(self, data):
self._run_all_btn.setEnabled(False)
self._stop_btn.setEnabled(True)
- self._progress_bar.setValue(0)
+ self._log_manager.progress_bar.setValue(0)
def _on_pipeline_finished(self, data):
self._run_all_btn.setEnabled(True)
@@ -472,7 +399,7 @@ class WaterQualityGUI(QMainWindow):
success = data.get('success', False)
message = data.get('message', '')
if success:
- self._progress_bar.setValue(100)
+ self._log_manager.progress_bar.setValue(100)
QMessageBox.information(self, "完成", "流程执行成功!\n\n请查看工作目录中的结果文件。")
else:
QMessageBox.critical(self, "失败", f"流程执行失败:\n\n{message[:200]}")
@@ -481,24 +408,6 @@ class WaterQualityGUI(QMainWindow):
self._run_all_btn.setEnabled(True)
self._stop_btn.setEnabled(False)
- def _on_log_message(self, data):
- message = data.get('message', '')
- level = data.get('level', 'info')
- timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
- color_map = {'error': 'red', 'warning': 'orange'}
- color = color_map.get(level, 'black')
- formatted = f'[{timestamp}] {message}'
- self._log_text.append(formatted)
- cursor = self._log_text.textCursor()
- cursor.movePosition(QTextCursor.End)
- self._log_text.setTextCursor(cursor)
-
- def _on_progress_update(self, data):
- percentage = data.get('percentage', 0)
- message = data.get('message', '')
- self._progress_bar.setValue(percentage)
- self.statusBar().showMessage(message)
-
def _on_navigate_to_tab(self, data):
tab_index = data.get('tab_index', 0)
if 0 <= tab_index < self._tab_widget.count():
@@ -593,56 +502,32 @@ class WaterQualityGUI(QMainWindow):
cb.wheelEvent = lambda e, c=cb: None
# ================================================================
- # 菜单回调(待提取到对应 Manager 的临时占位)
+ # 菜单回调(全部委托给对应 Manager)
# ================================================================
def _on_new_config(self):
- reply = QMessageBox.question(self, "新建配置", "是否清空当前配置?",
- QMessageBox.Yes | QMessageBox.No)
- if reply == QMessageBox.Yes:
- self._event_bus.publish('LogMessage', {'message': '已清空配置', 'level': 'info'})
+ self._config_manager.new_config()
def _on_load_config(self):
- file_path, _ = QFileDialog.getOpenFileName(
- self, "加载配置", "", "JSON Files (*.json);;All Files (*.*)")
- if file_path:
- self._event_bus.publish('LogMessage',
- {'message': f'已加载配置: {file_path}', 'level': 'info'})
+ self._config_manager.load_config()
def _on_save_config(self):
- file_path, _ = QFileDialog.getSaveFileName(
- self, "保存配置", "config.json", "JSON Files (*.json);;All Files (*.*)")
- if file_path:
- self._event_bus.publish('LogMessage',
- {'message': f'已保存配置: {file_path}', 'level': 'info'})
+ self._config_manager.save_config()
def _on_ai_settings(self):
- from src.gui.dialogs import AISettingsDialog
- AISettingsDialog(self).exec_()
+ self._dialog_service.show_ai_settings()
def _on_toggle_training_mode(self, checked):
- self._event_bus.publish('LogMessage', {
- 'message': f'切换到{"有训练数据" if checked else "无训练数据"}模式',
- 'level': 'info',
- })
- self._training_mode_action.setText("有训练数据模式" if checked else "无训练数据模式")
+ self._training_mode_manager.toggle(checked)
+ self._training_mode_action.setText(
+ self._training_mode_manager.get_action_text(checked)
+ )
def _on_show_pipeline_status(self):
- from src.gui.core.worker_thread import PIPELINE_AVAILABLE, PIPELINE_ERROR_INFO
- if PIPELINE_AVAILABLE:
- QMessageBox.information(self, "Pipeline状态", "Pipeline模块: 正常加载")
- else:
- detail = "\n".join(PIPELINE_ERROR_INFO)
- QMessageBox.warning(self, "Pipeline状态", f"Pipeline模块无法加载\n\n{detail}")
+ self._dialog_service.show_pipeline_status()
def _on_show_about(self):
- QMessageBox.about(self, "关于",
- "MegaCube-Water Quality V1.2\n\n"
- "一个完整的水质参数反演工作流程工具\n\n"
- "公司:北京依锐思遥感技术有限公司\n"
- "地址:北京市海淀区清河安宁庄东路18号5号楼二层205\n"
- "电话:010-51292601\n"
- "邮箱:hanshanlong@iris-rs.cn")
+ self._dialog_service.show_about()
# ================================================================
# 向后兼容属性
@@ -675,11 +560,7 @@ class WaterQualityGUI(QMainWindow):
self._event_bus.publish('ProgressUpdate', {'percentage': percentage, 'message': message})
def get_current_config(self):
- config = {}
- for step_id, panel in self._panel_factory.get_loaded_panels().items():
- if hasattr(panel, 'get_config'):
- config[step_id] = panel.get_config()
- return config
+ return self._config_manager.get_current_config()
# ============================================================