Coverage for graphica/gui/rendering/appearance.py: 98.6%

254 statements  

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

1"""1 つの軸の外観の設定。_AxisStyle を共有する決まった順の手順で、あとの手順は前の手順が作った状態を読む。""" 

2import matplotlib.dates as mdates 

3import matplotlib.ticker as ticker 

4import numpy as np 

5from graphica.core.axis_settings import axis_inverted, axis_setting, manual_axis_range 

6from graphica.core.unit_conversion import X_AXIS_UNIT_LABELS, X_AXIS_UNIT_NONE, convert_x_axis_unit 

7from graphica.gui.mathtext_preview import families_for_texts, font_kwargs_for_text 

8from graphica.gui.rendering.common import ( 

9 DARK_AXES_FACECOLOR, DARK_GRID_COLOR, DARK_LEGEND_EDGECOLOR, DARK_LEGEND_FACECOLOR, LIGHT_AXES_FACECOLOR, 

10 LIGHT_GRID_COLOR, LIGHT_LEGEND_EDGECOLOR, LIGHT_LEGEND_FACECOLOR, _AxisStyle, _LOG_MINOR_SUBS_PRESETS, 

11 _apply_legend_order, _apply_tick_decimal_places, _apply_tick_format_mode, _legend_position_from_settings, 

12 _resolve_tick_lengths, _safe_multiple_locator) 

13from matplotlib.font_manager import FontProperties 

14 

15 

16def apply_appearance(canvas, ax, axis_index, settings): 

17 """その軸に見た目の設定を当てる。matplotlib の状態に依存するので、下の順番は変えない。""" 

18 secondary_ax = canvas.all_secondary_axes[axis_index] 

19 ax.set_facecolor(DARK_AXES_FACECOLOR if canvas.dark_mode else LIGHT_AXES_FACECOLOR) 

20 

21 is_date_x = axis_index < len(canvas.axis_is_date_x) and canvas.axis_is_date_x[axis_index] 

22 # 文字列カテゴリ軸では、範囲・対数などの数値向けの設定はカテゴリの位置と噛み合わないので使わない 

23 is_category_x = axis_index < len(canvas.axis_is_category_x) and canvas.axis_is_category_x[axis_index] 

24 

25 canvas._apply_limits_and_scale(ax, settings, is_category_x, axis_index) 

26 canvas._apply_tick_locators(ax, settings, is_date_x, is_category_x) 

27 style = canvas._axis_text_and_line_style(settings) 

28 canvas._apply_titles_and_labels(ax, settings, style) 

29 canvas._apply_spines_and_tick_marks(ax, settings, style) 

30 canvas._apply_legend(ax, secondary_ax, settings) 

31 canvas._apply_grid(ax, settings) 

32 canvas._apply_secondary_y_axis(ax, secondary_ax, settings, style) 

33 canvas._apply_unit_conversion_x_axis(ax, settings, style, is_date_x, is_category_x) 

34 canvas._apply_colorbar(ax, axis_index, settings, style) 

35 apply_view_override(canvas, ax, axis_index, settings) 

36 

37 

38def range_signature(settings, axis_key): 

39 """その軸の表示範囲を決める設定。マウスで変えた範囲は、これが変わったら設定の方を優先する。""" 

40 return tuple(axis_setting(settings, f'{axis_key}_{name}') for name in ('autoscale', 'min', 'max', 'log', 'invert')) 

41 

42 

43def apply_view_override(canvas, ax, axis_index, settings): 

44 """マウスで変えた表示範囲(canvas.view_overrides)を、描き直した後も当て直す。""" 

45 overrides = getattr(canvas, 'view_overrides', {}).get(axis_index) 

46 if not overrides: 

47 return 

48 secondary_axes = canvas.all_secondary_axes 

49 secondary_ax = secondary_axes[axis_index] if axis_index < len(secondary_axes) else None 

50 for axis_key, (limits, signature) in list(overrides.items()): 

51 target = secondary_ax if axis_key == 'y2' else ax 

