graphica-plot 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. graphica/Graphica.ico +0 -0
  2. graphica/__init__.py +1 -0
  3. graphica/__main__.py +111 -0
  4. graphica/assets/__init__.py +0 -0
  5. graphica/assets/icons/__init__.py +0 -0
  6. graphica/assets/icons/arrow-right.svg +21 -0
  7. graphica/assets/icons/bold.svg +20 -0
  8. graphica/assets/icons/calculator.svg +26 -0
  9. graphica/assets/icons/chart-histogram.svg +24 -0
  10. graphica/assets/icons/chart-line.svg +20 -0
  11. graphica/assets/icons/chevron-down.svg +19 -0
  12. graphica/assets/icons/chevron-right.svg +19 -0
  13. graphica/assets/icons/color-swatch.svg +22 -0
  14. graphica/assets/icons/column-insert-right.svg +21 -0
  15. graphica/assets/icons/column-remove.svg +21 -0
  16. graphica/assets/icons/copy.svg +20 -0
  17. graphica/assets/icons/download.svg +21 -0
  18. graphica/assets/icons/edit.svg +21 -0
  19. graphica/assets/icons/eye-off.svg +21 -0
  20. graphica/assets/icons/eye.svg +20 -0
  21. graphica/assets/icons/file-plus.svg +22 -0
  22. graphica/assets/icons/folder-plus.svg +21 -0
  23. graphica/assets/icons/folder.svg +19 -0
  24. graphica/assets/icons/highlight.svg +22 -0
  25. graphica/assets/icons/history.svg +20 -0
  26. graphica/assets/icons/italic.svg +21 -0
  27. graphica/assets/icons/layout-grid.svg +22 -0
  28. graphica/assets/icons/math-function.svg +22 -0
  29. graphica/assets/icons/message-2.svg +21 -0
  30. graphica/assets/icons/mountain.svg +20 -0
  31. graphica/assets/icons/palette.svg +22 -0
  32. graphica/assets/icons/pointer.svg +19 -0
  33. graphica/assets/icons/refresh.svg +20 -0
  34. graphica/assets/icons/row-insert-bottom.svg +21 -0
  35. graphica/assets/icons/row-remove.svg +21 -0
  36. graphica/assets/icons/search.svg +20 -0
  37. graphica/assets/icons/select-all.svg +35 -0
  38. graphica/assets/icons/subscript.svg +20 -0
  39. graphica/assets/icons/superscript.svg +20 -0
  40. graphica/assets/icons/table.svg +21 -0
  41. graphica/assets/icons/trash.svg +23 -0
  42. graphica/assets/icons/typography.svg +23 -0
  43. graphica/assets/icons/x.svg +20 -0
  44. graphica/core/__init__.py +0 -0
  45. graphica/core/analysis.py +1278 -0
  46. graphica/core/app_paths.py +35 -0
  47. graphica/core/axis_settings.py +128 -0
  48. graphica/core/caption_export.py +47 -0
  49. graphica/core/color_palettes.py +56 -0
  50. graphica/core/commands.py +226 -0
  51. graphica/core/cvd_simulation.py +49 -0
  52. graphica/core/dataset.py +443 -0
  53. graphica/core/diagnostics.py +105 -0
  54. graphica/core/excel_utils.py +58 -0
  55. graphica/core/fit_models.py +379 -0
  56. graphica/core/grid_data.py +128 -0
  57. graphica/core/i18n.py +44 -0
  58. graphica/core/json_utils.py +27 -0
  59. graphica/core/label_utils.py +21 -0
  60. graphica/core/methods_text.py +115 -0
  61. graphica/core/named_colors.py +126 -0
  62. graphica/core/plugin_api.py +515 -0
  63. graphica/core/plugin_context.py +167 -0
  64. graphica/core/plugin_install.py +82 -0
  65. graphica/core/plugin_manifest.py +67 -0
  66. graphica/core/plugin_testing.py +213 -0
  67. graphica/core/plugin_types.py +118 -0
  68. graphica/core/provenance.py +19 -0
  69. graphica/core/report_export.py +51 -0
  70. graphica/core/safe_eval.py +194 -0
  71. graphica/core/script_export.py +272 -0
  72. graphica/core/translations_en.py +530 -0
  73. graphica/core/unit_conversion.py +51 -0
  74. graphica/core/update_check.py +43 -0
  75. graphica/core/version.py +14 -0
  76. graphica/gui/__init__.py +0 -0
  77. graphica/gui/app_settings.py +119 -0
  78. graphica/gui/axis_bindings.py +173 -0
  79. graphica/gui/binding.py +135 -0
  80. graphica/gui/builders/__init__.py +1 -0
  81. graphica/gui/builders/axis_panel.py +431 -0
  82. graphica/gui/builders/canvas_area.py +176 -0
  83. graphica/gui/builders/common.py +120 -0
  84. graphica/gui/builders/dataset_panel.py +433 -0
  85. graphica/gui/builders/property_sections.py +208 -0
  86. graphica/gui/canvas.py +519 -0
  87. graphica/gui/color_history.py +47 -0
  88. graphica/gui/color_picker_widget.py +198 -0
  89. graphica/gui/crash_handler.py +77 -0
  90. graphica/gui/cvd_preview.py +23 -0
  91. graphica/gui/data_editor.py +761 -0
  92. graphica/gui/data_import_flow.py +355 -0
  93. graphica/gui/dataset_bindings.py +107 -0
  94. graphica/gui/dataset_style_icon.py +80 -0
  95. graphica/gui/datasets/__init__.py +1 -0
  96. graphica/gui/datasets/actions_menu.py +114 -0
  97. graphica/gui/datasets/colors.py +218 -0
  98. graphica/gui/datasets/fitting.py +70 -0
  99. graphica/gui/datasets/host.py +162 -0
  100. graphica/gui/datasets/operations/__init__.py +1 -0
  101. graphica/gui/datasets/operations/fitting.py +468 -0
  102. graphica/gui/datasets/operations/peaks.py +109 -0
  103. graphica/gui/datasets/operations/processing.py +659 -0
  104. graphica/gui/datasets/operations/runner.py +148 -0
  105. graphica/gui/datasets/operations/transfer.py +227 -0
  106. graphica/gui/datasets/order.py +310 -0
  107. graphica/gui/datasets/overlays.py +74 -0
  108. graphica/gui/datasets/peaks.py +21 -0
  109. graphica/gui/datasets/plugin_runs.py +99 -0
  110. graphica/gui/datasets/processing.py +68 -0
  111. graphica/gui/datasets/property_panel.py +538 -0
  112. graphica/gui/datasets/transfer.py +36 -0
  113. graphica/gui/detached_canvas_window.py +22 -0
  114. graphica/gui/dialogs/__init__.py +137 -0
  115. graphica/gui/dialogs/analysis.py +1322 -0
  116. graphica/gui/dialogs/app.py +1015 -0
  117. graphica/gui/dialogs/appearance.py +851 -0
  118. graphica/gui/dialogs/data_edit.py +577 -0
  119. graphica/gui/dialogs/data_import.py +679 -0
  120. graphica/gui/dialogs/export.py +433 -0
  121. graphica/gui/dock_layout.py +176 -0
  122. graphica/gui/export_preview_panel.py +321 -0
  123. graphica/gui/export_settings.py +16 -0
  124. graphica/gui/file_association.py +97 -0
  125. graphica/gui/icon_utils.py +44 -0
  126. graphica/gui/main_app_window.py +210 -0
  127. graphica/gui/main_window.py +1151 -0
  128. graphica/gui/mathtext_preview.py +168 -0
  129. graphica/gui/menu_bar.py +320 -0
  130. graphica/gui/minimap_widget.py +151 -0
  131. graphica/gui/mixins/__init__.py +0 -0
  132. graphica/gui/mixins/export_mixin.py +520 -0
  133. graphica/gui/mixins/help_mixin.py +127 -0
  134. graphica/gui/mixins/project_io_mixin.py +289 -0
  135. graphica/gui/mixins/quick_access_mixin.py +194 -0
  136. graphica/gui/mixins/ui_setup_mixin.py +206 -0
  137. graphica/gui/notify.py +61 -0
  138. graphica/gui/panels/__init__.py +43 -0
  139. graphica/gui/panels/axis_settings.py +605 -0
  140. graphica/gui/panels/dataset_tree.py +424 -0
  141. graphica/gui/plot_type_drawers.py +131 -0
  142. graphica/gui/plugin_context.py +119 -0
  143. graphica/gui/project_files.py +361 -0
  144. graphica/gui/provenance_panel.py +65 -0
  145. graphica/gui/rendering/__init__.py +1 -0
  146. graphica/gui/rendering/annotations.py +177 -0
  147. graphica/gui/rendering/appearance.py +368 -0
  148. graphica/gui/rendering/common.py +316 -0
  149. graphica/gui/rendering/data_1d.py +376 -0
  150. graphica/gui/rendering/data_2d.py +66 -0
  151. graphica/gui/residual_panel.py +62 -0
  152. graphica/gui/resources.py +16 -0
  153. graphica/gui/single_instance.py +141 -0
  154. graphica/gui/splash.py +83 -0
  155. graphica/gui/task_runner.py +41 -0
  156. graphica/gui/theme.py +864 -0
  157. graphica/gui/tools/__init__.py +56 -0
  158. graphica/gui/tools/annotation.py +275 -0
  159. graphica/gui/tools/cursor.py +259 -0
  160. graphica/gui/tools/layout_edit.py +285 -0
  161. graphica/gui/tools/manager.py +78 -0
  162. graphica/gui/tools/peak_placement.py +132 -0
  163. graphica/gui/tools/range_select.py +191 -0
  164. graphica/gui/tools/region_highlight.py +222 -0
  165. graphica/gui/tools/slice_extraction.py +200 -0
  166. graphica/gui/widget_translation.py +46 -0
  167. graphica/gui/workers.py +280 -0
  168. graphica/models/__init__.py +0 -0
  169. graphica/models/project.py +197 -0
  170. graphica/plugin/__init__.py +20 -0
  171. graphica/plugin/testing.py +57 -0
  172. graphica/sample_data/__init__.py +0 -0
  173. graphica/sample_data/cooling_curve_sample.csv +42 -0
  174. graphica/ui_main_window.py +581 -0
  175. graphica_plot-2.0.0.dist-info/METADATA +317 -0
  176. graphica_plot-2.0.0.dist-info/RECORD +181 -0
  177. graphica_plot-2.0.0.dist-info/WHEEL +5 -0
  178. graphica_plot-2.0.0.dist-info/entry_points.txt +2 -0
  179. graphica_plot-2.0.0.dist-info/licenses/LICENSE +30 -0
  180. graphica_plot-2.0.0.dist-info/licenses/THIRD_PARTY_LICENSES.md +91 -0
  181. graphica_plot-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,206 @@
