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
« 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
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)
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]
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)
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'))
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)
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軸)。描画と範囲のリセットの共通の経路。
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])
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))
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')
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'))
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()
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))
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))
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())
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())
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'))
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 )
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)
178 for label in ax.get_xticklabels() + ax.get_yticklabels():
179 label.set(**style.tick_font)
180 label.set_color(style.tick_color)
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)
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')
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)
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
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)
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')
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)
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'))
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())
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)
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
352 def _forward(x, _from=source_unit, _to=target_unit):
353 return convert_x_axis_unit(x, _from, _to)
355 def _inverse(x, _from=source_unit, _to=target_unit):
356 return convert_x_axis_unit(x, _to, _from)
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)
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)