diff --git a/src/gui/panels/step4_sampling_panel.py b/src/gui/panels/step4_sampling_panel.py index 4603a1c..21d2848 100644 --- a/src/gui/panels/step4_sampling_panel.py +++ b/src/gui/panels/step4_sampling_panel.py @@ -14,9 +14,14 @@ import os import sys from pathlib import Path +import matplotlib.pyplot as plt import numpy as np import pandas as pd +# ── 全局中文字体配置(与 Step12 统一)── +plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'sans-serif'] +plt.rcParams['axes.unicode_minus'] = False + _HERE = os.path.dirname(os.path.abspath(__file__)) if _HERE not in sys.path: sys.path.insert(0, _HERE) @@ -169,41 +174,44 @@ class Step4SamplingPanel(QWidget): left_widget.setMinimumWidth(360) # ═══════════════════════════════════════════════ - # 右侧:可视化区(嵌入式 Matplotlib 视图) + # 右侧:可视化区(参考 Step12 ImageViewer 的白底卡片风格) # ═══════════════════════════════════════════════ right_widget = QWidget() right_layout = QVBoxLayout() - right_layout.setContentsMargins(12, 24, 24, 24) + right_layout.setContentsMargins(0, 0, 0, 0) right_layout.setSpacing(0) + # 给画板穿上卡片外衣,确保视觉上的绝对统一 viz_group = QGroupBox("📊 采样点交互式探索") viz_layout = QVBoxLayout() viz_layout.setContentsMargins(8, 20, 8, 8) viz_layout.setSpacing(0) # Matplotlib 画布(1×2 子图:散点图 + 光谱曲线) - self._fig = Figure(figsize=(9, 5)) + self._fig = Figure(figsize=(10, 7), facecolor='white') self._ax_scatter = self._fig.add_subplot(121) self._ax_spectrum = self._fig.add_subplot(122) self._canvas = FigureCanvas(self._fig) self._canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self._canvas.setStyleSheet("background-color: white;") # 设置子图初始状态 - self._ax_scatter.set_title("采样点分布", fontsize=11, fontweight='bold') - self._ax_scatter.set_xlabel("X 坐标") - self._ax_scatter.set_ylabel("Y 坐标") + for ax, title, xlabel, ylabel in [ + (self._ax_scatter, "采样点空间分布", "经度 / X", "纬度 / Y"), + (self._ax_spectrum, "光谱曲线", "波长 / 特征", "反射率"), + ]: + ax.set_title(title, fontsize=14, fontweight='bold', pad=12) + ax.set_xlabel(xlabel, fontsize=11) + ax.set_ylabel(ylabel, fontsize=11) + ax.set_facecolor('white') + ax.tick_params(labelsize=9) + self._ax_scatter.text(0.5, 0.5, "等待采样数据生成...\n\n请先配置参数并运行步骤\n或选择已有的 CSV 后刷新", ha='center', va='center', transform=self._ax_scatter.transAxes, - fontsize=12, color='#888888') - self._ax_scatter.grid(False) - - self._ax_spectrum.set_title("光谱曲线", fontsize=11, fontweight='bold') - self._ax_spectrum.set_xlabel("波长 (nm)") - self._ax_spectrum.set_ylabel("反射率") + fontsize=13, color='#AAAAAA') self._ax_spectrum.text(0.5, 0.5, "点击左侧散点\n查看光谱曲线", ha='center', va='center', transform=self._ax_spectrum.transAxes, - fontsize=12, color='#888888') - self._ax_spectrum.grid(False) + fontsize=13, color='#AAAAAA') self._fig.tight_layout(pad=2.0) @@ -213,13 +221,13 @@ class Step4SamplingPanel(QWidget): viz_layout.addWidget(self._canvas) viz_group.setLayout(viz_layout) - right_layout.addWidget(viz_group) + right_layout.addWidget(viz_group, 1) right_widget.setLayout(right_layout) # ── 组装分栏 ── splitter.addWidget(left_widget) splitter.addWidget(right_widget) - splitter.setSizes([420, 680]) # 初始比例 ≈ 38:62 + splitter.setSizes([400, 700]) splitter.setStretchFactor(0, 1) splitter.setStretchFactor(1, 2) @@ -499,53 +507,57 @@ class Step4SamplingPanel(QWidget): # ── 绘制 ax1:散点图 ── self._ax_scatter.clear() + self._ax_scatter.set_facecolor('white') x = df[x_col].values y = df[y_col].values self._scatter = self._ax_scatter.scatter( x, y, - c='#0078D7', alpha=0.7, edgecolors='white', - linewidth=0.5, s=40, picker=True, zorder=3 + c='#0078D7', alpha=0.75, edgecolors='white', + linewidth=0.6, s=45, picker=True, zorder=3 ) - self._ax_scatter.set_xlabel(str(x_col), fontsize=10) - self._ax_scatter.set_ylabel(str(y_col), fontsize=10) - self._ax_scatter.set_title(f"采样点分布 (共 {len(df)} 个点)", fontsize=11, fontweight='bold') - self._ax_scatter.grid(True, alpha=0.3, linestyle='--') - self._ax_scatter.set_facecolor('#F8F9FA') + self._ax_scatter.set_xlabel("经度 / X", fontsize=11) + self._ax_scatter.set_ylabel("纬度 / Y", fontsize=11) + self._ax_scatter.set_title(f"采样点空间分布 (共 {len(df)} 个点)", fontsize=14, fontweight='bold', pad=12) + self._ax_scatter.grid(True, alpha=0.2, linestyle='-', linewidth=0.5) + self._ax_scatter.tick_params(labelsize=9) # ── 绘制 ax2:光谱曲线(初始状态)── self._ax_spectrum.clear() + self._ax_spectrum.set_facecolor('white') if band_cols: - self._ax_spectrum.set_title("光谱曲线(点击左侧散点查看)", fontsize=11, fontweight='bold') - self._ax_spectrum.set_xlabel("波长 (nm)", fontsize=10) - self._ax_spectrum.set_ylabel("反射率", fontsize=10) - self._ax_spectrum.grid(True, alpha=0.3, linestyle='--') + self._ax_spectrum.set_title("光谱曲线(点击左侧散点查看)", fontsize=14, fontweight='bold', pad=12) + self._ax_spectrum.set_xlabel("波长 / 特征", fontsize=11) + self._ax_spectrum.set_ylabel("反射率", fontsize=11) + self._ax_spectrum.grid(True, alpha=0.2, linestyle='-', linewidth=0.5) + self._ax_spectrum.tick_params(labelsize=9) self._ax_spectrum.text(0.5, 0.5, "点击左侧散点\n查看光谱曲线", ha='center', va='center', transform=self._ax_spectrum.transAxes, - fontsize=12, color='#999999') + fontsize=13, color='#AAAAAA') else: self._ax_spectrum.text(0.5, 0.5, "缺少光谱数据列", ha='center', va='center', transform=self._ax_spectrum.transAxes, - fontsize=12, color='#999999') + fontsize=13, color='#AAAAAA') self._fig.tight_layout(pad=2.0) self._canvas.draw() def _show_empty_state(self, message: str): """在右侧画布显示提示信息。""" - self._ax_scatter.clear() - self._ax_scatter.set_title("采样点分布", fontsize=11, fontweight='bold') + for ax in (self._ax_scatter, self._ax_spectrum): + ax.clear() + ax.set_facecolor('white') + ax.tick_params(labelsize=0) + + self._ax_scatter.set_title("采样点空间分布", fontsize=14, fontweight='bold', pad=12) self._ax_scatter.text(0.5, 0.5, message, ha='center', va='center', transform=self._ax_scatter.transAxes, - fontsize=12, color='#888888') - self._ax_scatter.grid(False) + fontsize=13, color='#AAAAAA') - self._ax_spectrum.clear() - self._ax_spectrum.set_title("光谱曲线", fontsize=11, fontweight='bold') + self._ax_spectrum.set_title("光谱曲线", fontsize=14, fontweight='bold', pad=12) self._ax_spectrum.text(0.5, 0.5, "等待数据...", ha='center', va='center', transform=self._ax_spectrum.transAxes, - fontsize=12, color='#888888') - self._ax_spectrum.grid(False) + fontsize=13, color='#AAAAAA') self._fig.tight_layout(pad=2.0) self._canvas.draw() @@ -588,7 +600,7 @@ class Step4SamplingPanel(QWidget): y_val = row[self._y_col] # 创建或更新 Annotation - text = f"#{idx}\n({x_val:.4f}, {y_val:.4f})" + text = f"ID: {idx}\n经度: {x_val:.4f}\n纬度: {y_val:.4f}" if self._annot is None: self._annot = self._ax_scatter.annotate( text, @@ -629,31 +641,33 @@ class Step4SamplingPanel(QWidget): y = self._df[self._y_col].values colors = ['#0078D7'] * len(self._df) - sizes = [40] * len(self._df) + sizes = [45] * len(self._df) colors[idx] = '#E74C3C' sizes[idx] = 80 self._ax_scatter.clear() + self._ax_scatter.set_facecolor('white') self._scatter = self._ax_scatter.scatter( x, y, c=colors, s=sizes, - alpha=0.7, edgecolors='white', linewidth=0.5, + alpha=0.75, edgecolors='white', linewidth=0.6, picker=True, zorder=3 ) # 将选中点提升到顶层 self._ax_scatter.scatter( [x[idx]], [y[idx]], - c='#E74C3C', s=100, + c='#E74C3C', s=110, alpha=0.9, edgecolors='white', linewidth=1.5, zorder=5 ) - self._ax_scatter.set_xlabel(str(self._x_col), fontsize=10) - self._ax_scatter.set_ylabel(str(self._y_col), fontsize=10) - self._ax_scatter.set_title(f"采样点分布 (共 {len(self._df)} 个点)", fontsize=11, fontweight='bold') - self._ax_scatter.grid(True, alpha=0.3, linestyle='--') - self._ax_scatter.set_facecolor('#F8F9FA') + self._ax_scatter.set_xlabel("经度 / X", fontsize=11) + self._ax_scatter.set_ylabel("纬度 / Y", fontsize=11) + self._ax_scatter.set_title(f"采样点空间分布 (共 {len(self._df)} 个点)", fontsize=14, fontweight='bold', pad=12) + self._ax_scatter.grid(True, alpha=0.2, linestyle='-', linewidth=0.5) + self._ax_scatter.tick_params(labelsize=9) # ── 绘制光谱曲线 ── self._ax_spectrum.clear() + self._ax_spectrum.set_facecolor('white') if self._band_cols: wavelengths = [] reflectance = [] @@ -673,24 +687,25 @@ class Step4SamplingPanel(QWidget): wavelengths, reflectance = zip(*pairs) if pairs else ([], []) self._ax_spectrum.plot( wavelengths, reflectance, - color='#0078D7', lw=1.5, marker='.', markersize=3, alpha=0.8 + color='#0078D7', lw=1.5, marker='.', markersize=3, alpha=0.85 ) - self._ax_spectrum.fill_between(wavelengths, reflectance, alpha=0.1, color='#0078D7') - self._ax_spectrum.set_xlabel("波长 (nm)", fontsize=10) - self._ax_spectrum.set_ylabel("反射率", fontsize=10) - self._ax_spectrum.set_title(f"样本 #{idx} 的光谱曲线 ({len(wavelengths)} 个波段)", - fontsize=11, fontweight='bold') - self._ax_spectrum.grid(True, alpha=0.3, linestyle='--') + self._ax_spectrum.fill_between(wavelengths, reflectance, alpha=0.08, color='#0078D7') + self._ax_spectrum.set_xlabel("波长 / 特征", fontsize=11) + self._ax_spectrum.set_ylabel("反射率", fontsize=11) + self._ax_spectrum.set_title(f"采样点 ID: {idx} 光谱曲线 ({len(wavelengths)} 个波段)", + fontsize=14, fontweight='bold', pad=12) + self._ax_spectrum.grid(True, alpha=0.2, linestyle='-', linewidth=0.5) + self._ax_spectrum.tick_params(labelsize=9) else: self._ax_spectrum.text(0.5, 0.5, "该样本无有效光谱数据", ha='center', va='center', transform=self._ax_spectrum.transAxes, - fontsize=12, color='#999999') + fontsize=13, color='#AAAAAA') else: self._ax_spectrum.text(0.5, 0.5, "缺少光谱波段列\n无法绘制光谱", ha='center', va='center', transform=self._ax_spectrum.transAxes, - fontsize=12, color='#999999') + fontsize=13, color='#AAAAAA') self._fig.tight_layout(pad=2.0) self._canvas.draw()