52 if target is None or signature != range_signature(settings, axis_key): 

53 del overrides[axis_key] 

54 continue 

55 (target.set_xlim if axis_key == 'x' else target.set_ylim)(limits) 

56 

57 

58def apply_axis_range(ax, settings, axis_key, is_category_x=False, shift=(0.0, 0.0)): 

59 """設定どおりの範囲を当てる。axis_key は 'x' / 'y' / 'y2'('y2' なら ax は第2Y軸)。描画と範囲のリセットの共通の経路。 

60 

61 shift は (左へ広げる量(0 以下), 右へ広げる量(0 以上))。ウォーターフォールで X をずらしたとき、手動の範囲を手前の 

62 トレースの範囲とみなし、奥のトレースが切れないように広げる(waterfall_x_shift)。向きは呼び出し側が axis_inverted で決める。 

63 """ 

64 which = 'x' if axis_key == 'x' else 'y' 

65 if (axis_key == 'x' and is_category_x) or axis_setting(settings, f'{axis_key}_autoscale'): 

66 ax.autoscale(enable=True, axis=which, tight=True) 

67 return 

68 manual = manual_axis_range(settings, axis_key) 

69 if manual is not None: 

70 (ax.set_xlim if which == 'x' else ax.set_ylim)(manual[0] + shift[0], manual[1] + shift[1]) 

71 

72 

73def waterfall_x_shift(canvas, axis_index): 

74 """その軸で、ウォーターフォールでトレースを X にずらした幅 (左へ, 右へ)。ずらしていなければ (0, 0)。""" 

75 return getattr(canvas, 'waterfall_x_shift', {}).get(axis_index, (0.0, 0.0)) 

76 

77 

78def apply_limits_and_scale(canvas, ax, settings, is_category_x, axis_index=None): 

79 apply_axis_range(ax, settings, 'x', is_category_x, 

80 waterfall_x_shift(canvas, axis_index) if axis_index is not None else (0.0, 0.0)) 

81 apply_axis_range(ax, settings, 'y') 

82 

83 if not is_category_x: 

84 # set_xscale は同じ 'linear' でも Locator/Formatter を既定に戻してしまう。 

85 # カテゴリ軸では matplotlib が付けたカテゴリ用のものを残したいので呼ばない 

86 ax.set_xscale('log' if axis_setting(settings, 'x_log') else 'linear') 

87 ax.xaxis.set_inverted(axis_inverted(settings, 'x')) 

88 ax.set_yscale('log' if axis_setting(settings, 'y_log') else 'linear') 

89 ax.yaxis.set_inverted(axis_inverted(settings, 'y')) 

90 

91 

92def apply_tick_locators(canvas, ax, settings, is_date_x, is_category_x): 

93 x_min_lim, x_max_lim = ax.get_xlim() 

94 y_min_lim, y_max_lim = ax.get_ylim() 

95 

96 if is_date_x: 

97 # 日時の軸は範囲に合わせて年・月・日・時刻の間隔と表記を自動で選ぶ(間隔の手動指定は使わない) 

98 date_locator = mdates.AutoDateLocator() 

99 ax.xaxis.set_major_locator(date_locator) 

100 ax.xaxis.set_major_formatter(mdates.ConciseDateFormatter(date_locator)) 

101 elif is_category_x: 

102 # matplotlib が付けたカテゴリ用の目盛りのまま(数値用で上書きするとラベルが崩れる) 

103 pass 

104 elif axis_setting(settings, 'x_major_tick_mode') == 0: ax.xaxis.set_major_locator(ticker.AutoLocator()) 

105 else: 

106 interval = axis_setting(settings, 'x_major_tick_interval') 

107 if interval > 0: ax.xaxis.set_major_locator(_safe_multiple_locator(interval, x_min_lim, x_max_lim)) 

108 

109 if axis_setting(settings, 'y_major_tick_mode') == 0: ax.yaxis.set_major_locator(ticker.AutoLocator()) 

110 else: 

