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

1"""軸の設定(core/axis_settings.py のキー)と画面の欄の対応表。欄を 1 つ足すときは表に 1 行足す。 

2 

3表の順は戻す順。集めた辞書のキーの順は AXIS_SETTING_DEFAULTS の順(保存形式)にそろえる。 

4""" 

5from PySide6.QtGui import QFont 

6 

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 

10 

11_CHANGED = ('_on_axis_setting_changed',) 

12 

13# 欄を持たず操作から直接書き込まれるキー。集めるときは今の軸の設定から引き継ぐ(辞書は丸ごと入れ替わるので) 

14CARRIED_AXIS_KEYS = ('annotations', 'legend_order', 'free_rect', 'legend_position') 

15 

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) 

21 

22 

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) 

32 

33 

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

38 

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) 

47 

48 

49def _attribute(key, attribute): 

50 return Binding(key, None, lambda owner, _w: getattr(owner, attribute), 

51 lambda owner, _w, value: setattr(owner, attribute, value)) 

52 

53 

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) 

60 

61 

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) 

66 

67 

68def _minor_tick_length(key, widget): 

69 """負値(スピンボックスの「自動」)は -1 に正規化して保存する。""" 

70 def read(_owner, spinbox): 

71 return spinbox.value() if spinbox.value() >= 0 else -1 

72 

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) 

76 

77 

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 ) 

98 

99 

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 ) 

107 

108 

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

117 

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

121 

122 *_axis_rows('y'), 

123 

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

130 

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

143 

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

155 

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

161 

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)