Skip to content

Commit b942be5

Browse files
authored
Merge pull request #181 from compas-dev/fix/obj_setting
Fix/obj setting
2 parents d5f974b + 19925df commit b942be5

File tree

14 files changed

+586
-315
lines changed

14 files changed

+586
-315
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
* Added `TextEdit` to handle `name` change.
13+
* Added `DefaultLayout` to handle gerneral `layout` setting to minimal.
14+
* Added `ColorDialog` to manage color dialog.
15+
* Added `SettingLayout` to manage complex layout with config input.
16+
* Added `robot.py` example.
17+
1218
### Changed
1319

1420
### Removed
@@ -22,12 +28,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2228
* Added `list[float]` to accepted types for `Camera.position` and `Camera.target`.
2329
* Added `unit` to `Viewer` and `Config`.
2430
* Added `bounding_box` and `_update_bounding_box` to `BufferObject`.
25-
* Added `robot.py` example.
2631

2732
### Changed
2833

2934
* Fixed `opacity` bug with `BufferObject`.
3035
* Updated `SceneForm` to avoid completely reload when scene objects not changed.
36+
* Updated callback to `SceneTree`.
37+
* Updated `ObjectSetting` and `CameraSetting` to support setting from config.
38+
* Updated `Slider` to be able change value with `TextEdit`
3139

3240
### Removed
3341

