diff --git a/CHANGELOG.md b/CHANGELOG.md index 21f8e79768..f4be337111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added +* Added dynamic import to enable access from expended `TreeForm`. ### Changed diff --git a/src/compas_viewer/components/sceneform.py b/src/compas_viewer/components/sceneform.py index 76f39c0356..dfa7d087de 100644 --- a/src/compas_viewer/components/sceneform.py +++ b/src/compas_viewer/components/sceneform.py @@ -5,6 +5,8 @@ from PySide6.QtWidgets import QTreeWidget from PySide6.QtWidgets import QTreeWidgetItem +from compas_viewer.components.treeform import Treeform + class Sceneform(QTreeWidget): """ @@ -37,12 +39,14 @@ class Sceneform(QTreeWidget): def __init__( self, columns: list[dict], + show_selected_tree: Optional[bool] = True, column_editable: Optional[list[bool]] = None, - show_headers: bool = True, + show_headers: Optional[bool] = True, callback: Optional[Callable] = None, ): super().__init__() self.columns = columns + self.show_selected_tree = show_selected_tree self.checkbox_columns: dict[int, str] = {} self.column_editable = (column_editable or [False]) + [False] * (len(columns) - len(column_editable or [False])) self.setColumnCount(len(columns)) @@ -75,6 +79,8 @@ def update(self): if node.is_selected: self.expand(node.parent) self.scrollToItem(widget) + if self.show_selected_tree: + self.update_selected(node) else: self._sceneobjects = list(self.scene.objects) @@ -109,6 +115,8 @@ def update(self): widget.setSelected(node.is_selected) if node.is_selected: self.expand(node.parent) + if self.show_selected_tree: + self.update_selected(node) widget.setFlags(widget.flags() | Qt.ItemIsUserCheckable | Qt.ItemIsSelectable | Qt.ItemIsEnabled) @@ -119,6 +127,13 @@ def update(self): self.adjust_column_widths() + def update_selected(self, node): + if not hasattr(self, "treeform"): + self.treeform = Treeform() + self.viewer.ui.sidebar.widget.addWidget(self.treeform) + + self.treeform.update_from_dict({"objtype": node.__class__, "item": node.item, "settings": node.settings}) + def expand(self, node): if node.attributes.get("widget"): node.attributes["widget"].setExpanded(True) diff --git a/src/compas_viewer/components/treeform.py b/src/compas_viewer/components/treeform.py index 8d2cc68631..e1381001d7 100644 --- a/src/compas_viewer/components/treeform.py +++ b/src/compas_viewer/components/treeform.py @@ -5,9 +5,15 @@ from PySide6.QtWidgets import QTreeWidget from PySide6.QtWidgets import QTreeWidgetItem +import compas.datastructures as ds +import compas.geometry as geom from compas.datastructures import Tree from compas.datastructures import TreeNode +# Collect all classes from compas.geometry dynamically +geometry_types = tuple(getattr(geom, attr) for attr in dir(geom) if isinstance(getattr(geom, attr), type)) +datastructures_types = tuple(getattr(ds, attr) for attr in dir(ds) if isinstance(getattr(ds, attr), type)) + class Treeform(QTreeWidget): """ @@ -16,10 +22,10 @@ class Treeform(QTreeWidget): Parameters ---------- - tree : :class:`compas.datastructures.Tree` + tree : :class:`compas.datastructures.Tree`, optional The tree to be displayed. An typical example is the scene object tree: :attr:`compas_viewer.viewer.Viewer._tree`. - columns : dict[str, callable] + columns : dict[str, callable], optional A dictionary of column names and their corresponding attributes. Example: ``{"Name": (lambda o: o.name), "Object": (lambda o: o)}`` show_headers : bool, optional @@ -67,10 +73,10 @@ class Treeform(QTreeWidget): def __init__( self, - tree: Tree = None, - columns: dict[str, Callable] = None, - show_headers: bool = True, - stretch: int = 2, + tree: Optional[Tree] = None, + columns: Optional[dict[str, Callable]] = None, + show_headers: Optional[bool] = True, + stretch: Optional[int] = 2, backgrounds: Optional[dict[str, Callable]] = None, callback: Optional[Callable] = None, ): @@ -115,6 +121,7 @@ def tree_from_dict(self, data): def add_children(key, data, parent): if isinstance(data, dict): + # TODO: bug - if key == 0, TreeNode name show Treenode instead of 0 node = TreeNode(name=key) for child_key, child_data in data.items(): add_children(child_key, child_data, node) @@ -122,6 +129,30 @@ def add_children(key, data, parent): node = TreeNode(name=key) for child_index, child_data in enumerate(data): add_children(child_index, child_data, node) + elif isinstance(data, geometry_types): + node = TreeNode(name=key) + for i, attr_name in enumerate(data.__data__): + if isinstance(attr_name, str): + if hasattr(data, attr_name): + attr_value = getattr(data, attr_name) + add_children(attr_name, attr_value, node) + elif isinstance(attr_name, float) and len(data.__data__) == 3: + attr_value = attr_name + if i == 0: + attr_name = "x" + elif i == 1: + attr_name = "y" + elif i == 2: + attr_name = "z" + add_children(attr_name, attr_value, node) + elif isinstance(data, datastructures_types): + node = TreeNode(name=key) + for attr_name in data.__data__: + if isinstance(attr_name, str): + if hasattr(data, attr_name): + attr_value = getattr(data, attr_name) + print(f"Attribute: {attr_name}, Value: {attr_value}") + add_children(attr_name, attr_value, node) else: node = TreeNode(name=key, value=data) diff --git a/src/compas_viewer/config.py b/src/compas_viewer/config.py index bf3571618f..628dd3819b 100644 --- a/src/compas_viewer/config.py +++ b/src/compas_viewer/config.py @@ -246,7 +246,7 @@ class StatusbarConfig(ConfigBase): class SidebarConfig(ConfigBase): show: bool = True show_widgets: bool = True - sceneform: bool = True + show_selected_tree: bool = True items: list[dict] = field( default_factory=lambda: [ { diff --git a/src/compas_viewer/ui/sidebar.py b/src/compas_viewer/ui/sidebar.py index 0fe5b52f88..5ccded7f71 100644 --- a/src/compas_viewer/ui/sidebar.py +++ b/src/compas_viewer/ui/sidebar.py @@ -31,7 +31,7 @@ def add_items(self) -> None: columns = item.get("columns", None) if columns is None: raise ValueError("Please setup config for Sceneform") - self.sceneform = Sceneform(columns=columns) + self.sceneform = Sceneform(columns=columns, show_selected_tree=self.ui.viewer.config.ui.sidebar.show_selected_tree) self.widget.addWidget(self.sceneform) elif itemtype == "ObjectSetting":