|
1 | | -from typing import TYPE_CHECKING |
2 | | - |
3 | 1 | from PySide6.QtCore import Qt |
4 | | -from PySide6.QtCore import Signal |
5 | | -from PySide6.QtWidgets import QDialog |
6 | | -from PySide6.QtWidgets import QPushButton |
| 2 | +from PySide6.QtWidgets import QHBoxLayout |
| 3 | +from PySide6.QtWidgets import QLabel |
7 | 4 | from PySide6.QtWidgets import QScrollArea |
8 | 5 | from PySide6.QtWidgets import QVBoxLayout |
9 | 6 | from PySide6.QtWidgets import QWidget |
10 | 7 |
|
11 | | -from compas_viewer.base import Base |
| 8 | +from compas_viewer.components.color import ColorDialog |
| 9 | +from compas_viewer.components.component import Component |
12 | 10 | from compas_viewer.components.double_edit import DoubleEdit |
13 | | -from compas_viewer.components.label import LabelWidget |
14 | | -from compas_viewer.components.layout import SettingLayout |
15 | 11 | from compas_viewer.components.textedit import TextEdit |
16 | 12 |
|
17 | | -if TYPE_CHECKING: |
18 | | - from compas_viewer import Viewer |
19 | | - |
20 | 13 |
|
21 | | -class ObjectSetting(QWidget): |
| 14 | +class ObjectSetting(Component): |
22 | 15 | """ |
23 | | - A QWidget to manage the settings of objects in the viewer. |
24 | | -
|
25 | | - Parameters |
26 | | - ---------- |
27 | | - viewer : Viewer |
28 | | - The viewer instance containing the objects. |
29 | | - items : list |
30 | | - A list of dictionaries containing the settings for the object. |
31 | | -
|
32 | | - Attributes |
33 | | - ---------- |
34 | | - viewer : Viewer |
35 | | - The viewer instance. |
36 | | - items : list |
37 | | - A list of dictionaries containing the settings for the object. |
38 | | - layout : QVBoxLayout |
39 | | - The main layout for the widget. |
40 | | - update_button : QPushButton |
41 | | - The button to trigger the object update. |
42 | | -
|
43 | | - Methods |
44 | | - ------- |
45 | | - clear_layout(layout) |
46 | | - Clears all widgets and sub-layouts from the given layout. |
47 | | - update() |
48 | | - Updates the layout with the latest object settings. |
49 | | - obj_update() |
50 | | - Applies the settings from spin boxes to the selected objects. |
| 16 | + A component to manage the settings of objects in the viewer. |
51 | 17 | """ |
52 | 18 |
|
53 | | - update_requested = Signal() |
54 | | - |
55 | | - def __init__(self, viewer: "Viewer", items: list[dict]): |
| 19 | + def __init__(self): |
56 | 20 | super().__init__() |
57 | | - self.viewer = viewer |
58 | | - self.items = items |
59 | | - self.setting_layout = SettingLayout(viewer=self.viewer, items=self.items, type="obj_setting") |
60 | | - # Main layout |
61 | | - self.main_layout = QVBoxLayout(self) |
62 | | - |
63 | | - # Scroll area setup |
64 | | - self.scroll_area = QScrollArea(self) |
65 | | - self.scroll_area.setWidgetResizable(True) |
| 21 | + self.widget = QScrollArea() |
| 22 | + self.widget.setWidgetResizable(True) |
| 23 | + self.reset() |
| 24 | + |
| 25 | + def reset(self): |
| 26 | + """Reset the content widget and layout to a clean state.""" |
| 27 | + self.sub_widgets = {} |
66 | 28 | self.scroll_content = QWidget() |
67 | 29 | self.scroll_layout = QVBoxLayout(self.scroll_content) |
68 | 30 | self.scroll_layout.setAlignment(Qt.AlignTop) |
69 | | - self.scroll_area.setWidget(self.scroll_content) |
70 | | - |
71 | | - self.main_layout.addWidget(self.scroll_area) |
72 | | - |
73 | | - def clear_layout(self, layout): |
74 | | - """Clear all widgets from the layout.""" |
75 | | - while layout.count(): |
76 | | - item = layout.takeAt(0) |
77 | | - widget = item.widget() |
78 | | - if widget is not None: |
79 | | - widget.deleteLater() |
80 | | - else: |
81 | | - sub_layout = item.layout() |
82 | | - if sub_layout is not None: |
83 | | - self.clear_layout(sub_layout) |
| 31 | + self.widget.setWidget(self.scroll_content) |
| 32 | + self.settings_layout = QVBoxLayout() |
| 33 | + self.settings_layout.setSpacing(0) |
| 34 | + self.settings_layout.setContentsMargins(0, 0, 0, 0) |
| 35 | + self.scroll_layout.addLayout(self.settings_layout) |
| 36 | + |
| 37 | + @property |
| 38 | + def selected(self): |
| 39 | + return [obj for obj in self.scene.objects if obj.is_selected] |
| 40 | + |
| 41 | + def add_row(self, attr_name: str, widget: QWidget = None) -> None: |
| 42 | + """Create a setting row with label and widget, then add it to the layout.""" |
| 43 | + row_layout = QHBoxLayout() |
| 44 | + row_layout.setSpacing(0) |
| 45 | + row_layout.setContentsMargins(0, 0, 0, 0) |
| 46 | + |
| 47 | + label = QLabel(attr_name) |
| 48 | + row_layout.addWidget(label) |
| 49 | + |
| 50 | + if widget: |
| 51 | + row_layout.addWidget(widget) |
| 52 | + self.sub_widgets[attr_name] = widget |
| 53 | + |
| 54 | + self.settings_layout.addLayout(row_layout) |
| 55 | + |
| 56 | + def populate(self) -> None: |
| 57 | + """Populate the layout with the settings of the selected object.""" |
| 58 | + obj = self.selected[0] |
| 59 | + |
| 60 | + if not obj: |
| 61 | + return |
| 62 | + |
| 63 | + self.add_row("name", TextEdit(text=str(obj.name))) |
| 64 | + |
| 65 | + if hasattr(obj, "pointcolor") and obj.pointcolor is not None: |
| 66 | + self.add_row("pointcolor", ColorDialog(obj=obj, attr="pointcolor")) |
| 67 | + |
| 68 | + if hasattr(obj, "linecolor") and obj.linecolor is not None: |
| 69 | + self.add_row("linecolor", ColorDialog(obj=obj, attr="linecolor")) |
| 70 | + |
| 71 | + if hasattr(obj, "facecolor") and obj.facecolor is not None: |
| 72 | + self.add_row("facecolor", ColorDialog(obj=obj, attr="facecolor")) |
| 73 | + |
| 74 | + if hasattr(obj, "linewidth") and obj.linewidth is not None: |
| 75 | + self.add_row("linewidth", DoubleEdit(title=None, value=obj.linewidth, min_val=0.0, max_val=10.0)) |
| 76 | + |
| 77 | + if hasattr(obj, "pointsize") and obj.pointsize is not None: |
| 78 | + self.add_row("pointsize", DoubleEdit(title=None, value=obj.pointsize, min_val=0.0, max_val=10.0)) |
| 79 | + |
| 80 | + if hasattr(obj, "opacity") and obj.opacity is not None: |
| 81 | + self.add_row("opacity", DoubleEdit(title=None, value=obj.opacity, min_val=0.0, max_val=1.0)) |
84 | 82 |
|
85 | 83 | def update(self): |
86 | 84 | """Update the layout with the latest object settings.""" |
87 | | - self.clear_layout(self.scroll_layout) |
88 | | - self.setting_layout.generate_layout() |
89 | | - |
90 | | - if len(self.setting_layout.widgets) != 0: |
91 | | - self.scroll_layout.addLayout(self.setting_layout.layout) |
92 | | - for _, widget in self.setting_layout.widgets.items(): |
93 | | - if isinstance(widget, DoubleEdit): |
94 | | - widget.spinbox.valueChanged.connect(self.obj_update) |
95 | | - elif isinstance(widget, TextEdit): |
96 | | - widget.text_edit.textChanged.connect(self.obj_update) |
| 85 | + self.reset() |
| 86 | + |
| 87 | + if len(self.selected) == 1: |
| 88 | + self.populate() |
| 89 | + self._add_event_listeners() |
| 90 | + elif len(self.selected) > 1: |
| 91 | + self.add_row("Multiple objects selected") |
97 | 92 | else: |
98 | | - self.scroll_layout.addWidget(LabelWidget(text="No object Selected", alignment="center")) |
| 93 | + self.add_row("No object selected") |
99 | 94 |
|
100 | | - def obj_update(self): |
101 | | - """Apply the settings from spin boxes to the selected objects.""" |
102 | | - for obj in self.viewer.scene.objects: |
103 | | - if obj.is_selected: |
104 | | - obj.name = self.setting_layout.widgets["Name_text_edit"].text_edit.toPlainText() |
105 | | - obj.linewidth = self.setting_layout.widgets["Line_Width_double_edit"].spinbox.value() |
106 | | - obj.pointsize = self.setting_layout.widgets["Point_Size_double_edit"].spinbox.value() |
107 | | - obj.opacity = self.setting_layout.widgets["Opacity_double_edit"].spinbox.value() |
108 | | - obj.update() |
| 95 | + def _add_event_listeners(self): |
| 96 | + """Add event listeners to the sub widgets.""" |
109 | 97 |
|
| 98 | + def _update_obj(): |
| 99 | + if len(self.selected) == 0: |
| 100 | + return |
110 | 101 |
|
111 | | -class ObjectSettingDialog(QDialog, Base): |
112 | | - """ |
113 | | - A dialog for displaying and updating object settings in Qt applications. |
114 | | - This dialog allows users to modify object properties such as line width, point size, and opacity, |
115 | | - and applies these changes dynamically. |
116 | | -
|
117 | | - Parameters |
118 | | - ---------- |
119 | | - items : list |
120 | | - A list of dictionaries containing the settings for the object. |
121 | | -
|
122 | | - Attributes |
123 | | - ---------- |
124 | | - layout : QVBoxLayout |
125 | | - The layout of the dialog. |
126 | | - items : list |
127 | | - A list of dictionaries containing the settings for the object. |
128 | | - update_button : QPushButton |
129 | | - Button to apply changes to the selected objects. |
130 | | -
|
131 | | - Methods |
132 | | - ------- |
133 | | - update() |
134 | | - Updates the properties of selected objects and closes the dialog. |
135 | | -
|
136 | | - Example |
137 | | - ------- |
138 | | - >>> dialog = ObjectInfoDialog() |
139 | | - >>> dialog.exec() |
140 | | - """ |
| 102 | + obj = self.selected[0] |
141 | 103 |
|
142 | | - def __init__(self, items: list[dict]) -> None: |
143 | | - super().__init__() |
144 | | - self.items = items |
145 | | - self.setWindowTitle("Object Settings") |
146 | | - self.layout = QVBoxLayout(self) |
147 | | - self.setting_layout = SettingLayout(viewer=self.viewer, items=self.items, type="obj_setting") |
148 | | - |
149 | | - if self.setting_layout is not None: |
150 | | - text = "Update Object" |
151 | | - self.layout.addLayout(self.setting_layout.layout) |
152 | | - else: |
153 | | - text = "No object selected." |
| 104 | + for attr_name, widget in self.sub_widgets.items(): |
| 105 | + if not hasattr(obj, attr_name): |
| 106 | + continue |
| 107 | + if isinstance(widget, TextEdit): |
| 108 | + value = widget.text_edit.toPlainText() |
| 109 | + elif isinstance(widget, DoubleEdit): |
| 110 | + value = widget.spinbox.value() |
| 111 | + else: |
| 112 | + continue |
154 | 113 |
|
155 | | - self.update_button = QPushButton(text, self) |
156 | | - self.update_button.clicked.connect(self.obj_update) |
157 | | - self.layout.addWidget(self.update_button) |
| 114 | + setattr(obj, attr_name, value) |
158 | 115 |
|
159 | | - def obj_update(self) -> None: |
160 | | - for obj in self.viewer.scene.objects: |
161 | | - if obj.is_selected: |
162 | | - obj.linewidth = self.setting_layout.widgets["Line_Width_double_edit"].spinbox.value() |
163 | | - obj.pointsize = self.setting_layout.widgets["Point_Size_double_edit"].spinbox.value() |
164 | | - obj.opacity = self.setting_layout.widgets["Opacity_double_edit"].spinbox.value() |
165 | | - obj.update() |
| 116 | + obj.update() |
166 | 117 |
|
167 | | - self.accept() |
| 118 | + for widget in self.sub_widgets.values(): |
| 119 | + if isinstance(widget, TextEdit): |
| 120 | + widget.text_edit.textChanged.connect(_update_obj) |
| 121 | + elif isinstance(widget, DoubleEdit): |
| 122 | + widget.spinbox.valueChanged.connect(_update_obj) |
0 commit comments