111 interval = axis_setting(settings, 'y_major_tick_interval') 

112 if interval > 0: ax.yaxis.set_major_locator(_safe_multiple_locator(interval, y_min_lim, y_max_lim)) 

113 

114 if is_date_x or is_category_x: 

115 ax.xaxis.set_minor_locator(ticker.NullLocator()) 

116 elif axis_setting(settings, 'x_minor_ticks_visible'): 

117 if axis_setting(settings, 'x_log'): 

118 # 対数軸は間隔指定の MultipleLocator ではなく LogLocator にする 

119 subs = _LOG_MINOR_SUBS_PRESETS.get(axis_setting(settings, 'x_log_minor_subs'), 'auto') 

120 ax.xaxis.set_minor_locator(ticker.LogLocator(base=10.0, subs=subs)) 

121 ax.xaxis.set_minor_formatter( 

122 ticker.LogFormatterSciNotation(base=10.0, labelOnlyBase=False) 

123 if axis_setting(settings, 'x_log_minor_labels') else ticker.NullFormatter() 

124 ) 

125 else: 

126 interval = axis_setting(settings, 'x_minor_tick_interval') 

127 if interval > 0: ax.xaxis.set_minor_locator(_safe_multiple_locator(interval, x_min_lim, x_max_lim)) 

128 else: ax.xaxis.set_minor_locator(ticker.NullLocator()) 

129 

130 if axis_setting(settings, 'y_minor_ticks_visible'): 

131 if axis_setting(settings, 'y_log'): 

132 subs = _LOG_MINOR_SUBS_PRESETS.get(axis_setting(settings, 'y_log_minor_subs'), 'auto') 

133 ax.yaxis.set_minor_locator(ticker.LogLocator(base=10.0, subs=subs)) 

134 ax.yaxis.set_minor_formatter( 

135 ticker.LogFormatterSciNotation(base=10.0, labelOnlyBase=False) 

136 if axis_setting(settings, 'y_log_minor_labels') else ticker.NullFormatter() 

137 ) 

138 else: 

139 interval = axis_setting(settings, 'y_minor_tick_interval') 

140 if interval > 0: ax.yaxis.set_minor_locator(_safe_multiple_locator(interval, y_min_lim, y_max_lim)) 

141 else: ax.yaxis.set_minor_locator(ticker.NullLocator()) 

142 

143 # 日付・カテゴリの X 軸は専用の表記を付けてあるので、数値の軸だけ 

144 if not is_date_x and not is_category_x: 

145 _apply_tick_format_mode(ax.xaxis, axis_setting(settings, 'x_tick_format_mode')) 

146 _apply_tick_decimal_places(ax.xaxis, axis_setting(settings, 'x_tick_decimals')) 

147 _apply_tick_format_mode(ax.yaxis, axis_setting(settings, 'y_tick_format_mode')) 

148 _apply_tick_decimal_places(ax.yaxis, axis_setting(settings, 'y_tick_decimals')) 

149 

150 

151def axis_text_and_line_style(canvas, settings): 

152 """文字と線の見た目のうち、軸・第2軸・カラーバーで共通に使うもの(ダークモードの色の読み替え後)。""" 

153 major_tick_length, minor_tick_length = _resolve_tick_lengths(settings) 

154 return _AxisStyle( 

155 tick_font=axis_setting(settings, 'tick_font'), 

156 label_font=axis_setting(settings, 'axis_label_font'), 

157 tick_color=canvas._effective_text_color(axis_setting(settings, 'tick_color')), 

158 label_color=canvas._effective_text_color(axis_setting(settings, 'axis_label_color')), 

159 spine_width=axis_setting(settings, 'spine_width'), 

160 spine_color=canvas._effective_text_color(axis_setting(settings, 'spine_color')), 

161 tick_width=axis_setting(settings, 'tick_width'), 

162 major_tick_length=major_tick_length, 

163 minor_tick_length=minor_tick_length, 

164 ) 

165 

166 

167def apply_titles_and_labels(canvas, ax, settings, style): 

