Coverage for graphica/gui/log_axis_notes.py: 96.6%
42 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"""対数軸で表示されない値(0 以下)を、対数のチェックの下で知らせる。
3matplotlib は対数軸の 0 以下の点を黙って消し、0 以下の最小値も黙って無視するので、何も描かれない理由が分からなくなる。
4"""
5import numpy as np
6import pandas as pd
8from graphica.core.axis_settings import axis_setting, manual_axis_range
9from graphica.core.i18n import tr
11# 軸のキー -> (対数の設定, 説明の欄の名前)
12_AXES = {
13 'x': ('x_log', 'x_log_note'),
14 'y': ('y_log', 'y_log_note'),
15 'y2': ('y2_log', 'y2_log_note'),
16}
19def _numbers(values):
20 return pd.to_numeric(pd.Series(np.asarray(values).ravel()), errors='coerce').to_numpy(dtype=float)
23def nonpositive_point_count(canvas, datasets, axis_index, axis_key):
24 """axis_index のサブプロットで、axis_key の軸に描く値が 0 以下の点の数(描かれる位置で数える)。"""
25 count = 0
26 for ds in datasets:
27 if not ds.visible or ds.data_kind == '2d_grid' or ds.subplot_target != axis_index:
28 continue
29 if axis_key == 'y' and ds.use_secondary_y or axis_key == 'y2' and not ds.use_secondary_y:
30 continue
31 # 文字の X(カテゴリ軸)は数にならないので数えない
32 x, y = _numbers(ds.x_data), _numbers(ds.y_data)
33 x, y = canvas.data_to_display(ds, x, y)
34 values = np.asarray(x if axis_key == 'x' else y, dtype=float)
35 count += int(np.count_nonzero(values <= 0))
36 return count
39def log_note_text(count, min_ignored):
40 """説明の文。知らせることが無ければ空。"""
41 lines = []
42 if count:
43 lines.append(tr("0 以下の値の点が {count} 個あり、対数軸では表示されません。").format(count=f"{count:,}"))
44 if min_ignored:
45 lines.append(tr("最小値が 0 以下なので、対数軸では無視されます。"))
46 return "\n".join(lines)
49def update_log_axis_notes(app, _event=None):
50 """表示中の軸の設定で、X・Y・第 2 Y 軸の説明を出し入れする。描くたびと、表示中の軸を切り替えたときに呼ぶ。"""
51 settings_list = app.project.all_plot_settings
52 index = app.project.active_axis_index
53 settings = settings_list[index] if 0 <= index < len(settings_list) else None
54 for axis_key, (log_key, note_name) in _AXES.items():
55 note = getattr(app, note_name, None)
56 if note is None: 56 ↛ 57line 56 didn't jump to line 57 because the condition on line 56 was never true
57 continue
58 text = ""
59 if settings is not None and axis_setting(settings, log_key):
60 manual = manual_axis_range(settings, axis_key)
61 min_ignored = manual is not None and manual[0] <= 0
62 count = nonpositive_point_count(app.canvas, app.project.datasets, index, axis_key)
63 text = log_note_text(count, min_ignored)
64 note.setText(text)
65 note.setVisible(bool(text))