Coverage for graphica/gui/dock_layout.py: 99.0%
81 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"""ドックの配置: 右のインスペクタの組み立てと、起動時の復元(パネルは固定で、動かせない)。"""
2import logging
3from PySide6.QtWidgets import QDockWidget, QFormLayout, QVBoxLayout, QWidget
4from graphica.core.i18n import tr
5from graphica.gui import app_settings
6from graphica.gui.builders.common import DOCK_LAYOUT_VERSION
7from graphica.gui.export_preview_panel import ExportPreviewPanel
8from graphica.gui.shell.inspector import (INSPECTOR_PAGES, Inspector, TargetHeader, scroll_page,
9 section_state_texts, wrap_long_rows)
11logger = logging.getLogger(__name__)
14def arrange_property_docks(app):
15 """右のインスペクタ(GUI 刷新): プロパティ・軸・凡例・エクスポートのタブを、プロパティのドックの中に並べる。
17 ドックは動かさない(決定 5)。見出しの帯はタブが兼ねるので出さない。properties_dock_widget は
18 control_dock_widget の別名(表示メニューなどがこの名前で使う)。
19 """
20 dock = app.ui.control_dock_widget
21 app.properties_dock_widget = dock
22 dock.setWindowTitle(tr("プロパティ"))
23 dock.setTitleBarWidget(QWidget())
24 dock.setFeatures(QDockWidget.DockWidgetFeature.NoDockWidgetFeatures)
26 plot_properties = dock.widget()
27 if plot_properties is None:
28 logger.warning("control_dock_widget の中身が見つかりません。")
29 plot_properties = QWidget()
30 legend_page = _take_legend_rows(app)
31 app.ui.properties_groupbox.setTitle("")
32 # theme.py はグループボックスのタイトル用に上の余白を取る。タイトルが無いので、それを 0 にする目印
33 app.ui.properties_groupbox.setProperty("collapsibleBody", True)
35 app.inspector = Inspector()
36 app.inspector_target = TargetHeader()
37 properties_page = QWidget()
38 properties_page.setObjectName("inspector_page")
39 properties_layout = QVBoxLayout(properties_page)
40 properties_layout.setContentsMargins(0, 0, 0, 0)
41 properties_layout.setSpacing(0)
42 properties_layout.addWidget(app.inspector_target)
43 properties_layout.addWidget(app.ui.properties_groupbox)
44 properties_layout.addStretch(1)
46 plot_properties.setObjectName("inspector_axes_content")
47 wrap_long_rows(plot_properties)
48 # 見本どおり左右 16px の余白(欄が欄の端に付かないように)
49 app.ui.gridLayout_4.setContentsMargins(16, 0, 16, 8)
50 if plot_properties.layout() is not None:
51 plot_properties.layout().setContentsMargins(16, 8, 16, 8)
53 # エクスポートのプレビュー。見えている間だけ描く(ExportPreviewPanel.refresh_preview)ので、裏のタブにあれば重くならない
54 app.export_preview_panel = ExportPreviewPanel(app)
56 for key, title in INSPECTOR_PAGES:
57 widget = {"properties": properties_page, "axes": plot_properties, "legend": legend_page,
58 "export": app.export_preview_panel}[key]
59 app.inspector.add_page(key, title, scroll_page(widget, f"inspector_scroll_{key}"))
60 app.inspector.tab_bar.currentChanged.connect(lambda _index: _on_inspector_page_changed(app))
61 dock.setWidget(app.inspector)
63 # 選んでいるデータセットの見出しと節の状態は、描き直すたびに合わせる(選択・名前・種類の変更をまとめて拾える)
64 app.canvas.mpl_connect('draw_event', lambda _event: refresh_inspector(app))
67# 凡例のタブに移す行(軸の設定の formLayout_3 から)。表示のチェックは格子の表示と同じ行の中なので、それも取り出す
68_LEGEND_FIELDS = ("legend_loc_combo", "legend_font_button", "legend_color_button", "legend_order_button")
71def _take_legend_rows(app):
72 page = QWidget()
73 page.setObjectName("inspector_legend_content")
74 form = QFormLayout(page)
75 form.setContentsMargins(16, 10, 16, 10)
76 form.setSpacing(6)
77 form.setRowWrapPolicy(QFormLayout.RowWrapPolicy.WrapLongRows)
78 visible_checkbox = app.ui.legend_visible_checkbox
79 visible_checkbox.parentWidget().layout().removeWidget(visible_checkbox)
80 form.addRow(visible_checkbox)
81 source = app.ui.formLayout_3
82 for attr in _LEGEND_FIELDS:
83 row, _role = source.getWidgetPosition(getattr(app, attr))
84 taken = source.takeRow(row)
85 label = taken.labelItem.widget() if taken.labelItem is not None else None
86 field = taken.fieldItem.widget() if taken.fieldItem is not None else None
87 if label is not None and field is not None:
88 form.addRow(label, field)
89 else:
90 form.addRow(field or label)
91 return page
94def _on_inspector_page_changed(app):
95 if app.inspector.current_page() == "export":
96 app.export_preview_panel.refresh_preview()
99def refresh_inspector(app):
100 """右の見出しと、節の見出しの右の状態の文字を、表示しているデータセットに合わせる。"""
101 dataset = app.property_panel.shown_dataset()
102 app.inspector_target.set_dataset(dataset)
103 states = section_state_texts(dataset)
104 for key, entry in getattr(app, '_prop_sections', {}).items():
105 toggle = entry['toggle']
106 if hasattr(toggle, 'set_state_text'): 106 ↛ 104line 106 didn't jump to line 104 because the condition on line 106 was always true
107 toggle.set_state_text(states.get(key, ""))
110def restore_dock_layout(app):
111 """前回のドック配置を戻す。タブが最終の大きさになってから呼ぶ(でないとスプリッターの位置がずれる)。"""
112 saved_layout_version = (app_settings.DOCK_LAYOUT_VERSION.read(app.settings) if app._run_startup_checks
113 else DOCK_LAYOUT_VERSION)
114 saved_state = app_settings.WINDOW_STATE.read(app.settings) if app._run_startup_checks else None
115 if saved_state is not None and saved_layout_version == DOCK_LAYOUT_VERSION and app.restoreState(saved_state):
116 return
117 # 戻せなければ(初回・追加したタブ)組み立てたときの配置のまま。タブで並べたドックは後から足した方が前に出るので
118 # プロパティを前にする(表示される前の raise_() は効かないので、表示された後のここで行う)
119 app.ui.control_dock_widget.raise_()