168 title = axis_setting(settings, 'title') 

169 ax.set_title(title, **font_kwargs_for_text(title, style.label_font), color=style.label_color) 

170 # 非表示にしても文字列は消さない(表示に戻したときに打ち直さなくて済むように) 

171 x_label_text = axis_setting(settings, 'x_label') if axis_setting(settings, 'x_label_visible') else '' 

172 y_label_text = axis_setting(settings, 'y_label') if axis_setting(settings, 'y_label_visible') else '' 

173 ax.set_xlabel(x_label_text, **font_kwargs_for_text(x_label_text, style.label_font), color=style.label_color) 

174 ax.set_ylabel(y_label_text, **font_kwargs_for_text(y_label_text, style.label_font), color=style.label_color) 

175 # タイトルのクリックで、その軸を編集対象にする 

176 ax.title.set_picker(5) 

177 

178 for label in ax.get_xticklabels() + ax.get_yticklabels(): 

179 label.set(**style.tick_font) 

180 label.set_color(style.tick_color) 

181 

182 

183def apply_spines_and_tick_marks(canvas, ax, settings, style): 

184 for spine in ax.spines.values(): 

185 spine.set_linewidth(style.spine_width) 

186 spine.set_color(style.spine_color) 

187 

188 # 表示/非表示は毎回 True/False を明示する。tick_params の値は ax.cla() をまたいで残るので、 

189 # 隠すときだけ指定すると表示に戻せない。軸の共有で内側の目盛数値を隠すのは呼び出し側が後で行う 

190 major_dir = axis_setting(settings, 'major_tick_direction') 

191 minor_dir = axis_setting(settings, 'minor_tick_direction') 

192 x_ticks_visible = axis_setting(settings, 'x_ticks_visible') 

193 y_ticks_visible = axis_setting(settings, 'y_ticks_visible') 

194 x_tick_labels_visible = axis_setting(settings, 'x_tick_labels_visible') 

195 y_tick_labels_visible = axis_setting(settings, 'y_tick_labels_visible') 

196 

197 ax.tick_params(axis='x', which='major', width=style.tick_width, length=style.major_tick_length, color=style.spine_color, labelcolor=style.tick_color, 

198 direction=major_dir, bottom=x_ticks_visible, labelbottom=x_tick_labels_visible) 

199 ax.tick_params(axis='x', which='minor', width=style.tick_width * 0.75, length=style.minor_tick_length, color=style.spine_color, 

200 direction=minor_dir, bottom=x_ticks_visible, labelbottom=x_tick_labels_visible) 

201 ax.tick_params(axis='y', which='major', width=style.tick_width, length=style.major_tick_length, color=style.spine_color, labelcolor=style.tick_color, 

202 direction=major_dir, left=y_ticks_visible, labelleft=y_tick_labels_visible) 

203 ax.tick_params(axis='y', which='minor', width=style.tick_width * 0.75, length=style.minor_tick_length, color=style.spine_color, 

204 direction=minor_dir, left=y_ticks_visible, labelleft=y_tick_labels_visible) 

205 

206 

207def apply_legend(canvas, ax, secondary_ax, settings): 

208 lines_primary, labels_primary, lines_secondary, labels_secondary = [], [], [], [] 

209 if axis_setting(settings, 'legend_visible'): 

210 lines_primary, labels_primary = ax.get_legend_handles_labels() 

211 if secondary_ax: 

212 lines_secondary, labels_secondary = secondary_ax.get_legend_handles_labels() 

213 has_primary_data = bool(lines_primary) 

214 has_secondary_data = bool(lines_secondary) 

215 if not (has_primary_data or has_secondary_data): 

216 if ax.get_legend() is not None: ax.get_legend().remove() 

217 if secondary_ax and secondary_ax.get_legend() is not None: secondary_ax.get_legend().remove() 

218 return 

219 

220 loc_code = axis_setting(settings, 'legend_loc') 

