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
« 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
5from graphica.gui import notify
6from graphica.gui.dialogs import InsetDialog
8# 表示名 -> 描画側(canvas の _compute_stat_label_text)が解釈するキー
9STAT_LABEL_CHOICES = {
10 'R²': 'r_squared', 'Y平均': 'mean', 'Y標準偏差': 'std',
11 'Y最大値': 'max', 'Y最小値': 'min',
12}
15class OverlayController:
16 """今のデータセットに結びつけて図に重ねる表示(統計値ラベル、拡大図)を足す。"""
18 def __init__(self, host):
19 self._host = host
21 def add_stat_label(self):
22 """
23 今のデータセットの統計値(R²・平均など)を、その軸の左上から縦に積んで表示する。
24 値は描画のたびに計算し直すので、後でフィットし直しても追従する。
25 """
26 dataset = self._host.current_dataset()
27 if dataset is None:
28 return
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]
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))
44 self._host.add_annotation(axis_index, {
45 'type': 'stat', 'dataset_id': dataset.dataset_id, 'stat': stat,
46 'xy': xy, 'color': '#000000',
47 }, description="統計値アンカーラベルの追加")
49 def add_inset(self):
50 """今のデータセットの X 範囲の一部を拡大した小さな図を、その軸に重ねる。"""
51 dataset = self._host.current_dataset()
52 if dataset is None:
53 return
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
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)
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()
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="インセット(拡大図)の追加")