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,431 @@
1
+ """軸の設定の欄(凡例の位置・目盛り・グリッド・カラーバー・目盛りの書式・タイトルと軸ラベル・ラベルのタブ)の組み立て。"""
2
+ from PySide6.QtCore import Qt
3
+ from PySide6.QtGui import QFont
4
+ from PySide6.QtWidgets import (
5
+ QCheckBox, QComboBox, QDoubleSpinBox, QFormLayout, QGroupBox, QHBoxLayout, QLabel, QLineEdit, QPushButton,
6
+ QSizePolicy, QSpinBox, QToolButton, QVBoxLayout, QWidget)
7
+ from graphica.core.i18n import tr
8
+ from graphica.core.unit_conversion import X_AXIS_UNIT_CHOICES, X_AXIS_UNIT_LABELS
9
+ from graphica.gui.builders.common import _ClickableMathPreviewLabel, _insert_form_row_after, _svg_icon
10
+ from graphica.gui.canvas import DEFAULT_MAJOR_TICK_LENGTH, MINOR_TICK_LENGTH_AUTO
11
+ from graphica.gui.tools.layout_edit import MIN_FREE_RECT_SIZE
12
+
13
+
14
+ def build_legend_location_control(app):
15
+ app.legend_loc_label = QLabel("凡例の位置")
16
+ app.legend_loc_combo = QComboBox()
17
+ app.legend_loc_combo.addItems([
18
+ "best", "upper right", "upper left", "lower left", "lower right", "center"
19
+ ])
20
+ _insert_form_row_after(app.ui.formLayout_3, app.ui.legend_visible_checkbox,
21
+ app.legend_loc_label, app.legend_loc_combo)
22
+
23
+ app.legend_font_label = QLabel("凡例フォント")
24
+ app.legend_font_button = QPushButton("フォント選択...")
25
+ _insert_form_row_after(app.ui.formLayout_3, app.legend_loc_combo,
26
+ app.legend_font_label, app.legend_font_button)
27
+
28
+ app.legend_color_label = QLabel("凡例 文字色")
29
+ app.legend_color_button = QPushButton("色選択...")
30
+ _insert_form_row_after(app.ui.formLayout_3, app.legend_font_button,
31
+ app.legend_color_label, app.legend_color_button)
32
+
33
+ # 凡例の並びは描画順とは別に決められる
34
+ app.legend_order_button = QPushButton("凡例の順序...")
35
+ _insert_form_row_after(app.ui.formLayout_3, app.legend_color_button, app.legend_order_button)
36
+
37
+ # ダークモードの切り替えで作り直すので(_refresh_custom_svg_icons)、対応を持っておく
38
+ app._field_icon_buttons = {
39
+ app.ui.tick_font_button: "typography",
40
+ app.ui.tick_color_button: "color-swatch",
41
+ app.ui.axis_label_font_button: "typography",
42
+ app.ui.axis_label_color_button: "color-swatch",
43
+ app.ui.spine_color_button: "color-swatch",
44
+ app.legend_font_button: "typography",
45
+ app.legend_color_button: "color-swatch",
46
+ }
47
+ for button, icon_name in app._field_icon_buttons.items():
48
+ button.setIcon(_svg_icon(icon_name, size=16))
49
+
50
+
51
+ def build_tick_grid_and_colorbar_controls(app):
52
+ app.tick_direction_label = QLabel("主軸目盛(主/補助)")
53
+ app.major_tick_direction_combo = QComboBox()
54
+ app.major_tick_direction_combo.addItems(["out", "in", "inout"])
55
+ app.minor_tick_direction_combo = QComboBox()
56
+ app.minor_tick_direction_combo.addItems(["out", "in", "inout"])
57
+ dir_layout = QHBoxLayout()
58
+ dir_layout.addWidget(app.major_tick_direction_combo)
59
+ dir_layout.addWidget(app.minor_tick_direction_combo)
60
+
61
+ app.tick_direction_y2_label = QLabel("第2軸目盛(主/補助)")
62
+ app.major_tick_direction_y2_combo = QComboBox()
63
+ app.major_tick_direction_y2_combo.addItems(["out", "in", "inout"])
64
+ app.minor_tick_direction_y2_combo = QComboBox()
65
+ app.minor_tick_direction_y2_combo.addItems(["out", "in", "inout"])
66
+ dir_y2_layout = QHBoxLayout()
67
+ dir_y2_layout.addWidget(app.major_tick_direction_y2_combo)
68
+ dir_y2_layout.addWidget(app.minor_tick_direction_y2_combo)
69
+
70
+ _insert_form_row_after(app.ui.formLayout_3, app.ui.tick_format_label, app.tick_direction_label, dir_layout)
71
+ _insert_form_row_after(app.ui.formLayout_3, app.tick_direction_label, app.tick_direction_y2_label, dir_y2_layout)
72
+
73
+ # グリッド線の線種・太さ・透明度(X/Y × 主/補助)。「補助グリッドの表示」のすぐ下に置く
74
+ app.grid_linestyle_choices = [
75
+ (tr("実線"), '-'), (tr("破線"), '--'), (tr("点線"), ':'), (tr("一点鎖線"), '-.'),
76
+ ]
77
+
78
+ def _make_grid_style_row(default_linestyle, default_width):
79
+ linestyle_combo = QComboBox()
80
+ for choice_label, _code in app.grid_linestyle_choices:
81
+ linestyle_combo.addItem(choice_label)
82
+ default_index = next(
83
+ (i for i, (_label, code) in enumerate(app.grid_linestyle_choices) if code == default_linestyle),
84
+ 0
85
+ )
86
+ linestyle_combo.setCurrentIndex(default_index)
87
+ linestyle_combo.setToolTip(tr("線種"))
88
+ linestyle_combo.setMaximumWidth(90)
89
+
90
+ width_spinbox = QDoubleSpinBox()
91
+ width_spinbox.setRange(0.1, 10.0)
92
+ width_spinbox.setSingleStep(0.1)
93
+ width_spinbox.setDecimals(1)
94
+ width_spinbox.setValue(default_width)
95
+ width_spinbox.setToolTip(tr("太さ"))
96
+ # これより狭いと矢印ボタンと重なって "10.0" のような値が見切れる
97
+ width_spinbox.setMinimumWidth(60)
98
+ width_spinbox.setMaximumWidth(78)
99
+
100
+ alpha_spinbox = QDoubleSpinBox()
101
+ alpha_spinbox.setRange(0.0, 1.0)
102
+ alpha_spinbox.setSingleStep(0.05)
103
+ alpha_spinbox.setDecimals(2)
104
+ alpha_spinbox.setValue(1.0)
105
+ alpha_spinbox.setToolTip(tr("透過度(アルファ)"))
106
+ alpha_spinbox.setMinimumWidth(60)
107
+ alpha_spinbox.setMaximumWidth(78)
108
+
109
+ row_layout = QHBoxLayout()
110
+ row_layout.addWidget(linestyle_combo)
111
+ row_layout.addWidget(width_spinbox)
112
+ row_layout.addWidget(alpha_spinbox)
113
+ return linestyle_combo, width_spinbox, alpha_spinbox, row_layout
114
+
115
+ (app.x_major_grid_linestyle_combo, app.x_major_grid_width_spinbox,
116
+ app.x_major_grid_alpha_spinbox, x_major_grid_layout) = _make_grid_style_row('-', 0.8)
117
+ (app.x_minor_grid_linestyle_combo, app.x_minor_grid_width_spinbox,
118
+ app.x_minor_grid_alpha_spinbox, x_minor_grid_layout) = _make_grid_style_row('--', 0.5)
119
+ (app.y_major_grid_linestyle_combo, app.y_major_grid_width_spinbox,
120
+ app.y_major_grid_alpha_spinbox, y_major_grid_layout) = _make_grid_style_row('-', 0.8)
121
+ (app.y_minor_grid_linestyle_combo, app.y_minor_grid_width_spinbox,
122
+ app.y_minor_grid_alpha_spinbox, y_minor_grid_layout) = _make_grid_style_row('--', 0.5)
123
+
124
+ app.x_major_grid_style_label = QLabel(tr("X軸主目盛"))
125
+ app.x_minor_grid_style_label = QLabel(tr("X軸補助目盛"))
126
+ app.y_major_grid_style_label = QLabel(tr("Y軸主目盛"))
127
+ app.y_minor_grid_style_label = QLabel(tr("Y軸補助目盛"))
128
+
129
+ _grid_style_anchor = app.ui.minor_grid_visible_checkbox
130
+ for _grid_style_label, _grid_style_layout in (
131
+ (app.x_major_grid_style_label, x_major_grid_layout),
132
+ (app.x_minor_grid_style_label, x_minor_grid_layout),
133
+ (app.y_major_grid_style_label, y_major_grid_layout),
134
+ (app.y_minor_grid_style_label, y_minor_grid_layout),
135
+ ):
136
+ _insert_form_row_after(app.ui.formLayout_3, _grid_style_anchor, _grid_style_label, _grid_style_layout)
137
+ _grid_style_anchor = _grid_style_label
138
+
139
+ # 目盛線の長さ(pt)。「目盛の太さ」の直後。補助目盛の最小値(-0.5)は「自動」(主目盛 × MINOR_TICK_LENGTH_RATIO)
140
+ app.tick_length_label = QLabel(tr("目盛の長さ(主/補助)"))
141
+ app.major_tick_length_spinbox = QDoubleSpinBox()
142
+ app.major_tick_length_spinbox.setRange(0.0, 20.0)
143
+ app.major_tick_length_spinbox.setSingleStep(0.5)
144
+ app.major_tick_length_spinbox.setDecimals(1)
145
+ app.major_tick_length_spinbox.setSuffix(" pt")
146
+ app.major_tick_length_spinbox.setValue(DEFAULT_MAJOR_TICK_LENGTH)
147
+ app.major_tick_length_spinbox.setToolTip(tr("主目盛の線の長さ(pt)"))
148
+ app.minor_tick_length_spinbox = QDoubleSpinBox()
149
+ app.minor_tick_length_spinbox.setRange(MINOR_TICK_LENGTH_AUTO, 20.0)
150
+ app.minor_tick_length_spinbox.setSingleStep(0.5)
151
+ app.minor_tick_length_spinbox.setDecimals(1)
152
+ app.minor_tick_length_spinbox.setSuffix(" pt")
153
+ app.minor_tick_length_spinbox.setSpecialValueText(tr("自動"))
154
+ app.minor_tick_length_spinbox.setValue(MINOR_TICK_LENGTH_AUTO)
155
+ app.minor_tick_length_spinbox.setToolTip(
156
+ tr("補助目盛の線の長さ(pt)。「自動」は主目盛の長さの約0.57倍"))
157
+ # QDoubleSpinBox を2つ並べると最小幅が約306pxになり、フォームの列幅を広げてドックに横スクロールが出る
158
+ # (tests/test_main_window.py が検出)。すぐ上の目盛方向の欄と同じ幅に揃える
159
+ for _tick_length_spin in (app.major_tick_length_spinbox, app.minor_tick_length_spinbox):
160
+ _tick_length_spin.setMinimumWidth(app.major_tick_direction_combo.minimumSizeHint().width())
161
+ tick_length_layout = QHBoxLayout()
162
+ tick_length_layout.addWidget(app.major_tick_length_spinbox)
163
+ tick_length_layout.addWidget(app.minor_tick_length_spinbox)
164
+ _insert_form_row_after(app.ui.formLayout_3, app.ui.tick_width_spinbox,
165
+ app.tick_length_label, tick_length_layout)
166
+
167
+ # カラーバーは2Dマップ(か値で色分けした散布図)がある軸でだけ効く
168
+ app.colorbar_enabled_checkbox = QCheckBox(tr("カラーバーを表示"))
169
+ app.colorbar_enabled_checkbox.setChecked(True)
170
+ app.ui.formLayout_3.addRow(app.colorbar_enabled_checkbox)
171
+
172
+ app.colorbar_position_label = QLabel(tr("カラーバーの位置"))
173
+ app.colorbar_position_combo = QComboBox()
174
+ app.colorbar_position_combo.addItem(tr("右"), "right")
175
+ app.colorbar_position_combo.addItem(tr("左"), "left")
176
+ app.colorbar_position_combo.addItem(tr("上"), "top")
177
+ app.colorbar_position_combo.addItem(tr("下"), "bottom")
178
+ app.ui.formLayout_3.addRow(app.colorbar_position_label, app.colorbar_position_combo)
179
+
180
+ app.colorbar_width_label = QLabel(tr("カラーバーの幅(割合)"))
181
+ app.colorbar_width_spinbox = QDoubleSpinBox()
182
+ app.colorbar_width_spinbox.setRange(0.01, 0.5)
183
+ app.colorbar_width_spinbox.setSingleStep(0.01)
184
+ app.colorbar_width_spinbox.setDecimals(2)
185
+ app.colorbar_width_spinbox.setValue(0.05)
186
+ app.ui.formLayout_3.addRow(app.colorbar_width_label, app.colorbar_width_spinbox)
187
+
188
+ app.colorbar_label_label = QLabel(tr("カラーバーのラベル"))
189
+ app.colorbar_label_edit = QLineEdit()
190
+ app.ui.formLayout_3.addRow(app.colorbar_label_label, app.colorbar_label_edit)
191
+
192
+
193
+ def build_tick_format_controls(app):
194
+ tick_format_choices = [
195
+ tr("自動"),
196
+ tr("軸端にまとめて指数表記 (×10ⁿ)"),
197
+ tr("目盛りごとに指数表記 (例: 1.0×10¹⁰)"),
198
+ tr("常に小数表記"),
199
+ ]
200
+ app.x_tick_format_label = QLabel(tr("目盛り表記"))
201
+ app.x_tick_format_combo = QComboBox()
202
+ app.x_tick_format_combo.addItems(tick_format_choices)
203
+ app.ui.formLayout.addRow(app.x_tick_format_label, app.x_tick_format_combo)
204
+
205
+ # 目盛線と目盛数値の表示は X/Y で別々なので、それぞれの軸のタブに置く
206
+ app.x_ticks_visible_checkbox = QCheckBox(tr("目盛を表示"))
207
+ app.x_ticks_visible_checkbox.setChecked(True)
208
+ app.ui.formLayout.addRow(app.x_ticks_visible_checkbox)
209
+ app.x_tick_labels_visible_checkbox = QCheckBox(tr("目盛の数値を表示"))
210
+ app.x_tick_labels_visible_checkbox.setChecked(True)
211
+ app.ui.formLayout.addRow(app.x_tick_labels_visible_checkbox)
212
+
213
+ # 目盛数値の小数点以下の桁数。-1(最小値)は「自動」で、指数表記の設定に任せる
214
+ app.x_tick_decimals_spinbox = QSpinBox()
215
+ app.x_tick_decimals_spinbox.setRange(-1, 10)
216
+ app.x_tick_decimals_spinbox.setSpecialValueText(tr("自動"))
217
+ app.x_tick_decimals_spinbox.setValue(-1)
218
+ app.x_tick_decimals_spinbox.setToolTip(
219
+ tr("目盛りの数値を表示する小数点以下の桁数(「自動」以外を選ぶと指数表記モードより優先されます)"))
220
+ app.ui.formLayout.addRow(QLabel(tr("小数桁数")), app.x_tick_decimals_spinbox)
221
+
222
+ # 対数軸で補助目盛を出すときだけ使う(出し入れは _on_x_minor_tick_visibility_changed)。
223
+ # ラベルの列幅は X/Y のタブで揃えるので、長いと両方の入力欄が狭まる。説明はツールチップに回す
224
+ app.x_log_minor_subs_label = QLabel(tr("対数補助目盛"))
225
+ app.x_log_minor_subs_label.setToolTip(tr("対数軸の補助目盛りをどこに打つか"))
226
+ app.x_log_minor_subs_combo = QComboBox()
227
+ app.x_log_minor_subs_combo.addItem(tr("自動(既定)"), "auto")
228
+ app.x_log_minor_subs_combo.addItem(tr("全て(2〜9)"), "all")
229
+ app.x_log_minor_subs_combo.addItem(tr("少なめ(2, 5)"), "few")
230
+ app.x_log_minor_subs_combo.addItem(tr("最小限(5)"), "one")
231
+ app.ui.formLayout.addRow(app.x_log_minor_subs_label, app.x_log_minor_subs_combo)
232
+ app.x_log_minor_labels_checkbox = QCheckBox(tr("補助目盛りに数値ラベルを表示"))
233
+ app.ui.formLayout.addRow(app.x_log_minor_labels_checkbox)
234
+ app.x_log_minor_subs_label.setVisible(False)
235
+ app.x_log_minor_subs_combo.setVisible(False)
236
+ app.x_log_minor_labels_checkbox.setVisible(False)
237
+
238
+ # X の単位と上に出したい単位が別々に選ばれていれば、単位を変換した第2X軸を上に付ける。
239
+ # ラベルは短く保つ(フォームの全行でラベルの列幅を共有するので、長いとドックに横スクロールが出る。
240
+ # test_properties_dock_has_no_horizontal_scrollbar)。説明はツールチップに置く
241
+ # 選んだ番号で値を持つ(choice の束ね)ので、表示だけを訳してよい
242
+ unit_combo_choices = [tr(X_AXIS_UNIT_LABELS[u]) for u in X_AXIS_UNIT_CHOICES]
243
+ app.x_secondary_axis_source_unit_label = QLabel(tr("X軸単位"))
244
+ app.x_secondary_axis_source_unit_combo = QComboBox()
245
+ app.x_secondary_axis_source_unit_combo.addItems(unit_combo_choices)
246
+ app.x_secondary_axis_source_unit_combo.setToolTip(tr("X軸データが表している物理量の単位"))
247
+ app.ui.formLayout.addRow(
248
+ app.x_secondary_axis_source_unit_label, app.x_secondary_axis_source_unit_combo)
249
+
250
+ app.x_secondary_axis_target_unit_label = QLabel(tr("第2X軸単位"))
251
+ app.x_secondary_axis_target_unit_combo = QComboBox()
252
+ app.x_secondary_axis_target_unit_combo.addItems(unit_combo_choices)
253
+ app.x_secondary_axis_target_unit_combo.setToolTip(tr("上部に追加する第2X軸に変換して表示する単位"))
254
+ app.ui.formLayout.addRow(
255
+ app.x_secondary_axis_target_unit_label, app.x_secondary_axis_target_unit_combo)
256
+
257
+ app.y_tick_format_label = QLabel(tr("目盛り表記"))
258
+ app.y_tick_format_combo = QComboBox()
259
+ app.y_tick_format_combo.addItems(tick_format_choices)
260
+ app.ui.formLayout_2.addRow(app.y_tick_format_label, app.y_tick_format_combo)
261
+
262
+ app.y_ticks_visible_checkbox = QCheckBox(tr("目盛を表示"))
263
+ app.y_ticks_visible_checkbox.setChecked(True)
264
+ app.ui.formLayout_2.addRow(app.y_ticks_visible_checkbox)
265
+ app.y_tick_labels_visible_checkbox = QCheckBox(tr("目盛の数値を表示"))
266
+ app.y_tick_labels_visible_checkbox.setChecked(True)
267
+ app.ui.formLayout_2.addRow(app.y_tick_labels_visible_checkbox)
268
+
269
+ app.y_tick_decimals_spinbox = QSpinBox()
270
+ app.y_tick_decimals_spinbox.setRange(-1, 10)
271
+ app.y_tick_decimals_spinbox.setSpecialValueText(tr("自動"))
272
+ app.y_tick_decimals_spinbox.setValue(-1)
273
+ app.y_tick_decimals_spinbox.setToolTip(
274
+ tr("目盛りの数値を表示する小数点以下の桁数(「自動」以外を選ぶと指数表記モードより優先されます)"))
275
+ app.ui.formLayout_2.addRow(QLabel(tr("小数桁数")), app.y_tick_decimals_spinbox)
276
+
277
+ # X 軸と同じく、ラベルは短くして説明はツールチップへ
278
+ app.y_log_minor_subs_label = QLabel(tr("対数補助目盛"))
279
+ app.y_log_minor_subs_label.setToolTip(tr("対数軸の補助目盛りをどこに打つか"))
280
+ app.y_log_minor_subs_combo = QComboBox()
281
+ app.y_log_minor_subs_combo.addItem(tr("自動(既定)"), "auto")
282
+ app.y_log_minor_subs_combo.addItem(tr("全て(2〜9)"), "all")
283
+ app.y_log_minor_subs_combo.addItem(tr("少なめ(2, 5)"), "few")
284
+ app.y_log_minor_subs_combo.addItem(tr("最小限(5)"), "one")
285
+ app.ui.formLayout_2.addRow(app.y_log_minor_subs_label, app.y_log_minor_subs_combo)
286
+ app.y_log_minor_labels_checkbox = QCheckBox(tr("補助目盛りに数値ラベルを表示"))
287
+ app.ui.formLayout_2.addRow(app.y_log_minor_labels_checkbox)
288
+ app.y_log_minor_subs_label.setVisible(False)
289
+ app.y_log_minor_subs_combo.setVisible(False)
290
+ app.y_log_minor_labels_checkbox.setVisible(False)
291
+
292
+
293
+ def build_label_editors(app):
294
+ # タイトルと軸ラベルは、mathtext を描いたプレビューに差し替え、クリックで編集ダイアログを開く。
295
+ # 元の QLineEdit は値の置き場と textChanged の発信元として、見えないまま残す
296
+ app._label_preview_widgets = [] # [(preview_label, line_edit, placeholder), ...]
297
+ for field_key, line_edit, dialog_title, placeholder in (
298
+ ('title', app.ui.title_text_edit, tr("タイトルを編集"), tr("タイトルを入力")),
299
+ ('x_label', app.ui.x_label_text_edit, tr("X軸ラベルを編集"), tr("X軸ラベルを入力")),
300
+ ('y_label', app.ui.y_label_text_edit, tr("Y軸ラベルを編集"), tr("Y軸ラベルを入力")),
301
+ ):
302
+ wrapper = QWidget()
303
+ wrapper_layout = QHBoxLayout(wrapper)
304
+ wrapper_layout.setContentsMargins(0, 0, 0, 0)
305
+ wrapper_layout.setSpacing(4)
306
+
307
+ # 同じ位置に差し替える(行の位置がずれない)
308
+ app.ui.formLayout_3.replaceWidget(line_edit, wrapper)
309
+ # レイアウトには入れない(隠れていても余白の計算に関わることがある)。親を wrapper にするだけ
310
+ line_edit.setParent(wrapper)
311
+ line_edit.hide()
312
+
313
+ preview_label = _ClickableMathPreviewLabel(wrapper)
314
+ wrapper_layout.addWidget(preview_label, 1)
315
+ preview_label.clicked.connect(
316
+ lambda le=line_edit, dt=dialog_title: app._open_label_edit_dialog(le, dt)
317
+ )
318
+ app._label_preview_widgets.append((preview_label, line_edit, placeholder))
319
+ line_edit.textChanged.connect(
320
+ lambda text, lbl=preview_label, ph=placeholder:
321
+ app._refresh_label_preview(lbl, text, ph)
322
+ )
323
+ app._refresh_label_preview(preview_label, line_edit.text(), placeholder)
324
+
325
+ # 軸ラベルだけ、文字を残したまま隠せる(タイトルには付けない)
326
+ if field_key in ('x_label', 'y_label'):
327
+ visible_checkbox = QCheckBox()
328
+ visible_checkbox.setChecked(True)
329
+ visible_checkbox.setToolTip(tr("ラベルの表示/非表示(テキスト自体は保持されます)"))
330
+ wrapper_layout.addWidget(visible_checkbox)
331
+ setattr(app, f'{field_key}_visible_checkbox', visible_checkbox)
332
+
333
+ format_button = QToolButton()
334
+ format_button.setText("Aa")
335
+ format_button.setToolTip(tr("タイトル/ラベルを編集(書式・記号入力)"))
336
+ format_button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
337
+ # パネルの幅が狭いので、ボタンは小さくして欄の幅を取らない
338
+ format_button.setFixedSize(26, 22)
339
+ small_font = QFont(format_button.font())
340
+ small_font.setPointSize(max(7, small_font.pointSize() - 2))
341
+ format_button.setFont(small_font)
342
+ wrapper_layout.addWidget(format_button)
343
+ format_button.clicked.connect(
344
+ lambda checked=False, le=line_edit, dt=dialog_title:
345
+ app._open_label_edit_dialog(le, dt)
346
+ )
347
+
348
+
349
+ def rebuild_label_tab(app):
350
+ # 「ラベル/書式」タブの先頭に、グラフ全体のレイアウトと編集対象の軸の欄を入れる
351
+ layout_group = QGroupBox(tr("グラフ全体レイアウト"))
352
+ layout_form = QFormLayout()
353
+ sizePolicy = layout_group.sizePolicy()
354
+ sizePolicy.setVerticalPolicy(QSizePolicy.Policy.Fixed)
355
+ layout_group.setSizePolicy(sizePolicy)
356
+ app.subplot_rows_spinbox = QSpinBox()
357
+ app.subplot_rows_spinbox.setRange(1, 10)
358
+ app.subplot_rows_spinbox.setValue(1)
359
+ app.subplot_cols_spinbox = QSpinBox()
360
+ app.subplot_cols_spinbox.setRange(1, 10)
361
+ app.subplot_cols_spinbox.setValue(1)
362
+ layout_form.addRow(tr("行数"), app.subplot_rows_spinbox)
363
+ layout_form.addRow(tr("列数"), app.subplot_cols_spinbox)
364
+
365
+ # 軸の共有は自由配置では意味が無いので、_on_toggle_free_layout で有効/無効を合わせる
366
+ app.share_x_checkbox = QCheckBox(tr("X軸を共有(グリッドレイアウト時)"))
367
+ app.share_y_checkbox = QCheckBox(tr("Y軸を共有(グリッドレイアウト時)"))
368
+ layout_form.addRow(app.share_x_checkbox)
369
+ layout_form.addRow(app.share_y_checkbox)
370
+
371
+ app.free_layout_checkbox = QCheckBox(tr("自由配置レイアウト(ドラッグで配置)"))
372
+ layout_form.addRow(app.free_layout_checkbox)
373
+
374
+ free_layout_button_row = QHBoxLayout()
375
+ app.add_free_subplot_button = QPushButton(tr("+ プロット追加"))
376
+ app.remove_free_subplot_button = QPushButton(tr("- プロット削除"))
377
+ app.add_free_subplot_button.setEnabled(False)
378
+ app.remove_free_subplot_button.setEnabled(False)
379
+ free_layout_button_row.addWidget(app.add_free_subplot_button)
380
+ free_layout_button_row.addWidget(app.remove_free_subplot_button)
381
+ layout_form.addRow(free_layout_button_row)
382
+
383
+ # 自由配置で選んだ軸の位置と大きさを数値でも入れられる。値は Figure に対する割合(ax.set_position と同じ)で、
384
+ # ドラッグと同じく少しなら範囲外も許す
385
+ app.free_layout_position_group = QGroupBox(tr("選択中のサブプロットの位置・サイズ"))
386
+ free_layout_position_form = QFormLayout()
387
+ app.free_layout_x_spinbox = QDoubleSpinBox()
388
+ app.free_layout_y_spinbox = QDoubleSpinBox()
389
+ app.free_layout_width_spinbox = QDoubleSpinBox()
390
+ app.free_layout_height_spinbox = QDoubleSpinBox()
391
+ for spinbox in (app.free_layout_x_spinbox, app.free_layout_y_spinbox):
392
+ spinbox.setRange(-1.0, 2.0)
393
+ spinbox.setDecimals(3)
394
+ spinbox.setSingleStep(0.01)
395
+ for spinbox in (app.free_layout_width_spinbox, app.free_layout_height_spinbox):
396
+ spinbox.setRange(MIN_FREE_RECT_SIZE, 2.0)
397
+ spinbox.setDecimals(3)
398
+ spinbox.setSingleStep(0.01)
399
+ free_layout_position_form.addRow(tr("X"), app.free_layout_x_spinbox)
400
+ free_layout_position_form.addRow(tr("Y"), app.free_layout_y_spinbox)
401
+ free_layout_position_form.addRow(tr("幅"), app.free_layout_width_spinbox)
402
+ free_layout_position_form.addRow(tr("高さ"), app.free_layout_height_spinbox)
403
+ app.free_layout_position_group.setLayout(free_layout_position_form)
404
+ # 自由配置モードで、かつサブプロットが選択されている間だけ表示する
405
+ app.free_layout_position_group.setVisible(False)
406
+ layout_form.addRow(app.free_layout_position_group)
407
+
408
+ layout_group.setLayout(layout_form)
409
+
410
+ active_axis_group = QGroupBox(tr("編集対象のプロット"))
411
+ active_axis_layout = QVBoxLayout()
412
+ sizePolicy = active_axis_group.sizePolicy()
413
+ sizePolicy.setVerticalPolicy(QSizePolicy.Policy.Fixed)
414
+ active_axis_group.setSizePolicy(sizePolicy)
415
+ app.active_axis_combo = QComboBox()
416
+ active_axis_layout.addWidget(app.active_axis_combo)
417
+ active_axis_group.setLayout(active_axis_layout)
418
+
419
+ grid_layout = app.ui.tab_3.layout() # QGridLayout
420
+
421
+ # 既存の formLayout_3 を (0, 0) から外し、新しい2つの下(2行目)に入れ直す
422
+ existing_layout_item = grid_layout.itemAtPosition(0, 0)
423
+
424
+ if existing_layout_item:
425
+ grid_layout.removeItem(existing_layout_item)
426
+
427
+ grid_layout.addWidget(layout_group, 0, 0)
428
+ grid_layout.addWidget(active_axis_group, 1, 0)
429
+
430
+ if existing_layout_item:
431
+ grid_layout.addItem(existing_layout_item, 2, 0)
@@ -0,0 +1,176 @@
1
+ """キャンバス・ツールバー・ミニマップ・ステータスバーの組み立て。"""
2
+ import logging
3
+ from PySide6.QtCore import QSize
4
+ from PySide6.QtGui import QAction
5
+ from PySide6.QtWidgets import QApplication, QFrame, QLabel, QVBoxLayout
6
+ from graphica.core.i18n import tr
7
+ from graphica.gui import app_settings, theme
8
+ from graphica.gui.builders.common import TOOLBAR_ICON_SIZE, _svg_icon
9
+ from graphica.gui.canvas import MplCanvas
10
+ from graphica.gui.minimap_widget import MinimapWidget
11
+ from matplotlib.backends.backend_qtagg import NavigationToolbar2QT as NavigationToolbar
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ def build_canvas_and_toolbar(app):
17
+ app.canvas = MplCanvas(app, width=5, height=4, dpi=100)
18
+ app.canvas.dark_mode = app_settings.DARK_MODE.read(app.settings)
19
+ # アイコンは作るときにテーマの色を焼き込むので、アイコンを作り始める前にテーマを当てる
20
+ # (当てないと、ダークモードで起動したときツールバーのアイコンがライト用の色になって見えない)
21
+ theme.apply_theme(QApplication.instance(), app.canvas.dark_mode)
22
+ app.canvas.point_label_max_points = app_settings.POINT_LABEL_MAX_POINTS.read(app.settings)
23
+ toolbar = NavigationToolbar(app.canvas, app)
24
+ # matplotlib のツールバーはアイコンの明暗を作ったときに一度だけ決めるので、
25
+ # ダークモードの切り替え(_on_toggle_dark_mode)で作り直せるよう持っておく
26
+ app.mpl_toolbar = toolbar
27
+ # 普通のレイアウトに入れたツールバーは、幅が足りないとボタンが小さな「>>」に押し込まれて見つからない。
28
+ # アイコンを小さくしてはみ出しにくくする
29
+ toolbar.setIconSize(QSize(TOOLBAR_ICON_SIZE, TOOLBAR_ICON_SIZE))
30
+ app._localize_navigation_toolbar(toolbar)
31
+
32
+ # マウス操作のモード。互いに排他にするため、アクションは属性で持つ(mouse_mode_mixin の表から操作する)
33
+ toolbar.addSeparator()
34
+ app.cursor_action = QAction(
35
+ _svg_icon("pointer"),
36
+ tr("データカーソル"),
37
+ app
38
+ )
39
+ app.cursor_action.setCheckable(True)
40
+ app.cursor_action.triggered.connect(app._toggle_cursor_mode)
41
+ toolbar.addAction(app.cursor_action)
42
+
43
+ app.annotation_action = QAction(
44
+ _svg_icon("message-2"),
45
+ tr("注釈 (クリック:テキスト / ドラッグ:矢印 / 右クリック:削除)"),
46
+ app
47
+ )
48
+ app.annotation_action.setCheckable(True)
49
+ app.annotation_action.triggered.connect(app._toggle_annotation_mode)
50
+ toolbar.addAction(app.annotation_action)
51
+
52
+ # 自由配置レイアウトのときだけ使える
53
+ app.layout_edit_action = QAction(
54
+ _svg_icon("layout-grid"),
55
+ tr("レイアウト編集 (自由配置レイアウト時のみ: ドラッグでプロットを移動/リサイズ)"),
56
+ app
57
+ )
58
+ app.layout_edit_action.setCheckable(True)
59
+ app.layout_edit_action.setEnabled(False)
60
+ app.layout_edit_action.triggered.connect(app._toggle_layout_edit_mode)
61
+ toolbar.addAction(app.layout_edit_action)
62
+
63
+ app.range_select_action = QAction(
64
+ _svg_icon("select-all"),
65
+ tr("範囲選択 (ドラッグした範囲のカレントデータセットをマスク)"),
66
+ app
67
+ )
68
+ app.range_select_action.setCheckable(True)
69
+ app.range_select_action.triggered.connect(app._toggle_range_select_mode)
70
+ toolbar.addAction(app.range_select_action)
71
+
72
+ app.peak_placement_action = QAction(
73
+ _svg_icon("mountain"), # ピーク検出ボタンと同じ
74
+ tr("ピーク配置 (クリックで多峰分離フィットの初期値を追加、右クリックで削除)"),
75
+ app
76
+ )
77
+ app.peak_placement_action.setCheckable(True)
78
+ app.peak_placement_action.triggered.connect(app._toggle_peak_placement_mode)
79
+ toolbar.addAction(app.peak_placement_action)
80
+
81
+ app.slice_extraction_action = QAction(
82
+ _svg_icon("chart-line"),
83
+ tr("スライス抽出 (2Dマップ上でドラッグした線分に沿って1Dデータを抽出)"),
84
+ app
85
+ )
86
+ app.slice_extraction_action.setCheckable(True)
87
+ app.slice_extraction_action.triggered.connect(app._toggle_slice_extraction_mode)
88
+ toolbar.addAction(app.slice_extraction_action)
89
+
90
+ app.region_highlight_action = QAction(
91
+ _svg_icon("highlight"),
92
+ tr("領域ハイライト (横ドラッグ:縦帯 / 縦ドラッグ:横帯 / 右クリック:削除)"),
93
+ app
94
+ )
95
+ app.region_highlight_action.setCheckable(True)
96
+ app.region_highlight_action.triggered.connect(app._toggle_region_highlight_mode)
97
+ toolbar.addAction(app.region_highlight_action)
98
+
99
+ app.reset_zoom_action = QAction(
100
+ _svg_icon("refresh"),
101
+ tr("表示をリセット (拡大/パンを元に戻す)"),
102
+ app
103
+ )
104
+ app.reset_zoom_action.triggered.connect(app._reset_zoom)
105
+ toolbar.addAction(app.reset_zoom_action)
106
+
107
+ # 統計情報のボタンは stats_summary_label を作った後(_build_fit_info_and_stats)で足す
108
+
109
+ # キャンバスの周りに余白と区切り線を置いて、右のパネルとの境目をはっきりさせる
110
+ app.ui.plot_container.setObjectName("plot_container")
111
+ plot_layout = QVBoxLayout(app.ui.plot_container)
112
+ plot_layout.setContentsMargins(6, 6, 6, 6)
113
+ plot_layout.setSpacing(6)
114
+ plot_layout.addWidget(toolbar)
115
+
116
+ canvas_separator = QFrame()
117
+ canvas_separator.setFrameShape(QFrame.Shape.HLine)
118
+ canvas_separator.setObjectName("canvas_separator")
119
+ plot_layout.addWidget(canvas_separator)
120
+
121
+ plot_layout.addWidget(app.canvas)
122
+
123
+ # キャンバスを別ウィンドウへ切り離したあと、元の位置に戻せるようにする
124
+ # (この後に足すミニマップはキャンバスより後ろなので、この位置は変わらない)
125
+ app._plot_layout = plot_layout
126
+ app._canvas_layout_index = plot_layout.indexOf(app.canvas)
127
+ app._canvas_detach_window = None
128
+ app.canvas_detached = False
129
+
130
+ # グラフの下の小さな全体図。ドラッグで全部の軸の X の範囲を絞る。区切り線は canvas_separator のスタイルを使う
131
+ app.minimap_separator = QFrame()
132
+ app.minimap_separator.setFrameShape(QFrame.Shape.HLine)
133
+ app.minimap_separator.setObjectName("canvas_separator")
134
+ plot_layout.addWidget(app.minimap_separator)
135
+
136
+ app.minimap = MinimapWidget(app)
137
+ app.minimap.range_selected.connect(app._on_minimap_range_selected)
138
+ plot_layout.addWidget(app.minimap)
139
+
140
+ app.minimap_visible = app_settings.MINIMAP_VISIBLE.read(app.settings)
141
+ app.minimap.setVisible(app.minimap_visible)
142
+ app.minimap_separator.setVisible(app.minimap_visible)
143
+
144
+
145
+ def setup_status_bar(app):
146
+ app.coordinate_label = QLabel("X= ---, Y= ---")
147
+ app.ui.statusbar.addPermanentWidget(app.coordinate_label)
148
+
149
+
150
+ def localize_navigation_toolbar(app, toolbar):
151
+ """matplotlib のツールバーの英語のツールチップを差し替える。
152
+
153
+ _actions は matplotlib の内部なので、構造が変わっていたら何もしない(英語のまま)。
154
+ """
155
+ labels = {
156
+ 'home': (tr("元の表示に戻す"), tr("最初の表示範囲にリセットします")),
157
+ 'back': (tr("前の表示に戻る"), tr("1つ前の表示範囲に戻ります")),
158
+ 'forward': (tr("次の表示に進む"), tr("戻る前の表示範囲に進みます")),
159
+ 'pan': (tr("パン/ズーム"),
160
+ tr("左ドラッグで移動、右ドラッグで拡大縮小(x/yキーで軸固定)")),
161
+ 'zoom': (tr("矩形ズーム"), tr("ドラッグした矩形範囲に拡大します(x/yキーで軸固定)")),
162
+ 'configure_subplots': (tr("サブプロット調整"), tr("サブプロット間の余白を調整します")),
163
+ 'save_figure': (tr("画像として保存"), tr("グラフを画像ファイルとして保存します")),
164
+ }
165
+ try:
166
+ actions = toolbar._actions
167
+ except AttributeError:
168
+ logger.warning("NavigationToolbar2QTの_actionsが見つからず、ツールチップの日本語化をスキップしました。")
169
+ return
170
+ for name, action in actions.items():
171
+ localized = labels.get(name)
172
+ if localized is None:
173
+ continue
174
+ tooltip, status_tip = localized
175
+ action.setToolTip(tooltip)
176
+ action.setStatusTip(status_tip)