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

1"""選んだデータセットのフィットの残差(下のパネルの「残差」のタブ)。fit_result の残差をそのまま描く(計算し直さない)。 

2 

3メインのグラフを上下2段に分けないのは、サブプロットの数・配置・自由配置・第2Y軸などの前提と絡み合うため。 

4""" 

5import logging 

6 

7from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas 

8from matplotlib.figure import Figure 

9from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel 

10 

11from graphica.core.i18n import tr 

12from graphica.gui import theme 

13from graphica.gui.mathtext_preview import JP_CAPABLE_FONT_FAMILIES 

14 

15logger = logging.getLogger(__name__) 

16 

17 

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) 

23 

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) 

31 

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) 

38 

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 

45 

46 if not residual_x or not residuals: 

47 self.canvas.setVisible(False) 

48 self.placeholder_label.setVisible(True) 

49 return 

50 

51 self.placeholder_label.setVisible(False) 

52 self.canvas.setVisible(True) 

53 

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()