Coverage for graphica/gui/builders/dataset_panel.py: 100.0%
353 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"""データセットの一覧とプロパティ欄の欄の組み立て。"""
2from PySide6.QtCore import Qt, Signal
3from PySide6.QtWidgets import (
4 QAbstractItemView, QCheckBox, QComboBox, QDoubleSpinBox, QFormLayout, QFrame, QGridLayout, QGroupBox, QHeaderView,
5 QLabel, QLineEdit,
6 QMenu, QPushButton, QSpinBox, QTextEdit, QToolButton, QTreeWidget, QTreeWidgetItemIterator, QVBoxLayout, QWidget)
7from graphica.core.dataset import WATERFALL_OFFSET_ABSOLUTE, WATERFALL_OFFSET_STEP
8from graphica.core.i18n import tr
9from graphica.core.marker_style import (
10 DEFAULT_MARKER_EDGE_WIDTH, MARKER_EDGE_COLOR, MARKER_EDGE_SAME, MARKER_FILL_COLOR, MARKER_FILL_HOLLOW,
11 MARKER_FILL_SOLID)
12from graphica.gui.builders import property_widgets
13from graphica.gui.builders.common import (
14 COLORMAP_CHOICES, DATASET_TREE_VISIBILITY_COLUMN_WIDTH, _DatasetTreeSelectionDelegate, _insert_form_row_after,
15 _svg_icon, cap_combo_width)
16from graphica.gui.color_picker_widget import ColorPickerWidget
17from graphica.gui.dataset_bindings import MARKER_CHOICES
18from graphica.gui.dataset_style_icon import (
19 DATASET_TREE_NAME_COLUMN, DATASET_TREE_VISIBILITY_COLUMN, marker_choice_icon)
22def build_dataset_color_picker(app):
23 old_color_button = app.ui.color_button
24 app.color_picker_widget = ColorPickerWidget(app.settings, app)
25 app.ui.formLayout_4.replaceWidget(old_color_button, app.color_picker_widget)
26 old_color_button.hide()
27 old_color_button.deleteLater()
29 # 「データ追加」のすぐ隣
30 app.new_dataset_button = QPushButton(tr("新規データセット作成..."))
31 app.ui.horizontalLayout_3.insertWidget(1, app.new_dataset_button)
34def build_dataset_list_buttons(app):
35 app.duplicate_dataset_button = QPushButton(tr("プロット複製"))
36 app.view_edit_data_button = QPushButton(tr("データ表示/編集"))
37 app.fit_curve_button = QPushButton(tr("曲線フィット"))
38 app.find_peaks_button = QPushButton(tr("ピーク検出"))
39 app.multi_peak_fit_button = QPushButton(tr("多峰フィット"))
40 app.auto_color_button = QPushButton(tr("自動配色"))
41 app.new_folder_button = QPushButton(tr("新しいフォルダ"))
43 # ボタンをアイコンだけにして「データ処理」「解析」「整理」の3組に分ける(文字はツールチップに残す)。
44 # ダークモードの切り替えでアイコンを作り直すので(_refresh_custom_svg_icons)、対応を持っておく
45 app._dataset_action_button_icons = {
46 app.ui.add_dataset_button: ("file-plus", tr("データ追加")),
47 app.new_dataset_button: ("table", tr("新規作成")),
48 app.duplicate_dataset_button: ("copy", tr("複製")),
49 app.view_edit_data_button: ("edit", tr("表示/編集")),
50 app.ui.remove_dataset_button: ("trash", tr("削除")),
51 app.fit_curve_button: ("chart-line", tr("曲線フィット")),
52 app.find_peaks_button: ("mountain", tr("ピーク検出")),
53 app.multi_peak_fit_button: ("chart-histogram", tr("多峰フィット")),
54 app.auto_color_button: ("palette", tr("自動配色")),
55 app.new_folder_button: ("folder-plus", tr("新しいフォルダ")),
56 }
57 for button, (icon_name, short_label) in app._dataset_action_button_icons.items():
58 button.setToolTip(button.text() or short_label)
59 button.setText("")
60 button.setIcon(_svg_icon(icon_name, size=18))
61 button.setProperty("iconOnly", True)
62 button.setFixedSize(34, 34)
63 # フォーカスを持つと、押した後も :focus の枠が残って押されたままに見える
64 button.setFocusPolicy(Qt.FocusPolicy.NoFocus)
66 def _make_group_separator():
67 sep = QFrame()
68 sep.setFrameShape(QFrame.Shape.VLine)
69 sep.setObjectName("button_row_separator")
70 sep.setFixedWidth(2)
71 return sep
73 # データ処理: 追加・新規作成・複製・表示編集・削除(削除は Designer の配置のまま)
74 app.ui.horizontalLayout_3.insertWidget(2, app.duplicate_dataset_button)
75 app.ui.horizontalLayout_3.insertWidget(3, app.view_edit_data_button)
77 app.ui.horizontalLayout_3.insertWidget(5, _make_group_separator())
78 # 解析
79 app.ui.horizontalLayout_3.addWidget(app.fit_curve_button)
80 app.ui.horizontalLayout_3.addWidget(app.find_peaks_button)
81 app.ui.horizontalLayout_3.addWidget(app.multi_peak_fit_button)
83 app.ui.horizontalLayout_3.addWidget(_make_group_separator())
84 # 整理
85 app.ui.horizontalLayout_3.addWidget(app.auto_color_button)
86 app.ui.horizontalLayout_3.addWidget(app.new_folder_button)
88 app.ui.horizontalLayout_3.addStretch()
90 # 「⋯」: たまにしか使わない操作
91 app.dataset_overflow_button = QToolButton()
92 app.dataset_overflow_button.setText("⋯")
93 app.dataset_overflow_button.setToolTip(tr("その他の操作"))
94 app.dataset_overflow_button.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
95 app.dataset_overflow_button.setFixedSize(34, 34)
96 overflow_menu = QMenu(app.dataset_overflow_button)
97 app.manage_palette_action = overflow_menu.addAction(
98 _svg_icon("palette", size=16), tr("パレット管理...")
99 )
100 app.colormap_assign_action = overflow_menu.addAction(
101 _svg_icon("palette", size=16), tr("カラーマップから自動配色...")
102 )
103 # 登録はいつでも変わるので開くたびに作り直す。QMenu と menuAction() の両方を持つ(持たないと回収される)
104 app._named_color_apply_menu = overflow_menu.addMenu(
105 _svg_icon("color-swatch", size=16), tr("登録した色を適用"))
106 app._named_color_apply_menu_action = app._named_color_apply_menu.menuAction()
107 app._named_color_apply_menu.aboutToShow.connect(
108 app.colors.populate_named_color_menu)
109 app.colors.populate_named_color_menu()
110 app.dataset_overflow_button.setMenu(overflow_menu)
111 app.ui.horizontalLayout_3.addWidget(app.dataset_overflow_button)
113 # データセットのプロパティ欄を7つの節に分ける。Designer の行もここで移すので、formLayout_4 を触る
114 # 色欄の差し替え(_build_dataset_color_picker)より後であること。以降の行は app._prop_form(キー) に足す
115 app._build_dataset_property_sections()
118def build_marker_style_controls(app):
119 """マーカーの形の選択肢を名前と形の絵にし、塗りと枠線の欄を作る。出し入れは property_panel.update_marker_controls_visibility。"""
120 combo = app.ui.marker_combo
121 combo.clear()
122 for marker, label in MARKER_CHOICES:
123 icon = marker_choice_icon(marker)
124 if icon is None:
125 combo.addItem(tr(label), marker)
126 else:
127 combo.addItem(icon, tr(label), marker)
129 marker_form = app._prop_form('marker')
130 app.marker_fill_label = QLabel(tr("マーカーの塗り"))
131 app.marker_fill_combo = QComboBox()
132 app.marker_fill_combo.addItem(tr("塗りつぶし"), MARKER_FILL_SOLID)
133 app.marker_fill_combo.addItem(tr("白抜き"), MARKER_FILL_HOLLOW)
134 app.marker_fill_combo.addItem(tr("色を指定"), MARKER_FILL_COLOR)
135 marker_form.addRow(app.marker_fill_label, app.marker_fill_combo)
137 app.marker_face_color_label = QLabel(tr("塗りの色"))
138 app.marker_face_color_picker = ColorPickerWidget(app.settings, app, initial_color='#ffffff')
139 marker_form.addRow(app.marker_face_color_label, app.marker_face_color_picker)
141 app.marker_edge_label = QLabel(tr("マーカーの枠線"))
142 app.marker_edge_combo = QComboBox()
143 app.marker_edge_combo.addItem(tr("系列の色"), MARKER_EDGE_SAME)
144 app.marker_edge_combo.addItem(tr("色を指定"), MARKER_EDGE_COLOR)
145 marker_form.addRow(app.marker_edge_label, app.marker_edge_combo)
147 app.marker_edge_color_label = QLabel(tr("枠線の色"))
148 app.marker_edge_color_picker = ColorPickerWidget(app.settings, app, initial_color='#000000')
149 marker_form.addRow(app.marker_edge_color_label, app.marker_edge_color_picker)
151 app.marker_edge_width_label = QLabel(tr("枠線の太さ"))
152 app.marker_edge_width_spinbox = QDoubleSpinBox()
153 app.marker_edge_width_spinbox.setRange(0.0, 10.0)
154 app.marker_edge_width_spinbox.setSingleStep(0.5)
155 app.marker_edge_width_spinbox.setDecimals(1)
156 app.marker_edge_width_spinbox.setValue(DEFAULT_MARKER_EDGE_WIDTH)
157 marker_form.addRow(app.marker_edge_width_label, app.marker_edge_width_spinbox)
160def build_dataset_style_controls(app):
161 app.x_col_combo = QComboBox()
162 app.y_col_combo = QComboBox()
163 app._prop_form('data').addRow("X軸の列", app.x_col_combo)
164 app._prop_form('data').addRow("Y軸の列", app.y_col_combo)
166 app.x_err_col_combo = QComboBox()
167 app.y_err_col_combo = QComboBox()
168 app._prop_form('data').addRow("X誤差列", app.x_err_col_combo)
169 app._prop_form('data').addRow("Y誤差列", app.y_err_col_combo)
171 app.alpha_label = QLabel("透明度")
172 app.alpha_spinbox = QDoubleSpinBox()
173 app.alpha_spinbox.setRange(0.0, 1.0)
174 app.alpha_spinbox.setSingleStep(0.05)
175 app.alpha_spinbox.setDecimals(2)
176 app.alpha_spinbox.setValue(1.0)
177 app._prop_form('style').addRow(app.alpha_label, app.alpha_spinbox)
179 build_marker_style_controls(app)
181 # グラデーション(線・面の塗り)。出し入れは property_panel.update_gradient_controls_visibility
182 app.gradient_checkbox = QCheckBox(tr("グラデーションを適用"))
183 app._prop_form('gradient').addRow(app.gradient_checkbox)
185 app.gradient_color2_label = QLabel(tr("終端色"))
186 app.gradient_color2_picker = ColorPickerWidget(app.settings, app, initial_color='#ffffff')
187 app._prop_form('gradient').addRow(app.gradient_color2_label, app.gradient_color2_picker)
189 app.gradient_target_label = QLabel(tr("対象"))
190 app.gradient_target_combo = QComboBox()
191 app.gradient_target_combo.addItem(tr("線"), "line")
192 app.gradient_target_combo.addItem(tr("塗り"), "fill")
193 app.gradient_target_combo.addItem(tr("両方"), "both")
194 app._prop_form('gradient').addRow(app.gradient_target_label, app.gradient_target_combo)
196 # ウォーターフォールは種類ではなく、どの種類とも組み合わせられるオプション。
197 # 出し入れは property_panel.update_waterfall_controls_visibility
198 app.waterfall_checkbox = QCheckBox(tr("ウォーターフォール表示(積み重ね)"))
199 app._prop_form('waterfall').addRow(app.waterfall_checkbox)
201 # オフセットの欄は大きな値や指数表記(1.5E6)も入れられる(main_window の _enable_scientific_notation_input)
202 app.waterfall_offset_mode_label = QLabel(tr("オフセットの指定"))
203 app.waterfall_offset_mode_combo = QComboBox()
204 app.waterfall_offset_mode_combo.addItem(tr("段ごとの間隔(段の番号 × オフセット)"), WATERFALL_OFFSET_STEP)
205 app.waterfall_offset_mode_combo.addItem(tr("ずらし量をそのまま"), WATERFALL_OFFSET_ABSOLUTE)
206 cap_combo_width(app.waterfall_offset_mode_combo)
207 app._prop_form('waterfall').addRow(app.waterfall_offset_mode_label, app.waterfall_offset_mode_combo)
209 app.waterfall_offset_x_label = QLabel(tr("Xオフセット"))
210 app.waterfall_offset_x_spinbox = QDoubleSpinBox()
211 app.waterfall_offset_x_spinbox.setRange(-1e6, 1e6)
212 app.waterfall_offset_x_spinbox.setSingleStep(0.1)
213 app.waterfall_offset_x_spinbox.setDecimals(4)
214 app.waterfall_offset_x_spinbox.setValue(0.0)
215 app._prop_form('waterfall').addRow(app.waterfall_offset_x_label, app.waterfall_offset_x_spinbox)
217 app.waterfall_offset_y_label = QLabel(tr("Yオフセット"))
218 app.waterfall_offset_y_spinbox = QDoubleSpinBox()
219 app.waterfall_offset_y_spinbox.setRange(-1e6, 1e6)
220 app.waterfall_offset_y_spinbox.setSingleStep(0.1)
221 app.waterfall_offset_y_spinbox.setDecimals(4)
222 app.waterfall_offset_y_spinbox.setValue(1.0)
223 app._prop_form('waterfall').addRow(app.waterfall_offset_y_label, app.waterfall_offset_y_spinbox)
225 app.waterfall_occlusion_checkbox = QCheckBox(tr("背面のトレースを隠す(オクルージョン)"))
226 app.waterfall_occlusion_checkbox.setChecked(True)
227 app._prop_form('waterfall').addRow(app.waterfall_occlusion_checkbox)
229 # 奥の段ほど Y をわずかに縮めて立体風にする
230 app.waterfall_depth_checkbox = QCheckBox(tr("奥行き効果(奥のトレースをわずかに縮小)"))
231 app.waterfall_depth_checkbox.setChecked(False)
232 app._prop_form('waterfall').addRow(app.waterfall_depth_checkbox)
234 app.waterfall_depth_ratio_label = QLabel(tr("1段あたりの縮小率"))
235 app.waterfall_depth_ratio_spinbox = QDoubleSpinBox()
236 app.waterfall_depth_ratio_spinbox.setRange(0.0, 0.9)
237 app.waterfall_depth_ratio_spinbox.setSingleStep(0.01)
238 app.waterfall_depth_ratio_spinbox.setDecimals(3)
239 app.waterfall_depth_ratio_spinbox.setValue(0.03)
240 app._prop_form('waterfall').addRow(app.waterfall_depth_ratio_label, app.waterfall_depth_ratio_spinbox)
242 app.point_labels_checkbox = QCheckBox("データ点にラベルを表示")
243 app._prop_form('extra').addRow(app.point_labels_checkbox)
244 # 点が上限を超えてラベルを描かないときに、その理由を出す
245 app.point_labels_limit_note = QLabel()
246 app.point_labels_limit_note.setObjectName("point_labels_limit_note")
247 app.point_labels_limit_note.setWordWrap(True)
248 app.point_labels_limit_note.setVisible(False)
249 app._prop_form('extra').addRow(app.point_labels_limit_note)
250 app.point_label_col_label = QLabel("ラベルの内容")
251 app.point_label_col_combo = QComboBox()
252 app._prop_form('extra').addRow(app.point_label_col_label, app.point_label_col_combo)
254 # 誤差の表し方。誤差列が無ければどれを選んでも描かれない
255 app.error_display_label = QLabel(tr("誤差の表示形式"))
256 app.error_display_combo = QComboBox()
257 app.error_display_combo.addItem(tr("エラーバー"), "bar")
258 app.error_display_combo.addItem(tr("誤差バンド"), "band")
259 app.error_display_combo.addItem(tr("両方"), "both")
260 app._prop_form('extra').addRow(app.error_display_label, app.error_display_combo)
262 # 凡例名(= データセット名)を変えずに凡例の文字だけ変える。空ならデータセット名
263 app.legend_label_label = QLabel(tr("凡例の表示名"))
264 app.legend_label_edit = QLineEdit()
265 app.legend_label_edit.setPlaceholderText(tr("空ならデータセット名"))
266 app._prop_form('extra').addRow(app.legend_label_label, app.legend_label_edit)
267 app.hide_from_legend_checkbox = QCheckBox(tr("凡例に表示しない"))
268 app._prop_form('extra').addRow(app.hide_from_legend_checkbox)
270 # X/Y/Z 列の長形式を2Dマップとして描く。関係する欄の出し入れは property_panel.update_2d_controls_visibility
271 app.data_2d_checkbox = QCheckBox(tr("2Dグリッドとして扱う(ヒートマップ)"))
272 app._prop_form('map').addRow(app.data_2d_checkbox)
274 app.z_col_label = QLabel(tr("Z軸の列"))
275 app.z_col_combo = QComboBox()
276 app._prop_form('map').addRow(app.z_col_label, app.z_col_combo)
278 app.colormap_label = QLabel(tr("カラーマップ"))
279 app.colormap_combo = QComboBox()
280 app.colormap_combo.addItems(COLORMAP_CHOICES)
281 app._prop_form('map').addRow(app.colormap_label, app.colormap_combo)
283 app.map_display_mode_label = QLabel(tr("表示方式"))
284 app.map_display_mode_combo = QComboBox()
285 app.map_display_mode_combo.addItem(tr("ヒートマップ"), "heatmap")
286 app.map_display_mode_combo.addItem(tr("等高線(線)"), "contour")
287 app.map_display_mode_combo.addItem(tr("等高線(塗りつぶし)"), "contour_filled")
288 app.map_display_mode_combo.addItem(tr("ヒートマップ+等高線"), "heatmap_contour")
289 app._prop_form('map').addRow(app.map_display_mode_label, app.map_display_mode_combo)
291 app.contour_levels_label = QLabel(tr("等高線レベル数"))
292 app.contour_levels_spinbox = QSpinBox()
293 app.contour_levels_spinbox.setRange(2, 100)
294 app.contour_levels_spinbox.setValue(10)
295 app._prop_form('map').addRow(app.contour_levels_label, app.contour_levels_spinbox)
297 app.grid_interp_method_label = QLabel(tr("補間方法"))
298 app.grid_interp_method_combo = QComboBox()
299 app.grid_interp_method_combo.addItems(['linear', 'cubic', 'nearest'])
300 app._prop_form('map').addRow(app.grid_interp_method_label, app.grid_interp_method_combo)
302 app.color_range_auto_checkbox = QCheckBox(tr("値域を自動"))
303 app.color_range_auto_checkbox.setChecked(True)
304 app._prop_form('map').addRow(app.color_range_auto_checkbox)
306 app.vmin_label = QLabel(tr("値域の最小"))
307 app.vmin_spinbox = QDoubleSpinBox()
308 app.vmin_spinbox.setRange(-1e12, 1e12)
309 app.vmin_spinbox.setDecimals(4)
310 app.vmin_spinbox.setEnabled(False)
311 app._prop_form('map').addRow(app.vmin_label, app.vmin_spinbox)
313 app.vmax_label = QLabel(tr("値域の最大"))
314 app.vmax_spinbox = QDoubleSpinBox()
315 app.vmax_spinbox.setRange(-1e12, 1e12)
316 app.vmax_spinbox.setDecimals(4)
317 app.vmax_spinbox.setValue(1.0)
318 app.vmax_spinbox.setEnabled(False)
319 app._prop_form('map').addRow(app.vmax_label, app.vmax_spinbox)
321 # 欠損値の扱いは描くときだけに効く(データ自体は変えない)。
322 # ラベルの列幅は全部の節で揃えるので、ここが一番長いと全体の入力欄が狭まる。「(NaN)」はツールチップに回す
323 app.nan_policy_label = QLabel(tr("欠損値の扱い"))
324 app.nan_policy_label.setToolTip(tr("欠損値(NaN)を含む点の描画方法"))
325 app.nan_policy_combo = QComboBox()
326 app.nan_policy_combo.addItem(tr("線を切る(既定)"), "gap")
327 app.nan_policy_combo.addItem(tr("前の値で埋める"), "ffill")
328 app.nan_policy_combo.addItem(tr("無視してつなぐ"), "drop")
329 app._prop_form('data').addRow(app.nan_policy_label, app.nan_policy_combo)
331 # 平滑化の手法。オン/オフは Designer の smoothing_checkbox
332 app.smoothing_method_label = QLabel(tr("平滑化の手法"))
333 app.smoothing_method_combo = QComboBox()
334 app.smoothing_method_combo.addItem(tr("CubicSpline(既定)"), "cubic_spline")
335 app.smoothing_method_combo.addItem(tr("移動平均"), "moving_average")
336 app.smoothing_method_combo.addItem(tr("中央値フィルタ"), "median")
337 app.smoothing_method_combo.addItem(tr("ガウシアンフィルタ"), "gaussian")
338 # 「平滑化」のチェックは手法と対なので、_build_dataset_property_sections では移さずここで手法の直前に置く
339 # (そうしないと間に「透明度」が挟まる)
340 app._prop_form('style').addRow(app.ui.smoothing_checkbox)
341 app._prop_form('style').addRow(app.smoothing_method_label, app.smoothing_method_combo)
343 property_widgets.build_property_widgets(app)
346def build_fit_info_and_stats(app):
347 # フィットの結果の文。置き場所は下のパネルの「フィット結果」のタブ(shell/bottom_panel.py)
348 app.fit_info_textedit = QTextEdit()
349 app.fit_info_textedit.setObjectName("fit_info_textedit")
350 app.fit_info_textedit.setReadOnly(True)
351 app.fit_info_textedit.setPlaceholderText(tr("フィットした曲線か、フィットしたデータセットを選ぶと、ここに式とパラメータが出ます。"))
353 # 統計の1行(件数・平均・標準偏差・最小・最大)。選択が変わるたびに property_panel.update_stats_summary_label が
354 # 更新する。画面には左パネルの統計カードを出す(GUI 刷新の決定 10 でツールバーの統計ボタンはやめた)
355 app.stats_summary_label = QLabel("-")
356 app.stats_summary_label.setWordWrap(True)
357 app.stats_summary_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
360Y2_SPINBOX_MIN_WIDTH = 100
363def _y2_value_spinbox(decimals=2):
364 spinbox = QDoubleSpinBox()
365 spinbox.setRange(-2147483648.0, 2147483648.0)
366 spinbox.setDecimals(decimals)
367 # 既定の最小幅は範囲の桁数(-2147483648.00)で決まり、右のインスペクタ(340px)からはみ出す
368 spinbox.setMinimumWidth(Y2_SPINBOX_MIN_WIDTH)
369 return spinbox
372def _build_secondary_y_axis_group(app):
373 """「Y軸」タブの末尾に、第 2 Y 軸の範囲と目盛りの欄を足す(主の Y 軸の欄と同じ並び)。"""
374 app.y2_axis_group = QGroupBox(tr("第2Y軸"))
375 form = QFormLayout(app.y2_axis_group)
376 app.y2_autoscale_checkbox = QCheckBox(tr("自動スケール"))
377 app.y2_autoscale_checkbox.setChecked(True)
378 form.addRow(app.y2_autoscale_checkbox)
379 app.y2_min_spinbox = _y2_value_spinbox()
380 form.addRow(tr("最小値"), app.y2_min_spinbox)
381 app.y2_max_spinbox = _y2_value_spinbox()
382 app.y2_max_spinbox.setValue(1.0)
383 form.addRow(tr("最大値"), app.y2_max_spinbox)
384 app.y2_log_checkbox = QCheckBox(tr("対数表示"))
385 form.addRow(app.y2_log_checkbox)
386 # 対数軸で表示されない 0 以下の値を知らせる(中身は log_axis_notes.update_log_axis_notes)
387 app.y2_log_note = QLabel()
388 app.y2_log_note.setObjectName("y2_log_note")
389 app.y2_log_note.setWordWrap(True)
390 app.y2_log_note.setVisible(False)
391 form.addRow(app.y2_log_note)
392 app.y2_invert_checkbox = QCheckBox(tr("軸を反転"))
393 form.addRow(app.y2_invert_checkbox)
394 app.y2_major_tick_mode_combo = QComboBox()
395 # 選んだ番号で値を持つ(index の束ね)ので、表示を訳してよい
396 app.y2_major_tick_mode_combo.addItems([tr("自動"), tr("固定間隔")])
397 form.addRow(tr("主目盛"), app.y2_major_tick_mode_combo)
398 app.y2_major_tick_interval_spinbox = _y2_value_spinbox()
399 app.y2_major_tick_interval_spinbox.setValue(1.0)
400 form.addRow(tr("間隔"), app.y2_major_tick_interval_spinbox)
401 app.y2_minor_ticks_visible_checkbox = QCheckBox(tr("補助目盛を表示"))
402 form.addRow(app.y2_minor_ticks_visible_checkbox)
403 app.y2_minor_tick_interval_spinbox = _y2_value_spinbox()
404 app.y2_minor_tick_interval_spinbox.setValue(0.5)
405 form.addRow(tr("補助目盛の間隔"), app.y2_minor_tick_interval_spinbox)
406 layout = app.ui.gridLayout_3
407 layout.addWidget(app.y2_axis_group, layout.rowCount(), 0, 1, layout.columnCount() or 1)
410def build_secondary_y_controls(app):
411 app.use_secondary_y_checkbox = QCheckBox("第2Y軸 (右側) を使用")
412 app._prop_form('place').addRow(app.use_secondary_y_checkbox)
414 app.y2_label_text_label = QLabel("第2Y軸ラベル")
415 app.y2_label_text_edit = QLineEdit()
416 _insert_form_row_after(app.ui.formLayout_3, app.ui.y_label_text_edit,
417 app.y2_label_text_label, app.y2_label_text_edit)
419 _build_secondary_y_axis_group(app)
421def add_subplot_target_row(app):
422 app.subplot_target_label = QLabel("描画先プロット")
423 app.subplot_target_combo = QComboBox()
424 app._prop_form('place').addRow(app.subplot_target_label, app.subplot_target_combo)
427class DatasetTreeWidget(QTreeWidget):
428 """ドロップで項目を動かした後に items_dropped を出す。
430 QTreeWidget のドロップは項目を取り出して入れ直すので、モデルの rowsMoved は出ない。
431 引数は、どの項目も親(フォルダ)が変わらなかったか。
432 """
433 items_dropped = Signal(bool)
435 def dropEvent(self, event):
436 parents = [(item, item.parent()) for item in self._all_items()]
437 super().dropEvent(event)
438 self.items_dropped.emit(all(item.parent() is parent for item, parent in parents))
440 def _all_items(self):
441 items = []
442 iterator = QTreeWidgetItemIterator(self)
443 while iterator.value() is not None:
444 items.append(iterator.value())
445 iterator += 1
446 return items
449def replace_dataset_list_with_tree(app):
450 """Designer の QListWidget を、同じセルに「検索欄 + QTreeWidget」を縦に並べたものへ差し替える。"""
451 old_widget = app.ui.dataset_list_widget
452 parent_widget = old_widget.parentWidget()
453 parent_layout = parent_widget.layout()
455 idx = parent_layout.indexOf(old_widget)
456 row = col = rowspan = colspan = None
457 if isinstance(parent_layout, QGridLayout):
458 row, col, rowspan, colspan = parent_layout.getItemPosition(idx)
460 parent_layout.removeWidget(old_widget)
461 old_widget.setParent(None)
462 old_widget.deleteLater()
464 container = QWidget(parent_widget)
465 container_layout = QVBoxLayout(container)
466 container_layout.setContentsMargins(0, 0, 0, 0)
467 container_layout.setSpacing(6)
469 search_edit = QLineEdit(container)
470 search_edit.setObjectName("dataset_search_edit")
471 search_edit.setPlaceholderText("データセットを検索...")
472 search_edit.setClearButtonEnabled(True)
473 container_layout.addWidget(search_edit)
475 tree = DatasetTreeWidget(container)
476 tree.setObjectName("dataset_list_widget")
477 tree.setHeaderHidden(True)
478 # 列1は表示/非表示の目のアイコン。stretchLastSection のままだと目の列が余白を吸って広がる
479 tree.setColumnCount(2)
480 header = tree.header()
481 header.setStretchLastSection(False)
482 header.setSectionResizeMode(DATASET_TREE_NAME_COLUMN, QHeaderView.ResizeMode.Stretch)
483 header.setSectionResizeMode(DATASET_TREE_VISIBILITY_COLUMN, QHeaderView.ResizeMode.Fixed)
484 tree.setColumnWidth(DATASET_TREE_VISIBILITY_COLUMN, DATASET_TREE_VISIBILITY_COLUMN_WIDTH)
485 tree.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
486 tree.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
487 tree.setDefaultDropAction(Qt.DropAction.MoveAction)
488 tree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
489 # 行の伸びをキャンバスに譲ったので、放っておくと数件でも窮屈な高さまで縮む
490 tree.setMinimumHeight(90)
491 tree.setItemDelegate(_DatasetTreeSelectionDelegate(tree))
492 container_layout.addWidget(tree)
494 if isinstance(parent_layout, QGridLayout) and row is not None:
495 parent_layout.addWidget(container, row, col, rowspan, colspan)
496 else:
497 parent_layout.addWidget(container)
499 app.ui.dataset_list_widget = tree
500 app.dataset_search_edit = search_edit