Coverage for graphica/gui/builders/property_widgets.py: 96.5%
105 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"""プロパティの見た目の部品: 線の種類のセグメント、太さ・透明度のスライダー、平滑化のスイッチ、色のチップ、
2シンプルの種類のタイルと「アドバンスドですべて表示」の案内。
4値の持ち主は今のコンボ・スピンボックス・チェックボックスのまま(バインディングの表はそちらを見る)。
5バインディングで戻すとシグナルが止まるので、戻したあとに refresh_property_widgets() で見た目を合わせる。
6"""
7from PySide6.QtCore import Qt
8from PySide6.QtWidgets import QFrame, QHBoxLayout, QLabel, QPushButton, QVBoxLayout, QWidget
10from graphica.core.i18n import tr
11from graphica.gui import theme
12from graphica.gui.builders.common import _svg_icon
13from graphica.gui.dataset_bindings import NO_MARKER
14from graphica.gui.widgets import ComboMirror, SegmentedControl, SliderSpin, TypeTileGroup
16LINE_STYLE_ITEMS = (("solid", "実線"), ("dashed", "破線"), ("dotted", "点線"), ("dashdot", "一点鎖線"))
18# (絵, 表示名, 種別のコンボの値)
19TYPE_TILES = (
20 ("line", "線", "Line"),
21 ("scatter", "点", "Scatter"),
22 ("line_scatter", "線+点", "Line+Scatter"),
23 ("bar", "棒", "Bar"),
24)
25# 点を描く種類。マーカーが「なし」のまま選ぶと何も見えないので、円にする
26_MARKER_TYPES = ("Scatter", "Line+Scatter")
27_DEFAULT_MARKER = "o"
29# スライダーの範囲(よく使う範囲。数値の欄はこれより外の値も受け付ける)
30LINEWIDTH_SLIDER_RANGE = (0.0, 5.0)
31ALPHA_SLIDER_RANGE = (0.0, 1.0)
33QUICK_COLOR_COUNT = 4
36class _TypeTileMirror(ComboMirror):
37 """タイルで点を描く種類を選んだとき、マーカーが無ければ円にする。種類の変更と1つの「元に戻す」にまとめる。"""
39 def __init__(self, view, combo, app):
40 self._app = app
41 super().__init__(view, combo)
43 def _on_view_changed(self, index):
44 app = self._app
45 needs_marker = (self.view.itemData(index) in _MARKER_TYPES
46 and app.ui.marker_combo.currentData() == NO_MARKER)
47 if not needs_marker:
48 super()._on_view_changed(index)
49 return
50 app.undo_stack.beginMacro(tr("グラフの種類"))
51 try:
52 app.ui.marker_combo.setCurrentIndex(app.ui.marker_combo.findData(_DEFAULT_MARKER))
53 super()._on_view_changed(index)
54 finally:
55 app.undo_stack.endMacro()
58def _swap_field(form, old_field, new_field):
59 """フォームの欄を差し替える(行の位置はそのまま)。old_field は new_field の中に取り込む前に呼ぶこと。"""
60 if form.replaceWidget(old_field, new_field) is None: 60 ↛ 61line 60 didn't jump to line 61 because the condition on line 60 was never true
61 raise ValueError("欄がフォームに見つからない")
64def _hidden_owner_row(view, owner):
65 """見た目の部品と、隠した値の持ち主(コンボ)を1つの欄にまとめる(持ち主が節の外に浮かないように)。"""
66 field = QWidget()
67 layout = QHBoxLayout(field)
68 layout.setContentsMargins(0, 0, 0, 0)
69 layout.setSpacing(0)
70 layout.addWidget(view, 1)
71 owner.setParent(field)
72 owner.hide()
73 return field
76def build_property_widgets(app):
77 """基本スタイルの節の部品を見本の形に替える。build_dataset_style_controls の最後に呼ぶ(行が全部できた後)。"""
78 style_form = app._prop_form('style')
80 # 線の種類: 線の絵のセグメント
81 app.linestyle_segments = SegmentedControl()
82 for line_style, tooltip in LINE_STYLE_ITEMS:
83 app.linestyle_segments.addLineStyleItem(line_style, tooltip=tr(tooltip))
84 placeholder = QWidget()
85 _swap_field(style_form, app.ui.linestyle_combo, placeholder)
86 app.linestyle_field = _hidden_owner_row(app.linestyle_segments, app.ui.linestyle_combo)
87 _swap_field(style_form, placeholder, app.linestyle_field)
88 placeholder.deleteLater()
89 app.linestyle_mirror = ComboMirror(app.linestyle_segments, app.ui.linestyle_combo)
91 # 線の太さ・透明度: スライダー+数値(数値の欄は今のスピンボックス)
92 app.linewidth_slider = _slider_field(style_form, app.ui.linewidth_spinbox, *LINEWIDTH_SLIDER_RANGE)
93 app.alpha_slider = _slider_field(style_form, app.alpha_spinbox, *ALPHA_SLIDER_RANGE)
95 # 平滑化: 部品は替えず、描き方だけスイッチに
96 theme.make_toggle_switch(app.ui.smoothing_checkbox)
98 # 色: 見本の右に、今の配色パレットの色
99 app.color_picker_widget.show_quick_colors(quick_colors(app))
101 # シンプルの種類: アイコンの4択(裏で「種別」のコンボを書く)
102 app.plot_type_tiles = TypeTileGroup()
103 for glyph, text, value in TYPE_TILES:
104 app.plot_type_tiles.addTile(glyph, tr(text), value)
105 app.plot_type_tiles_mirror = _TypeTileMirror(app.plot_type_tiles, app.ui.plot_type_combo, app)
106 app._plot_type_tiles_row.layout().addWidget(app.plot_type_tiles)
108 app.lite_more_box = _build_lite_more_box(app)
109 app._dataset_property_sections_container.layout().addWidget(app.lite_more_box)
112def _slider_field(form, spinbox, slider_min, slider_max):
113 placeholder = QWidget()
114 _swap_field(form, spinbox, placeholder)
115 slider = SliderSpin(spinbox, slider_min, slider_max)
116 _swap_field(form, placeholder, slider)
117 placeholder.deleteLater()
118 return slider
121def _build_lite_more_box(app):
122 """シンプルで隠している項目の案内と、アドバンスドに切り替えるリンク。"""
123 box = QFrame()
124 box.setObjectName("lite_more_box")
125 layout = QVBoxLayout(box)
126 layout.setContentsMargins(12, 10, 12, 10)
127 layout.setSpacing(6)
129 row = QHBoxLayout()
130 row.setSpacing(8)
131 app.lite_more_icon = QLabel()
132 app.lite_more_icon.setObjectName("lite_more_icon")
133 app.lite_more_icon.setPixmap(_svg_icon("lock", size=15).pixmap(15, 15))
134 app.lite_more_icon.setAlignment(Qt.AlignmentFlag.AlignTop)
135 text = QLabel(tr("誤差・透明度・平滑化・マーカー・グラデーション・ウォーターフォール・2D マップなど"))
136 text.setObjectName("lite_more_text")
137 text.setWordWrap(True)
138 row.addWidget(app.lite_more_icon)
139 row.addWidget(text, 1)
140 layout.addLayout(row)
142 app.lite_more_button = QPushButton(tr("アドバンスドですべて表示 →"))
143 app.lite_more_button.setObjectName("lite_more_button")
144 app.lite_more_button.setFlat(True)
145 app.lite_more_button.setCursor(Qt.CursorShape.PointingHandCursor)
146 app.lite_more_button.clicked.connect(app.show_advanced_mode)
147 layout.addWidget(app.lite_more_button, 0, Qt.AlignmentFlag.AlignLeft)
148 return box
151def quick_colors(app):
152 return list(app.colors.active_color_cycle())[:QUICK_COLOR_COUNT]
155def refresh_property_widgets(app):
156 """バインディングで欄を戻したあと(シグナルが止まっている)、見た目の部品を合わせる。"""
157 if not hasattr(app, 'linestyle_mirror'): 157 ↛ 158line 157 didn't jump to line 158 because the condition on line 157 was never true
158 return
159 app.linestyle_mirror.refresh()
160 app.plot_type_tiles_mirror.refresh()
161 app.linewidth_slider.refresh()
162 app.alpha_slider.refresh()
163 app.color_picker_widget.show_quick_colors(quick_colors(app))