Coverage for graphica/gui/residual_panel.py: 100.0%
46 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
1"""選んだデータセットのフィットの残差(下のパネルの「残差」のタブ)。fit_result の残差をそのまま描く(計算し直さない)。
3メインのグラフを上下2段に分けないのは、サブプロットの数・配置・自由配置・第2Y軸などの前提と絡み合うため。
4"""
5import logging
7from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
8from matplotlib.figure import Figure
9from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel
11from graphica.core.i18n import tr
12from graphica.gui import theme
13from graphica.gui.mathtext_preview import JP_CAPABLE_FONT_FAMILIES
15logger = logging.getLogger(__name__)
18class ResidualPanel(QWidget):
19 def __init__(self, parent=None, dpi=100):
20 super().__init__(parent)
21 layout = QVBoxLayout(self)
22 layout.setContentsMargins(4, 4, 4, 4)
24 self.placeholder_label = QLabel(
25 "曲線フィットの結果を持つデータセットを選択すると、\n"
26 "ここに残差(実測値 - フィット値)が表示されます。"
27 )
28 self.placeholder_label.setWordWrap(True)
29 self.placeholder_label.setStyleSheet("color: gray; padding: 12px;")
30 layout.addWidget(self.placeholder_label)
32 self.fig = Figure(figsize=(4, 2.2), dpi=dpi, tight_layout=True)
33 self.canvas = FigureCanvas(self.fig)
34 self.canvas.setMinimumHeight(110)
35 self.ax = self.fig.add_subplot(111)
36 layout.addWidget(self.canvas)
37 self.canvas.setVisible(False)
39 def refresh(self, dataset, fit_result=None):
40 """fit_result を省くと dataset の fit_result。無いか残差が空なら案内の文を出す。"""
41 if fit_result is None:
42 fit_result = dataset.fit_result if dataset is not None else None
43 residual_x = fit_result.get('residual_x') if fit_result else None
44 residuals = fit_result.get('residuals') if fit_result else None
46 if not residual_x or not residuals:
47 self.canvas.setVisible(False)
48 self.placeholder_label.setVisible(True)
49 return
51 self.placeholder_label.setVisible(False)
52 self.canvas.setVisible(True)
54 tokens = theme.current_tokens()
55 self.fig.set_facecolor(tokens['surface'])
56 self.ax.cla()
57 self.ax.set_facecolor(tokens['surface'])
58 self.ax.axhline(0, color=tokens['border_strong'], linewidth=0.8, linestyle='--')
59 self.ax.scatter(residual_x, residuals, s=14, color=tokens['accent'])
60 self.ax.set_xlabel("X", fontsize=8, color=tokens['text_primary'])
61 # matplotlib の既定の字体には日本語が無い
62 self.ax.set_ylabel(tr("残差"), fontsize=8, color=tokens['text_primary'], fontfamily=JP_CAPABLE_FONT_FAMILIES)
63 self.ax.tick_params(labelsize=7, colors=tokens['text_primary'])
64 for spine in self.ax.spines.values():
65 spine.set_color(tokens['border_strong'])
66 self.canvas.draw_idle()