1
+ """PlotterApp の一度きりの組み立て(シグナル、メニューバー、初めの画面の状態)と、ダークモードの切り替え。"""
2
+ from PySide6.QtWidgets import QApplication
3
+
4
+ from graphica.gui import app_settings
5
+ from graphica.gui.theme import apply_theme
6
+ from graphica.gui.dialogs import CommandPaletteDialog
7
+ from graphica.gui.datasets.actions_menu import populate_dataset_actions_menu
8
+ from graphica.gui.menu_bar import build_menu_bar
9
+
10
+
11
+ class UISetupMixin:
12
+ def _connect_signals(self):
13
+ """画面の部品のシグナルをつなぐ。__init__ から1回だけ。同じシグナルに複数つなぐものは、つないだ順に呼ばれる。"""
14
+ self._connect_layout_signals()
15
+ self._connect_axis_setting_signals()
16
+ self._connect_dataset_signals()
17
+
18
+ def _create_menu_bar(self):
19
+ build_menu_bar(self)
20
+
21
+ def _populate_dataset_menu(self):
22
+ populate_dataset_actions_menu(self, self._dataset_menu)
23
+
24
+ def _collect_menu_actions(self):
25
+ """
26
+ メニューの全項目を、階層のパス付きで集める(コマンドパレット・クイックアクセス・ショートカット一覧が使う)。
27
+ 区切り線・空の項目・サブメニュー自体は除く。「最近使ったファイル」はコマンドではないので除く。
28
+
29
+ 最上位のメニューは menuBar().actions() から .menu() で辿らず、組み立て時に持たせた self._file_menu などを使う
30
+ (辿ると、参照が切れたときにメニューごと回収されることがある)。「最近使ったファイル」の判定も、QMenu の
31
+ 同一性ではなく持たせてある self._recent_files_menu_action で行う(繰り返すうちに QMenu の同一性が崩れ、
32
+ 除いたはずの子の項目が混ざることがあった)。
33
+ """
34
+ results = []
35
+ recent_files_action = getattr(self, '_recent_files_menu_action', None)
36
+
37
+ def walk(menu, path):
38
+ for action in menu.actions():
39
+ if action.isSeparator() or menu is self.recent_files_menu:
40
+ continue
41
+ if action is recent_files_action:
42
+ continue
43
+ submenu = action.menu()
44
+ if submenu is not None:
45
+ if submenu is not self.recent_files_menu:
46
+ walk(submenu, path + [action.text().replace('&', '')])
47
+ else:
48
+ text = action.text().replace('&', '').strip()
49
+ if text:
50
+ results.append((path + [text], action))
51
+
52
+ # 「データセット」メニューは含めない。開くたびに作り直すので、候補がそのときの選択で変わり、
53
+ # ピン留めの識別子も安定しない(作り直すサブメニューを辿るとメニューの回収も起きやすい)
54
+ top_menus = [self._file_menu, self._edit_menu, self._view_menu, self._help_menu]
55
+ # 「プラグイン」メニューは、プラグインが何も登録していなければ作られない
56
+ plugin_menu = getattr(self, '_plugin_menu', None)
57
+ if plugin_menu is not None:
58
+ top_menus.append(plugin_menu)
59
+ for top_menu in top_menus:
60
+ walk(top_menu, [top_menu.title().replace('&', '')])
61
+ return results
62
+
63
+ def _on_show_command_palette(self):
64
+ dialog = CommandPaletteDialog(self._collect_menu_actions, self)
65
+ dialog.exec()
66
+
67
+ def _on_toggle_dark_mode(self, checked):
68
+ """アプリ全体の配色(Qt のパレットと QSS)と、このタブのグラフ・アイコンを切り替える。"""
69
+ apply_theme(QApplication.instance(), checked)
70
+ self.canvas.dark_mode = checked
71
+ app_settings.DARK_MODE.write(self.settings, checked)
72
+ self._update_plot(light=True) # 軸の数は変わらないので軽い描き直しでよい
73
+ # 以下は作ったときにテーマの色を焼き込んでいるので、描き直す:
74
+ # タイトルと軸ラベルのプレビュー
75
+ self._refresh_all_label_previews()
76
+ # 色欄の見本の枠
77
+ self.color_picker_widget.refresh_theme()
78
+ self.gradient_color2_picker.refresh_theme()
79
+ # matplotlib のツールバーのアイコン
80
+ self._refresh_mpl_toolbar_icons()
81
+ # 自前の SVG アイコン
82
+ self._refresh_custom_svg_icons()
83
+ # 開いたままのデータエディタ(除外した行の背景色)
84
+ if getattr(self, 'data_editor_dialog', None) is not None:
85
+ self.data_editor_dialog._populate_table()
86
+ # 開いたままのヘルプ(表の見出しの色)
87
+ for dialog_attr in ('help_dialog', 'calc_help_dialog'):
88
+ dialog = getattr(self, dialog_attr, None)
89
+ if dialog is not None and hasattr(dialog, 'refresh_theme'):
90
+ dialog.refresh_theme()
91
+ # QSS はアプリ全体に効くが、グラフ・アイコン・メニューのチェックはタブごとなので、ほかのタブにも当てる
92
+ self._sync_dark_mode_to_sibling_tabs(checked)
93
+
94
+ def _sync_dark_mode_to_sibling_tabs(self, checked):
95
+ """ほかのタブにも同じダークモードを当てる(QSS はアプリ全体だが、グラフやアイコンはタブごと)。"""
96
+ main_app_window = self.window()
97
+ tab_widget = getattr(main_app_window, 'tab_widget', None)
98
+ if tab_widget is None:
99
+ return
100
+ for i in range(tab_widget.count()):
101
+ tab = tab_widget.widget(i)
102
+ if tab is None or tab is self:
103
+ continue
104
+ if not hasattr(tab, 'dark_mode_action') or not hasattr(tab, 'canvas'):
105
+ continue
106
+ if tab.canvas.dark_mode == checked:
107
+ continue
108
+ tab.canvas.dark_mode = checked
109
+ app_settings.DARK_MODE.write(tab.settings, checked)
110
+ # toggled を出すと、そのタブがまたほかのタブへ当て直しを連鎖させるので、シグナルを止めてチェックだけ揃える
111
+ tab.dark_mode_action.blockSignals(True)
112
+ tab.dark_mode_action.setChecked(checked)
113
+ tab.dark_mode_action.blockSignals(False)
114
+ tab._update_plot(light=True)
115
+ tab._refresh_all_label_previews()
116
+ tab.color_picker_widget.refresh_theme()
117
+ tab.gradient_color2_picker.refresh_theme()
118
+ tab._refresh_mpl_toolbar_icons()
119
+ tab._refresh_custom_svg_icons()
120
+
121
+ def _refresh_mpl_toolbar_icons(self):
122
+ """
123
+ matplotlib のツールバーのアイコンを今のパレットで読み直す。matplotlib は作ったときに一度だけ明暗を決めるので、
124
+ ダークモードを切り替えても古い色のまま残る。非公開の toolitems/_actions を使うので、無ければ何もしない。
125
+ """
126
+ toolbar = getattr(self, 'mpl_toolbar', None)
127
+ if toolbar is None:
128
+ return
129
+ toolitems = getattr(toolbar, 'toolitems', None)
130
+ actions = getattr(toolbar, '_actions', None)
131
+ if toolitems is None or actions is None:
132
+ return
133
+ for text, _tooltip_text, image_file, callback in toolitems:
134
+ if text is None:
135
+ continue
136
+ action = actions.get(callback)
137
+ if action is not None:
138
+ action.setIcon(toolbar._icon(image_file + '.png'))
139
+
140
+ def _refresh_custom_svg_icons(self):
141
+ """
142
+ 自前の SVG アイコンを今のテーマで作り直す。_svg_icon() は作るときに色を決めるだけで、
143
+ setIcon() 済みのアイコンは変わらない(ダイアログは開くたびに作るので対象外)。
144
+ """
145
+ from graphica.gui.main_window import _svg_icon
146
+
147
+ if hasattr(self, 'cursor_action'):
148
+ self.cursor_action.setIcon(_svg_icon("pointer"))
149
+ if hasattr(self, 'annotation_action'):
150
+ self.annotation_action.setIcon(_svg_icon("message-2"))
151
+ if hasattr(self, 'layout_edit_action'):
152
+ self.layout_edit_action.setIcon(_svg_icon("layout-grid"))
153
+ if hasattr(self, 'reset_zoom_action'):
154
+ self.reset_zoom_action.setIcon(_svg_icon("refresh"))
155
+ if hasattr(self, 'stats_toolbar_button'):
156
+ self.stats_toolbar_button.setIcon(_svg_icon("chart-histogram"))
157
+ if hasattr(self, 'manage_palette_action'):
158
+ self.manage_palette_action.setIcon(_svg_icon("palette", size=16))
159
+ if hasattr(self, 'colormap_assign_action'):
160
+ self.colormap_assign_action.setIcon(_svg_icon("palette", size=16))
161
+
162
+ for button, (icon_name, _label) in getattr(self, '_dataset_action_button_icons', {}).items():
163
+ button.setIcon(_svg_icon(icon_name, size=18))
164
+ for button, icon_name in getattr(self, '_field_icon_buttons', {}).items():
165
+ button.setIcon(_svg_icon(icon_name, size=16))
166
+ for toggle_button in getattr(self, '_collapsible_toggle_buttons', []):
167
+ toggle_button.setIcon(
168
+ _svg_icon("chevron-down" if toggle_button.isChecked() else "chevron-right", size=14)
169
+ )
170
+ # プロパティ欄の節の見出しも同じ矢印
171
+ for entry in getattr(self, '_prop_sections', {}).values():
172
+ button = entry['toggle']
173
+ button.setIcon(
174
+ _svg_icon("chevron-down" if button.isChecked() else "chevron-right", size=13)
175
+ )
176
+
177
+ def _set_initial_ui_state(self):
178
+
179
+ self.ui.x_autoscale_checkbox.setChecked(True)
180
+ self.ui.y_autoscale_checkbox.setChecked(True)
181
+ self.ui.legend_visible_checkbox.setChecked(True)
182
+
183
+
184
+ self.y2_label_text_label.setVisible(False)
185
+ self.y2_label_text_edit.setVisible(False)
186
+ self.tick_direction_y2_label.setVisible(False)
187
+ self.major_tick_direction_y2_combo.setVisible(False)
188
+ self.minor_tick_direction_y2_combo.setVisible(False)
189
+
190
+ self.fit_info_label.setVisible(False)
191
+ self.fit_info_textedit.setVisible(False)
192
+
193
+ self.ui.grid_visible_checkbox.setChecked(False)
194
+ self.ui.minor_grid_visible_checkbox.setChecked(False)
195
+
196
+ self.ui.spine_width_spinbox.setValue(self._spine_width)
197
+ self.ui.tick_width_spinbox.setValue(self._tick_width)
198
+
199
+ # チェックの状態に合わせて関係する欄の有効/無効を揃える
200
+ self._on_x_minor_tick_visibility_changed()
201
+ self._on_y_minor_tick_visibility_changed()
202
+ self._on_grid_visibility_changed()
203
+ self._on_legend_visibility_changed() # 凡例関連のUIを有効化
204
+ self._refresh_y2_axis_state()
205
+
206
+ self.property_panel.update_ui_state()
graphica/gui/notify.py ADDED
@@ -0,0 +1,61 @@
1
+ """利用者へのメッセージと問い合わせ(ファイル・文字の入力)の窓口。
2
+
3
+ Qt の静的メソッドを同じ引数でそのまま呼ぶだけで、文言・題・ボタン・既定のボタン・親・戻り値は呼ぶ側が決める。
4
+ 呼ぶときにクラスの属性を引くので、テストは QMessageBox などのクラスの属性を差し替えれば、どの画面の通知も記録できる。
5
+ crash_handler は例外処理の最中に動くので、ここを通さない(import の順と初期化の失敗を避ける)。
6
+ """
7
+ from typing import Any
8
+
9
+ from PySide6.QtWidgets import QFileDialog, QInputDialog, QMessageBox
10
+
11
+
12
+ def information(*args: Any, **kwargs: Any) -> Any:
13
+ return QMessageBox.information(*args, **kwargs)
14
+
15
+
16
+ def warning(*args: Any, **kwargs: Any) -> Any:
17
+ return QMessageBox.warning(*args, **kwargs)
18
+
19
+
20
+ def critical(*args: Any, **kwargs: Any) -> Any:
21
+ return QMessageBox.critical(*args, **kwargs)
22
+
23
+
24
+ def question(*args: Any, **kwargs: Any) -> Any:
25
+ return QMessageBox.question(*args, **kwargs)
26
+
27
+
28
+ def about(*args: Any, **kwargs: Any) -> Any:
29
+ return QMessageBox.about(*args, **kwargs)
30
+
31
+
32
+ def get_open_file_name(*args: Any, **kwargs: Any) -> Any:
33
+ return QFileDialog.getOpenFileName(*args, **kwargs)
34
+
35
+
36
+ def get_open_file_names(*args: Any, **kwargs: Any) -> Any:
37
+ return QFileDialog.getOpenFileNames(*args, **kwargs)
38
+
39
+
40
+ def get_save_file_name(*args: Any, **kwargs: Any) -> Any:
41
+ return QFileDialog.getSaveFileName(*args, **kwargs)
42
+
43
+
44
+ def get_existing_directory(*args: Any, **kwargs: Any) -> Any:
45
+ return QFileDialog.getExistingDirectory(*args, **kwargs)
46
+
47
+
48
+ def get_text(*args: Any, **kwargs: Any) -> Any:
49
+ return QInputDialog.getText(*args, **kwargs)
50
+
51
+
52
+ def get_int(*args: Any, **kwargs: Any) -> Any:
53
+ return QInputDialog.getInt(*args, **kwargs)
54
+
55
+
56
+ def get_double(*args: Any, **kwargs: Any) -> Any:
57
+ return QInputDialog.getDouble(*args, **kwargs)
58
+
59
+
60
+ def get_item(*args: Any, **kwargs: Any) -> Any:
61
+ return QInputDialog.getItem(*args, **kwargs)
@@ -0,0 +1,43 @@
1
+ """画面の欄をまとめる部品(gui/panels/*.py)と、PlotterApp に部品の名前を出す窓口。
2
+
3
+ 欄のウィジェットと状態はタブが持ち、部品は欄の読み書き・処理・信号の配線を持つ。PlotterApp は各部品の EXPOSED_NAMES を
4
+ 同じ名前の属性として出し、読み書きを部品へ回す(テスト・メニュー・ほかの部品が今までの名前で使うため。monkeypatch も部品に届く)。
5
+ """
6
+ from graphica.gui.panels.axis_settings import AxisSettingsPanel
7
+ from graphica.gui.panels.dataset_tree import DatasetTreePanel
8
+
9
+ __all__ = ["PANEL_CLASSES", "create_panels", "expose_panel_names"]
10
+
11
+ PANEL_CLASSES = {
12
+ "axis_settings": AxisSettingsPanel,
13
+ "dataset_tree": DatasetTreePanel,
14
+ }
15
+
16
+
17
+ def create_panels(app):
18
+ return {key: panel_class(app) for key, panel_class in PANEL_CLASSES.items()}
19
+
20
+
21
+ class _PanelName:
22
+ """PlotterApp の属性として、部品の同じ名前の属性を読み書きする。"""
23
+
24
+ def __init__(self, panel_key, name):
25
+ self._panel_key = panel_key
26
+ self._name = name
27
+
28
+ def __get__(self, app, owner=None):
29
+ if app is None:
30
+ # クラスから引いたとき(メニューの表の検査など)は、部品のクラスの同じ名前を返す
31
+ return getattr(PANEL_CLASSES[self._panel_key], self._name, self)
32
+ return getattr(app.panels[self._panel_key], self._name)
33
+
34
+ def __set__(self, app, value):
35
+ setattr(app.panels[self._panel_key], self._name, value)
36
+
37
+
38
+ def expose_panel_names(app_class):
39
+ for key, panel_class in PANEL_CLASSES.items():
40
+ for name in panel_class.EXPOSED_NAMES:
41
+ if any(name in klass.__dict__ for klass in app_class.__mro__):
42
+ raise TypeError(f"{app_class.__name__}.{name} は部品 {key} の名前とぶつかる")
43
+ setattr(app_class, name, _PanelName(key, name))