Coverage for graphica/gui/axis_bindings.py: 98.1%
48 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
1"""軸の設定(core/axis_settings.py のキー)と画面の欄の対応表。欄を 1 つ足すときは表に 1 行足す。
3表の順は戻す順。集めた辞書のキーの順は AXIS_SETTING_DEFAULTS の順(保存形式)にそろえる。
4"""
5from PySide6.QtGui import QFont
7from graphica.core.unit_conversion import X_AXIS_UNIT_CHOICES
8from graphica.gui.binding import Binding, check, choice, index, item_data, number, shown_text, text, typed_text
9from graphica.gui.canvas import MINOR_TICK_LENGTH_AUTO
11_CHANGED = ('_on_axis_setting_changed',)
13# 欄を持たず操作から直接書き込まれるキー。集めるときは今の軸の設定から引き継ぐ(辞書は丸ごと入れ替わるので)
14CARRIED_AXIS_KEYS = ('annotations', 'legend_order', 'free_rect', 'legend_position')
16# 戻す間は信号を止めるが、値は持たない欄
17AXIS_BUTTONS = (
18 'ui.tick_font_button', 'ui.tick_color_button', 'ui.axis_label_font_button', 'ui.axis_label_color_button',
19 'legend_font_button', 'legend_color_button', 'ui.spine_color_button',
20)
23def qfont_from_family_props(font_props: dict) -> QFont:
24 """'family' は候補のリストか、古いプロジェクトでは1つの名前。QFont(list) は無いので setFamilies() を使う。"""
25 family = font_props.get('family', 'Sans Serif')
26 if isinstance(family, (list, tuple)):
27 font = QFont()
28 if family: 28 ↛ 30line 28 didn't jump to line 30 because the condition on line 28 was always true
29 font.setFamilies(list(family))
30 return font
31 return QFont(family)
34def _font(key, attribute, size_only_when_given=False):
35 """フォントは持ち主の属性に QFont で持つ。凡例は保存にサイズが無ければ QFont の既定のサイズのまま。"""
36 def read(owner, _widget):
37 return owner._font_props_to_dict(getattr(owner, attribute))
39 def write(owner, _widget, props):
40 setattr(owner, attribute, qfont_from_family_props(props))
41 font = getattr(owner, attribute)
42 if not size_only_when_given or 'size' in props:
43 font.setPointSize(props.get('size', 10))
44 font.setBold(props.get('weight') == 'bold')
45 font.setItalic(props.get('style') == 'italic')
46 return Binding(key, None, read, write)
49def _attribute(key, attribute):
50 return Binding(key, None, lambda owner, _w: getattr(owner, attribute),
51 lambda owner, _w, value: setattr(owner, attribute, value))
54def _width(key, widget, attribute):
55 """線の太さは描画が持ち主の属性から読むので、欄と一緒に属性にも入れる。"""
56 def write(owner, spinbox, value):
57 setattr(owner, attribute, value)
58 spinbox.setValue(getattr(owner, attribute))
59 return Binding(key, widget, lambda _o, w: w.value(), write, 'valueChanged', _CHANGED)
62def _grid_linestyle(key, widget):
63 return Binding(key, widget, lambda owner, w: owner._grid_linestyle_code(w.currentIndex()),
64 lambda owner, w, value: w.setCurrentIndex(owner._grid_linestyle_index(value)),
65 'currentIndexChanged', _CHANGED)
68def _minor_tick_length(key, widget):
69 """負値(スピンボックスの「自動」)は -1 に正規化して保存する。"""
70 def read(_owner, spinbox):
71 return spinbox.value() if spinbox.value() >= 0 else -1
73 def write(_owner, spinbox, value):
74 spinbox.setValue(MINOR_TICK_LENGTH_AUTO if value is None or value < 0 else value)
75 return Binding(key, widget, read, write, 'valueChanged', _CHANGED)
78def _axis_rows(axis):
79 """X と Y で同じ並びの行。autoscale・目盛りの方式・補助目盛りの表示・対数は、見え方を切り替える処理もつなぐ。"""
80 ui = f'ui.{axis}'
81 return (
82 check(f'{axis}_autoscale', f'{ui}_autoscale_checkbox', _CHANGED + (f'_on_{axis}_autoscale_changed',)),
83 number(f'{axis}_min', f'{ui}_min_spinbox', _CHANGED),
84 number(f'{axis}_max', f'{ui}_max_spinbox', _CHANGED),
85 check(f'{axis}_log', f'{ui}_log_checkbox', _CHANGED + (f'_on_{axis}_minor_tick_visibility_changed',)),
86 check(f'{axis}_invert', f'{ui}_invert_checkbox', _CHANGED),
87 index(f'{axis}_major_tick_mode', f'{ui}_major_tick_mode_combo', _CHANGED + (f'_on_{axis}_tick_mode_changed',)),
88 number(f'{axis}_major_tick_interval', f'{ui}_major_tick_interval_spinbox', _CHANGED),
89 check(f'{axis}_minor_ticks_visible', f'{ui}_minor_ticks_visible_checkbox',
90 _CHANGED + (f'_on_{axis}_minor_tick_visibility_changed',)),
91 number(f'{axis}_minor_tick_interval', f'{ui}_minor_tick_interval_spinbox', _CHANGED),
92 # 対数軸のときだけ効く
93 item_data(f'{axis}_log_minor_subs', f'{axis}_log_minor_subs_combo', _CHANGED),
94 check(f'{axis}_log_minor_labels', f'{axis}_log_minor_labels_checkbox', _CHANGED),
95 index(f'{axis}_tick_format_mode', f'{axis}_tick_format_combo', _CHANGED),
96 number(f'{axis}_tick_decimals', f'{axis}_tick_decimals_spinbox', _CHANGED),
97 )
100def _grid_rows(axis, which):
101 name = f'{axis}_{which}_grid'
102 return (
103 _grid_linestyle(f'{name}_linestyle', f'{name}_linestyle_combo'),
104 number(f'{name}_width', f'{name}_width_spinbox', _CHANGED),
105 number(f'{name}_alpha', f'{name}_alpha_spinbox', _CHANGED),
106 )
109AXIS_BINDINGS = (
110 text('title', 'ui.title_text_edit', _CHANGED),
111 text('x_label', 'ui.x_label_text_edit', _CHANGED),
112 text('y_label', 'ui.y_label_text_edit', _CHANGED),
113 check('x_label_visible', 'x_label_visible_checkbox', _CHANGED),
114 check('y_label_visible', 'y_label_visible_checkbox', _CHANGED),
115 # 見えている文字の欄。タイトルと X・Y 軸ラベルは編集ダイアログの確定で1回だけ setText される
116 typed_text('y2_label', 'y2_label_text_edit', _CHANGED),
118 *_axis_rows('x'),
119 choice('x_secondary_axis_source_unit', 'x_secondary_axis_source_unit_combo', X_AXIS_UNIT_CHOICES, _CHANGED),
120 choice('x_secondary_axis_target_unit', 'x_secondary_axis_target_unit_combo', X_AXIS_UNIT_CHOICES, _CHANGED),
122 *_axis_rows('y'),
124 check('legend_visible', 'ui.legend_visible_checkbox', _CHANGED + ('_on_legend_visibility_changed',)),
125 # ドラッグした位置を消してから書き戻すので、ほかの欄と違う処理につなぐ
126 shown_text('legend_loc', 'legend_loc_combo', ('_on_legend_loc_changed',)),
127 # グリッドのチェックは補助グリッドと詳細の欄の有効/無効も切り替える
128 check('grid_visible', 'ui.grid_visible_checkbox', _CHANGED + ('_on_grid_visibility_changed',)),
129 check('minor_grid_visible', 'ui.minor_grid_visible_checkbox', _CHANGED + ('_on_grid_visibility_changed',)),
131 *_grid_rows('x', 'major'),
132 *_grid_rows('x', 'minor'),
133 *_grid_rows('y', 'major'),
134 *_grid_rows('y', 'minor'),
135 shown_text('major_tick_direction', 'major_tick_direction_combo', _CHANGED),
136 shown_text('minor_tick_direction', 'minor_tick_direction_combo', _CHANGED),
137 shown_text('major_tick_direction_y2', 'major_tick_direction_y2_combo', _CHANGED),
138 shown_text('minor_tick_direction_y2', 'minor_tick_direction_y2_combo', _CHANGED),
139 check('x_ticks_visible', 'x_ticks_visible_checkbox', _CHANGED),
140 check('x_tick_labels_visible', 'x_tick_labels_visible_checkbox', _CHANGED),
141 check('y_ticks_visible', 'y_ticks_visible_checkbox', _CHANGED),
142 check('y_tick_labels_visible', 'y_tick_labels_visible_checkbox', _CHANGED),
144 _font('tick_font', '_tick_font'),
145 _font('axis_label_font', '_axis_label_font'),
146 _font('legend_font', '_legend_font', size_only_when_given=True),
147 _attribute('tick_color', '_tick_color'),
148 _width('tick_width', 'ui.tick_width_spinbox', '_tick_width'),
149 number('major_tick_length', 'major_tick_length_spinbox', _CHANGED),
150 _minor_tick_length('minor_tick_length', 'minor_tick_length_spinbox'),
151 _attribute('axis_label_color', '_axis_label_color'),
152 _attribute('legend_color', '_legend_color'),
153 _width('spine_width', 'ui.spine_width_spinbox', '_spine_width'),
154 _attribute('spine_color', '_spine_color'),
156 # 2Dマップが無い軸では効かない
157 check('colorbar_enabled', 'colorbar_enabled_checkbox', _CHANGED),
158 item_data('colorbar_position', 'colorbar_position_combo', _CHANGED),
159 number('colorbar_width_fraction', 'colorbar_width_spinbox', _CHANGED),
160 typed_text('colorbar_label', 'colorbar_label_edit', _CHANGED),
162 # 第 2 Y 軸。見え方(有効・無効)の切り替えもつなぐ
163 # 範囲を欄に入れてから設定を集め直す(_on_y2_autoscale_changed が最後に _on_axis_setting_changed を呼ぶ)。
164 # 先に描き直すと、欄の古い値で範囲が決まってしまい、今の範囲を入れられない
165 check('y2_autoscale', 'y2_autoscale_checkbox', ('_on_y2_autoscale_changed',)),
166 number('y2_min', 'y2_min_spinbox', _CHANGED),
167 number('y2_max', 'y2_max_spinbox', _CHANGED),
168 check('y2_log', 'y2_log_checkbox', _CHANGED + ('_on_y2_axis_state_changed',)),
169 check('y2_invert', 'y2_invert_checkbox', _CHANGED),
170 index('y2_major_tick_mode', 'y2_major_tick_mode_combo', _CHANGED + ('_on_y2_axis_state_changed',)),
171 number('y2_major_tick_interval', 'y2_major_tick_interval_spinbox', _CHANGED),
172 check('y2_minor_ticks_visible', 'y2_minor_ticks_visible_checkbox', _CHANGED + ('_on_y2_axis_state_changed',)),
173 number('y2_minor_tick_interval', 'y2_minor_tick_interval_spinbox', _CHANGED),
174)