1.2.1
This commit is contained in:
@ -67,8 +67,7 @@ docs = [
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
water-quality-gui = "gui.water_quality_gui:main"
|
||||
water-quality-pipeline = "core.water_quality_inversion_pipeline:main"
|
||||
water-quality-gui = "src.gui.water_quality_gui_v2:main"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/waterquality/water-quality-inversion"
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
chcp 65001 >nul
|
||||
echo.
|
||||
echo ================================================
|
||||
echo 水质参数反演分析系统 - 打包工具
|
||||
echo 水质参数反演分析系统 - 打包工具 (V2)
|
||||
echo ================================================
|
||||
echo.
|
||||
|
||||
:: 检查是否在正确目录
|
||||
if not exist "src\gui\water_quality_gui.py" (
|
||||
:: 检查是否在正确目录 (V2 入口)
|
||||
if not exist "src\gui\water_quality_gui_v2.py" (
|
||||
echo [错误] 请在项目根目录下运行此脚本!
|
||||
pause
|
||||
exit /b 1
|
||||
@ -22,19 +22,14 @@ python -m pip install -r requirements.txt --quiet
|
||||
python -m pip install pyinstaller --quiet
|
||||
|
||||
echo [3/4] 开始打包(首次可能需要 5-15 分钟,请耐心等待)...
|
||||
pyinstaller --clean scripts/water_quality_gui.spec
|
||||
|
||||
echo.
|
||||
echo [打包提示] 如果仍然出现 "No module named 'styles'",请检查:
|
||||
echo 1. dist\water_quality_gui\styles.py 是否存在
|
||||
echo 2. 是否需要添加 --collect-all styles 参数
|
||||
pyinstaller --clean water_quality_app.spec
|
||||
|
||||
echo.
|
||||
echo [4/4] 打包完成!
|
||||
echo.
|
||||
echo 输出位置:
|
||||
echo dist\water_quality_gui\water_quality_gui.exe
|
||||
echo dist\water_quality_gui\_internal\
|
||||
echo dist\MegaWater\MegaWater.exe
|
||||
echo dist\MegaWater\_internal\
|
||||
echo.
|
||||
echo 建议:
|
||||
echo 1. 将 dist 文件夹整个复制给用户(包含所有依赖)
|
||||
|
||||
2
setup.py
2
setup.py
@ -68,7 +68,7 @@ setup(
|
||||
},
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"water-quality-gui=gui.water_quality_gui:main",
|
||||
"water-quality-gui=src.gui.water_quality_gui_v2:main",
|
||||
"water-quality-pipeline=core.water_quality_inversion_pipeline:main",
|
||||
],
|
||||
},
|
||||
|
||||
@ -266,6 +266,13 @@ class PanelFactory:
|
||||
# ★ 重新禁用滚轮:update_from_config 可能动态创建了新的 SpinBox/ComboBox
|
||||
_disable_wheel_for_widget(panel)
|
||||
|
||||
def replay_live_panel_inputs(self):
|
||||
"""公开方法:重播所有已加载面板的输入值(供主窗口切页时调用)。
|
||||
|
||||
内部委托给 _replay_live_panel_inputs。
|
||||
"""
|
||||
self._replay_live_panel_inputs()
|
||||
|
||||
def _replay_live_panel_inputs(self):
|
||||
"""遍历 PANEL_REGISTRY 依赖声明,从已加载面板实时读取属性值并强制广播。
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -16,9 +16,6 @@ WaterQualityGUI 只负责窗口框架(标题栏、菜单栏、状态栏、QTab
|
||||
- TrainingModeManager → 训练模式切换(发布 TrainingModeChanged 事件)
|
||||
"""
|
||||
|
||||
import osgeo # noqa: F401
|
||||
from osgeo import gdal, ogr # noqa: F401
|
||||
|
||||
import os
|
||||
import sys
|
||||
import ctypes
|
||||
@ -450,7 +447,9 @@ class WaterQualityGUI(QMainWindow):
|
||||
self._log_manager.progress_bar.setValue(100)
|
||||
QMessageBox.information(self, "完成", "流程执行成功!\n\n请查看工作目录中的结果文件。")
|
||||
else:
|
||||
QMessageBox.critical(self, "失败", f"流程执行失败:\n\n{message[:200]}")
|
||||
QMessageBox.critical(self, "失败",
|
||||
f"流程执行失败:\n\n{message[:600]}"
|
||||
+ ("\n...(已截断)" if len(message) > 600 else ""))
|
||||
|
||||
def _on_pipeline_stopped(self, data):
|
||||
self._stop_btn.setEnabled(False)
|
||||
@ -464,18 +463,20 @@ class WaterQualityGUI(QMainWindow):
|
||||
work_dir = data.get('work_dir', '')
|
||||
self.statusBar().showMessage(f"工作目录: {work_dir}")
|
||||
|
||||
# 遍历已加载面板,调用 update_from_config 重建内存级参数和默认输出路径
|
||||
for step_id, panel in self._panel_factory.get_loaded_panels().items():
|
||||
if not hasattr(panel, 'update_from_config'):
|
||||
continue
|
||||
try:
|
||||
panel.update_from_config(work_dir=work_dir, pipeline=None)
|
||||
except Exception as e:
|
||||
# 2026-06-30:不再静默吞异常,至少通过 EventBus 输出日志
|
||||
self._event_bus.publish('LogMessage', {
|
||||
'message': f'[警告] 面板 {step_id} 的 update_from_config 失败: {e}',
|
||||
'level': 'warning',
|
||||
})
|
||||
# 让当前激活的 Tab 立即基于新目录重算默认路径,覆盖掉旧值,绕过非空保护
|
||||
from src.gui.core.panel_registry import PANEL_REGISTRY
|
||||
active_idx = self._tab_widget.currentIndex()
|
||||
if 0 <= active_idx < len(PANEL_REGISTRY):
|
||||
active_step_id = PANEL_REGISTRY[active_idx]['step_id']
|
||||
panel = self._panel_factory.get_panel(active_step_id)
|
||||
if panel is not None and hasattr(panel, 'update_from_config'):
|
||||
try:
|
||||
panel.update_from_config(
|
||||
work_dir=self._workspace_initializer.work_dir,
|
||||
pipeline=None
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ================================================================
|
||||
# 导航 → Tab 单向路由(左侧 List 驱动右侧 Tab,Tab 头部已隐藏)
|
||||
@ -528,21 +529,16 @@ class WaterQualityGUI(QMainWindow):
|
||||
"如果当前有流程正在运行,请勿修改此页面的参数。"
|
||||
)
|
||||
|
||||
# 【新增修复】:在跳之前,再核实一遍要去的 tab_index 和 item_data 的 step_id 是否吻合!
|
||||
# 如果因为删除了死文件导致索引错位,这里强行用真实 step_id 再找一遍!
|
||||
# 【防御】:用已加载 panel 实例反查真实 Tab 索引,防止索引错位
|
||||
try:
|
||||
self._panel_factory.get_panel(item_data)
|
||||
# 遍历右侧所有已加载的 Tab,找到属于当前 step_id 的那个正确的 Tab 索引
|
||||
for i in range(self._tab_widget.count()):
|
||||
scroll_area = self._tab_widget.widget(i)
|
||||
if scroll_area and hasattr(scroll_area, 'widget'):
|
||||
real_panel = scroll_area.widget()
|
||||
# 如果这个 panel 就是我们要找的 panel,强制更新 tab_index
|
||||
if real_panel == self._panel_factory.get_panel(item_data):
|
||||
if scroll_area.widget() == panel:
|
||||
tab_index = i
|
||||
break
|
||||
except Exception as e:
|
||||
self._log_manager.error(f"页面实例化失败: {str(e)}")
|
||||
self._log_manager.error(f"页面索引校验失败: {str(e)}")
|
||||
return
|
||||
|
||||
# 2. 强制使用注册表的固定索引进行跳转
|
||||
@ -551,7 +547,7 @@ class WaterQualityGUI(QMainWindow):
|
||||
# 【新增修复】:每次切页时,强制重播所有面板的输入值!
|
||||
# 原理:打破 preload_window 造成的时序差,确保目标页面能拿到源页面最新填写的值
|
||||
if hasattr(self, '_panel_factory'):
|
||||
self._panel_factory._replay_live_panel_inputs()
|
||||
self._panel_factory.replay_live_panel_inputs()
|
||||
|
||||
# 3. 防撕裂回弹机制:如果由于某种原因没跳过去,把左边的蓝条强行拽回当前真实页面
|
||||
if self._tab_widget.currentIndex() != tab_index:
|
||||
@ -695,10 +691,6 @@ class WaterQualityGUI(QMainWindow):
|
||||
def work_dir(self, value):
|
||||
self._workspace_initializer.work_dir = value
|
||||
|
||||
@property
|
||||
def pipeline(self):
|
||||
return None
|
||||
|
||||
@property
|
||||
def worker(self):
|
||||
return self._pipeline_executor.worker
|
||||
@ -718,6 +710,22 @@ def main():
|
||||
if multiprocessing.current_process().name != 'MainProcess':
|
||||
sys.exit(0)
|
||||
|
||||
# ★ GDAL 依赖检查(移至此处以利用 global_exception_handler 展示友好弹窗)
|
||||
try:
|
||||
import osgeo # noqa: F401
|
||||
from osgeo import gdal, ogr # noqa: F401
|
||||
except ImportError:
|
||||
from PyQt5.QtWidgets import QMessageBox, QApplication as _QA
|
||||
_qa = _QA.instance() or _QA(sys.argv)
|
||||
QMessageBox.critical(
|
||||
None, "依赖缺失",
|
||||
"GDAL (osgeo) 未安装,程序无法运行。\n\n"
|
||||
"请运行以下命令安装:\n"
|
||||
" conda install -c conda-forge gdal\n"
|
||||
"或访问 https://gdal.org/download.html"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
app = QApplication.instance()
|
||||
if not app:
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
Reference in New Issue
Block a user