221 # ドラッグで動かした位置(軸の左下 (0, 0)・右上 (1, 1) での凡例の左下)があれば、位置の選択より優先する 

222 dragged_position = _legend_position_from_settings(settings) 

223 if dragged_position is not None: 

224 loc_code = dragged_position 

225 legend_font_dict = axis_setting(settings, 'legend_font') 

226 legend_color = canvas._effective_text_color(axis_setting(settings, 'legend_color')) 

227 legend_font_prop = FontProperties( 

228 family=families_for_texts(labels_primary + labels_secondary, legend_font_dict.get('family')), 

229 size=legend_font_dict.get('size'), 

230 weight=legend_font_dict.get('weight'), 

231 style=legend_font_dict.get('style'), 

232 stretch=legend_font_dict.get('stretch') 

233 ) 

234 # 凡例の並びは描画順とは別に指定できる 

235 legend_order = axis_setting(settings, 'legend_order') 

236 legend_obj = None 

237 if secondary_ax and has_primary_data and has_secondary_data: 

238 combined_lines, combined_labels = _apply_legend_order( 

239 lines_primary + lines_secondary, labels_primary + labels_secondary, legend_order 

240 ) 

241 legend_obj = ax.legend(combined_lines, combined_labels, loc=loc_code, prop=legend_font_prop) 

242 elif has_primary_data: 

243 ordered_lines, ordered_labels = _apply_legend_order(lines_primary, labels_primary, legend_order) 

244 legend_obj = ax.legend(ordered_lines, ordered_labels, loc=loc_code, prop=legend_font_prop) 

245 elif secondary_ax and has_secondary_data: 245 ↛ 249line 245 didn't jump to line 249 because the condition on line 245 was always true

246 ordered_lines, ordered_labels = _apply_legend_order(lines_secondary, labels_secondary, legend_order) 

247 legend_obj = secondary_ax.legend(ordered_lines, ordered_labels, loc=loc_code, prop=legend_font_prop) 

248 

249 if legend_obj: 249 ↛ exitline 249 didn't return from function 'apply_legend' because the condition on line 249 was always true

250 for text in legend_obj.get_texts(): text.set_color(legend_color) 

251 frame = legend_obj.get_frame() 

252 if canvas.dark_mode: 

253 frame.set_facecolor(DARK_LEGEND_FACECOLOR) 

254 frame.set_edgecolor(DARK_LEGEND_EDGECOLOR) 

255 else: 

256 frame.set_facecolor(LIGHT_LEGEND_FACECOLOR) 

257 frame.set_edgecolor(LIGHT_LEGEND_EDGECOLOR) 

258 frame.set_alpha(0.92) 

259 # 離した位置が legend._loc に入り、PlotterApp がそれを設定へ保存する 

260 # (matplotlib 3.x に Legend.draggable() は無い) 

261 legend_obj.set_draggable(True, update='loc') 

262 

263 

264def apply_grid(canvas, ax, settings): 

265 """ax.grid() は指定した which/axis にしか効かないので、X/Y × 主/補助 を個別に呼ぶ。""" 

266 if not axis_setting(settings, 'grid_visible'): 

267 ax.grid(False, which='both') 

268 return 

269 grid_color = DARK_GRID_COLOR if canvas.dark_mode else LIGHT_GRID_COLOR 

270 for grid_axis in ('x', 'y'): 

271 ax.grid( 

272 True, which='major', axis=grid_axis, 

273 linestyle=axis_setting(settings, f'{grid_axis}_major_grid_linestyle'), 

274 linewidth=axis_setting(settings, f'{grid_axis}_major_grid_width'), 

275 alpha=axis_setting(settings, f'{grid_axis}_major_grid_alpha'), 

276 color=grid_color, 

277 ) 

278 if axis_setting(settings, 'minor_grid_visible'): 

279 ax.grid( 

280 True, which='minor', axis=grid_axis, 

281 linestyle=axis_setting(settings, f'{grid_axis}_minor_grid_linestyle'), 

282 linewidth=axis_setting(settings, f'{grid_axis}_minor_grid_width'), 

283 alpha=axis_setting(settings, f'{grid_axis}_minor_grid_alpha'), 

284 color=grid_color, 

285 ) 

