Coverage for graphica/core/axis_settings.py: 100.0%

24 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-12 16:38 +0000

1""" 

21つの軸の設定(保存形式は文字列キーの辞書)のキーと既定値。 

3 

4キーを持たない古いプロジェクトは、画面に戻すときも描画するときもここの既定値で読む。 

5既定値を変えると既存のプロジェクトの見た目が変わるので、変えるときは保存形式の移行として扱う。 

6""" 

7import copy 

8 

9from graphica.core.unit_conversion import X_AXIS_UNIT_NONE 

10from typing import Any 

11 

12AXIS_SETTING_DEFAULTS = { 

13 'title': '', 

14 'x_label': '', 

15 'y_label': '', 

16 'x_label_visible': True, 

17 'y_label_visible': True, 

18 'y2_label': '', 

19 

20 'x_autoscale': True, 

21 'x_min': 0, 

22 'x_max': 1, 

23 'x_log': False, 

24 'x_invert': False, 

25 'x_major_tick_mode': 0, 

26 'x_major_tick_interval': 1, 

27 'x_minor_ticks_visible': False, 

28 'x_minor_tick_interval': 0.5, 

29 'x_log_minor_subs': 'auto', 

30 'x_log_minor_labels': False, 

31 'x_tick_format_mode': 0, 

32 'x_tick_decimals': -1, 

33 'x_secondary_axis_source_unit': X_AXIS_UNIT_NONE, 

34 'x_secondary_axis_target_unit': X_AXIS_UNIT_NONE, 

35 

36 'y_autoscale': True, 

37 'y_min': 0, 

38 'y_max': 1, 

39 'y_log': False, 

40 'y_invert': False, 

41 'y_major_tick_mode': 0, 

42 'y_major_tick_interval': 1, 

43 'y_minor_ticks_visible': False, 

44 'y_minor_tick_interval': 0.5, 

45 'y_log_minor_subs': 'auto', 

46 'y_log_minor_labels': False, 

47 'y_tick_format_mode': 0, 

48 'y_tick_decimals': -1, 

49 

50 'legend_visible': True, 

51 'legend_loc': 'best', 

52 'grid_visible': False, 

53 'minor_grid_visible': False, 

54 'x_major_grid_linestyle': '-', 

55 'x_major_grid_width': 0.8, 

56 'x_major_grid_alpha': 1.0, 

57 'x_minor_grid_linestyle': '--', 

58 'x_minor_grid_width': 0.5, 

59 'x_minor_grid_alpha': 1.0, 

60 'y_major_grid_linestyle': '-', 

61 'y_major_grid_width': 0.8, 

62 'y_major_grid_alpha': 1.0, 

63 'y_minor_grid_linestyle': '--', 

64 'y_minor_grid_width': 0.5, 

65 'y_minor_grid_alpha': 1.0, 

66 'major_tick_direction': 'out', 

67 'minor_tick_direction': 'out', 

68 'major_tick_direction_y2': 'out', 

69 'minor_tick_direction_y2': 'out', 

70 'x_ticks_visible': True, 

71 'x_tick_labels_visible': True, 

72 'y_ticks_visible': True, 

73 'y_tick_labels_visible': True, 

74 

75 # フォントは {family, size, weight, style}。空なら画面側・描画側の既定のフォント 

76 'tick_font': {}, 

77 'tick_color': '#000000', 

78 'tick_width': 0.8, 

79 # matplotlib の既定(xtick.major.size)と同じ。-1(負値)の補助目盛は「主目盛から自動」 

80 'major_tick_length': 3.5, 

81 'minor_tick_length': -1, 

82 'axis_label_font': {}, 

83 'axis_label_color': '#000000', 

84 'legend_font': {}, 

85 'legend_color': '#000000', 

86 'spine_width': 1.0, 

87 'spine_color': '#000000', 

88 

89 'colorbar_enabled': True, 

90 'colorbar_position': 'right', 

91 'colorbar_width_fraction': 0.05, 

92 'colorbar_label': '', 

93 

94 # 画面の欄を持たず、操作から直接書き込まれるもの 

95 'annotations': [], 

96 'legend_order': [], 

97 'free_rect': None, 

98 'legend_position': None, 

99 # 第 2 Y 軸(主の Y 軸と同じ意味)。既定は範囲・目盛りとも自動 

100 'y2_autoscale': True, 

101 'y2_min': 0, 

102 'y2_max': 1, 

103 'y2_log': False, 

104 'y2_invert': False, 

105 'y2_major_tick_mode': 0, 

106 'y2_major_tick_interval': 1, 

107 'y2_minor_ticks_visible': False, 

108 'y2_minor_tick_interval': 0.5, 

109} 

110 

111# 軸ごとに分ける前の共通キー。新しいキーが無いプロジェクトでは、その値を X/Y 両方に使う。 

112LEGACY_FALLBACK_KEYS = { 

113 'x_ticks_visible': 'ticks_visible', 

114 'y_ticks_visible': 'ticks_visible', 

115 'x_tick_labels_visible': 'tick_labels_visible', 

116 'y_tick_labels_visible': 'tick_labels_visible', 

117} 

118 

119 

120def manual_axis_range(settings: dict[str, Any], axis_key: str) -> tuple[float, float] | None: 

121 """手動で決めた範囲を小さい順で。自動スケールか、最小と最大が同じなら None。axis_key は 'x' / 'y' / 'y2'。 

122 

123 最小に大きい方を入れても範囲として使う(向きは axis_inverted で決める)。黙って無視すると自動の範囲になって紛らわしい。 

124 """ 

125 if axis_setting(settings, f'{axis_key}_autoscale'): 

126 return None 

127 low, high = axis_setting(settings, f'{axis_key}_min'), axis_setting(settings, f'{axis_key}_max') 

128 if low == high: 

129 return None 

130 return (low, high) if low < high else (high, low) 

131 

132 

133def axis_inverted(settings: dict[str, Any], axis_key: str) -> bool: 

134 """軸を大きい方から並べるか。「軸を反転」に加え、手動の範囲で最小に大きい方を入れていれば向きを入れ替える。""" 

135 inverted = bool(axis_setting(settings, f'{axis_key}_invert')) 

136 if not axis_setting(settings, f'{axis_key}_autoscale') \ 

137 and axis_setting(settings, f'{axis_key}_min') > axis_setting(settings, f'{axis_key}_max'): 

138 inverted = not inverted 

139 return inverted 

140 

141 

142def axis_setting(settings: dict[str, Any], key: str) -> Any: 

143 """settings の key の値。無ければ(古いキーがあればその値、それも無ければ)既定値。""" 

144 if key in settings: 

145 return settings[key] 

146 legacy_key = LEGACY_FALLBACK_KEYS.get(key) 

147 if legacy_key is not None and legacy_key in settings: 

148 return settings[legacy_key] 

149 # 既定値のリストや辞書を呼び出し側が書き換えても、既定値が変わらないように 

150 return copy.deepcopy(AXIS_SETTING_DEFAULTS[key])