界面优化
This commit is contained in:
BIN
data/icons/Mega Water 1.0.jpg
Normal file
BIN
data/icons/Mega Water 1.0.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@ -989,7 +989,12 @@ class WaterQualityInversionPipeline:
|
|||||||
if not GDAL_AVAILABLE:
|
if not GDAL_AVAILABLE:
|
||||||
raise ImportError("GDAL未安装,无法保存影像文件")
|
raise ImportError("GDAL未安装,无法保存影像文件")
|
||||||
|
|
||||||
height, width, n_bands = image_array.shape
|
# 兼容 (H,W) 和 (H,W,C) 两种 shape 格式
|
||||||
|
if image_array.ndim == 2:
|
||||||
|
height, width = image_array.shape
|
||||||
|
n_bands = 1
|
||||||
|
else:
|
||||||
|
height, width, n_bands = image_array.shape
|
||||||
|
|
||||||
# 获取驱动
|
# 获取驱动
|
||||||
driver = gdal.GetDriverByName('ENVI')
|
driver = gdal.GetDriverByName('ENVI')
|
||||||
@ -1100,11 +1105,8 @@ class WaterQualityInversionPipeline:
|
|||||||
Returns:
|
Returns:
|
||||||
numpy数组或None,1表示水域,0表示非水域
|
numpy数组或None,1表示水域,0表示非水域
|
||||||
"""
|
"""
|
||||||
# 获取图像尺寸
|
# 获取图像尺寸(统一从 shape 元组中提取前两个维度,兼容 (H,W)、(H,W,C)、(B,H,W) 等多种格式)
|
||||||
if isinstance(image_shape, np.ndarray):
|
img_height, img_width = image_shape[0], image_shape[1]
|
||||||
img_height, img_width = image_shape.shape[:2]
|
|
||||||
else:
|
|
||||||
img_height, img_width = image_shape
|
|
||||||
|
|
||||||
if water_mask is None:
|
if water_mask is None:
|
||||||
# 如果water_mask为None,使用步骤1生成的dat格式掩膜
|
# 如果water_mask为None,使用步骤1生成的dat格式掩膜
|
||||||
@ -1362,6 +1364,19 @@ class WaterQualityInversionPipeline:
|
|||||||
interpolated_bands.append(band_data)
|
interpolated_bands.append(band_data)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 兼容中文和各种格式
|
||||||
|
raw_interp = str(interpolation_method).lower()
|
||||||
|
if 'nearest' in raw_interp or '邻近' in raw_interp or '最邻近' in raw_interp:
|
||||||
|
interpolation_method = 'nearest'
|
||||||
|
elif 'bilinear' in raw_interp or '线性' in raw_interp or '双线性' in raw_interp:
|
||||||
|
interpolation_method = 'bilinear'
|
||||||
|
elif 'spline' in raw_interp or '样条' in raw_interp or 'rbf' in raw_interp:
|
||||||
|
interpolation_method = 'spline'
|
||||||
|
elif 'kriging' in raw_interp or '克里金' in raw_interp:
|
||||||
|
interpolation_method = 'kriging'
|
||||||
|
else:
|
||||||
|
interpolation_method = 'nearest'
|
||||||
|
|
||||||
# 对需要插值的像素进行插值
|
# 对需要插值的像素进行插值
|
||||||
if interpolation_method == 'nearest':
|
if interpolation_method == 'nearest':
|
||||||
# 邻近插值
|
# 邻近插值
|
||||||
@ -1591,6 +1606,18 @@ class WaterQualityInversionPipeline:
|
|||||||
|
|
||||||
step_start_time = time.time()
|
step_start_time = time.time()
|
||||||
try:
|
try:
|
||||||
|
# 兼容中文和各种格式
|
||||||
|
raw_method = str(method).lower()
|
||||||
|
if 'kutser' in raw_method:
|
||||||
|
method = 'kutser'
|
||||||
|
elif 'goodman' in raw_method:
|
||||||
|
method = 'goodman'
|
||||||
|
elif 'hedley' in raw_method:
|
||||||
|
method = 'hedley'
|
||||||
|
elif 'sugar' in raw_method:
|
||||||
|
method = 'sugar'
|
||||||
|
# 其余方法(subtract_nir, regression_slope, oxygen_absorption)保持原值
|
||||||
|
|
||||||
# 如果未启用,直接跳过处理并把原始影像路径作为后续流程输入
|
# 如果未启用,直接跳过处理并把原始影像路径作为后续流程输入
|
||||||
if not enabled:
|
if not enabled:
|
||||||
print("已设置跳过去除耀斑(enabled=False),将直接使用原始影像。")
|
print("已设置跳过去除耀斑(enabled=False),将直接使用原始影像。")
|
||||||
@ -1807,6 +1834,16 @@ class WaterQualityInversionPipeline:
|
|||||||
del corrected_bands
|
del corrected_bands
|
||||||
|
|
||||||
elif method == "sugar":
|
elif method == "sugar":
|
||||||
|
# 强行转换暗号,兼容中文和各种格式
|
||||||
|
raw_method = str(sugar_glint_mask_method).lower()
|
||||||
|
if 'cdf' in raw_method or '累积' in raw_method:
|
||||||
|
sugar_glint_mask_method = 'cdf'
|
||||||
|
elif 'otsu' in raw_method or '大津' in raw_method:
|
||||||
|
sugar_glint_mask_method = 'otsu'
|
||||||
|
else:
|
||||||
|
# 默认回退到 cdf 确保不崩溃
|
||||||
|
sugar_glint_mask_method = 'cdf'
|
||||||
|
|
||||||
print(f"使用方法: SUGAR (迭代次数={sugar_iter}, 掩膜方法={sugar_glint_mask_method})")
|
print(f"使用方法: SUGAR (迭代次数={sugar_iter}, 掩膜方法={sugar_glint_mask_method})")
|
||||||
|
|
||||||
# 确定输出路径
|
# 确定输出路径
|
||||||
@ -3603,6 +3640,29 @@ class WaterQualityInversionPipeline:
|
|||||||
Returns:
|
Returns:
|
||||||
预处理后的CSV文件路径
|
预处理后的CSV文件路径
|
||||||
"""
|
"""
|
||||||
|
# 兼容中文和各种格式
|
||||||
|
raw_p = str(preprocess_method).lower()
|
||||||
|
if raw_p == 'none' or '无' in raw_p or '跳过' in raw_p:
|
||||||
|
preprocess_method = 'None'
|
||||||
|
elif raw_p == 'mms' or 'minmax' in raw_p or '最大最小' in raw_p:
|
||||||
|
preprocess_method = 'MMS'
|
||||||
|
elif raw_p == 'ss' or '标准' in raw_p or '标准化' in raw_p:
|
||||||
|
preprocess_method = 'SS'
|
||||||
|
elif raw_p == 'snv' or '标准正态' in raw_p:
|
||||||
|
preprocess_method = 'SNV'
|
||||||
|
elif raw_p == 'ma' or '移动' in raw_p:
|
||||||
|
preprocess_method = 'MA'
|
||||||
|
elif raw_p == 'sg' or 'savitzky' in raw_p or '平滑' in raw_p:
|
||||||
|
preprocess_method = 'SG'
|
||||||
|
elif raw_p == 'msc' or '多元散射' in raw_p:
|
||||||
|
preprocess_method = 'MSC'
|
||||||
|
elif raw_p == 'd1' or 'd2' or 'dt' or '导数' in raw_p:
|
||||||
|
preprocess_method = {'d1': 'D1', 'd2': 'D2', 'dt': 'DT'}.get(raw_p, raw_p.upper())
|
||||||
|
elif raw_p == 'ct' or '去趋势' in raw_p:
|
||||||
|
preprocess_method = 'CT'
|
||||||
|
else:
|
||||||
|
preprocess_method = preprocess_method # 保持原值
|
||||||
|
|
||||||
# 如果不需要预处理,直接返回原文件
|
# 如果不需要预处理,直接返回原文件
|
||||||
if preprocess_method == 'None':
|
if preprocess_method == 'None':
|
||||||
return csv_path
|
return csv_path
|
||||||
|
|||||||
@ -195,13 +195,23 @@ class ReportGenerationPanel(QWidget):
|
|||||||
self.text_model_edit.setText(self.vision_model_edit.text())
|
self.text_model_edit.setText(self.vision_model_edit.text())
|
||||||
self.text_model_edit.blockSignals(False)
|
self.text_model_edit.blockSignals(False)
|
||||||
|
|
||||||
|
def _get_default_work_dir(self):
|
||||||
|
"""获取 work_dir,优先用主窗口缓存的 work_dir"""
|
||||||
|
if self.main_window and hasattr(self.main_window, 'work_dir') and self.main_window.work_dir:
|
||||||
|
return str(self.main_window.work_dir)
|
||||||
|
return ""
|
||||||
|
|
||||||
def browse_work_dir(self):
|
def browse_work_dir(self):
|
||||||
d = QFileDialog.getExistingDirectory(self, "选择工作目录")
|
default = self._get_default_work_dir()
|
||||||
|
d = QFileDialog.getExistingDirectory(self, "选择工作目录", default)
|
||||||
if d:
|
if d:
|
||||||
self.work_dir_edit.setText(d)
|
self.work_dir_edit.setText(d)
|
||||||
|
|
||||||
def browse_output_dir(self):
|
def browse_output_dir(self):
|
||||||
d = QFileDialog.getExistingDirectory(self, "选择报告输出目录")
|
default = self._get_default_work_dir()
|
||||||
|
if default:
|
||||||
|
default = os.path.join(default, "14_visualization")
|
||||||
|
d = QFileDialog.getExistingDirectory(self, "选择报告输出目录", default)
|
||||||
if d:
|
if d:
|
||||||
self.output_dir_edit.setText(d)
|
self.output_dir_edit.setText(d)
|
||||||
|
|
||||||
|
|||||||
@ -168,7 +168,10 @@ class Step4Panel(QWidget):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
rows_to_preview = max(1, self.preview_rows_spin.value())
|
rows_to_preview = max(1, self.preview_rows_spin.value())
|
||||||
df = pd.read_csv(csv_path, nrows=rows_to_preview)
|
# dtype=object 确保所有列以字符串读取,避免空值/混合类型导致 dtype 报错
|
||||||
|
df = pd.read_csv(csv_path, nrows=rows_to_preview, dtype=object)
|
||||||
|
# fillna 在 PandasTableModel.__init__ 中已执行,此处再次防御性处理
|
||||||
|
df = df.fillna('')
|
||||||
if df.empty:
|
if df.empty:
|
||||||
self.reset_preview("CSV文件为空")
|
self.reset_preview("CSV文件为空")
|
||||||
return
|
return
|
||||||
|
|||||||
@ -191,6 +191,15 @@ class Step5Panel(QWidget):
|
|||||||
else:
|
else:
|
||||||
self.output_file.set_path("")
|
self.output_file.set_path("")
|
||||||
|
|
||||||
|
# 5. 尝试从 Step4 界面读取已处理的水质参数 CSV 路径,自动填入本面板
|
||||||
|
main_window = self.window()
|
||||||
|
if main_window and hasattr(main_window, 'step4_panel'):
|
||||||
|
step4_output_path = main_window.step4_panel.output_file.get_path()
|
||||||
|
if step4_output_path:
|
||||||
|
existing_csv = self.csv_file.get_path()
|
||||||
|
if not existing_csv or not existing_csv.strip():
|
||||||
|
self.csv_file.set_path(step4_output_path)
|
||||||
|
|
||||||
def run_step(self):
|
def run_step(self):
|
||||||
"""独立运行步骤5"""
|
"""独立运行步骤5"""
|
||||||
# 验证输入
|
# 验证输入
|
||||||
|
|||||||
@ -211,9 +211,52 @@ class Step6_5Panel(QWidget):
|
|||||||
if 'csv_path' in config:
|
if 'csv_path' in config:
|
||||||
self.training_csv_file.set_path(config['csv_path'])
|
self.training_csv_file.set_path(config['csv_path'])
|
||||||
|
|
||||||
|
def update_from_config(self, work_dir=None, pipeline=None):
|
||||||
|
"""从全局配置自动填充训练数据和输出路径
|
||||||
|
|
||||||
|
Args:
|
||||||
|
work_dir: 工作目录路径
|
||||||
|
pipeline: Pipeline 实例(未使用,保留接口兼容性)
|
||||||
|
"""
|
||||||
|
if work_dir:
|
||||||
|
self.work_dir = work_dir
|
||||||
|
elif hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.work_dir = None
|
||||||
|
|
||||||
|
# 1. 尝试从 Step5 界面读取训练光谱 CSV 路径
|
||||||
|
main_window = self.window()
|
||||||
|
if main_window and hasattr(main_window, 'step5_panel'):
|
||||||
|
step5_output_path = main_window.step5_panel.output_file.get_path()
|
||||||
|
if step5_output_path:
|
||||||
|
existing = self.training_csv_file.get_path()
|
||||||
|
if not existing or not existing.strip():
|
||||||
|
self.training_csv_file.set_path(step5_output_path)
|
||||||
|
|
||||||
|
# 2. 自动填充输出目录(8_Regression_Modeling)
|
||||||
|
if self.work_dir:
|
||||||
|
output_dir = os.path.join(self.work_dir, "8_Regression_Modeling")
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
existing_out = self.output_dir.get_path()
|
||||||
|
if not existing_out or not existing_out.strip():
|
||||||
|
self.output_dir.set_path(output_dir)
|
||||||
|
|
||||||
|
def _get_default_work_dir(self):
|
||||||
|
"""获取 work_dir,优先用 panel 自身缓存的,否则尝试从主窗口取"""
|
||||||
|
if hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
return str(self.work_dir)
|
||||||
|
mw = self.window()
|
||||||
|
if mw and hasattr(mw, 'work_dir') and mw.work_dir:
|
||||||
|
return str(mw.work_dir)
|
||||||
|
return ""
|
||||||
|
|
||||||
def browse_output_dir(self):
|
def browse_output_dir(self):
|
||||||
"""浏览输出目录"""
|
"""浏览输出目录"""
|
||||||
dir_path = QFileDialog.getExistingDirectory(self, "选择输出模型目录", "")
|
default = self._get_default_work_dir()
|
||||||
|
if default:
|
||||||
|
default = os.path.join(default, "8_Regression_Modeling")
|
||||||
|
dir_path = QFileDialog.getExistingDirectory(self, "选择输出模型目录", default)
|
||||||
if dir_path:
|
if dir_path:
|
||||||
self.output_dir.set_path(dir_path)
|
self.output_dir.set_path(dir_path)
|
||||||
|
|
||||||
|
|||||||
@ -280,6 +280,37 @@ class Step6_75Panel(QWidget):
|
|||||||
if 'enabled' in config:
|
if 'enabled' in config:
|
||||||
self.enable_checkbox.setChecked(config['enabled'])
|
self.enable_checkbox.setChecked(config['enabled'])
|
||||||
|
|
||||||
|
def update_from_config(self, work_dir=None, pipeline=None):
|
||||||
|
"""从全局配置自动填充训练数据和输出路径
|
||||||
|
|
||||||
|
Args:
|
||||||
|
work_dir: 工作目录路径
|
||||||
|
pipeline: Pipeline 实例(未使用,保留接口兼容性)
|
||||||
|
"""
|
||||||
|
if work_dir:
|
||||||
|
self.work_dir = work_dir
|
||||||
|
elif hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.work_dir = None
|
||||||
|
|
||||||
|
# 1. 尝试从 Step5 界面读取训练光谱 CSV 路径
|
||||||
|
main_window = self.window()
|
||||||
|
if main_window and hasattr(main_window, 'step5_panel'):
|
||||||
|
step5_output_path = main_window.step5_panel.output_file.get_path()
|
||||||
|
if step5_output_path:
|
||||||
|
existing = self.csv_file.get_path()
|
||||||
|
if not existing or not existing.strip():
|
||||||
|
self.csv_file.set_path(step5_output_path)
|
||||||
|
|
||||||
|
# 2. 自动填充输出目录(9_Custom_Regression_Modeling)
|
||||||
|
if self.work_dir:
|
||||||
|
output_dir = os.path.join(self.work_dir, "9_Custom_Regression_Modeling")
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
existing_out = self.output_dir.text().strip()
|
||||||
|
if not existing_out:
|
||||||
|
self.output_dir.setText(output_dir)
|
||||||
|
|
||||||
def run_step(self):
|
def run_step(self):
|
||||||
"""独立运行步骤6.75"""
|
"""独立运行步骤6.75"""
|
||||||
csv_path = self.csv_file.get_path()
|
csv_path = self.csv_file.get_path()
|
||||||
|
|||||||
@ -42,15 +42,15 @@ class Step6Panel(QWidget):
|
|||||||
layout.addWidget(self.ml_page)
|
layout.addWidget(self.ml_page)
|
||||||
|
|
||||||
# 输出文件路径
|
# 输出文件路径
|
||||||
self.output_dir = FileSelectWidget(
|
self.output_path = FileSelectWidget(
|
||||||
"输出模型目录:",
|
"输出文件:",
|
||||||
"Directories;;All Files (*.*)"
|
"CSV Files (*.csv);;All Files (*.*)",
|
||||||
|
mode="save"
|
||||||
)
|
)
|
||||||
self.output_dir.line_edit.setPlaceholderText("models_output")
|
self.output_path.line_edit.setPlaceholderText("自动生成,或手动指定输出文件路径...")
|
||||||
# 修改浏览按钮为选择目录
|
self.output_path.browse_btn.clicked.disconnect()
|
||||||
self.output_dir.browse_btn.clicked.disconnect()
|
self.output_path.browse_btn.clicked.connect(self.browse_output_path)
|
||||||
self.output_dir.browse_btn.clicked.connect(self.browse_output_dir)
|
layout.addWidget(self.output_path)
|
||||||
layout.addWidget(self.output_dir)
|
|
||||||
|
|
||||||
# 启用步骤
|
# 启用步骤
|
||||||
self.enable_checkbox = QCheckBox("启用此步骤")
|
self.enable_checkbox = QCheckBox("启用此步骤")
|
||||||
@ -198,11 +198,38 @@ class Step6Panel(QWidget):
|
|||||||
for checkbox in checkboxes_dict.values():
|
for checkbox in checkboxes_dict.values():
|
||||||
checkbox.setChecked(checked)
|
checkbox.setChecked(checked)
|
||||||
|
|
||||||
def browse_output_dir(self):
|
def _get_default_work_dir(self):
|
||||||
"""浏览输出目录"""
|
"""获取 work_dir,优先用 panel 自身缓存的,否则尝试从主窗口取"""
|
||||||
dir_path = QFileDialog.getExistingDirectory(self, "选择输出模型目录", "")
|
if hasattr(self, 'work_dir') and self.work_dir:
|
||||||
if dir_path:
|
return str(self.work_dir)
|
||||||
self.output_dir.set_path(dir_path)
|
mw = self.window()
|
||||||
|
if mw and hasattr(mw, 'work_dir') and mw.work_dir:
|
||||||
|
return str(mw.work_dir)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def browse_output_path(self):
|
||||||
|
"""浏览输出文件路径(保存对话框)"""
|
||||||
|
current = self.output_path.get_path().strip()
|
||||||
|
if current:
|
||||||
|
initial_dir = os.path.dirname(current)
|
||||||
|
initial_file = os.path.basename(current)
|
||||||
|
else:
|
||||||
|
initial_dir = ""
|
||||||
|
initial_file = ""
|
||||||
|
|
||||||
|
if not initial_dir or not os.path.isdir(initial_dir):
|
||||||
|
# 默认定位到 indices 目录
|
||||||
|
work_dir = self._get_default_work_dir()
|
||||||
|
initial_dir = os.path.join(work_dir, "6_water_quality_indices") if work_dir else ""
|
||||||
|
if initial_dir and not os.path.isdir(initial_dir):
|
||||||
|
os.makedirs(initial_dir, exist_ok=True)
|
||||||
|
|
||||||
|
file_path, _ = QFileDialog.getSaveFileName(
|
||||||
|
self, "保存输出文件", os.path.join(initial_dir, initial_file) if initial_file else initial_dir,
|
||||||
|
"CSV Files (*.csv);;All Files (*.*)"
|
||||||
|
)
|
||||||
|
if file_path:
|
||||||
|
self.output_path.set_path(file_path)
|
||||||
|
|
||||||
def get_config(self):
|
def get_config(self):
|
||||||
"""获取配置"""
|
"""获取配置"""
|
||||||
@ -229,7 +256,9 @@ class Step6Panel(QWidget):
|
|||||||
training_csv_path = self.training_csv_file.get_path()
|
training_csv_path = self.training_csv_file.get_path()
|
||||||
if training_csv_path:
|
if training_csv_path:
|
||||||
config['training_csv_path'] = training_csv_path
|
config['training_csv_path'] = training_csv_path
|
||||||
# 注意:step6_train_models 不接受 output_dir 参数,输出目录由 pipeline 内部根据 work_dir 生成
|
output_path = self.output_path.get_path()
|
||||||
|
if output_path:
|
||||||
|
config['output_path'] = output_path
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def set_config(self, config):
|
def set_config(self, config):
|
||||||
@ -252,8 +281,8 @@ class Step6Panel(QWidget):
|
|||||||
checkbox.setChecked(method in methods)
|
checkbox.setChecked(method in methods)
|
||||||
if 'training_csv_path' in config:
|
if 'training_csv_path' in config:
|
||||||
self.training_csv_file.set_path(config['training_csv_path'])
|
self.training_csv_file.set_path(config['training_csv_path'])
|
||||||
if 'output_dir' in config:
|
if 'output_path' in config:
|
||||||
self.output_dir.set_path(config['output_dir'])
|
self.output_path.set_path(config['output_path'])
|
||||||
|
|
||||||
def update_from_config(self, work_dir=None, pipeline=None):
|
def update_from_config(self, work_dir=None, pipeline=None):
|
||||||
"""从全局配置自动填充训练数据和输出路径
|
"""从全局配置自动填充训练数据和输出路径
|
||||||
@ -288,13 +317,22 @@ class Step6Panel(QWidget):
|
|||||||
if step5_csv:
|
if step5_csv:
|
||||||
self.training_csv_file.set_path(step5_csv)
|
self.training_csv_file.set_path(step5_csv)
|
||||||
|
|
||||||
# 2. 自动填充输出目录(基于工作目录)
|
# 2. 自动填充输出文件路径(基于工作目录和输入文件名)
|
||||||
|
# 输入是 training_spectra.csv → 输出 {work_dir}/6_water_quality_indices/training_spectra_indices.csv
|
||||||
|
# 输入是 sampling_spectra.csv → 输出 {work_dir}/6_water_quality_indices/sampling_spectra_indices.csv
|
||||||
if self.work_dir:
|
if self.work_dir:
|
||||||
output_dir = os.path.join(self.work_dir, "7_Supervised_Model_Training")
|
indices_dir = os.path.join(self.work_dir, "6_water_quality_indices")
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
os.makedirs(indices_dir, exist_ok=True)
|
||||||
self.output_dir.set_path(output_dir.replace('\\', '/'))
|
training_csv = self.training_csv_file.get_path()
|
||||||
|
if training_csv:
|
||||||
|
basename = os.path.splitext(os.path.basename(training_csv))[0]
|
||||||
|
output_file = f"{basename}_indices.csv"
|
||||||
|
else:
|
||||||
|
output_file = "water_quality_indices.csv"
|
||||||
|
output_path = os.path.join(indices_dir, output_file).replace('\\', '/')
|
||||||
|
self.output_path.set_path(output_path)
|
||||||
else:
|
else:
|
||||||
self.output_dir.set_path("")
|
self.output_path.set_path("")
|
||||||
|
|
||||||
def run_step(self):
|
def run_step(self):
|
||||||
"""独立运行步骤6"""
|
"""独立运行步骤6"""
|
||||||
|
|||||||
@ -4,6 +4,9 @@
|
|||||||
Step8_5 面板 - 非经验模型预测
|
Step8_5 面板 - 非经验模型预测
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout,
|
QWidget, QVBoxLayout, QGroupBox, QFormLayout,
|
||||||
QPushButton, QCheckBox, QComboBox, QLineEdit, QMessageBox,
|
QPushButton, QCheckBox, QComboBox, QLineEdit, QMessageBox,
|
||||||
@ -79,15 +82,70 @@ class Step8_5Panel(QWidget):
|
|||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def update_from_config(self, work_dir=None, pipeline=None):
|
||||||
|
"""从全局配置自动填充采样光谱和回归模型目录
|
||||||
|
|
||||||
|
Args:
|
||||||
|
work_dir: 工作目录路径
|
||||||
|
pipeline: Pipeline 实例(未使用,保留接口兼容性)
|
||||||
|
"""
|
||||||
|
if work_dir:
|
||||||
|
self.work_dir = work_dir
|
||||||
|
elif hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.work_dir = None
|
||||||
|
|
||||||
|
main_window = self.window()
|
||||||
|
|
||||||
|
# 1. 尝试从 Step7 界面读取全湖采样点 CSV 路径
|
||||||
|
if main_window and hasattr(main_window, 'step7_panel'):
|
||||||
|
step7_output_path = main_window.step7_panel.output_file.get_path()
|
||||||
|
if step7_output_path:
|
||||||
|
existing = self.sampling_csv_file.get_path()
|
||||||
|
if not existing or not existing.strip():
|
||||||
|
self.sampling_csv_file.set_path(step7_output_path)
|
||||||
|
|
||||||
|
# 2. 尝试从 Step6.5 界面读取回归模型目录
|
||||||
|
if main_window and hasattr(main_window, 'step6_5_panel'):
|
||||||
|
step6_5_models_dir = main_window.step6_5_panel.output_dir.get_path()
|
||||||
|
if step6_5_models_dir:
|
||||||
|
existing_models = self.models_dir_file.get_path()
|
||||||
|
if not existing_models or not existing_models.strip():
|
||||||
|
self.models_dir_file.set_path(step6_5_models_dir)
|
||||||
|
|
||||||
|
# 3. 自动填充输出路径(非经验模型预测目录)
|
||||||
|
if self.work_dir:
|
||||||
|
output_dir = os.path.join(self.work_dir, "11_12_13_predictions/Non_Empirical_Prediction")
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
existing_out = self.output_file.get_path()
|
||||||
|
if not existing_out or not existing_out.strip():
|
||||||
|
self.output_file.set_path(output_dir)
|
||||||
|
|
||||||
|
def _get_default_work_dir(self):
|
||||||
|
"""获取 work_dir,优先用 panel 自身缓存的,否则尝试从主窗口取"""
|
||||||
|
if hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
return str(self.work_dir)
|
||||||
|
mw = self.window()
|
||||||
|
if mw and hasattr(mw, 'work_dir') and mw.work_dir:
|
||||||
|
return str(mw.work_dir)
|
||||||
|
return ""
|
||||||
|
|
||||||
def browse_models_dir(self):
|
def browse_models_dir(self):
|
||||||
"""浏览模型目录"""
|
"""浏览模型目录"""
|
||||||
dir_path = QFileDialog.getExistingDirectory(self, "选择模型目录", "")
|
default = self._get_default_work_dir()
|
||||||
|
if default:
|
||||||
|
default = os.path.join(default, "8_Regression_Modeling")
|
||||||
|
dir_path = QFileDialog.getExistingDirectory(self, "选择模型目录", default)
|
||||||
if dir_path:
|
if dir_path:
|
||||||
self.models_dir_file.set_path(dir_path)
|
self.models_dir_file.set_path(dir_path)
|
||||||
|
|
||||||
def browse_output_dir(self):
|
def browse_output_dir(self):
|
||||||
"""浏览输出目录"""
|
"""浏览输出目录"""
|
||||||
dir_path = QFileDialog.getExistingDirectory(self, "选择输出文件夹", "")
|
default = self._get_default_work_dir()
|
||||||
|
if default:
|
||||||
|
default = os.path.join(default, "11_12_13_predictions/Non_Empirical_Prediction")
|
||||||
|
dir_path = QFileDialog.getExistingDirectory(self, "选择输出文件夹", default)
|
||||||
if dir_path:
|
if dir_path:
|
||||||
self.output_file.set_path(dir_path)
|
self.output_file.set_path(dir_path)
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
Step8_75 面板 - 自定义回归预测
|
Step8_75 面板 - 自定义回归预测
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QWidget, QVBoxLayout, QGroupBox,
|
QWidget, QVBoxLayout, QGroupBox,
|
||||||
QPushButton, QCheckBox, QMessageBox, QFileDialog,
|
QPushButton, QCheckBox, QMessageBox, QFileDialog,
|
||||||
@ -73,15 +75,70 @@ class Step8_75Panel(QWidget):
|
|||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def update_from_config(self, work_dir=None, pipeline=None):
|
||||||
|
"""从全局配置自动填充采样光谱和自定义回归模型目录
|
||||||
|
|
||||||
|
Args:
|
||||||
|
work_dir: 工作目录路径
|
||||||
|
pipeline: Pipeline 实例(未使用,保留接口兼容性)
|
||||||
|
"""
|
||||||
|
if work_dir:
|
||||||
|
self.work_dir = work_dir
|
||||||
|
elif hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.work_dir = None
|
||||||
|
|
||||||
|
main_window = self.window()
|
||||||
|
|
||||||
|
# 1. 尝试从 Step7 界面读取全湖采样点 CSV 路径
|
||||||
|
if main_window and hasattr(main_window, 'step7_panel'):
|
||||||
|
step7_output_path = main_window.step7_panel.output_file.get_path()
|
||||||
|
if step7_output_path:
|
||||||
|
existing = self.sampling_csv_file.get_path()
|
||||||
|
if not existing or not existing.strip():
|
||||||
|
self.sampling_csv_file.set_path(step7_output_path)
|
||||||
|
|
||||||
|
# 2. 尝试从 Step6.75 界面读取自定义回归模型目录
|
||||||
|
if main_window and hasattr(main_window, 'step6_75_panel'):
|
||||||
|
step6_75_models_dir = main_window.step6_75_panel.output_dir.text().strip()
|
||||||
|
if step6_75_models_dir:
|
||||||
|
existing_models = self.regression_models_dir.get_path()
|
||||||
|
if not existing_models or not existing_models.strip():
|
||||||
|
self.regression_models_dir.set_path(step6_75_models_dir)
|
||||||
|
|
||||||
|
# 3. 自动填充输出目录(自定义回归预测目录)
|
||||||
|
if self.work_dir:
|
||||||
|
output_dir = os.path.join(self.work_dir, "11_12_13_predictions/Custom_Regression_Prediction")
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
existing_out = self.output_dir_widget.get_path()
|
||||||
|
if not existing_out or not existing_out.strip():
|
||||||
|
self.output_dir_widget.set_path(output_dir)
|
||||||
|
|
||||||
|
def _get_default_work_dir(self):
|
||||||
|
"""获取 work_dir,优先用 panel 自身缓存的,否则尝试从主窗口取"""
|
||||||
|
if hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
return str(self.work_dir)
|
||||||
|
mw = self.window()
|
||||||
|
if mw and hasattr(mw, 'work_dir') and mw.work_dir:
|
||||||
|
return str(mw.work_dir)
|
||||||
|
return ""
|
||||||
|
|
||||||
def browse_regression_models_dir(self):
|
def browse_regression_models_dir(self):
|
||||||
"""浏览回归模型目录"""
|
"""浏览回归模型目录"""
|
||||||
dir_path = QFileDialog.getExistingDirectory(self, "选择回归模型目录", "")
|
default = self._get_default_work_dir()
|
||||||
|
if default:
|
||||||
|
default = os.path.join(default, "9_Custom_Regression_Modeling")
|
||||||
|
dir_path = QFileDialog.getExistingDirectory(self, "选择回归模型目录", default)
|
||||||
if dir_path:
|
if dir_path:
|
||||||
self.regression_models_dir.set_path(dir_path)
|
self.regression_models_dir.set_path(dir_path)
|
||||||
|
|
||||||
def browse_output_dir(self):
|
def browse_output_dir(self):
|
||||||
"""浏览输出目录"""
|
"""浏览输出目录"""
|
||||||
dir_path = QFileDialog.getExistingDirectory(self, "选择输出目录", "")
|
default = self._get_default_work_dir()
|
||||||
|
if default:
|
||||||
|
default = os.path.join(default, "11_12_13_predictions/Custom_Regression_Prediction")
|
||||||
|
dir_path = QFileDialog.getExistingDirectory(self, "选择输出目录", default)
|
||||||
if dir_path:
|
if dir_path:
|
||||||
self.output_dir_widget.set_path(dir_path)
|
self.output_dir_widget.set_path(dir_path)
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,9 @@
|
|||||||
Step8 面板 - 机器学习预测
|
Step8 面板 - 机器学习预测
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout,
|
QWidget, QVBoxLayout, QGroupBox, QFormLayout,
|
||||||
QPushButton, QCheckBox, QComboBox, QLineEdit, QMessageBox,
|
QPushButton, QCheckBox, QComboBox, QLineEdit, QMessageBox,
|
||||||
@ -76,9 +79,61 @@ class Step8Panel(QWidget):
|
|||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def update_from_config(self, work_dir=None, pipeline=None):
|
||||||
|
"""从全局配置自动填充采样光谱和模型目录
|
||||||
|
|
||||||
|
Args:
|
||||||
|
work_dir: 工作目录路径
|
||||||
|
pipeline: Pipeline 实例(未使用,保留接口兼容性)
|
||||||
|
"""
|
||||||
|
if work_dir:
|
||||||
|
self.work_dir = work_dir
|
||||||
|
elif hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.work_dir = None
|
||||||
|
|
||||||
|
main_window = self.window()
|
||||||
|
|
||||||
|
# 1. 尝试从 Step7 界面读取全湖采样点 CSV 路径
|
||||||
|
if main_window and hasattr(main_window, 'step7_panel'):
|
||||||
|
step7_output_path = main_window.step7_panel.output_file.get_path()
|
||||||
|
if step7_output_path:
|
||||||
|
existing = self.sampling_csv_file.get_path()
|
||||||
|
if not existing or not existing.strip():
|
||||||
|
self.sampling_csv_file.set_path(step7_output_path)
|
||||||
|
|
||||||
|
# 2. 尝试从 Step6 界面读取监督模型目录
|
||||||
|
if main_window and hasattr(main_window, 'step6_panel'):
|
||||||
|
step6_models_dir = main_window.step6_panel.output_dir.get_path()
|
||||||
|
if step6_models_dir:
|
||||||
|
existing_models = self.models_dir_file.get_path()
|
||||||
|
if not existing_models or not existing_models.strip():
|
||||||
|
self.models_dir_file.set_path(step6_models_dir)
|
||||||
|
|
||||||
|
# 3. 自动填充输出路径(机器学习预测目录)
|
||||||
|
if self.work_dir:
|
||||||
|
output_dir = os.path.join(self.work_dir, "11_12_13_predictions/Machine_Learning_Prediction")
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
existing_out = self.output_file.get_path()
|
||||||
|
if not existing_out or not existing_out.strip():
|
||||||
|
self.output_file.set_path(output_dir)
|
||||||
|
|
||||||
|
def _get_default_work_dir(self):
|
||||||
|
"""获取 work_dir,优先用 panel 自身缓存的,否则尝试从主窗口取"""
|
||||||
|
if hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
return str(self.work_dir)
|
||||||
|
mw = self.window()
|
||||||
|
if mw and hasattr(mw, 'work_dir') and mw.work_dir:
|
||||||
|
return str(mw.work_dir)
|
||||||
|
return ""
|
||||||
|
|
||||||
def browse_models_dir(self):
|
def browse_models_dir(self):
|
||||||
"""浏览模型目录"""
|
"""浏览模型目录"""
|
||||||
dir_path = QFileDialog.getExistingDirectory(self, "选择模型目录", "")
|
default = self._get_default_work_dir()
|
||||||
|
if default:
|
||||||
|
default = os.path.join(default, "7_Supervised_Model_Training")
|
||||||
|
dir_path = QFileDialog.getExistingDirectory(self, "选择模型目录", default)
|
||||||
if dir_path:
|
if dir_path:
|
||||||
self.models_dir_file.set_path(dir_path)
|
self.models_dir_file.set_path(dir_path)
|
||||||
|
|
||||||
|
|||||||
@ -101,12 +101,9 @@ class Step9Panel(QWidget):
|
|||||||
mode_row = QHBoxLayout()
|
mode_row = QHBoxLayout()
|
||||||
self.mode_single_rb = QRadioButton("单个 CSV 文件")
|
self.mode_single_rb = QRadioButton("单个 CSV 文件")
|
||||||
self.mode_folder_rb = QRadioButton("文件夹批量")
|
self.mode_folder_rb = QRadioButton("文件夹批量")
|
||||||
self.mode_single_rb.setChecked(True)
|
|
||||||
self._mode_group = QButtonGroup(self)
|
self._mode_group = QButtonGroup(self)
|
||||||
self._mode_group.addButton(self.mode_single_rb, 0)
|
self._mode_group.addButton(self.mode_single_rb, 0)
|
||||||
self._mode_group.addButton(self.mode_folder_rb, 1)
|
self._mode_group.addButton(self.mode_folder_rb, 1)
|
||||||
self.mode_single_rb.toggled.connect(self._on_step9_mode_changed)
|
|
||||||
self.mode_folder_rb.toggled.connect(self._on_step9_mode_changed)
|
|
||||||
mode_row.addWidget(self.mode_single_rb)
|
mode_row.addWidget(self.mode_single_rb)
|
||||||
mode_row.addWidget(self.mode_folder_rb)
|
mode_row.addWidget(self.mode_folder_rb)
|
||||||
mode_row.addStretch()
|
mode_row.addStretch()
|
||||||
@ -192,16 +189,36 @@ class Step9Panel(QWidget):
|
|||||||
|
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
self._on_step9_mode_changed()
|
|
||||||
|
|
||||||
def _on_step9_mode_changed(self):
|
# 信号绑定与初始状态
|
||||||
|
self.mode_single_rb.toggled.connect(self._toggle_input_mode)
|
||||||
|
self.mode_folder_rb.toggled.connect(self._toggle_input_mode)
|
||||||
|
self.mode_single_rb.setChecked(True) # 默认选中"单个 CSV"
|
||||||
|
self._toggle_input_mode() # 根据默认值设置初始显示状态
|
||||||
|
|
||||||
|
def _toggle_input_mode(self):
|
||||||
|
"""槽函数:根据单选框状态动态显示/隐藏对应的输入组件。"""
|
||||||
folder_mode = self.mode_folder_rb.isChecked()
|
folder_mode = self.mode_folder_rb.isChecked()
|
||||||
self.prediction_csv_file.setEnabled(not folder_mode)
|
# 单个 CSV 模式:显示单文件选择,隐藏文件夹选择
|
||||||
self._folder_row_widget.setEnabled(folder_mode)
|
self.prediction_csv_file.setVisible(not folder_mode)
|
||||||
self.recursive_csv_cb.setEnabled(folder_mode)
|
# 文件夹批量模式:显示文件夹选择 + 递归选项,隐藏单文件选择
|
||||||
|
self._folder_row_widget.setVisible(folder_mode)
|
||||||
|
self.recursive_csv_cb.setVisible(folder_mode)
|
||||||
|
|
||||||
|
def _get_default_work_dir(self):
|
||||||
|
"""获取 work_dir,优先用 panel 自身缓存的,否则尝试从主窗口取"""
|
||||||
|
if hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
return str(self.work_dir)
|
||||||
|
mw = self.window()
|
||||||
|
if mw and hasattr(mw, 'work_dir') and mw.work_dir:
|
||||||
|
return str(mw.work_dir)
|
||||||
|
return ""
|
||||||
|
|
||||||
def browse_prediction_csv_dir(self):
|
def browse_prediction_csv_dir(self):
|
||||||
d = QFileDialog.getExistingDirectory(self, "选择预测结果 CSV 所在文件夹")
|
default = self._get_default_work_dir()
|
||||||
|
if default:
|
||||||
|
default = os.path.join(default, "11_12_13_predictions")
|
||||||
|
d = QFileDialog.getExistingDirectory(self, "选择预测结果 CSV 所在文件夹", default)
|
||||||
if d:
|
if d:
|
||||||
self.prediction_csv_dir_edit.setText(d)
|
self.prediction_csv_dir_edit.setText(d)
|
||||||
|
|
||||||
@ -281,9 +298,68 @@ class Step9Panel(QWidget):
|
|||||||
if p.parent and str(p.parent) != '.':
|
if p.parent and str(p.parent) != '.':
|
||||||
self.output_dir.set_path(str(p.parent))
|
self.output_dir.set_path(str(p.parent))
|
||||||
|
|
||||||
|
def update_from_config(self, work_dir=None, pipeline=None):
|
||||||
|
"""从全局配置自动填充预测结果目录
|
||||||
|
|
||||||
|
优先使用 Step8(机器学习预测)的输出目录作为待预测 CSV 目录;
|
||||||
|
其次回退到 Step8.5(回归预测)或 Step8.75(自定义回归预测)的输出目录。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
work_dir: 工作目录路径
|
||||||
|
pipeline: Pipeline 实例(未使用,保留接口兼容性)
|
||||||
|
"""
|
||||||
|
if work_dir:
|
||||||
|
self.work_dir = work_dir
|
||||||
|
elif hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.work_dir = None
|
||||||
|
|
||||||
|
main_window = self.window()
|
||||||
|
if not main_window:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 1. 尝试从 Step8 界面读取机器学习预测输出目录(优先)
|
||||||
|
pred_dir = None
|
||||||
|
if hasattr(main_window, 'step8_panel'):
|
||||||
|
step8_output = main_window.step8_panel.output_file.get_path()
|
||||||
|
if step8_output:
|
||||||
|
pred_dir = str(Path(step8_output).parent)
|
||||||
|
|
||||||
|
# 2. 备选:从 Step8.5 界面读取非经验预测输出目录
|
||||||
|
if not pred_dir and hasattr(main_window, 'step8_5_panel'):
|
||||||
|
step8_5_output = main_window.step8_5_panel.output_file.get_path()
|
||||||
|
if step8_5_output:
|
||||||
|
pred_dir = str(Path(step8_5_output).parent)
|
||||||
|
|
||||||
|
# 3. 备选:从 Step8.75 界面读取自定义回归预测输出目录
|
||||||
|
if not pred_dir and hasattr(main_window, 'step8_75_panel'):
|
||||||
|
step8_75_output = main_window.step8_75_panel.output_dir_widget.get_path()
|
||||||
|
if step8_75_output:
|
||||||
|
pred_dir = step8_75_output
|
||||||
|
|
||||||
|
# 自动填入"预测CSV目录"(文件夹批量模式)
|
||||||
|
if pred_dir:
|
||||||
|
existing_dir = (self.prediction_csv_dir_edit.text() or "").strip()
|
||||||
|
if not existing_dir:
|
||||||
|
self.prediction_csv_dir_edit.setText(pred_dir)
|
||||||
|
# 切换到文件夹批量模式
|
||||||
|
self.mode_folder_rb.setChecked(True)
|
||||||
|
|
||||||
|
# 4. 自动填充输出目录(14_visualization)
|
||||||
|
if self.work_dir:
|
||||||
|
output_dir = os.path.join(self.work_dir, "14_visualization")
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
existing_out = self.output_dir.get_path()
|
||||||
|
if not existing_out or not existing_out.strip():
|
||||||
|
self.output_dir.set_path(output_dir)
|
||||||
|
|
||||||
def browse_output_dir(self):
|
def browse_output_dir(self):
|
||||||
"""浏览输出目录"""
|
"""浏览输出目录"""
|
||||||
dir_path = QFileDialog.getExistingDirectory(self, "选择输出模型目录", "")
|
default = self._get_default_work_dir()
|
||||||
|
if default:
|
||||||
|
default = os.path.join(default, "14_visualization")
|
||||||
|
dir_path = QFileDialog.getExistingDirectory(self, "选择输出分布图目录", default)
|
||||||
if dir_path:
|
if dir_path:
|
||||||
self.output_dir.set_path(dir_path)
|
self.output_dir.set_path(dir_path)
|
||||||
|
|
||||||
|
|||||||
@ -1215,9 +1215,19 @@ class VisualizationPanel(QWidget):
|
|||||||
if work_dir:
|
if work_dir:
|
||||||
QTimer.singleShot(100, self.scan_work_directory)
|
QTimer.singleShot(100, self.scan_work_directory)
|
||||||
|
|
||||||
|
def _get_default_work_dir(self):
|
||||||
|
"""获取 work_dir,优先用 panel 自身缓存的,否则尝试从主窗口取"""
|
||||||
|
if hasattr(self, 'work_dir') and self.work_dir:
|
||||||
|
return str(self.work_dir)
|
||||||
|
mw = self.window()
|
||||||
|
if mw and hasattr(mw, 'work_dir') and mw.work_dir:
|
||||||
|
return str(mw.work_dir)
|
||||||
|
return ""
|
||||||
|
|
||||||
def browse_work_dir(self):
|
def browse_work_dir(self):
|
||||||
"""浏览工作目录"""
|
"""浏览工作目录"""
|
||||||
dir_path = QFileDialog.getExistingDirectory(self, "选择工作目录")
|
default = self._get_default_work_dir()
|
||||||
|
dir_path = QFileDialog.getExistingDirectory(self, "选择工作目录", default)
|
||||||
if dir_path:
|
if dir_path:
|
||||||
self.work_dir = dir_path
|
self.work_dir = dir_path
|
||||||
self.work_dir_edit.setText(dir_path)
|
self.work_dir_edit.setText(dir_path)
|
||||||
@ -1225,7 +1235,8 @@ class VisualizationPanel(QWidget):
|
|||||||
|
|
||||||
def browse_img_dir(self):
|
def browse_img_dir(self):
|
||||||
"""手动浏览图像目录"""
|
"""手动浏览图像目录"""
|
||||||
dir_path = QFileDialog.getExistingDirectory(self, "选择图像目录")
|
default = self._get_default_work_dir()
|
||||||
|
dir_path = QFileDialog.getExistingDirectory(self, "选择图像目录", default)
|
||||||
if dir_path:
|
if dir_path:
|
||||||
self.img_dir_edit.setText(dir_path)
|
self.img_dir_edit.setText(dir_path)
|
||||||
self.image_tree.scan_directory(dir_path)
|
self.image_tree.scan_directory(dir_path)
|
||||||
|
|||||||
@ -1475,7 +1475,7 @@ class WaterQualityGUI(QMainWindow):
|
|||||||
|
|
||||||
def init_ui(self):
|
def init_ui(self):
|
||||||
"""初始化UI"""
|
"""初始化UI"""
|
||||||
self.setWindowTitle("水质参数反演分析系统 v1.0")
|
self.setWindowTitle("水质参数反演分析系统 v1.1")
|
||||||
|
|
||||||
# 获取屏幕可用区域(排除任务栏)
|
# 获取屏幕可用区域(排除任务栏)
|
||||||
screen_geometry = QApplication.primaryScreen().availableGeometry()
|
screen_geometry = QApplication.primaryScreen().availableGeometry()
|
||||||
@ -1658,48 +1658,66 @@ class WaterQualityGUI(QMainWindow):
|
|||||||
|
|
||||||
def create_banner_widget(self):
|
def create_banner_widget(self):
|
||||||
"""创建横幅区域 - 支持自适应等比缩放"""
|
"""创建横幅区域 - 支持自适应等比缩放"""
|
||||||
|
# 横幅标题文字(方便后续直接修改版本号)
|
||||||
|
self._APP_TITLE = "Mega Water V1.1"
|
||||||
|
|
||||||
# 创建横幅容器
|
# 创建横幅容器
|
||||||
banner_widget = QWidget()
|
banner_widget = QWidget()
|
||||||
banner_layout = QHBoxLayout()
|
banner_layout = QHBoxLayout()
|
||||||
banner_layout.setContentsMargins(0, 0, 0, 0)
|
banner_layout.setContentsMargins(0, 0, 0, 0)
|
||||||
banner_layout.setSpacing(0)
|
banner_layout.setSpacing(0)
|
||||||
# 不设置居中对齐,让横幅填满整个容器
|
|
||||||
|
|
||||||
# 创建横幅标签 - 完全跟随窗口等比缩放,填满整个区域
|
# ===== 底图层 =====
|
||||||
self.banner_label = QLabel()
|
self.banner_label = QLabel()
|
||||||
# 最小高度保证:当窗口很小时至少显示 38px 高 (200px 宽 / 5.25)
|
self.banner_label.setMinimumHeight(140)
|
||||||
self.banner_label.setMinimumHeight(int(200 / 5.25)) # ≈ 38px
|
|
||||||
# 使用 Expanding 策略让标签填满可用空间
|
|
||||||
self.banner_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
self.banner_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||||
self.banner_label.setScaledContents(False)
|
self.banner_label.setScaledContents(False)
|
||||||
# 清除 QLabel 默认的 margin 和 padding,消除右侧空白
|
|
||||||
self.banner_label.setStyleSheet("margin: 0px; padding: 0px; border: none;")
|
self.banner_label.setStyleSheet("margin: 0px; padding: 0px; border: none;")
|
||||||
|
|
||||||
# 保存原始pixmap用于后续缩放
|
# 强制 banner_widget 展开填充 toolbar 全部宽度(清除 addWidget 的默认居中行为)
|
||||||
|
banner_widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||||
|
banner_widget.setMinimumWidth(0) # 确保可以被 layout 压缩/扩展到任意宽度
|
||||||
|
banner_widget.setStyleSheet("margin: 0px; padding: 0px; border: none;")
|
||||||
|
|
||||||
|
# 纯净底图路径(无水印文字)
|
||||||
if hasattr(sys, '_MEIPASS'):
|
if hasattr(sys, '_MEIPASS'):
|
||||||
banner_path = os.path.join(sys._MEIPASS, 'data', 'icons', 'Mega Water 1.0.png')
|
banner_path = os.path.join(sys._MEIPASS, 'data', 'icons', 'Mega Water 1.0.jpg')
|
||||||
else:
|
else:
|
||||||
banner_path = str(Path(__file__).parent.parent.parent / "data" / "icons" / "Mega Water 1.0.png")
|
banner_path = str(Path(__file__).parent.parent.parent / "data" / "icons" / "Mega Water 1.0.jpg")
|
||||||
self.banner_pixmap = QPixmap(banner_path)
|
self.banner_pixmap = QPixmap(banner_path)
|
||||||
|
|
||||||
if not self.banner_pixmap.isNull():
|
if not self.banner_pixmap.isNull():
|
||||||
# 延迟执行,确保窗口已初始化
|
|
||||||
QTimer.singleShot(50, self.update_banner_image)
|
QTimer.singleShot(50, self.update_banner_image)
|
||||||
else:
|
else:
|
||||||
# 如果图片加载失败,显示占位符
|
self.banner_label.setText("背景图加载失败")
|
||||||
self.banner_label.setText("水质参数反演分析系统")
|
|
||||||
self.banner_label.setStyleSheet("""
|
self.banner_label.setStyleSheet("""
|
||||||
QLabel {
|
QLabel {
|
||||||
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
||||||
stop:0 #0078d4, stop:1 #00a0e9);
|
stop:0 #0078d4, stop:1 #00a0e9);
|
||||||
color: white;
|
color: white; font-size: 26px; font-weight: bold;
|
||||||
font-size: 26px;
|
|
||||||
font-weight: bold;
|
|
||||||
border-bottom: 3px solid #005a9e;
|
border-bottom: 3px solid #005a9e;
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
banner_layout.addWidget(self.banner_label)
|
banner_layout.addWidget(self.banner_label)
|
||||||
|
|
||||||
|
# ===== 文字叠加层 =====
|
||||||
|
# 注意这里:第二个参数改成了 self.banner_label,直接附着在底图上!
|
||||||
|
self.banner_title_label = QLabel(self._APP_TITLE, self.banner_label)
|
||||||
|
self.banner_title_label.setStyleSheet("""
|
||||||
|
QLabel {
|
||||||
|
background: transparent;
|
||||||
|
color: white;
|
||||||
|
font-size: 48px; /* 显著增大字号 */
|
||||||
|
font-weight: normal; /* 取消粗体,还原原图的优雅感 */
|
||||||
|
font-family: "Times New Roman", "Georgia", "STZhongsong", serif; /* 衬线字体家族 */
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
self.banner_title_label.setAttribute(Qt.WA_TransparentForMouseEvents) # 鼠标穿透
|
||||||
|
self.banner_title_label.show() # 第一道保险:强制现身
|
||||||
|
self.banner_title_label.raise_() # 第二道保险:强制图层置顶
|
||||||
|
|
||||||
banner_widget.setLayout(banner_layout)
|
banner_widget.setLayout(banner_layout)
|
||||||
|
|
||||||
# 将横幅添加到窗口顶部(在标题栏下方)
|
# 将横幅添加到窗口顶部(在标题栏下方)
|
||||||
@ -1721,6 +1739,10 @@ class WaterQualityGUI(QMainWindow):
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
QToolBar > QWidget {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
self.addToolBar(Qt.TopToolBarArea, banner_toolbar)
|
self.addToolBar(Qt.TopToolBarArea, banner_toolbar)
|
||||||
@ -2122,10 +2144,34 @@ class WaterQualityGUI(QMainWindow):
|
|||||||
elif index == 6:
|
elif index == 6:
|
||||||
self.step6_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
self.step6_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||||
|
|
||||||
|
# Step6.5(非经验回归建模)切换时自动填充训练数据和模型目录
|
||||||
|
elif index == 7:
|
||||||
|
self.step6_5_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||||
|
|
||||||
|
# Step6.75(自定义回归建模)切换时自动填充训练数据和模型目录
|
||||||
|
elif index == 8:
|
||||||
|
self.step6_75_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||||
|
|
||||||
# Step7(采样点布设)切换时自动填充掩膜和输出路径
|
# Step7(采样点布设)切换时自动填充掩膜和输出路径
|
||||||
elif index == 9:
|
elif index == 9:
|
||||||
self.step7_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
self.step7_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||||
|
|
||||||
|
# Step8(机器学习预测)切换时自动填充采样光谱和模型目录
|
||||||
|
elif index == 10:
|
||||||
|
self.step8_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||||
|
|
||||||
|
# Step8.5(非经验模型预测)切换时自动填充采样光谱和回归模型目录
|
||||||
|
elif index == 11:
|
||||||
|
self.step8_5_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||||
|
|
||||||
|
# Step8.75(自定义回归预测)切换时自动填充采样光谱和自定义回归模型目录
|
||||||
|
elif index == 12:
|
||||||
|
self.step8_75_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||||
|
|
||||||
|
# Step9(专题图生成)切换时自动填充预测结果目录
|
||||||
|
elif index == 13:
|
||||||
|
self.step9_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||||
|
|
||||||
# 可视化分析面板切换时自动推断图像目录并加载目录树
|
# 可视化分析面板切换时自动推断图像目录并加载目录树
|
||||||
elif index == 14:
|
elif index == 14:
|
||||||
self.viz_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
self.viz_panel.update_from_config(work_dir=self.work_dir, pipeline=self.pipeline)
|
||||||
@ -2853,36 +2899,43 @@ class WaterQualityGUI(QMainWindow):
|
|||||||
self.training_mode_action.setText("有训练数据模式" if checked else "无训练数据模式")
|
self.training_mode_action.setText("有训练数据模式" if checked else "无训练数据模式")
|
||||||
|
|
||||||
def update_banner_image(self):
|
def update_banner_image(self):
|
||||||
"""更新横幅图片 - 完全跟随窗口等比缩放,填满可用宽度"""
|
"""更新横幅图片 - 固定高度 + 居中裁剪(类似 CSS object-fit: cover)"""
|
||||||
if not hasattr(self, 'banner_pixmap') or self.banner_pixmap.isNull():
|
if not hasattr(self, 'banner_pixmap') or self.banner_pixmap.isNull():
|
||||||
return
|
return
|
||||||
|
|
||||||
# 获取可用宽度(考虑工具栏边距),跟随窗口实时变化
|
TARGET_HEIGHT = 140
|
||||||
available_width = max(200, self.width() - 60)
|
target_width = self.width()
|
||||||
|
|
||||||
# 先根据可用宽度计算目标高度(严格 5.25:1)
|
|
||||||
target_height = int(available_width / 5.25)
|
|
||||||
|
|
||||||
# 限制最小高度
|
|
||||||
if target_height < 38:
|
|
||||||
target_height = 38
|
|
||||||
available_width = int(38 * 5.25)
|
|
||||||
|
|
||||||
# 计算图片目标尺寸(保持 5.25:1 比例)
|
# 手动计算 Cover 缩放比例:取宽/高各自所需比例的最大值,
|
||||||
target_width = available_width
|
# 确保缩放后图片的宽 ≥ target_width 且高 ≥ TARGET_HEIGHT
|
||||||
|
orig_w = self.banner_pixmap.width()
|
||||||
# 设置固定尺寸,确保标签严格填满整个区域
|
orig_h = self.banner_pixmap.height()
|
||||||
self.banner_label.setFixedSize(target_width, target_height)
|
scale_factor = max(target_width / orig_w, TARGET_HEIGHT / orig_h)
|
||||||
|
new_w = int(orig_w * scale_factor)
|
||||||
|
new_h = int(orig_h * scale_factor)
|
||||||
|
|
||||||
# 等比缩放到目标尺寸,填满整个区域(允许轻微裁剪)
|
|
||||||
scaled_pixmap = self.banner_pixmap.scaled(
|
scaled_pixmap = self.banner_pixmap.scaled(
|
||||||
target_width,
|
new_w, new_h,
|
||||||
target_height,
|
Qt.IgnoreAspectRatio,
|
||||||
Qt.KeepAspectRatioByExpanding, # 保持比例,填满区域,允许裁剪超出部分
|
|
||||||
Qt.SmoothTransformation
|
Qt.SmoothTransformation
|
||||||
)
|
)
|
||||||
|
|
||||||
self.banner_label.setPixmap(scaled_pixmap)
|
# 居中裁剪,截取中间核心区域
|
||||||
|
crop_x = (new_w - target_width) // 2
|
||||||
|
crop_y = (new_h - TARGET_HEIGHT) // 2
|
||||||
|
final_pixmap = scaled_pixmap.copy(crop_x, crop_y, target_width, TARGET_HEIGHT)
|
||||||
|
|
||||||
|
self.banner_label.setFixedHeight(TARGET_HEIGHT)
|
||||||
|
self.banner_label.setFixedWidth(target_width) # 强制宽度填满容器
|
||||||
|
self.banner_label.setPixmap(final_pixmap)
|
||||||
|
self.banner_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||||
|
|
||||||
|
# 文字叠加层:绝对定位,保持垂直居中
|
||||||
|
if hasattr(self, 'banner_title_label'):
|
||||||
|
title_x = 160
|
||||||
|
title_y = max(0, (TARGET_HEIGHT - 60) // 2)
|
||||||
|
self.banner_title_label.move(title_x, title_y)
|
||||||
|
self.banner_title_label.resize(target_width - title_x - 20, 60)
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
"""窗口大小改变事件 - 实时更新横幅图片等比缩放"""
|
"""窗口大小改变事件 - 实时更新横幅图片等比缩放"""
|
||||||
|
|||||||
Reference in New Issue
Block a user