286 else: 

287 ax.grid(False, which='minor', axis=grid_axis) 

288 

289 

290def _apply_secondary_y_limits_and_ticks(secondary_ax, settings): 

291 """主の Y 軸と同じ手順(範囲 → 対数 → 反転 → 目盛り)。既定(自動・線形・目盛り自動)では何も上書きしない。""" 

292 apply_axis_range(secondary_ax, settings, 'y2') 

293 is_log = axis_setting(settings, 'y2_log') 

294 # set_yscale は同じ値でも目盛りの Locator を既定に戻すので、変わるときだけ呼ぶ 

295 if secondary_ax.get_yscale() != ('log' if is_log else 'linear'): 

296 secondary_ax.set_yscale('log' if is_log else 'linear') 

297 secondary_ax.yaxis.set_inverted(axis_inverted(settings, 'y2')) 

298 

299 y_min_lim, y_max_lim = secondary_ax.get_ylim() 

300 if axis_setting(settings, 'y2_major_tick_mode') == 1: 

301 interval = axis_setting(settings, 'y2_major_tick_interval') 

302 if interval > 0 and not is_log: 

303 secondary_ax.yaxis.set_major_locator(_safe_multiple_locator(interval, y_min_lim, y_max_lim)) 

304 if axis_setting(settings, 'y2_minor_ticks_visible'): 

305 if is_log: 

306 secondary_ax.yaxis.set_minor_locator(ticker.LogLocator(base=10.0, subs='auto')) 

307 else: 

308 interval = axis_setting(settings, 'y2_minor_tick_interval') 

309 if interval > 0: 309 ↛ exitline 309 didn't return from function '_apply_secondary_y_limits_and_ticks' because the condition on line 309 was always true

310 secondary_ax.yaxis.set_minor_locator(_safe_multiple_locator(interval, y_min_lim, y_max_lim)) 

311 elif not is_log: 

312 secondary_ax.yaxis.set_minor_locator(ticker.NullLocator()) 

313 

314 

315def apply_secondary_y_axis(canvas, ax, secondary_ax, settings, style): 

316 """第2Y軸があれば右の枠線と目盛りを第2Y軸に任せ、無ければ主軸の右の枠線を出す。""" 

317 if not secondary_ax: 

318 ax.spines['right'].set_visible(True) 

319 ax.spines['right'].set_linewidth(style.spine_width) 

320 ax.spines['right'].set_color(style.spine_color) 

321 return 

322 _apply_secondary_y_limits_and_ticks(secondary_ax, settings) 

323 y2_label = axis_setting(settings, 'y2_label') 

324 secondary_ax.set_ylabel(y2_label, **font_kwargs_for_text(y2_label, style.label_font), color=style.label_color) 

325 major_dir_y2 = axis_setting(settings, 'major_tick_direction_y2') 

326 minor_dir_y2 = axis_setting(settings, 'minor_tick_direction_y2') 

327 for label in secondary_ax.get_yticklabels(): 

328 label.set(**style.tick_font) 

329 label.set_color(style.tick_color) 

330 secondary_ax.tick_params(axis='y', which='major', width=style.tick_width, length=style.major_tick_length, color=style.spine_color, labelcolor=style.tick_color, direction=major_dir_y2) 

331 secondary_ax.tick_params(axis='y', which='minor', width=style.tick_width * 0.75, length=style.minor_tick_length, color=style.spine_color, direction=minor_dir_y2) 

332 secondary_ax.spines['right'].set_linewidth(style.spine_width) 

333 secondary_ax.spines['right'].set_color(style.spine_color) 

334 secondary_ax.spines['right'].set_visible(True) 

335 secondary_ax.spines['left'].set_visible(False) 

336 secondary_ax.spines['top'].set_visible(False) 

337 secondary_ax.spines['bottom'].set_visible(False) 