src/compas_viewer/commands.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ def change_view(viewer: "Viewer", mode: Literal["Perspective", "Top", "Front", "
114114

115115

116116
def camera_settings(viewer: "Viewer"):
117-
CameraSettingsDialog().exec()
117+
items = viewer.config.camera.dialog_settings
118+
CameraSettingsDialog(items=items).exec()
118119

119120

120121
camera_settings_cmd = Command(title="Camera Settings", callback=camera_settings)

src/compas_viewer/components/camerasetting.py

Lines changed: 17 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from PySide6.QtWidgets import QVBoxLayout
44

55
from compas_viewer.base import Base
6-
from compas_viewer.components.layout import base_layout
6+
from compas_viewer.components.layout import SettingLayout
77

88

99
class CameraSettingsDialog(QDialog, Base):
@@ -12,6 +12,11 @@ class CameraSettingsDialog(QDialog, Base):
1212
This dialog allows users to modify the camera's target and position and
1313
applies these changes dynamically.
1414
15+
Parameters
16+
----------
17+
items : list
18+
A list of dictionaries containing the settings for the camera.
19+
1520
Attributes
1621
----------
1722
layout : QVBoxLayout
@@ -30,52 +35,33 @@ class CameraSettingsDialog(QDialog, Base):
3035
3136
Example
3237
-------
33-
>>> dialog = CameraSettingsDialog()
38+
>>> dialog = CameraSettingsDialog(items=items)
3439
>>> dialog.exec()
3540
"""
3641

37-
def __init__(self) -> None:
42+
def __init__(self, items: list[dict]) -> None:
3843
super().__init__()
3944
self.setWindowTitle("Camera Settings")
4045

4146
self.layout = QVBoxLayout(self)
42-
self.camera = self.viewer.renderer.camera
43-
items = [
44-
{
45-
"title": "Camera_Target",
46-
"items": [
47-
{"type": "double_edit", "title": "X", "value": self.camera.target.x, "min_val": None, "max_val": None},
48-
{"type": "double_edit", "title": "Y", "value": self.camera.target.y, "min_val": None, "max_val": None},
49-
{"type": "double_edit", "title": "Z", "value": self.camera.target.z, "min_val": None, "max_val": None},
50-
],
51-
},
52-
{
53-
"title": "Camera_Position",
54-
"items": [
55-
{"type": "double_edit", "title": "X", "value": self.camera.position.x, "min_val": None, "max_val": None},
56-
{"type": "double_edit", "title": "Y", "value": self.camera.position.y, "min_val": None, "max_val": None},
57-
{"type": "double_edit", "title": "Z", "value": self.camera.position.z, "min_val": None, "max_val": None},
58-
],
59-
},
60-
]
61-
62-
camera_setting_layout, self.spin_boxes = base_layout(items)
47+
self.setting_layout = SettingLayout(viewer=self.viewer, items=items, type="camera_setting")
48+
self.setting_layout.generate_layout()
6349

64-
self.layout.addLayout(camera_setting_layout)
50+
self.layout.addLayout(self.setting_layout.layout)
6551

6652
self.update_button = QPushButton("Update Camera", self)
6753
self.update_button.clicked.connect(self.update)
6854
self.layout.addWidget(self.update_button)
6955

7056
def update(self) -> None:
7157
self.viewer.renderer.camera.target.set(
72-
self.spin_boxes["Camera_Target_X"].spinbox.value(),
73-
self.spin_boxes["Camera_Target_Y"].spinbox.value(),
74-
self.spin_boxes["Camera_Target_Z"].spinbox.value(),
58+
self.setting_layout.widgets["Camera_Target_X_double_edit"].spinbox.value(),
59+
self.setting_layout.widgets["Camera_Target_Y_double_edit"].spinbox.value(),
60+
self.setting_layout.widgets["Camera_Target_Z_double_edit"].spinbox.value(),
7561
)
7662
self.viewer.renderer.camera.position.set(
77-
self.spin_boxes["Camera_Position_X"].spinbox.value(),
78-
self.spin_boxes["Camera_Position_Y"].spinbox.value(),
79-
self.spin_boxes["Camera_Position_Z"].spinbox.value(),
63+
self.setting_layout.widgets["Camera_Position_X_double_edit"].spinbox.value(),
64+
self.setting_layout.widgets["Camera_Position_Y_double_edit"].spinbox.value(),
65+
self.setting_layout.widgets["Camera_Position_Z_double_edit"].spinbox.value(),
8066
)
8167
self.accept()
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
from typing import TYPE_CHECKING
2+
3+
from PySide6.QtGui import QColor
4+
from PySide6.QtWidgets import QColorDialog
5+
from PySide6.QtWidgets import QPushButton
6+
from PySide6.QtWidgets import QVBoxLayout
7+
from PySide6.QtWidgets import QWidget
8+
9+
from compas.colors import Color
10+
from compas.colors.colordict import ColorDict
11+
from compas_viewer.base import Base
12+
from compas_viewer.components.combobox import ComboBox
13+
14+
if TYPE_CHECKING:
15+
from compas_viewer.scene import ViewerSceneObject
16+
17+
18+
def remap_rgb(value, to_range_one=True):
19+
"""
20+
Remap an RGB value between the range (0, 255) and (0, 1).
21+
22+
Parameters
23+
----------
24+
value : tuple
25+
The RGB value to remap.
26+
to_range_one : bool, optional
27+
If True, remap from (0, 255) to (0, 1). If False, remap from (0, 1) to (0, 255).
28+
29+
Returns
30+
-------
31+
tuple
32+
The remapped RGB value.
33+
"""
34+
factor = 1 / 255 if to_range_one else 255
35+
return tuple(v * factor for v in value)
36+
37+
38+
class ColorComboBox(QWidget, Base):
39+
"""
40+
A custom QWidget for selecting colors from a predefined list and applying the selected color to an object's attribute.
41+
42+
Parameters
43+
----------
44+
obj : ViewerSceneObject, optional
45+
The object to which the selected color will be applied. Defaults to None.
46+
attr : str, optional
47+
The attribute of the object to which the selected color will be applied. Defaults to None.
48+
49+
Attributes
50+
----------
51+
obj : ViewerSceneObject
52+
The object to which the selected color will be applied.
53+
attr : str
54+
The attribute of the object to which the selected color will be applied.
55+
color_options : list of QColor
56+
A list of predefined QColor objects representing available colors.
57+
layout : QVBoxLayout
58+
The layout of the widget.
59+
color_selector : ComboBox
60+
A combo box for selecting colors.
61+
62+
Methods
63+
-------
64+
change_color(color: QColor) -> None
65+
Changes the color of the object's attribute to the selected color.
66+
67+
Example
68+
-------
69+
>>> color_combobox = ColorComboBox(obj=some_obj, attr="linecolor")
70+
>>> color_combobox.show()
71+
"""
72+
73+
def __init__(
74+
self,
75+
obj: "ViewerSceneObject" = None,
76+
attr: str = None,
77+
):
78+
super().__init__()
79+
self.obj = obj
80+
self.attr = attr
81+
82+
self.color_options = [
83+
QColor(255, 255, 255), # White
84+
QColor(211, 211, 211), # LightGray
85+
QColor(190, 190, 190), # Gray
86+
QColor(0, 0, 0), # Black
87+
QColor(255, 0, 0), # Red
88+
QColor(0, 255, 0), # Green
89+
QColor(0, 0, 255), # Blue
90+
QColor(255, 255, 0), # Yellow
91+
QColor(0, 255, 255), # Cyan
92+
QColor(255, 0, 255), # Magenta
93+
]
94+
95+
default_color = getattr(self.obj, self.attr)
96+
97+
if isinstance(default_color, Color):
98+
default_color = default_color.rgb
99+
elif isinstance(default_color, ColorDict):
100+
default_color = default_color.default
101+
else:
102+
raise ValueError("Invalid color type.")
103+
default_color = QColor(*remap_rgb(default_color, to_range_one=False))
104+
105+
self.layout = QVBoxLayout(self)
106+
self.color_selector = ComboBox(self.color_options, self.change_color, paint=True)
107+
self.color_selector.setAssignedColor(default_color)
108+
self.layout.addWidget(self.color_selector)
109+
110+
def change_color(self, color):
111+
rgb = remap_rgb(color.getRgb())[:-1] # rgba to rgb(0-1)
112+
setattr(self.obj, self.attr, Color(*rgb))
113+
self.obj.update()
114+
115+
116+
class ColorDialog(QWidget):
117+
"""
118+
A custom QWidget that provides a QPushButton to open a QColorDialog for selecting colors.
119+
120+
This class is used to manage and display a color attribute of a ViewerSceneObject.
121+
The button shows the current color and allows the user to change the color via a color dialog.
122+
123+
Parameters
124+
----------
125+
obj : ViewerSceneObject, optional
126+
The object whose color attribute is being managed.
127+
attr : str, optional
128+
The attribute name of the color in the object.
129+
130+
Attributes
131+
----------
132+
obj : ViewerSceneObject
133+
The object whose color attribute is being managed.
134+
attr : str
135+
The attribute name of the color in the object.
136+
color_button : QPushButton
137+
The button that displays the current color and opens the color dialog when clicked.
138+
layout : QVBoxLayout
139+
The layout of the widget, which contains the color button.
140+
current_color : QColor
141+
The currently selected color.
142+
143+
Methods
144+
-------
145+
open_color_dialog()
146+
Opens a QColorDialog for the user to select a new color.
147+
set_button_color(color: QColor)
148+
Sets the button's background and text to the provided color.
149+
change_color(color: QColor)
150+
Changes the color attribute of the object to the provided color and updates the object.
151+
152+
Example
153+
-------
154+
>>> obj = ViewerSceneObject() # Assume this is a valid object with a color attribute
155+
>>> color_button = ColorButton(obj=obj, attr="linecolor")
156+
>>> layout = QVBoxLayout()
157+
>>> layout.addWidget(color_button)
158+
>>> window = QWidget()
159+
>>> window.setLayout(layout)
160+
>>> window.show()
161+
"""
162+
163+
def __init__(
164+
self,
165+
obj: "ViewerSceneObject" = None,
166+
attr: str = None,
167+
):
168+
super().__init__()
169+
170+
self.obj = obj
171+
self.attr = attr
172+
173+
default_color = getattr(self.obj, self.attr)
174+
if isinstance(default_color, Color):
175+
default_color = default_color.rgb
176+
elif isinstance(default_color, ColorDict):
177+
default_color = default_color.default
178+
else:
179+
raise ValueError("Invalid color type.")
180+
default_color = QColor(*remap_rgb(default_color, to_range_one=False))
181+
182+
self.color_button = QPushButton(self)
183+
self.layout = QVBoxLayout(self)
184+
self.layout.addWidget(self.color_button)
185+
self.color_button.clicked.connect(self.open_color_dialog)
186+
self.set_button_color(default_color)
187+
188+
def open_color_dialog(self):
189+
color = QColorDialog.getColor()
190+
191+
if color.isValid():
192+
self.change_color(color)
193+
self.set_button_color(color)
194+
195+
def set_button_color(self, color: QColor):
196+
self.color_button.setStyleSheet(f"background-color: {color.name()};")
197+
self.color_button.setText(color.name())
198+
self.current_color = color
199+
200+
def change_color(self, color):
201+
rgb = remap_rgb(color.getRgb())[:-1] # rgba to rgb(0-1)
202+
setattr(self.obj, self.attr, Color(*rgb))
203+
self.obj.update()

0 commit comments

Comments
 (0)