From 8942580c467976a8c97f65e063335d31211553bc Mon Sep 17 00:00:00 2001 From: PingHsunTsai <47770211+PingHsunTsai@users.noreply.github.com> Date: Wed, 26 Jun 2024 11:21:30 +0200 Subject: [PATCH] coclor dialog --- src/compas_viewer/components/color.py | 157 ++++++++++++++++++ src/compas_viewer/components/combobox.py | 104 ------------ src/compas_viewer/components/layout.py | 6 +- src/compas_viewer/components/objectsetting.py | 6 +- 4 files changed, 165 insertions(+), 108 deletions(-) create mode 100644 src/compas_viewer/components/color.py diff --git a/src/compas_viewer/components/color.py b/src/compas_viewer/components/color.py new file mode 100644 index 0000000000..b2f639ccb6 --- /dev/null +++ b/src/compas_viewer/components/color.py @@ -0,0 +1,157 @@ +from typing import TYPE_CHECKING + +from PySide6.QtGui import QColor +from PySide6.QtWidgets import QColorDialog +from PySide6.QtWidgets import QPushButton +from PySide6.QtWidgets import QVBoxLayout +from PySide6.QtWidgets import QWidget + +from compas.colors import Color +from compas.colors.colordict import ColorDict +from compas_viewer.base import Base +from compas_viewer.components.combobox import ComboBox + +if TYPE_CHECKING: + from compas_viewer.scene import ViewerSceneObject + + +def remap_rgb(value, to_range_one=True): + """ + Remap an RGB value between the range (0, 255) and (0, 1). + + Parameters + ---------- + value : tuple + The RGB value to remap. + to_range_one : bool, optional + If True, remap from (0, 255) to (0, 1). If False, remap from (0, 1) to (0, 255). + + Returns + ------- + tuple + The remapped RGB value. + """ + factor = 1 / 255 if to_range_one else 255 + return tuple(v * factor for v in value) + + +class ColorComboBox(QWidget, Base): + """ + A custom QWidget for selecting colors from a predefined list and applying the selected color to an object's attribute. + + Parameters + ---------- + obj : ViewerSceneObject, optional + The object to which the selected color will be applied. Defaults to None. + attr : str, optional + The attribute of the object to which the selected color will be applied. Defaults to None. + + Attributes + ---------- + obj : ViewerSceneObject + The object to which the selected color will be applied. + attr : str + The attribute of the object to which the selected color will be applied. + color_options : list of QColor + A list of predefined QColor objects representing available colors. + layout : QVBoxLayout + The layout of the widget. + color_selector : ComboBox + A combo box for selecting colors. + + Methods + ------- + change_color(color: QColor) -> None + Changes the color of the object's attribute to the selected color. + + Example + ------- + >>> color_combobox = ColorComboBox(obj=some_obj, attr="linecolor") + >>> color_combobox.show() + """ + + def __init__( + self, + obj: "ViewerSceneObject" = None, + attr: str = None, + ): + super().__init__() + self.obj = obj + self.attr = attr + + self.color_options = [ + QColor(255, 255, 255), # White + QColor(211, 211, 211), # LightGray + QColor(190, 190, 190), # Gray + QColor(0, 0, 0), # Black + QColor(255, 0, 0), # Red + QColor(0, 255, 0), # Green + QColor(0, 0, 255), # Blue + QColor(255, 255, 0), # Yellow + QColor(0, 255, 255), # Cyan + QColor(255, 0, 255), # Magenta + ] + + default_color = getattr(self.obj, self.attr) + + if isinstance(default_color, Color): + default_color = default_color.rgb + elif isinstance(default_color, ColorDict): + default_color = default_color.default + else: + raise ValueError("Invalid color type.") + default_color = QColor(*remap_rgb(default_color, to_range_one=False)) + + self.layout = QVBoxLayout(self) + self.color_selector = ComboBox(self.color_options, self.change_color, paint=True) + self.color_selector.setAssignedColor(default_color) + self.layout.addWidget(self.color_selector) + + def change_color(self, color): + rgb = remap_rgb(color.getRgb())[:-1] # rgba to rgb(0-1) + setattr(self.obj, self.attr, Color(*rgb)) + self.obj.update() + + +class ColorButton(QWidget): + def __init__( + self, + obj: "ViewerSceneObject" = None, + attr: str = None, + ): + super().__init__() + + self.obj = obj + self.attr = attr + + default_color = getattr(self.obj, self.attr) + if isinstance(default_color, Color): + default_color = default_color.rgb + elif isinstance(default_color, ColorDict): + default_color = default_color.default + else: + raise ValueError("Invalid color type.") + default_color = QColor(*remap_rgb(default_color, to_range_one=False)) + + self.color_button = QPushButton(self) + self.layout = QVBoxLayout(self) + self.layout.addWidget(self.color_button) + self.color_button.clicked.connect(self.open_color_dialog) + self.set_button_color(default_color) + + def open_color_dialog(self): + color = QColorDialog.getColor() + + if color.isValid(): + self.change_color(color) + self.set_button_color(color) + + def set_button_color(self, color: QColor): + self.color_button.setStyleSheet(f"background-color: {color.name()};") + self.color_button.setText(color.name()) + self.current_color = color + + def change_color(self, color): + rgb = remap_rgb(color.getRgb())[:-1] # rgba to rgb(0-1) + setattr(self.obj, self.attr, Color(*rgb)) + self.obj.update() diff --git a/src/compas_viewer/components/combobox.py b/src/compas_viewer/components/combobox.py index 18857211c3..79fdf0dc26 100644 --- a/src/compas_viewer/components/combobox.py +++ b/src/compas_viewer/components/combobox.py @@ -1,4 +1,3 @@ -from typing import TYPE_CHECKING from typing import Callable from typing import Optional @@ -11,33 +10,8 @@ from PySide6.QtWidgets import QVBoxLayout from PySide6.QtWidgets import QWidget -from compas.colors import Color -from compas.colors.colordict import ColorDict from compas_viewer.base import Base -if TYPE_CHECKING: - from compas_viewer.scene import ViewerSceneObject - - -def remap_rgb(value, to_range_one=True): - """ - Remap an RGB value between the range (0, 255) and (0, 1). - - Parameters - ---------- - value : tuple - The RGB value to remap. - to_range_one : bool, optional - If True, remap from (0, 255) to (0, 1). If False, remap from (0, 1) to (0, 255). - - Returns - ------- - tuple - The remapped RGB value. - """ - factor = 1 / 255 if to_range_one else 255 - return tuple(v * factor for v in value) - class ColorDelegate(QStyledItemDelegate): def paint(self, painter, option, index): @@ -171,84 +145,6 @@ def paintEvent(self, event) -> None: painter.end() -class ColorComboBox(QWidget, Base): - """ - A custom QWidget for selecting colors from a predefined list and applying the selected color to an object's attribute. - - Parameters - ---------- - obj : ViewerSceneObject, optional - The object to which the selected color will be applied. Defaults to None. - attr : str, optional - The attribute of the object to which the selected color will be applied. Defaults to None. - - Attributes - ---------- - obj : ViewerSceneObject - The object to which the selected color will be applied. - attr : str - The attribute of the object to which the selected color will be applied. - color_options : list of QColor - A list of predefined QColor objects representing available colors. - layout : QVBoxLayout - The layout of the widget. - color_selector : ComboBox - A combo box for selecting colors. - - Methods - ------- - change_color(color: QColor) -> None - Changes the color of the object's attribute to the selected color. - - Example - ------- - >>> color_combobox = ColorComboBox(obj=some_obj, attr="linecolor") - >>> color_combobox.show() - """ - - def __init__( - self, - obj: "ViewerSceneObject" = None, - attr: str = None, - ): - super().__init__() - self.obj = obj - self.attr = attr - - self.color_options = [ - QColor(255, 255, 255), # White - QColor(211, 211, 211), # LightGray - QColor(190, 190, 190), # Gray - QColor(0, 0, 0), # Black - QColor(255, 0, 0), # Red - QColor(0, 255, 0), # Green - QColor(0, 0, 255), # Blue - QColor(255, 255, 0), # Yellow - QColor(0, 255, 255), # Cyan - QColor(255, 0, 255), # Magenta - ] - - default_color = getattr(self.obj, self.attr) - - if isinstance(default_color, Color): - default_color = default_color.rgb - elif isinstance(default_color, ColorDict): - default_color = default_color.default - else: - raise ValueError("Invalid color type.") - default_color = QColor(*remap_rgb(default_color, to_range_one=False)) - - self.layout = QVBoxLayout(self) - self.color_selector = ComboBox(self.color_options, self.change_color, paint=True) - self.color_selector.setAssignedColor(default_color) - self.layout.addWidget(self.color_selector) - - def change_color(self, color): - rgb = remap_rgb(color.getRgb())[:-1] # rgba to rgb(0-1) - setattr(self.obj, self.attr, Color(*rgb)) - self.obj.update() - - class ViewModeAction(QWidget, Base): def __init__(self): super().__init__() diff --git a/src/compas_viewer/components/layout.py b/src/compas_viewer/components/layout.py index d7e81ed363..3cb8cf6f7b 100644 --- a/src/compas_viewer/components/layout.py +++ b/src/compas_viewer/components/layout.py @@ -3,7 +3,8 @@ from PySide6.QtWidgets import QLayout from PySide6.QtWidgets import QVBoxLayout -from compas_viewer.components.combobox import ColorComboBox +from compas_viewer.components.color import ColorButton +from compas_viewer.components.color import ColorComboBox from compas_viewer.components.double_edit import DoubleEdit from compas_viewer.components.label import LabelWidget from compas_viewer.components.textedit import TextEdit @@ -84,6 +85,9 @@ def base_layout(items: list) -> tuple[QVBoxLayout, dict]: widget = TextEdit(text=text) right_layout.addWidget(widget) widgets[f"{l_title}_{type}"] = widget + elif type == "color_dialog": + widget = ColorButton(obj=obj, attr=attr) + right_layout.addWidget(widget) sub_layout.addLayout(left_layout) sub_layout.addLayout(right_layout) diff --git a/src/compas_viewer/components/objectsetting.py b/src/compas_viewer/components/objectsetting.py index 663417e033..0f68830e4d 100644 --- a/src/compas_viewer/components/objectsetting.py +++ b/src/compas_viewer/components/objectsetting.py @@ -39,9 +39,9 @@ def object_setting_layout(viewer: "Viewer"): status = True new_items = [ {"title": "Name", "items": [{"type": "text_edit", "text": str(obj.name)}]}, - {"title": "Point_Color", "items": [{"type": "color_combobox", "obj": obj, "attr": "pointcolor"}]}, - {"title": "Line_Color", "items": [{"type": "color_combobox", "obj": obj, "attr": "linecolor"}]}, - {"title": "Face_Color", "items": [{"type": "color_combobox", "obj": obj, "attr": "facecolor"}]}, + {"title": "Point_Color", "items": [{"type": "color_dialog", "obj": obj, "attr": "pointcolor"}]}, + {"title": "Line_Color", "items": [{"type": "color_dialog", "obj": obj, "attr": "linecolor"}]}, + {"title": "Face_Color", "items": [{"type": "color_dialog", "obj": obj, "attr": "facecolor"}]}, {"title": "Line_Width", "items": [{"type": "double_edit", "value": obj.linewidth, "min_val": 0.0, "max_val": 10.0}]}, {"title": "Point_Size", "items": [{"type": "double_edit", "value": obj.pointsize, "min_val": 0.0, "max_val": 10.0}]}, {"title": "Opacity", "items": [{"type": "double_edit", "value": obj.opacity, "min_val": 0.0, "max_val": 1.0}]},