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

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) 

10 

11logger = logging.getLogger(__name__) 

12 

13 

14def arrange_property_docks(app): 

15 """右のインスペクタ(GUI 刷新): プロパティ・軸・凡例・エクスポートのタブを、プロパティのドックの中に並べる。 

16 

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) 

25 

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) 

34 

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) 

45 

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) 

52 

53 # エクスポートのプレビュー。見えている間だけ描く(ExportPreviewPanel.refresh_preview)ので、裏のタブにあれば重くならない 

54 app.export_preview_panel = ExportPreviewPanel(app) 

55 

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) 

62 

63 # 選んでいるデータセットの見出しと節の状態は、描き直すたびに合わせる(選択・名前・種類の変更をまとめて拾える) 

64 app.canvas.mpl_connect('draw_event', lambda _event: refresh_inspector(app)) 

65 

66 

67# 凡例のタブに移す行(軸の設定の formLayout_3 から)。表示のチェックは格子の表示と同じ行の中なので、それも取り出す 

68_LEGEND_FIELDS = ("legend_loc_combo", "legend_font_button", "legend_color_button", "legend_order_button") 

69 

70 

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 

92 

93 

94def _on_inspector_page_changed(app): 

95 if app.inspector.current_page() == "export": 

96 app.export_preview_panel.refresh_preview() 

97 

98 

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, "")) 

108 

109 

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_()