界面优化
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:
|
||||
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')
|
||||
@ -1100,11 +1105,8 @@ class WaterQualityInversionPipeline:
|
||||
Returns:
|
||||
numpy数组或None,1表示水域,0表示非水域
|
||||
"""
|
||||
# 获取图像尺寸
|
||||
if isinstance(image_shape, np.ndarray):
|
||||
img_height, img_width = image_shape.shape[:2]
|
||||
else:
|
||||
img_height, img_width = image_shape
|
||||
# 获取图像尺寸(统一从 shape 元组中提取前两个维度,兼容 (H,W)、(H,W,C)、(B,H,W) 等多种格式)
|
||||
img_height, img_width = image_shape[0], image_shape[1]
|
||||
|
||||
if water_mask is None:
|
||||
# 如果water_mask为None,使用步骤1生成的dat格式掩膜
|
||||
@ -1362,6 +1364,19 @@ class WaterQualityInversionPipeline:
|
||||
interpolated_bands.append(band_data)
|
||||
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':
|
||||
# 邻近插值
|
||||
@ -1591,6 +1606,18 @@ class WaterQualityInversionPipeline:
|
||||
|
||||
step_start_time = time.time()
|
||||
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:
|
||||
print("已设置跳过去除耀斑(enabled=False),将直接使用原始影像。")
|
||||
@ -1807,6 +1834,16 @@ class WaterQualityInversionPipeline:
|
||||
del corrected_bands
|
||||
|
||||
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})")
|
||||
|
||||
# 确定输出路径
|
||||
@ -3603,6 +3640,29 @@ class WaterQualityInversionPipeline:
|
||||
Returns:
|
||||
预处理后的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':
|
||||
return csv_path
|
||||
|
||||
@ -195,13 +195,23 @@ class ReportGenerationPanel(QWidget):
|
||||
self.text_model_edit.setText(self.vision_model_edit.text())
|
||||
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):
|
||||
d = QFileDialog.getExistingDirectory(self, "选择工作目录")
|
||||
default = self._get_default_work_dir()
|
||||
d = QFileDialog.getExistingDirectory(self, "选择工作目录", default)
|
||||
if d:
|
||||
self.work_dir_edit.setText(d)
|
||||
|
||||
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:
|
||||
self.output_dir_edit.setText(d)
|
||||
|
||||
|
||||
@ -168,7 +168,10 @@ class Step4Panel(QWidget):
|
||||
|
||||
try:
|
||||
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:
|
||||
self.reset_preview("CSV文件为空")
|
||||
return
|
||||
|
||||
@ -191,6 +191,15 @@ class Step5Panel(QWidget):
|
||||
else:
|
||||
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):
|
||||
"""独立运行步骤5"""
|
||||
# 验证输入
|
||||
|
||||
@ -211,9 +211,52 @@ class Step6_5Panel(QWidget):
|
||||
if 'csv_path' in config:
|
||||
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):
|
||||
"""浏览输出目录"""
|
||||
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:
|
||||
self.output_dir.set_path(dir_path)
|
||||
|
||||
|
||||
@ -280,6 +280,37 @@ class Step6_75Panel(QWidget):
|
||||
if 'enabled' in config:
|
||||
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):
|
||||
"""独立运行步骤6.75"""
|
||||
csv_path = self.csv_file.get_path()
|
||||
|
||||
@ -42,15 +42,15 @@ class Step6Panel(QWidget):
|
||||
layout.addWidget(self.ml_page)
|
||||
|
||||
# 输出文件路径
|
||||
self.output_dir = FileSelectWidget(
|
||||
"输出模型目录:",
|
||||
"Directories;;All Files (*.*)"
|
||||
self.output_path = FileSelectWidget(
|
||||
"输出文件:",
|
||||
"CSV Files (*.csv);;All Files (*.*)",
|
||||
mode="save"
|
||||
)
|
||||
self.output_dir.line_edit.setPlaceholderText("models_output")
|
||||
# 修改浏览按钮为选择目录
|
||||
self.output_dir.browse_btn.clicked.disconnect()
|
||||
self.output_dir.browse_btn.clicked.connect(self.browse_output_dir)
|
||||
layout.addWidget(self.output_dir)
|
||||
self.output_path.line_edit.setPlaceholderText("自动生成,或手动指定输出文件路径...")
|
||||
self.output_path.browse_btn.clicked.disconnect()
|
||||
self.output_path.browse_btn.clicked.connect(self.browse_output_path)
|
||||
layout.addWidget(self.output_path)
|
||||
|
||||
# 启用步骤
|
||||
self.enable_checkbox = QCheckBox("启用此步骤")
|
||||
@ -198,11 +198,38 @@ class Step6Panel(QWidget):
|
||||
for checkbox in checkboxes_dict.values():
|
||||
checkbox.setChecked(checked)
|
||||
|
||||
def browse_output_dir(self):
|
||||
"""浏览输出目录"""
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "选择输出模型目录", "")
|
||||
if dir_path:
|
||||
self.output_dir.set_path(dir_path)
|
||||
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_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):
|
||||
"""获取配置"""
|
||||
@ -229,7 +256,9 @@ class Step6Panel(QWidget):
|
||||
training_csv_path = self.training_csv_file.get_path()
|
||||
if 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
|
||||
|
||||
def set_config(self, config):
|
||||
@ -252,8 +281,8 @@ class Step6Panel(QWidget):
|
||||
checkbox.setChecked(method in methods)
|
||||
if 'training_csv_path' in config:
|
||||
self.training_csv_file.set_path(config['training_csv_path'])
|
||||
if 'output_dir' in config:
|
||||
self.output_dir.set_path(config['output_dir'])
|
||||
if 'output_path' in config:
|
||||
self.output_path.set_path(config['output_path'])
|
||||
|
||||
def update_from_config(self, work_dir=None, pipeline=None):
|
||||
"""从全局配置自动填充训练数据和输出路径
|
||||
@ -288,13 +317,22 @@ class Step6Panel(QWidget):
|
||||
if 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:
|
||||
output_dir = os.path.join(self.work_dir, "7_Supervised_Model_Training")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
self.output_dir.set_path(output_dir.replace('\\', '/'))
|
||||
indices_dir = os.path.join(self.work_dir, "6_water_quality_indices")
|
||||
os.makedirs(indices_dir, exist_ok=True)
|
||||
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:
|
||||
self.output_dir.set_path("")
|
||||
self.output_path.set_path("")
|
||||
|
||||
def run_step(self):
|
||||
"""独立运行步骤6"""
|
||||
|
||||
@ -4,6 +4,9 @@
|
||||
Step8_5 面板 - 非经验模型预测
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout,
|
||||
QPushButton, QCheckBox, QComboBox, QLineEdit, QMessageBox,
|
||||
@ -79,15 +82,70 @@ class Step8_5Panel(QWidget):
|
||||
layout.addStretch()
|
||||
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):
|
||||
"""浏览模型目录"""
|
||||
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:
|
||||
self.models_dir_file.set_path(dir_path)
|
||||
|
||||
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:
|
||||
self.output_file.set_path(dir_path)
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
Step8_75 面板 - 自定义回归预测
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox,
|
||||
QPushButton, QCheckBox, QMessageBox, QFileDialog,
|
||||
@ -73,15 +75,70 @@ class Step8_75Panel(QWidget):
|
||||
layout.addStretch()
|
||||
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):
|
||||
"""浏览回归模型目录"""
|
||||
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:
|
||||
self.regression_models_dir.set_path(dir_path)
|
||||
|
||||
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:
|
||||
self.output_dir_widget.set_path(dir_path)
|
||||
|
||||
|
||||
@ -4,6 +4,9 @@
|
||||
Step8 面板 - 机器学习预测
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QGroupBox, QFormLayout,
|
||||
QPushButton, QCheckBox, QComboBox, QLineEdit, QMessageBox,
|
||||
@ -76,9 +79,61 @@ class Step8Panel(QWidget):
|
||||
layout.addStretch()
|
||||
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):
|
||||
"""浏览模型目录"""
|
||||
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:
|
||||
self.models_dir_file.set_path(dir_path)
|
||||
|
||||
|
||||
@ -101,12 +101,9 @@ class Step9Panel(QWidget):
|
||||
mode_row = QHBoxLayout()
|
||||
self.mode_single_rb = QRadioButton("单个 CSV 文件")
|
||||
self.mode_folder_rb = QRadioButton("文件夹批量")
|
||||
self.mode_single_rb.setChecked(True)
|
||||
self._mode_group = QButtonGroup(self)
|
||||
self._mode_group.addButton(self.mode_single_rb, 0)
|
||||
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_folder_rb)
|
||||
mode_row.addStretch()
|
||||
@ -192,16 +189,36 @@ class Step9Panel(QWidget):
|
||||
|
||||
layout.addStretch()
|
||||
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()
|
||||
self.prediction_csv_file.setEnabled(not folder_mode)
|
||||
self._folder_row_widget.setEnabled(folder_mode)
|
||||
self.recursive_csv_cb.setEnabled(folder_mode)
|
||||
# 单个 CSV 模式:显示单文件选择,隐藏文件夹选择
|
||||
self.prediction_csv_file.setVisible(not 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):
|
||||
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:
|
||||
self.prediction_csv_dir_edit.setText(d)
|
||||
|
||||
@ -281,9 +298,68 @@ class Step9Panel(QWidget):
|
||||
if p.parent and 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):
|
||||
"""浏览输出目录"""
|
||||
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:
|
||||
self.output_dir.set_path(dir_path)
|
||||
|
||||
|
||||
@ -1215,9 +1215,19 @@ class VisualizationPanel(QWidget):
|
||||
if work_dir:
|
||||
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):
|
||||
"""浏览工作目录"""
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "选择工作目录")
|
||||
default = self._get_default_work_dir()
|
||||
dir_path = QFileDialog.getExistingDirectory(self, "选择工作目录", default)
|
||||
if dir_path:
|
||||
self.work_dir = dir_path
|
||||
self.work_dir_edit.setText(dir_path)
|
||||
@ -1225,7 +1235,8 @@ class VisualizationPanel(QWidget):
|
||||
|
||||
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:
|
||||
self.img_dir_edit.setText(dir_path)
|
||||
self.image_tree.scan_directory(dir_path)
|
||||
|
||||
@ -1475,7 +1475,7 @@ class WaterQualityGUI(QMainWindow):
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化UI"""
|
||||
self.setWindowTitle("水质参数反演分析系统 v1.0")
|
||||
self.setWindowTitle("水质参数反演分析系统 v1.1")
|
||||
|
||||
# 获取屏幕可用区域(排除任务栏)
|
||||
screen_geometry = QApplication.primaryScreen().availableGeometry()
|
||||
@ -1658,48 +1658,66 @@ class WaterQualityGUI(QMainWindow):
|
||||
|
||||
def create_banner_widget(self):
|
||||
"""创建横幅区域 - 支持自适应等比缩放"""
|
||||
# 横幅标题文字(方便后续直接修改版本号)
|
||||
self._APP_TITLE = "Mega Water V1.1"
|
||||
|
||||
# 创建横幅容器
|
||||
banner_widget = QWidget()
|
||||
banner_layout = QHBoxLayout()
|
||||
banner_layout.setContentsMargins(0, 0, 0, 0)
|
||||
banner_layout.setSpacing(0)
|
||||
# 不设置居中对齐,让横幅填满整个容器
|
||||
|
||||
# 创建横幅标签 - 完全跟随窗口等比缩放,填满整个区域
|
||||
# ===== 底图层 =====
|
||||
self.banner_label = QLabel()
|
||||
# 最小高度保证:当窗口很小时至少显示 38px 高 (200px 宽 / 5.25)
|
||||
self.banner_label.setMinimumHeight(int(200 / 5.25)) # ≈ 38px
|
||||
# 使用 Expanding 策略让标签填满可用空间
|
||||
self.banner_label.setMinimumHeight(140)
|
||||
self.banner_label.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
||||
self.banner_label.setScaledContents(False)
|
||||
# 清除 QLabel 默认的 margin 和 padding,消除右侧空白
|
||||
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'):
|
||||
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:
|
||||
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)
|
||||
|
||||
if not self.banner_pixmap.isNull():
|
||||
# 延迟执行,确保窗口已初始化
|
||||
QTimer.singleShot(50, self.update_banner_image)
|
||||
else:
|
||||
# 如果图片加载失败,显示占位符
|
||||
self.banner_label.setText("水质参数反演分析系统")
|
||||
self.banner_label.setText("背景图加载失败")
|
||||
self.banner_label.setStyleSheet("""
|
||||
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);
|
||||
color: white;
|
||||
font-size: 26px;
|
||||
font-weight: bold;
|
||||
color: white; font-size: 26px; font-weight: bold;
|
||||
border-bottom: 3px solid #005a9e;
|
||||
}
|
||||
""")
|
||||
|
||||
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)
|
||||
|
||||
# 将横幅添加到窗口顶部(在标题栏下方)
|
||||
@ -1721,6 +1739,10 @@ class WaterQualityGUI(QMainWindow):
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
QToolBar > QWidget {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
""")
|
||||
|
||||
self.addToolBar(Qt.TopToolBarArea, banner_toolbar)
|
||||
@ -2122,10 +2144,34 @@ class WaterQualityGUI(QMainWindow):
|
||||
elif index == 6:
|
||||
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(采样点布设)切换时自动填充掩膜和输出路径
|
||||
elif index == 9:
|
||||
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:
|
||||
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 "无训练数据模式")
|
||||
|
||||
def update_banner_image(self):
|
||||
"""更新横幅图片 - 完全跟随窗口等比缩放,填满可用宽度"""
|
||||
"""更新横幅图片 - 固定高度 + 居中裁剪(类似 CSS object-fit: cover)"""
|
||||
if not hasattr(self, 'banner_pixmap') or self.banner_pixmap.isNull():
|
||||
return
|
||||
|
||||
# 获取可用宽度(考虑工具栏边距),跟随窗口实时变化
|
||||
available_width = max(200, self.width() - 60)
|
||||
|
||||
# 先根据可用宽度计算目标高度(严格 5.25:1)
|
||||
target_height = int(available_width / 5.25)
|
||||
|
||||
# 限制最小高度
|
||||
if target_height < 38:
|
||||
target_height = 38
|
||||
available_width = int(38 * 5.25)
|
||||
TARGET_HEIGHT = 140
|
||||
target_width = self.width()
|
||||
|
||||
# 计算图片目标尺寸(保持 5.25:1 比例)
|
||||
target_width = available_width
|
||||
|
||||
# 设置固定尺寸,确保标签严格填满整个区域
|
||||
self.banner_label.setFixedSize(target_width, target_height)
|
||||
# 手动计算 Cover 缩放比例:取宽/高各自所需比例的最大值,
|
||||
# 确保缩放后图片的宽 ≥ target_width 且高 ≥ TARGET_HEIGHT
|
||||
orig_w = self.banner_pixmap.width()
|
||||
orig_h = self.banner_pixmap.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(
|
||||
target_width,
|
||||
target_height,
|
||||
Qt.KeepAspectRatioByExpanding, # 保持比例,填满区域,允许裁剪超出部分
|
||||
new_w, new_h,
|
||||
Qt.IgnoreAspectRatio,
|
||||
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):
|
||||
"""窗口大小改变事件 - 实时更新横幅图片等比缩放"""
|
||||
|
||||
Reference in New Issue
Block a user