refactor: 实现第二批 Manager(LogManager/ConfigManager/DialogService/TrainingModeManager)
- log_manager.py: 日志区+进度条+清空按钮封装,内部订阅 LogMessage/ProgressUpdate
- config_manager.py: 配置读写(new/load/save/get_current_config),懒加载安全(未加载面板返回 {})
- dialog_service.py: 纯展示弹窗封装(Pipeline状态/关于/AI设置)
- training_mode_manager.py: 训练模式切换,发布 TrainingModeChanged 事件
- water_quality_gui_v2.py: 725→605 行,菜单回调全部委托给 Manager,移除 _create_log_panel/_create_progress_panel/_on_log_message/_on_progress_update
This commit is contained in:
@ -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'<span style="color: {color};">[{timestamp}] {message}</span>'
|
||||
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()
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
Reference in New Issue
Block a user