style: Step4 图表中文排版升级,对齐Step12风格

- 添加 matplotlib rcParams 中文字体配置(Microsoft YaHei / SimHei)
- 散点图标题→'采样点空间分布',坐标轴→'经度/X'、'纬度/Y'
- 光谱图标题→'采样点 ID: X 光谱曲线',坐标轴→'波长/特征'、'反射率'
- 悬停提示框中文化:ID + 经度 + 纬度
- 标题字号统一为 fontsize=14 fontweight='bold' pad=12
- 轴标签字号统一为 fontsize=11
This commit is contained in:
duxin
2026-06-30 09:45:50 +08:00
parent 48d17ef0ca
commit 1bdd623fe7

View File

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