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

1"""機能ごとのクラスが本体(PlotterApp の1タブ)に頼むことの窓口。""" 

2from contextlib import contextmanager 

3 

4from graphica.core.axis_settings import axis_setting 

5from graphica.core.commands import SetAnnotationsCommand, SetMaskedRowsCommand 

6 

7 

8class DatasetHost: 

9 """機能ごとのクラスが PlotterApp の内部に直接触らないよう、使う操作だけをここに集める。""" 

10 

11 def __init__(self, app): 

12 self._app = app 

13 

14 @property 

15 def parent_widget(self): 

16 return self._app 

17 

18 @property 

19 def undo_stack(self): 

20 return self._app.undo_stack 

21 

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

32 

33 def current_dataset(self): 

34 return self._app.current_dataset() 

35 

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 

40 

41 def add(): 

42 self._app.dataset_order.append(dataset, folder) 

43 self._app.redraw() 

44 

45 self._app.push_dataset_additions(add, [dataset], description) 

46 

47 def add_annotation(self, axis_index, annotation, description): 

48 self._app.add_annotation(axis_index, annotation, description) 

49 

50 def annotations(self, axis_index): 

51 """その軸の注釈(リストはコピー)。""" 

52 return list(axis_setting(self._app.project.all_plot_settings[axis_index], 'annotations')) 

53 

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

64 

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 

72 

73 def selected_datasets(self): 

74 return self._app.selected_datasets() 

75 

76 def target_folder_for_new_dataset(self): 

77 """ツリーで選ばれているのがフォルダならそのフォルダ(新しいデータセットの置き場所)、それ以外は None。""" 

78 return self._app.target_folder_for_new_dataset() 

79 

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

86 

87 self._app.push_dataset_additions(add, list(datasets), description) 

88 

89 def axis_count(self): 

90 return len(self._app.project.all_plot_settings) 

91 

92 def show_status(self, text, msecs=3000): 

93 self._app.statusBar().showMessage(text, msecs) 

94 

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

98 

99 def set_multi_peak_fit_button_enabled(self, enabled): 

100 self._app.multi_peak_fit_button.setEnabled(enabled) 

101 

102 def pending_peak_guesses(self): 

103 """グラフ上のクリックで置いた多峰フィットの初期値(のコピー)。""" 

104 return list(self._app.pending_peak_guesses()) 

105 

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) 

112 

113 def datasets(self): 

114 """タブの全データセット(リストはコピー)。""" 

115 return list(self._app.project.datasets) 

116 

117 def add_dataset(self, dataset, parent_folder=None, select=True): 

118 """ツリーと描画に足す(Undo の対象にはしない)。""" 

119 return self._app.add_dataset(dataset, parent_folder, select=select) 

120 

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) 

123 

124 def redraw(self): 

125 self._app.redraw() 

126 

127 def deferred_redraw(self): 

128 """中で頼まれた描き直しを、抜けるときの1回にまとめる。""" 

129 return self._app.deferred_redraw() 

130 

131 def batched_undo_group(self, text): 

132 """Undo 1回分にまとめ、描き直しも(Undo・Redo のときも)最後の1回にする。""" 

133 return self._app.batched_undo_group(text) 

134 

135 def refresh_ui_state(self): 

136 """選択中のデータセットに合わせてパネルとメニューの状態を更新する。""" 

137 self._app.property_panel.update_ui_state() 

138 

139 def active_color_cycle(self): 

140 return self._app.colors.active_color_cycle() 

141 

142 @property 

143 def project(self): 

144 """文書全体。読むためだけに使う(書き換えは host の操作を通す)。""" 

145 return self._app.project 

146 

147 @property 

148 def settings(self): 

149 """アプリ全体の設定(QSettings)。""" 

150 return self._app.settings 

151 

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

157 

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) 

162 

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) 

168 

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]