338 ax.spines['right'].set_visible(False) 

339 

340 

341def apply_unit_conversion_x_axis(canvas, ax, settings, style, is_date_x, is_category_x): 

342 """X の単位と表示したい単位が別々に選ばれていれば、上に単位を変換した第2X軸を付ける(数値の軸だけ)。""" 

343 source_unit = axis_setting(settings, 'x_secondary_axis_source_unit') 

344 target_unit = axis_setting(settings, 'x_secondary_axis_target_unit') 

345 # 範囲の端が変換で inf/nan になる(波長 0nm など)と secondary_xaxis が例外を出し、描画全体が失敗する 

346 if not (not is_date_x and not is_category_x 

347 and source_unit != X_AXIS_UNIT_NONE and target_unit != X_AXIS_UNIT_NONE 

348 and source_unit != target_unit 

349 and np.all(np.isfinite(convert_x_axis_unit(np.array(ax.get_xlim()), source_unit, target_unit)))): 

350 return 

351 

352 def _forward(x, _from=source_unit, _to=target_unit): 

353 return convert_x_axis_unit(x, _from, _to) 

354 

355 def _inverse(x, _from=source_unit, _to=target_unit): 

356 return convert_x_axis_unit(x, _to, _from) 

357 

358 secondary_x_ax = ax.secondary_xaxis('top', functions=(_forward, _inverse)) 

359 # 逆数の変換では値域が極端に広がり、既定の AutoLocator が千本を超える目盛りを作ろうとする 

360 secondary_x_ax.xaxis.set_major_locator(ticker.MaxNLocator(nbins=8)) 

361 secondary_x_ax.set_xlabel(X_AXIS_UNIT_LABELS.get(target_unit, target_unit), 

362 **style.label_font, color=style.label_color) 

363 for label in secondary_x_ax.get_xticklabels(): 

364 label.set(**style.tick_font) 

365 label.set_color(style.tick_color) 

366 secondary_x_ax.tick_params(axis='x', which='major', width=style.tick_width, length=style.major_tick_length, 

367 color=style.spine_color, labelcolor=style.tick_color, 

368 direction=axis_setting(settings, 'major_tick_direction')) 

369 secondary_x_ax.spines['top'].set_linewidth(style.spine_width) 

370 secondary_x_ax.spines['top'].set_color(style.spine_color) 

371 

372 

373def apply_colorbar(canvas, ax, axis_index, settings, style): 

374 """ 

375 2Dマップ(または値で色分けした散布図)がこの軸にあればカラーバーを付ける。 

376 location を渡すと向きは matplotlib が決めるので、orientation は渡さない(衝突しうる)。 

377 """ 

378 mappable = canvas._axis_2d_mappables.get(axis_index) 

379 if mappable is None or not axis_setting(settings, 'colorbar_enabled'): 

380 return 

381 position = axis_setting(settings, 'colorbar_position') 

382 if position not in ('right', 'left', 'top', 'bottom'): 

383 position = 'right' 

384 try: 

385 fraction = float(axis_setting(settings, 'colorbar_width_fraction')) 

386 except (TypeError, ValueError): 

387 fraction = 0.05 

388 if fraction <= 0: 388 ↛ 389line 388 didn't jump to line 389 because the condition on line 388 was never true

389 fraction = 0.05 

390 cbar = canvas.fig.colorbar(mappable, ax=ax, location=position, fraction=fraction, pad=0.04) 

391 colorbar_label = axis_setting(settings, 'colorbar_label') 

392 if colorbar_label: 

393 cbar.set_label(colorbar_label, **font_kwargs_for_text(colorbar_label, style.label_font), color=style.label_color) 

394 for tick_label in cbar.ax.get_yticklabels() + cbar.ax.get_xticklabels(): 

395 tick_label.set(**style.tick_font) 

396 tick_label.set_color(style.tick_color) 

397 cbar.outline.set_edgecolor(style.spine_color) 

398 cbar.outline.set_linewidth(style.spine_width)