Coverage for graphica/gui/datasets/overlays.py: 96.7%

48 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-12 16:38 +0000

1"""データセットに結びつけて図に重ねる表示: 統計値ラベルと拡大図(インセット)。""" 

2import numpy as np 

3from PySide6.QtWidgets import QDialog 

4 

5from graphica.gui import notify 

6from graphica.gui.dialogs import InsetDialog 

7 

8# 表示名 -> 描画側(canvas の _compute_stat_label_text)が解釈するキー 

9STAT_LABEL_CHOICES = { 

10 'R²': 'r_squared', 'Y平均': 'mean', 'Y標準偏差': 'std', 

11 'Y最大値': 'max', 'Y最小値': 'min', 

12} 

13 

14 

15class OverlayController: 

16 """今のデータセットに結びつけて図に重ねる表示(統計値ラベル、拡大図)を足す。""" 

17 

18 def __init__(self, host): 

19 self._host = host 

20 

21 def add_stat_label(self): 

22 """ 

23 今のデータセットの統計値(R²・平均など)を、その軸の左上から縦に積んで表示する。 

24 値は描画のたびに計算し直すので、後でフィットし直しても追従する。 

25 """ 

26 dataset = self._host.current_dataset() 

27 if dataset is None: 

28 return 

29 

30 choice, ok = notify.get_item( 

31 self._host.parent_widget, "統計値アンカーラベルの追加", "表示する統計値:", 

32 list(STAT_LABEL_CHOICES.keys()), 0, False 

33 ) 

34 if not ok: 

35 return 

36 stat = STAT_LABEL_CHOICES[choice] 

37 

38 axis_index = dataset.subplot_target 

39 existing_stat_count = sum( 

40 1 for ann in self._host.annotations(axis_index) if ann.get('type') == 'stat' 

41 ) 

42 xy = (0.05, max(0.95 - 0.07 * existing_stat_count, 0.05)) 

43 

44 self._host.add_annotation(axis_index, { 

45 'type': 'stat', 'dataset_id': dataset.dataset_id, 'stat': stat, 

46 'xy': xy, 'color': '#000000', 

47 }, description="統計値アンカーラベルの追加") 

48 

49 def add_inset(self): 

50 """今のデータセットの X 範囲の一部を拡大した小さな図を、その軸に重ねる。""" 

51 dataset = self._host.current_dataset() 

52 if dataset is None: 

53 return 

54 

55 x_data = np.asarray(dataset.x_data, dtype=float) 

56 x_data = x_data[~np.isnan(x_data)] 

57 if len(x_data) < 2: 

58 notify.warning(self._host.parent_widget, "インセット(拡大図)", "有効なデータ点が不足しています(最低2点必要)。") 

59 return 

60 x_min, x_max = float(np.min(x_data)), float(np.max(x_data)) 

61 span = x_max - x_min 

62 default_zoom_min = x_min + span * 0.4 

63 default_zoom_max = x_min + span * 0.6 

64 

65 # Y範囲の欄の初期値。Y が文字の列なら数値にできないので仮の値 

66 try: 

67 y_data = np.asarray(dataset.y_data, dtype=float) 

68 except (TypeError, ValueError): 

69 y_data = np.array([]) 

70 y_data = y_data[np.isfinite(y_data)] 

71 default_y_range = (float(np.min(y_data)), float(np.max(y_data))) if len(y_data) else (0.0, 1.0) 

72 

73 dialog = InsetDialog(x_min, x_max, default_zoom_min, default_zoom_max, parent=self._host.parent_widget, 

74 default_y_range=default_y_range) 

75 if dialog.exec() != QDialog.DialogCode.Accepted: 

76 return 

77 settings = dialog.get_settings() 

78 

79 axis_index = dataset.subplot_target 

80 annotation = { 

81 'type': 'inset', 'corner': settings['corner'], 'size': settings['size'], 

82 'zoom_x_range': settings['zoom_x_range'], 'color': '#000000', 

83 } 

84 # 指定しなかったときはキーを書かない(自動の範囲) 

85 if 'zoom_y_range' in settings: 

86 annotation['zoom_y_range'] = settings['zoom_y_range'] 

87 self._host.add_annotation(axis_index, annotation, description="インセット(拡大図)の追加")