Coverage for graphica/gui/datasets/host.py: 96.7%
107 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"""機能ごとのクラスが本体(PlotterApp の1タブ)に頼むことの窓口。"""
2from contextlib import contextmanager
4from graphica.core.axis_settings import axis_setting
5from graphica.core.commands import SetAnnotationsCommand, SetMaskedRowsCommand
8class DatasetHost:
9 """機能ごとのクラスが PlotterApp の内部に直接触らないよう、使う操作だけをここに集める。"""
11 def __init__(self, app):
12 self._app = app
14 @property
15 def parent_widget(self):
16 return self._app
18 @property
19 def undo_stack(self):
20 return self._app.undo_stack
22 @contextmanager
23 def undo_macro(self, text, enabled=True):
24 """中の操作を Undo 1回分にまとめる(enabled=False なら何もしない)。"""
25 if enabled:
26 self._app.undo_stack.beginMacro(text)
27 try:
28 yield
29 finally:
30 if enabled:
31 self._app.undo_stack.endMacro()
33 def current_dataset(self):
34 return self._app.current_dataset()
36 def add_derived_dataset(self, dataset, source, description):
37 """source と同じフォルダにデータセットを足して描き直す(Undo できる。今の選択は変えない)。"""
38 source_item = self._app.dataset_tree_item(source)
39 folder = source_item.parent() if source_item else None
41 def add():
42 self._app.dataset_order.append(dataset, folder)
43 self._app.redraw()
45 self._app.push_dataset_additions(add, [dataset], description)
47 def add_annotation(self, axis_index, annotation, description):
48 self._app.add_annotation(axis_index, annotation, description)
50 def annotations(self, axis_index):
51 """その軸の注釈(リストはコピー)。"""
52 return list(axis_setting(self._app.project.all_plot_settings[axis_index], 'annotations'))
54 def add_annotations_to_active_axis(self, annotations, description):
55 """今の軸に注釈をまとめて足す(Undo 1回分)。"""
56 project = self._app.project
57 index = project.active_axis_index
58 if index >= len(project.all_plot_settings): 58 ↛ 59line 58 didn't jump to line 59 because the condition on line 58 was never true
59 return
60 old_annotations = self.annotations(index)
61 self._app.undo_stack.push(SetAnnotationsCommand(
62 project, index, old_annotations, old_annotations + list(annotations),
63 self._app.redraw_appearance, description=description))
65 def axis_y_span(self, axis_index):
66 """その軸の今の Y の表示幅。軸が無ければ None。"""
67 axes = self._app.canvas.all_axes
68 if not 0 <= axis_index < len(axes): 68 ↛ 69line 68 didn't jump to line 69 because the condition on line 68 was never true
69 return None
70 y_lo, y_hi = axes[axis_index].get_ylim()
71 return y_hi - y_lo
73 def selected_datasets(self):
74 return self._app.selected_datasets()
76 def target_folder_for_new_dataset(self):
77 """ツリーで選ばれているのがフォルダならそのフォルダ(新しいデータセットの置き場所)、それ以外は None。"""
78 return self._app.target_folder_for_new_dataset()
80 def add_datasets_to_folder(self, datasets, folder, description):
81 """まとめて足してから1回だけ描き直す(1件ずつ描き直すと件数分のフル再描画になる)。Undo 1回で消える。"""
82 def add():
83 for dataset in datasets:
84 self._app.dataset_order.append(dataset, folder)
85 self._app.redraw()
87 self._app.push_dataset_additions(add, list(datasets), description)
89 def axis_count(self):
90 return len(self._app.project.all_plot_settings)
92 def show_status(self, text, msecs=3000):
93 self._app.statusBar().showMessage(text, msecs)
95 def set_fit_button_enabled(self, enabled):
96 # 計算の間に選択を外していたら、終わっても押せないままにする(選択でボタンを決める update_ui_state と同じ)
97 self._app.fit_curve_button.setEnabled(enabled and bool(self.selected_datasets()))
99 def set_multi_peak_fit_button_enabled(self, enabled):
100 self._app.multi_peak_fit_button.setEnabled(enabled)
102 def pending_peak_guesses(self):
103 """グラフ上のクリックで置いた多峰フィットの初期値(のコピー)。"""
104 return list(self._app.pending_peak_guesses())
106 def finish_peak_placement(self):
107 """置いた初期値を消し、ピーク配置モードを抜ける。"""
108 self._app.clear_pending_peak_guesses()
109 if getattr(self._app, 'peak_placement_mode_enabled', False):
110 self._app.peak_placement_action.setChecked(False)
111 self._app.toggle_peak_placement_mode(False)
113 def datasets(self):
114 """タブの全データセット(リストはコピー)。"""
115 return list(self._app.project.datasets)
117 def add_dataset(self, dataset, parent_folder=None, select=True):
118 """ツリーと描画に足す(Undo の対象にはしない)。"""
119 return self._app.add_dataset(dataset, parent_folder, select=select)
121 def add_dataset_with_undo(self, dataset, parent_folder=None, description="データセットの追加"):
122 self._app.add_dataset_with_undo(dataset, parent_folder=parent_folder, description=description)
124 def redraw(self):
125 self._app.redraw()
127 def deferred_redraw(self):
128 """中で頼まれた描き直しを、抜けるときの1回にまとめる。"""
129 return self._app.deferred_redraw()
131 def batched_undo_group(self, text):
132 """Undo 1回分にまとめ、描き直しも(Undo・Redo のときも)最後の1回にする。"""
133 return self._app.batched_undo_group(text)
135 def refresh_ui_state(self):
136 """選択中のデータセットに合わせてパネルとメニューの状態を更新する。"""
137 self._app.property_panel.update_ui_state()
139 def active_color_cycle(self):
140 return self._app.colors.active_color_cycle()
142 @property
143 def project(self):
144 """文書全体。読むためだけに使う(書き換えは host の操作を通す)。"""
145 return self._app.project
147 @property
148 def settings(self):
149 """アプリ全体の設定(QSettings)。"""
150 return self._app.settings
152 def push_masked_rows(self, dataset, old_masked, new_masked, description):
153 """行のマスクの変更を Undo できる形で積む。積んだときも Undo/Redo でも描き直し、統計値も合わせる。"""
154 self._app.undo_stack.push(SetMaskedRowsCommand(
155 dataset, old_masked, new_masked, description=description,
156 on_applied=self._app.property_panel.on_data_structure_changed))
158 def push_property_change(self, dataset, old_values, new_values, description, skip_if_unchanged=True):
159 """データセットの属性の変更を Undo できる形で積む(skip_if_unchanged なら変化が無いときは何もしない)。"""
160 self._app.push_dataset_property_command(dataset, old_values, new_values, description,
161 skip_if_unchanged=skip_if_unchanged)
163 def remove_datasets(self, datasets, description=None):
164 """確認なしで Undo できる形で消す。"""
165 items = [item for dataset in datasets
166 if (item := self._app.dataset_tree_item(dataset)) is not None]
167 self._app.remove_dataset_items_with_undo(items, description=description)
169 def sibling_tabs(self):
170 """ほかのタブの (タブの見出し, そのタブの DatasetHost)。タブの窓に入っていなければ空。"""
171 from graphica.gui.main_app_window import MainAppWindow # main_app_window がこのパッケージを読み込むので遅らせる
172 top = self._app.window()
173 if not isinstance(top, MainAppWindow):
174 return []
175 tabs = top.tab_widget
176 return [(tabs.tabText(i), tabs.widget(i).dataset_host)
177 for i in range(tabs.count()) if tabs.widget(i) is not self._app]