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() # ============================================================