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.
- graphica/Graphica.ico +0 -0
- graphica/__init__.py +1 -0
- graphica/__main__.py +111 -0
- graphica/assets/__init__.py +0 -0
- graphica/assets/icons/__init__.py +0 -0
- graphica/assets/icons/arrow-right.svg +21 -0
- graphica/assets/icons/bold.svg +20 -0
- graphica/assets/icons/calculator.svg +26 -0
- graphica/assets/icons/chart-histogram.svg +24 -0
- graphica/assets/icons/chart-line.svg +20 -0
- graphica/assets/icons/chevron-down.svg +19 -0
- graphica/assets/icons/chevron-right.svg +19 -0
- graphica/assets/icons/color-swatch.svg +22 -0
- graphica/assets/icons/column-insert-right.svg +21 -0
- graphica/assets/icons/column-remove.svg +21 -0
- graphica/assets/icons/copy.svg +20 -0
- graphica/assets/icons/download.svg +21 -0
- graphica/assets/icons/edit.svg +21 -0
- graphica/assets/icons/eye-off.svg +21 -0
- graphica/assets/icons/eye.svg +20 -0
- graphica/assets/icons/file-plus.svg +22 -0
- graphica/assets/icons/folder-plus.svg +21 -0
- graphica/assets/icons/folder.svg +19 -0
- graphica/assets/icons/highlight.svg +22 -0
- graphica/assets/icons/history.svg +20 -0
- graphica/assets/icons/italic.svg +21 -0
- graphica/assets/icons/layout-grid.svg +22 -0
- graphica/assets/icons/math-function.svg +22 -0
- graphica/assets/icons/message-2.svg +21 -0
- graphica/assets/icons/mountain.svg +20 -0
- graphica/assets/icons/palette.svg +22 -0
- graphica/assets/icons/pointer.svg +19 -0
- graphica/assets/icons/refresh.svg +20 -0
- graphica/assets/icons/row-insert-bottom.svg +21 -0
- graphica/assets/icons/row-remove.svg +21 -0
- graphica/assets/icons/search.svg +20 -0
- graphica/assets/icons/select-all.svg +35 -0
- graphica/assets/icons/subscript.svg +20 -0
- graphica/assets/icons/superscript.svg +20 -0
- graphica/assets/icons/table.svg +21 -0
- graphica/assets/icons/trash.svg +23 -0
- graphica/assets/icons/typography.svg +23 -0
- graphica/assets/icons/x.svg +20 -0
- graphica/core/__init__.py +0 -0
- graphica/core/analysis.py +1278 -0
- graphica/core/app_paths.py +35 -0
- graphica/core/axis_settings.py +128 -0
- graphica/core/caption_export.py +47 -0
- graphica/core/color_palettes.py +56 -0
- graphica/core/commands.py +226 -0
- graphica/core/cvd_simulation.py +49 -0
- graphica/core/dataset.py +443 -0
- graphica/core/diagnostics.py +105 -0
- graphica/core/excel_utils.py +58 -0
- graphica/core/fit_models.py +379 -0
- graphica/core/grid_data.py +128 -0
- graphica/core/i18n.py +44 -0
- graphica/core/json_utils.py +27 -0
- graphica/core/label_utils.py +21 -0
- graphica/core/methods_text.py +115 -0
- graphica/core/named_colors.py +126 -0
- graphica/core/plugin_api.py +515 -0
- graphica/core/plugin_context.py +167 -0
- graphica/core/plugin_install.py +82 -0
- graphica/core/plugin_manifest.py +67 -0
- graphica/core/plugin_testing.py +213 -0
- graphica/core/plugin_types.py +118 -0
- graphica/core/provenance.py +19 -0
- graphica/core/report_export.py +51 -0
- graphica/core/safe_eval.py +194 -0
- graphica/core/script_export.py +272 -0
- graphica/core/translations_en.py +530 -0
- graphica/core/unit_conversion.py +51 -0
- graphica/core/update_check.py +43 -0
- graphica/core/version.py +14 -0
- graphica/gui/__init__.py +0 -0
- graphica/gui/app_settings.py +119 -0
- graphica/gui/axis_bindings.py +173 -0
- graphica/gui/binding.py +135 -0
- graphica/gui/builders/__init__.py +1 -0
- graphica/gui/builders/axis_panel.py +431 -0
- graphica/gui/builders/canvas_area.py +176 -0
- graphica/gui/builders/common.py +120 -0
- graphica/gui/builders/dataset_panel.py +433 -0
- graphica/gui/builders/property_sections.py +208 -0
- graphica/gui/canvas.py +519 -0
- graphica/gui/color_history.py +47 -0
- graphica/gui/color_picker_widget.py +198 -0
- graphica/gui/crash_handler.py +77 -0
- graphica/gui/cvd_preview.py +23 -0
- graphica/gui/data_editor.py +761 -0
- graphica/gui/data_import_flow.py +355 -0
- graphica/gui/dataset_bindings.py +107 -0
- graphica/gui/dataset_style_icon.py +80 -0
- graphica/gui/datasets/__init__.py +1 -0
- graphica/gui/datasets/actions_menu.py +114 -0
- graphica/gui/datasets/colors.py +218 -0
- graphica/gui/datasets/fitting.py +70 -0
- graphica/gui/datasets/host.py +162 -0
- graphica/gui/datasets/operations/__init__.py +1 -0
- graphica/gui/datasets/operations/fitting.py +468 -0
- graphica/gui/datasets/operations/peaks.py +109 -0
- graphica/gui/datasets/operations/processing.py +659 -0
- graphica/gui/datasets/operations/runner.py +148 -0
- graphica/gui/datasets/operations/transfer.py +227 -0
- graphica/gui/datasets/order.py +310 -0
- graphica/gui/datasets/overlays.py +74 -0
- graphica/gui/datasets/peaks.py +21 -0
- graphica/gui/datasets/plugin_runs.py +99 -0
- graphica/gui/datasets/processing.py +68 -0
- graphica/gui/datasets/property_panel.py +538 -0
- graphica/gui/datasets/transfer.py +36 -0
- graphica/gui/detached_canvas_window.py +22 -0
- graphica/gui/dialogs/__init__.py +137 -0
- graphica/gui/dialogs/analysis.py +1322 -0
- graphica/gui/dialogs/app.py +1015 -0
- graphica/gui/dialogs/appearance.py +851 -0
- graphica/gui/dialogs/data_edit.py +577 -0
- graphica/gui/dialogs/data_import.py +679 -0
- graphica/gui/dialogs/export.py +433 -0
- graphica/gui/dock_layout.py +176 -0
- graphica/gui/export_preview_panel.py +321 -0
- graphica/gui/export_settings.py +16 -0
- graphica/gui/file_association.py +97 -0
- graphica/gui/icon_utils.py +44 -0
- graphica/gui/main_app_window.py +210 -0
- graphica/gui/main_window.py +1151 -0
- graphica/gui/mathtext_preview.py +168 -0
- graphica/gui/menu_bar.py +320 -0
- graphica/gui/minimap_widget.py +151 -0
- graphica/gui/mixins/__init__.py +0 -0
- graphica/gui/mixins/export_mixin.py +520 -0
- graphica/gui/mixins/help_mixin.py +127 -0
- graphica/gui/mixins/project_io_mixin.py +289 -0
- graphica/gui/mixins/quick_access_mixin.py +194 -0
- graphica/gui/mixins/ui_setup_mixin.py +206 -0
- graphica/gui/notify.py +61 -0
- graphica/gui/panels/__init__.py +43 -0
- graphica/gui/panels/axis_settings.py +605 -0
- graphica/gui/panels/dataset_tree.py +424 -0
- graphica/gui/plot_type_drawers.py +131 -0
- graphica/gui/plugin_context.py +119 -0
- graphica/gui/project_files.py +361 -0
- graphica/gui/provenance_panel.py +65 -0
- graphica/gui/rendering/__init__.py +1 -0
- graphica/gui/rendering/annotations.py +177 -0
- graphica/gui/rendering/appearance.py +368 -0
- graphica/gui/rendering/common.py +316 -0
- graphica/gui/rendering/data_1d.py +376 -0
- graphica/gui/rendering/data_2d.py +66 -0
- graphica/gui/residual_panel.py +62 -0
- graphica/gui/resources.py +16 -0
- graphica/gui/single_instance.py +141 -0
- graphica/gui/splash.py +83 -0
- graphica/gui/task_runner.py +41 -0
- graphica/gui/theme.py +864 -0
- graphica/gui/tools/__init__.py +56 -0
- graphica/gui/tools/annotation.py +275 -0
- graphica/gui/tools/cursor.py +259 -0
- graphica/gui/tools/layout_edit.py +285 -0
- graphica/gui/tools/manager.py +78 -0
- graphica/gui/tools/peak_placement.py +132 -0
- graphica/gui/tools/range_select.py +191 -0
- graphica/gui/tools/region_highlight.py +222 -0
- graphica/gui/tools/slice_extraction.py +200 -0
- graphica/gui/widget_translation.py +46 -0
- graphica/gui/workers.py +280 -0
- graphica/models/__init__.py +0 -0
- graphica/models/project.py +197 -0
- graphica/plugin/__init__.py +20 -0
- graphica/plugin/testing.py +57 -0
- graphica/sample_data/__init__.py +0 -0
- graphica/sample_data/cooling_curve_sample.csv +42 -0
- graphica/ui_main_window.py +581 -0
- graphica_plot-2.0.0.dist-info/METADATA +317 -0
- graphica_plot-2.0.0.dist-info/RECORD +181 -0
- graphica_plot-2.0.0.dist-info/WHEEL +5 -0
- graphica_plot-2.0.0.dist-info/entry_points.txt +2 -0
- graphica_plot-2.0.0.dist-info/licenses/LICENSE +30 -0
- graphica_plot-2.0.0.dist-info/licenses/THIRD_PARTY_LICENSES.md +91 -0
- graphica_plot-2.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
"""軸の設定の部品: 画面の欄と設定の辞書の相互変換、サブプロットの行数と列数、フォントと色の選択、その信号の配線。"""
|
|
2
|
+
import functools
|
|
3
|
+
import logging
|
|
4
|
+
from PySide6.QtCore import QTimer
|
|
5
|
+
from PySide6.QtGui import QFont
|
|
6
|
+
from PySide6.QtWidgets import QDialog, QFontDialog
|
|
7
|
+
|
|
8
|
+
from graphica.core.i18n import tr
|
|
9
|
+
from graphica.gui import notify
|
|
10
|
+
from graphica.core.axis_settings import AXIS_SETTING_DEFAULTS, axis_setting
|
|
11
|
+
from graphica.gui import theme
|
|
12
|
+
from graphica.gui.axis_bindings import CARRIED_AXIS_KEYS
|
|
13
|
+
from graphica.gui.color_history import get_color_with_history
|
|
14
|
+
from graphica.gui.dialogs import LegendOrderDialog, LabelEditDialog
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Windows の「Arial Narrow」は matplotlib には family="Arial", stretch="condensed" として登録されていて、
|
|
20
|
+
# 名前のままでは見つからず黙って既定のフォントになる。末尾の語を stretch に読み替えて探し直す。
|
|
21
|
+
_FONT_STRETCH_KEYWORDS = {
|
|
22
|
+
'narrow': 'condensed',
|
|
23
|
+
'condensed': 'condensed',
|
|
24
|
+
'semicondensed': 'semi-condensed',
|
|
25
|
+
'extracondensed': 'extra-condensed',
|
|
26
|
+
'ultracondensed': 'ultra-condensed',
|
|
27
|
+
'wide': 'expanded',
|
|
28
|
+
'expanded': 'expanded',
|
|
29
|
+
'semiexpanded': 'semi-expanded',
|
|
30
|
+
'extraexpanded': 'extra-expanded',
|
|
31
|
+
'ultraexpanded': 'ultra-expanded',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@functools.lru_cache(maxsize=256)
|
|
36
|
+
def _resolve_font_family_for_matplotlib(family_name: str):
|
|
37
|
+
"""(matplotlib に渡す family, stretch または None, 解決できたか) を返す。
|
|
38
|
+
|
|
39
|
+
軸の設定を変えるたびに呼ばれるので、findfont の結果をキャッシュする。
|
|
40
|
+
"""
|
|
41
|
+
import matplotlib.font_manager as fm
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
fm.findfont(fm.FontProperties(family=family_name), fallback_to_default=False)
|
|
45
|
+
return family_name, None, True
|
|
46
|
+
except ValueError:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
words = family_name.rsplit(None, 1)
|
|
50
|
+
if len(words) == 2:
|
|
51
|
+
base, suffix = words
|
|
52
|
+
stretch = _FONT_STRETCH_KEYWORDS.get(suffix.lower())
|
|
53
|
+
if stretch is not None:
|
|
54
|
+
try:
|
|
55
|
+
fm.findfont(fm.FontProperties(family=base, stretch=stretch), fallback_to_default=False)
|
|
56
|
+
return base, stretch, True
|
|
57
|
+
except ValueError:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
return family_name, None, False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _order_labels(labels, order):
|
|
64
|
+
"""order の順に並べ、order に無いラベルは元の順のまま末尾に置く。"""
|
|
65
|
+
if not order:
|
|
66
|
+
return list(labels)
|
|
67
|
+
order_index = {name: i for i, name in enumerate(order)}
|
|
68
|
+
indices = sorted(
|
|
69
|
+
range(len(labels)),
|
|
70
|
+
key=lambda i: (0, order_index[labels[i]]) if labels[i] in order_index else (1, i)
|
|
71
|
+
)
|
|
72
|
+
return [labels[i] for i in indices]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class AxisSettingsPanel:
|
|
76
|
+
"""状態はタブが持ち、この部品はメソッドと信号の配線だけを持つ。PlotterApp の同じ名前は窓口(gui/panels/__init__.py)。"""
|
|
77
|
+
|
|
78
|
+
# PlotterApp から同じ名前で使えるもの(テスト・メニュー・ほかの部品が使う)
|
|
79
|
+
EXPOSED_NAMES = (
|
|
80
|
+
'_on_y2_autoscale_changed', '_on_y2_axis_state_changed', '_refresh_y2_axis_state',
|
|
81
|
+
'_apply_settings_to_ui_controls', '_block_all_signals', '_connect_axis_setting_signals',
|
|
82
|
+
'_connect_layout_signals', '_current_active_axis', '_font_props_to_dict', '_gather_settings_from_ui',
|
|
83
|
+
'_grid_linestyle_code', '_grid_linestyle_index', '_on_active_axis_changed', '_on_axis_setting_changed',
|
|
84
|
+
'_on_change_axis_label_color', '_on_change_axis_label_font', '_on_change_legend_color',
|
|
85
|
+
'_on_change_legend_font', '_on_change_spine_color', '_on_change_tick_color', '_on_change_tick_font',
|
|
86
|
+
'_on_edit_legend_order', '_on_grid_visibility_changed', '_on_layout_changed', '_on_legend_drag_release',
|
|
87
|
+
'_on_legend_loc_changed', '_on_legend_visibility_changed', '_on_share_axis_changed',
|
|
88
|
+
'_on_x_autoscale_changed', '_on_x_minor_tick_visibility_changed', '_on_x_tick_mode_changed',
|
|
89
|
+
'_on_y_autoscale_changed', '_on_y_minor_tick_visibility_changed', '_on_y_tick_mode_changed',
|
|
90
|
+
'_open_label_edit_dialog', '_refresh_all_label_previews', '_refresh_grid_state', '_refresh_label_preview',
|
|
91
|
+
'_refresh_legend_state', '_refresh_x_autoscale_enabled_state', '_refresh_x_minor_tick_state',
|
|
92
|
+
'_refresh_x_tick_mode_state', '_refresh_y_autoscale_enabled_state', '_refresh_y_minor_tick_state',
|
|
93
|
+
'_refresh_y_tick_mode_state', '_seed_min_max_spinboxes', '_store_dragged_legend_positions',
|
|
94
|
+
'_update_subplot_combos', '_warn_if_font_family_unavailable_for_graph',
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def __init__(self, app):
|
|
98
|
+
self._app = app
|
|
99
|
+
|
|
100
|
+
def _on_layout_changed(self):
|
|
101
|
+
if getattr(self._app.project, 'layout_mode', 'grid') == 'free':
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
# 欄の値を変える間、変更の通知が連鎖しないように
|
|
105
|
+
self._block_all_signals(True)
|
|
106
|
+
|
|
107
|
+
rows = self._app.subplot_rows_spinbox.value()
|
|
108
|
+
cols = self._app.subplot_cols_spinbox.value()
|
|
109
|
+
total_plots = rows * cols
|
|
110
|
+
|
|
111
|
+
current_plot_count = len(self._app.project.all_plot_settings)
|
|
112
|
+
|
|
113
|
+
if total_plots > current_plot_count:
|
|
114
|
+
# 増えた分は今の軸の設定をもとにする
|
|
115
|
+
default_settings = self._gather_settings_from_ui()
|
|
116
|
+
for _ in range(total_plots - current_plot_count):
|
|
117
|
+
new_settings = default_settings.copy()
|
|
118
|
+
# 注釈は引き継がない。浅いコピーなので、空のリストにしないと全部が同じリストを共有する
|
|
119
|
+
new_settings['annotations'] = []
|
|
120
|
+
new_settings['legend_order'] = []
|
|
121
|
+
self._app.project.all_plot_settings.append(new_settings)
|
|
122
|
+
|
|
123
|
+
elif total_plots < current_plot_count:
|
|
124
|
+
self._app.project.all_plot_settings = self._app.project.all_plot_settings[:total_plots]
|
|
125
|
+
|
|
126
|
+
# 無くなったサブプロットを描画先にしていたデータセットは、どこにも描かれず
|
|
127
|
+
# エクスポートにも入らないのに気づけないので、最後のサブプロットに移す
|
|
128
|
+
for dataset in self._app.project.datasets:
|
|
129
|
+
if dataset.subplot_target >= total_plots:
|
|
130
|
+
dataset.subplot_target = total_plots - 1
|
|
131
|
+
|
|
132
|
+
if self._app.project.active_axis_index >= total_plots:
|
|
133
|
+
self._app.project.active_axis_index = 0
|
|
134
|
+
|
|
135
|
+
self._update_subplot_combos()
|
|
136
|
+
self._apply_settings_to_ui_controls(self._app.project.all_plot_settings[self._app.project.active_axis_index])
|
|
137
|
+
|
|
138
|
+
self._block_all_signals(False)
|
|
139
|
+
|
|
140
|
+
self._app._update_plot()
|
|
141
|
+
|
|
142
|
+
def _on_share_axis_changed(self):
|
|
143
|
+
self._app.project.share_x_axis = self._app.share_x_checkbox.isChecked()
|
|
144
|
+
self._app.project.share_y_axis = self._app.share_y_checkbox.isChecked()
|
|
145
|
+
self._app._update_plot()
|
|
146
|
+
|
|
147
|
+
def _on_active_axis_changed(self, index):
|
|
148
|
+
"""切り替える前の欄の値を元の軸に保存してから、新しい軸の設定を欄に読み込む。"""
|
|
149
|
+
|
|
150
|
+
if index == -1 or index >= len(self._app.project.all_plot_settings):
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
self._app.project.all_plot_settings[self._app.project.active_axis_index] = self._gather_settings_from_ui()
|
|
154
|
+
|
|
155
|
+
self._app.project.active_axis_index = index
|
|
156
|
+
|
|
157
|
+
settings_to_load = self._app.project.all_plot_settings[self._app.project.active_axis_index]
|
|
158
|
+
self._apply_settings_to_ui_controls(settings_to_load)
|
|
159
|
+
|
|
160
|
+
def _on_axis_setting_changed(self):
|
|
161
|
+
current_settings = self._gather_settings_from_ui()
|
|
162
|
+
|
|
163
|
+
self._app.project.all_plot_settings[self._app.project.active_axis_index] = current_settings
|
|
164
|
+
|
|
165
|
+
self._app._update_plot_appearance()
|
|
166
|
+
|
|
167
|
+
def _update_subplot_combos(self):
|
|
168
|
+
total_plots = len(self._app.project.all_plot_settings)
|
|
169
|
+
plot_names = [tr("プロット {number}").format(number=i + 1) for i in range(total_plots)]
|
|
170
|
+
|
|
171
|
+
self._app.active_axis_combo.blockSignals(True)
|
|
172
|
+
self._app.subplot_target_combo.blockSignals(True)
|
|
173
|
+
|
|
174
|
+
self._app.active_axis_combo.clear()
|
|
175
|
+
self._app.subplot_target_combo.clear()
|
|
176
|
+
|
|
177
|
+
self._app.active_axis_combo.addItems(plot_names)
|
|
178
|
+
self._app.subplot_target_combo.addItems(plot_names)
|
|
179
|
+
|
|
180
|
+
self._app.active_axis_combo.setCurrentIndex(self._app.project.active_axis_index)
|
|
181
|
+
|
|
182
|
+
# 「描画先」の選択はデータセットを選んだときに property_panel.update_ui_state が決める
|
|
183
|
+
self._app.active_axis_combo.blockSignals(False)
|
|
184
|
+
self._app.subplot_target_combo.blockSignals(False)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _seed_min_max_spinboxes(self, min_spinbox, max_spinbox, limits):
|
|
188
|
+
"""オートスケールを切った瞬間に、いま表示している範囲を最小値・最大値の欄に入れる。
|
|
189
|
+
|
|
190
|
+
欄が初期値 0/0 のままだと、最小値を先に変えたとき「最小 >= 最大」で反映されない。
|
|
191
|
+
"""
|
|
192
|
+
if limits is None:
|
|
193
|
+
return
|
|
194
|
+
min_lim, max_lim = limits
|
|
195
|
+
min_spinbox.blockSignals(True)
|
|
196
|
+
max_spinbox.blockSignals(True)
|
|
197
|
+
min_spinbox.setValue(min_lim)
|
|
198
|
+
max_spinbox.setValue(max_lim)
|
|
199
|
+
min_spinbox.blockSignals(False)
|
|
200
|
+
max_spinbox.blockSignals(False)
|
|
201
|
+
|
|
202
|
+
def _current_active_axis(self):
|
|
203
|
+
axis_index = self._app.project.active_axis_index
|
|
204
|
+
if 0 <= axis_index < len(self._app.canvas.all_axes):
|
|
205
|
+
return self._app.canvas.all_axes[axis_index]
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
def _refresh_x_autoscale_enabled_state(self):
|
|
209
|
+
"""最小値・最大値の欄の有効/無効だけをオートスケールに合わせる。値は書き換えない。
|
|
210
|
+
|
|
211
|
+
保存した設定を戻すときはこちらを使う。_on_x_autoscale_changed() は表示中の範囲を欄に入れるので、
|
|
212
|
+
保存されていた範囲が上書きされる。
|
|
213
|
+
"""
|
|
214
|
+
is_autoscale = self._app.ui.x_autoscale_checkbox.isChecked()
|
|
215
|
+
self._app.ui.x_min_spinbox.setEnabled(not is_autoscale)
|
|
216
|
+
self._app.ui.x_max_spinbox.setEnabled(not is_autoscale)
|
|
217
|
+
return is_autoscale
|
|
218
|
+
|
|
219
|
+
def _refresh_y_autoscale_enabled_state(self):
|
|
220
|
+
is_autoscale = self._app.ui.y_autoscale_checkbox.isChecked()
|
|
221
|
+
self._app.ui.y_min_spinbox.setEnabled(not is_autoscale)
|
|
222
|
+
self._app.ui.y_max_spinbox.setEnabled(not is_autoscale)
|
|
223
|
+
return is_autoscale
|
|
224
|
+
|
|
225
|
+
def _on_x_autoscale_changed(self):
|
|
226
|
+
"""利用者がチェックを変えたとき(設定を戻すときは _refresh_x_autoscale_enabled_state)。"""
|
|
227
|
+
is_autoscale = self._refresh_x_autoscale_enabled_state()
|
|
228
|
+
|
|
229
|
+
if not is_autoscale:
|
|
230
|
+
axis = self._current_active_axis()
|
|
231
|
+
self._seed_min_max_spinboxes(
|
|
232
|
+
self._app.ui.x_min_spinbox, self._app.ui.x_max_spinbox, axis.get_xlim() if axis is not None else None)
|
|
233
|
+
|
|
234
|
+
# 欄の値を書き換えたので、描き直すだけでなく設定も集め直す
|
|
235
|
+
self._on_axis_setting_changed()
|
|
236
|
+
|
|
237
|
+
def _on_y_autoscale_changed(self):
|
|
238
|
+
is_autoscale = self._refresh_y_autoscale_enabled_state()
|
|
239
|
+
|
|
240
|
+
if not is_autoscale:
|
|
241
|
+
axis = self._current_active_axis()
|
|
242
|
+
self._seed_min_max_spinboxes(
|
|
243
|
+
self._app.ui.y_min_spinbox, self._app.ui.y_max_spinbox, axis.get_ylim() if axis is not None else None)
|
|
244
|
+
|
|
245
|
+
self._on_axis_setting_changed()
|
|
246
|
+
|
|
247
|
+
def _on_x_tick_mode_changed(self):
|
|
248
|
+
self._refresh_x_tick_mode_state()
|
|
249
|
+
self._app._update_plot_appearance()
|
|
250
|
+
|
|
251
|
+
def _refresh_x_tick_mode_state(self):
|
|
252
|
+
is_fixed_interval = (self._app.ui.x_major_tick_mode_combo.currentIndex() == 1)
|
|
253
|
+
self._app.ui.x_major_tick_interval_spinbox.setEnabled(is_fixed_interval)
|
|
254
|
+
|
|
255
|
+
def _on_y_tick_mode_changed(self):
|
|
256
|
+
self._refresh_y_tick_mode_state()
|
|
257
|
+
self._app._update_plot_appearance()
|
|
258
|
+
|
|
259
|
+
def _refresh_y_tick_mode_state(self):
|
|
260
|
+
is_fixed_interval = (self._app.ui.y_major_tick_mode_combo.currentIndex() == 1)
|
|
261
|
+
self._app.ui.y_major_tick_interval_spinbox.setEnabled(is_fixed_interval)
|
|
262
|
+
|
|
263
|
+
def _on_x_minor_tick_visibility_changed(self):
|
|
264
|
+
"""補助目盛の表示と対数表示の両方に依存するので、同じスロットで受ける。"""
|
|
265
|
+
self._refresh_x_minor_tick_state()
|
|
266
|
+
self._app._update_plot_appearance()
|
|
267
|
+
|
|
268
|
+
def _refresh_x_minor_tick_state(self):
|
|
269
|
+
is_visible = self._app.ui.x_minor_ticks_visible_checkbox.isChecked()
|
|
270
|
+
is_log = self._app.ui.x_log_checkbox.isChecked()
|
|
271
|
+
# 対数軸は MultipleLocator ではなく LogLocator なので間隔は使わない
|
|
272
|
+
self._app.ui.x_minor_tick_interval_spinbox.setEnabled(is_visible and not is_log)
|
|
273
|
+
self._app.x_log_minor_subs_label.setVisible(is_log)
|
|
274
|
+
self._app.x_log_minor_subs_combo.setVisible(is_log)
|
|
275
|
+
self._app.x_log_minor_labels_checkbox.setVisible(is_log)
|
|
276
|
+
self._app.x_log_minor_subs_combo.setEnabled(is_log and is_visible)
|
|
277
|
+
self._app.x_log_minor_labels_checkbox.setEnabled(is_log and is_visible)
|
|
278
|
+
|
|
279
|
+
def _on_y_minor_tick_visibility_changed(self):
|
|
280
|
+
self._refresh_y_minor_tick_state()
|
|
281
|
+
self._app._update_plot_appearance()
|
|
282
|
+
|
|
283
|
+
def _refresh_y_minor_tick_state(self):
|
|
284
|
+
is_visible = self._app.ui.y_minor_ticks_visible_checkbox.isChecked()
|
|
285
|
+
is_log = self._app.ui.y_log_checkbox.isChecked()
|
|
286
|
+
self._app.ui.y_minor_tick_interval_spinbox.setEnabled(is_visible and not is_log)
|
|
287
|
+
self._app.y_log_minor_subs_label.setVisible(is_log)
|
|
288
|
+
self._app.y_log_minor_subs_combo.setVisible(is_log)
|
|
289
|
+
self._app.y_log_minor_labels_checkbox.setVisible(is_log)
|
|
290
|
+
self._app.y_log_minor_subs_combo.setEnabled(is_log and is_visible)
|
|
291
|
+
self._app.y_log_minor_labels_checkbox.setEnabled(is_log and is_visible)
|
|
292
|
+
|
|
293
|
+
def _refresh_y2_axis_state(self):
|
|
294
|
+
"""第 2 Y 軸の欄の有効/無効を、自動スケール・目盛りの方式・補助目盛り・対数に合わせる。値は書き換えない。"""
|
|
295
|
+
app = self._app
|
|
296
|
+
is_autoscale = app.y2_autoscale_checkbox.isChecked()
|
|
297
|
+
app.y2_min_spinbox.setEnabled(not is_autoscale)
|
|
298
|
+
app.y2_max_spinbox.setEnabled(not is_autoscale)
|
|
299
|
+
app.y2_major_tick_interval_spinbox.setEnabled(app.y2_major_tick_mode_combo.currentIndex() == 1)
|
|
300
|
+
# 対数軸の補助目盛りは間隔ではなく対数の刻みで打つ
|
|
301
|
+
app.y2_minor_tick_interval_spinbox.setEnabled(
|
|
302
|
+
app.y2_minor_ticks_visible_checkbox.isChecked() and not app.y2_log_checkbox.isChecked())
|
|
303
|
+
|
|
304
|
+
def _on_y2_axis_state_changed(self):
|
|
305
|
+
self._refresh_y2_axis_state()
|
|
306
|
+
|
|
307
|
+
def _on_y2_autoscale_changed(self):
|
|
308
|
+
"""自動スケールを切った瞬間に、いまの第 2 Y 軸の範囲を最小値・最大値の欄に入れる(主の Y 軸と同じ)。"""
|
|
309
|
+
self._refresh_y2_axis_state()
|
|
310
|
+
if not self._app.y2_autoscale_checkbox.isChecked():
|
|
311
|
+
axis_index = self._app.project.active_axis_index
|
|
312
|
+
secondary_axes = getattr(self._app.canvas, 'all_secondary_axes', [])
|
|
313
|
+
secondary = secondary_axes[axis_index] if 0 <= axis_index < len(secondary_axes) else None
|
|
314
|
+
self._seed_min_max_spinboxes(self._app.y2_min_spinbox, self._app.y2_max_spinbox,
|
|
315
|
+
secondary.get_ylim() if secondary is not None else None)
|
|
316
|
+
self._on_axis_setting_changed()
|
|
317
|
+
|
|
318
|
+
def _on_legend_visibility_changed(self):
|
|
319
|
+
self._refresh_legend_state()
|
|
320
|
+
self._app._update_plot_appearance()
|
|
321
|
+
|
|
322
|
+
def _refresh_legend_state(self):
|
|
323
|
+
is_visible = self._app.ui.legend_visible_checkbox.isChecked()
|
|
324
|
+
|
|
325
|
+
self._app.legend_loc_label.setEnabled(is_visible)
|
|
326
|
+
self._app.legend_loc_combo.setEnabled(is_visible)
|
|
327
|
+
self._app.legend_font_label.setEnabled(is_visible)
|
|
328
|
+
self._app.legend_font_button.setEnabled(is_visible)
|
|
329
|
+
self._app.legend_color_label.setEnabled(is_visible)
|
|
330
|
+
self._app.legend_color_button.setEnabled(is_visible)
|
|
331
|
+
|
|
332
|
+
def _grid_linestyle_code(self, combo_index: int) -> str:
|
|
333
|
+
choices = self._app.grid_linestyle_choices
|
|
334
|
+
if 0 <= combo_index < len(choices):
|
|
335
|
+
return choices[combo_index][1]
|
|
336
|
+
return '-'
|
|
337
|
+
|
|
338
|
+
def _grid_linestyle_index(self, linestyle_code: str) -> int:
|
|
339
|
+
"""未知の値は 0(実線)。"""
|
|
340
|
+
for i, (_label, code) in enumerate(self._app.grid_linestyle_choices):
|
|
341
|
+
if code == linestyle_code:
|
|
342
|
+
return i
|
|
343
|
+
return 0
|
|
344
|
+
|
|
345
|
+
def _on_grid_visibility_changed(self):
|
|
346
|
+
self._refresh_grid_state()
|
|
347
|
+
self._app._update_plot_appearance()
|
|
348
|
+
|
|
349
|
+
def _refresh_grid_state(self):
|
|
350
|
+
is_visible = self._app.ui.grid_visible_checkbox.isChecked()
|
|
351
|
+
|
|
352
|
+
self._app.ui.minor_grid_visible_checkbox.setEnabled(is_visible)
|
|
353
|
+
|
|
354
|
+
# 主のグリッドの欄はグリッドの表示に、補助の欄はさらに補助グリッドの表示にも従う
|
|
355
|
+
is_minor_visible = is_visible and self._app.ui.minor_grid_visible_checkbox.isChecked()
|
|
356
|
+
for widget in (
|
|
357
|
+
self._app.x_major_grid_linestyle_combo, self._app.x_major_grid_width_spinbox, self._app.x_major_grid_alpha_spinbox,
|
|
358
|
+
self._app.y_major_grid_linestyle_combo, self._app.y_major_grid_width_spinbox, self._app.y_major_grid_alpha_spinbox,
|
|
359
|
+
):
|
|
360
|
+
widget.setEnabled(is_visible)
|
|
361
|
+
for widget in (
|
|
362
|
+
self._app.x_minor_grid_linestyle_combo, self._app.x_minor_grid_width_spinbox, self._app.x_minor_grid_alpha_spinbox,
|
|
363
|
+
self._app.y_minor_grid_linestyle_combo, self._app.y_minor_grid_width_spinbox, self._app.y_minor_grid_alpha_spinbox,
|
|
364
|
+
):
|
|
365
|
+
widget.setEnabled(is_minor_visible)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _warn_if_font_family_unavailable_for_graph(self, font):
|
|
369
|
+
"""グラフのフォントが matplotlib で見つからなければ知らせる(選択は保存する)。
|
|
370
|
+
|
|
371
|
+
QFontDialog は OS のフォント一覧を出すが、matplotlib は別の一覧で探し、見つからないと黙って既定のフォントになる。
|
|
372
|
+
"""
|
|
373
|
+
family = font.family()
|
|
374
|
+
_resolved_name, _stretch, resolved = _resolve_font_family_for_matplotlib(family)
|
|
375
|
+
if not resolved:
|
|
376
|
+
notify.warning(
|
|
377
|
+
self._app, "フォントが見つかりません",
|
|
378
|
+
f"フォント「{family}」はグラフの描画エンジン(matplotlib)には認識されず、"
|
|
379
|
+
"代わりに既定のフォントで表示されます。\n\n"
|
|
380
|
+
"OS側のフォント一覧には表示されていても、グラフの描画には使えない"
|
|
381
|
+
"フォントがあります。別のフォントをお試しください。"
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
def _on_change_tick_font(self):
|
|
385
|
+
ok, font = QFontDialog.getFont(self._app._tick_font, self._app)
|
|
386
|
+
if ok:
|
|
387
|
+
self._warn_if_font_family_unavailable_for_graph(font)
|
|
388
|
+
self._app._tick_font = font
|
|
389
|
+
self._on_axis_setting_changed()
|
|
390
|
+
|
|
391
|
+
def _on_change_tick_color(self):
|
|
392
|
+
color = get_color_with_history(self._app.settings, self._app)
|
|
393
|
+
if color.isValid():
|
|
394
|
+
self._app._tick_color = color.name()
|
|
395
|
+
self._on_axis_setting_changed()
|
|
396
|
+
|
|
397
|
+
def _on_change_axis_label_font(self):
|
|
398
|
+
ok, font = QFontDialog.getFont(self._app._axis_label_font, self._app)
|
|
399
|
+
if ok:
|
|
400
|
+
self._warn_if_font_family_unavailable_for_graph(font)
|
|
401
|
+
self._app._axis_label_font = font
|
|
402
|
+
# _update_plot_appearance() だけでは all_plot_settings に保存されない
|
|
403
|
+
self._on_axis_setting_changed()
|
|
404
|
+
|
|
405
|
+
def _on_change_axis_label_color(self):
|
|
406
|
+
color = get_color_with_history(self._app.settings, self._app)
|
|
407
|
+
if color.isValid():
|
|
408
|
+
self._app._axis_label_color = color.name()
|
|
409
|
+
self._on_axis_setting_changed()
|
|
410
|
+
|
|
411
|
+
def _on_change_legend_font(self):
|
|
412
|
+
ok, font = QFontDialog.getFont(self._app._legend_font, self._app)
|
|
413
|
+
if ok:
|
|
414
|
+
self._warn_if_font_family_unavailable_for_graph(font)
|
|
415
|
+
self._app._legend_font = font
|
|
416
|
+
self._on_axis_setting_changed()
|
|
417
|
+
|
|
418
|
+
def _on_change_legend_color(self):
|
|
419
|
+
color = get_color_with_history(self._app.settings, self._app)
|
|
420
|
+
if color.isValid():
|
|
421
|
+
self._app._legend_color = color.name()
|
|
422
|
+
self._on_axis_setting_changed()
|
|
423
|
+
|
|
424
|
+
def _on_change_spine_color(self):
|
|
425
|
+
color = get_color_with_history(self._app.settings, self._app)
|
|
426
|
+
if color.isValid():
|
|
427
|
+
self._app._spine_color = color.name()
|
|
428
|
+
self._on_axis_setting_changed()
|
|
429
|
+
|
|
430
|
+
def _open_label_edit_dialog(self, line_edit, dialog_title):
|
|
431
|
+
"""編集ダイアログの結果を line_edit に setText() する(textChanged からいつもの経路で反映される)。
|
|
432
|
+
|
|
433
|
+
LABEL_SYMBOL_PALETTE は main_window がこの mixin を import しているので、ここで遅れて import する。
|
|
434
|
+
"""
|
|
435
|
+
from graphica.gui.main_window import LABEL_SYMBOL_PALETTE
|
|
436
|
+
|
|
437
|
+
dialog = LabelEditDialog(line_edit.text(), dialog_title, LABEL_SYMBOL_PALETTE, parent=self._app)
|
|
438
|
+
if dialog.exec() == QDialog.DialogCode.Accepted:
|
|
439
|
+
line_edit.setText(dialog.get_text())
|
|
440
|
+
|
|
441
|
+
def _refresh_label_preview(self, preview_label, text, placeholder):
|
|
442
|
+
"""プレビューを matplotlib で描いた見た目にする。空なら placeholder を text_muted の色で出す。"""
|
|
443
|
+
from graphica.gui.mathtext_preview import render_mathtext_to_pixmap
|
|
444
|
+
|
|
445
|
+
tokens = theme.current_tokens()
|
|
446
|
+
if text:
|
|
447
|
+
pixmap = render_mathtext_to_pixmap(text, color=tokens["text_primary"])
|
|
448
|
+
else:
|
|
449
|
+
pixmap = render_mathtext_to_pixmap(placeholder, color=tokens["text_muted"])
|
|
450
|
+
# 欄の幅が決まるたびに収まるよう縮める(setPixmap だと、この時点の幅がまだ確定していないとはみ出す)
|
|
451
|
+
preview_label.set_natural_pixmap(pixmap)
|
|
452
|
+
|
|
453
|
+
def _refresh_all_label_previews(self):
|
|
454
|
+
"""文字色がテーマに従うので、ダークモードの切り替えで描き直す。"""
|
|
455
|
+
for preview_label, line_edit, placeholder in self._app._label_preview_widgets:
|
|
456
|
+
self._refresh_label_preview(preview_label, line_edit.text(), placeholder)
|
|
457
|
+
|
|
458
|
+
def _on_legend_loc_changed(self, *_args):
|
|
459
|
+
"""ドラッグした位置(legend_position)が残っていると選んだ位置が効かないので消す。"""
|
|
460
|
+
axis_index = self._app.project.active_axis_index
|
|
461
|
+
if axis_index < len(self._app.project.all_plot_settings):
|
|
462
|
+
self._app.project.all_plot_settings[axis_index].pop('legend_position', None)
|
|
463
|
+
self._on_axis_setting_changed()
|
|
464
|
+
|
|
465
|
+
def _on_legend_drag_release(self, _event):
|
|
466
|
+
"""凡例のドラッグは matplotlib 側の button_release_event で確定し、こちらが先に呼ばれうるので、処理の後で読む。"""
|
|
467
|
+
QTimer.singleShot(0, self._store_dragged_legend_positions)
|
|
468
|
+
|
|
469
|
+
def _store_dragged_legend_positions(self):
|
|
470
|
+
"""凡例がドラッグで置かれていれば、その位置を legend_position に保存する(しないと次の描画で戻る)。どれか変えたら True。"""
|
|
471
|
+
changed = False
|
|
472
|
+
settings_list = self._app.project.all_plot_settings
|
|
473
|
+
secondary_axes = getattr(self._app.canvas, 'all_secondary_axes', [])
|
|
474
|
+
for index, ax in enumerate(getattr(self._app.canvas, 'all_axes', [])):
|
|
475
|
+
if index >= len(settings_list):
|
|
476
|
+
break
|
|
477
|
+
legend = ax.get_legend()
|
|
478
|
+
if legend is None and index < len(secondary_axes) and secondary_axes[index] is not None:
|
|
479
|
+
legend = secondary_axes[index].get_legend()
|
|
480
|
+
loc = getattr(legend, '_loc', None) if legend is not None else None
|
|
481
|
+
if not isinstance(loc, tuple) or len(loc) != 2:
|
|
482
|
+
continue
|
|
483
|
+
position = [round(float(loc[0]), 4), round(float(loc[1]), 4)]
|
|
484
|
+
if axis_setting(settings_list[index], 'legend_position') != position:
|
|
485
|
+
settings_list[index]['legend_position'] = position
|
|
486
|
+
changed = True
|
|
487
|
+
return changed
|
|
488
|
+
|
|
489
|
+
def _on_edit_legend_order(self):
|
|
490
|
+
"""今の軸の凡例の並びをダイアログで決め、legend_order に保存する。"""
|
|
491
|
+
axis_index = self._app.project.active_axis_index
|
|
492
|
+
if axis_index >= len(self._app.canvas.all_axes):
|
|
493
|
+
return
|
|
494
|
+
ax = self._app.canvas.all_axes[axis_index]
|
|
495
|
+
_, labels = ax.get_legend_handles_labels()
|
|
496
|
+
if axis_index < len(self._app.canvas.all_secondary_axes) and self._app.canvas.all_secondary_axes[axis_index] is not None:
|
|
497
|
+
_, secondary_labels = self._app.canvas.all_secondary_axes[axis_index].get_legend_handles_labels()
|
|
498
|
+
labels = labels + secondary_labels
|
|
499
|
+
if not labels:
|
|
500
|
+
notify.information(self._app, "凡例の順序", "この軸には凡例に表示するデータセットがありません。")
|
|
501
|
+
return
|
|
502
|
+
|
|
503
|
+
current_order = axis_setting(self._app.project.all_plot_settings[axis_index], 'legend_order') or []
|
|
504
|
+
dialog = LegendOrderDialog(_order_labels(labels, current_order), self._app)
|
|
505
|
+
if dialog.exec() != QDialog.DialogCode.Accepted:
|
|
506
|
+
return
|
|
507
|
+
self._app.project.all_plot_settings[axis_index]['legend_order'] = dialog.get_order()
|
|
508
|
+
self._app._update_plot()
|
|
509
|
+
|
|
510
|
+
def _font_props_to_dict(self, qfont: QFont) -> dict:
|
|
511
|
+
"""QFont を JSON に保存できる辞書にする。
|
|
512
|
+
|
|
513
|
+
'family' は families() の候補リストのまま保存する(.family() の先頭1つにすると、macOS に無い
|
|
514
|
+
"Yu Gothic" だけが残って日本語が化ける)。各候補は _resolve_font_family_for_matplotlib で解決する。
|
|
515
|
+
"""
|
|
516
|
+
resolved_families = []
|
|
517
|
+
stretch = None
|
|
518
|
+
for name in qfont.families():
|
|
519
|
+
resolved_name, resolved_stretch, _ok = _resolve_font_family_for_matplotlib(name)
|
|
520
|
+
resolved_families.append(resolved_name)
|
|
521
|
+
if stretch is None and resolved_stretch is not None:
|
|
522
|
+
stretch = resolved_stretch
|
|
523
|
+
|
|
524
|
+
result = {
|
|
525
|
+
'family': resolved_families,
|
|
526
|
+
'size': qfont.pointSize(),
|
|
527
|
+
'weight': 'bold' if qfont.bold() else 'normal',
|
|
528
|
+
'style': 'italic' if qfont.italic() else 'normal'
|
|
529
|
+
}
|
|
530
|
+
if stretch is not None:
|
|
531
|
+
result['stretch'] = stretch
|
|
532
|
+
return result
|
|
533
|
+
|
|
534
|
+
def _gather_settings_from_ui(self) -> dict:
|
|
535
|
+
"""今の軸の設定を、画面の欄から集めた辞書(保存形式そのもの)で返す。キーは AXIS_SETTING_DEFAULTS と同じ順。"""
|
|
536
|
+
values = self._app._axis_binder.gather()
|
|
537
|
+
# 欄を持たず操作から直接書き込まれるもの。辞書は丸ごと入れ替わるので、引き継がないと消える。
|
|
538
|
+
settings_list = self._app.project.all_plot_settings
|
|
539
|
+
current = settings_list[self._app.project.active_axis_index] if self._app.project.active_axis_index < len(settings_list) else {}
|
|
540
|
+
for key in CARRIED_AXIS_KEYS:
|
|
541
|
+
values[key] = axis_setting(current, key)
|
|
542
|
+
return {key: values[key] for key in AXIS_SETTING_DEFAULTS}
|
|
543
|
+
|
|
544
|
+
def _apply_settings_to_ui_controls(self, settings: dict):
|
|
545
|
+
"""設定の辞書を画面の欄に戻す。無いキーは AXIS_SETTING_DEFAULTS の既定値になる。"""
|
|
546
|
+
try:
|
|
547
|
+
# 欄に値を入れる間は、変更の通知で設定を書き換えないようにする
|
|
548
|
+
self._block_all_signals(True)
|
|
549
|
+
|
|
550
|
+
self._app._axis_binder.restore(settings, axis_setting)
|
|
551
|
+
|
|
552
|
+
# 復元では _on_x_autoscale_changed(今の表示範囲を欄に入れる)を呼ばない。
|
|
553
|
+
# 呼ぶと、オートスケールを切って保存した範囲が今の表示範囲で上書きされる。
|
|
554
|
+
self._refresh_x_autoscale_enabled_state()
|
|
555
|
+
self._refresh_y_autoscale_enabled_state()
|
|
556
|
+
self._refresh_x_tick_mode_state()
|
|
557
|
+
self._refresh_y_tick_mode_state()
|
|
558
|
+
self._refresh_x_minor_tick_state()
|
|
559
|
+
self._refresh_y_minor_tick_state()
|
|
560
|
+
self._refresh_legend_state()
|
|
561
|
+
self._refresh_grid_state()
|
|
562
|
+
self._refresh_y2_axis_state()
|
|
563
|
+
# 欄の状態をそろえてから 1 回だけ描く(どれも同じ設定で描くので、欄ごとに描いても結果は同じ)
|
|
564
|
+
self._app._update_plot_appearance()
|
|
565
|
+
|
|
566
|
+
except Exception as e:
|
|
567
|
+
notify.warning(self._app, "設定適用エラー", f"設定の適用中にエラーが発生しました:\n{e}")
|
|
568
|
+
logger.exception("設定の適用中にエラー")
|
|
569
|
+
finally:
|
|
570
|
+
# 途中で失敗しても必ず戻す
|
|
571
|
+
self._block_all_signals(False)
|
|
572
|
+
|
|
573
|
+
def _block_all_signals(self, block: bool):
|
|
574
|
+
self._app._axis_binder.block_signals(block)
|
|
575
|
+
|
|
576
|
+
def _connect_layout_signals(self):
|
|
577
|
+
self._app.subplot_rows_spinbox.valueChanged.connect(self._on_layout_changed)
|
|
578
|
+
self._app.subplot_cols_spinbox.valueChanged.connect(self._on_layout_changed)
|
|
579
|
+
|
|
580
|
+
self._app.share_x_checkbox.toggled.connect(self._on_share_axis_changed)
|
|
581
|
+
self._app.share_y_checkbox.toggled.connect(self._on_share_axis_changed)
|
|
582
|
+
|
|
583
|
+
self._app.active_axis_combo.currentIndexChanged.connect(self._on_active_axis_changed)
|
|
584
|
+
|
|
585
|
+
self._app.free_layout_checkbox.toggled.connect(self._app._on_toggle_free_layout)
|
|
586
|
+
self._app.add_free_subplot_button.clicked.connect(self._app._on_add_free_subplot)
|
|
587
|
+
self._app.remove_free_subplot_button.clicked.connect(self._app._on_remove_free_subplot)
|
|
588
|
+
|
|
589
|
+
self._app.free_layout_x_spinbox.valueChanged.connect(self._app._on_free_layout_position_spinbox_changed)
|
|
590
|
+
self._app.free_layout_y_spinbox.valueChanged.connect(self._app._on_free_layout_position_spinbox_changed)
|
|
591
|
+
self._app.free_layout_width_spinbox.valueChanged.connect(self._app._on_free_layout_position_spinbox_changed)
|
|
592
|
+
self._app.free_layout_height_spinbox.valueChanged.connect(self._app._on_free_layout_position_spinbox_changed)
|
|
593
|
+
|
|
594
|
+
def _connect_axis_setting_signals(self):
|
|
595
|
+
# 値を持つ欄は表(gui/axis_bindings.py)の順につなぐ。タイトルと軸ラベルの編集ボタンは _build_label_editors でつなぐ
|
|
596
|
+
self._app._axis_binder.connect()
|
|
597
|
+
|
|
598
|
+
self._app.ui.tick_font_button.clicked.connect(self._on_change_tick_font)
|
|
599
|
+
self._app.ui.tick_color_button.clicked.connect(self._on_change_tick_color)
|
|
600
|
+
self._app.ui.axis_label_font_button.clicked.connect(self._on_change_axis_label_font)
|
|
601
|
+
self._app.ui.axis_label_color_button.clicked.connect(self._on_change_axis_label_color)
|
|
602
|
+
self._app.legend_font_button.clicked.connect(self._on_change_legend_font)
|
|
603
|
+
self._app.legend_color_button.clicked.connect(self._on_change_legend_color)
|
|
604
|
+
self._app.legend_order_button.clicked.connect(self._on_edit_legend_order)
|
|
605
|
+
self._app.ui.spine_color_button.clicked.connect(self._on_change_spine_color)
|