Skip to content

tab feature #194

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased

### Added

* Added `QTabWidget` to `SideBarRight`.
### Changed

### Removed
25 changes: 25 additions & 0 deletions scripts/tablayout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from compas.colors import Color
from compas.geometry import Box
from compas.geometry import Frame
from compas_viewer.viewer import Viewer
from compas_viewer.config import Config

config = Config()
for item in config.ui.sidebar.items:
if item['type'] == 'Sceneform':
item['area'] = 'tab'
viewer = Viewer(config)

N = 10
M = 10

for i in range(N):
for j in range(M):
viewer.scene.add(
Box(0.5, 0.5, 0.5, Frame([i, j, 0], [1, 0, 0], [0, 1, 0])),
linecolor=Color.white(),
facecolor=Color(i / N, j / M, 0.0),
name=f"Box_{i}_{j}",
)

viewer.show()
16 changes: 8 additions & 8 deletions src/compas_viewer/components/objectsetting.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from typing import TYPE_CHECKING

from PySide6.QtCore import Qt
from PySide6.QtCore import Signal
from PySide6.QtWidgets import QDialog
@@ -14,9 +12,6 @@
from compas_viewer.components.layout import SettingLayout
from compas_viewer.components.textedit import TextEdit

if TYPE_CHECKING:
from compas_viewer import Viewer


class ObjectSetting(QWidget):
"""
@@ -52,11 +47,9 @@ class ObjectSetting(QWidget):

update_requested = Signal()

def __init__(self, viewer: "Viewer", items: list[dict]):
def __init__(self, items: list[dict]):
super().__init__()
self.viewer = viewer
self.items = items
self.setting_layout = SettingLayout(viewer=self.viewer, items=self.items, type="obj_setting")
# Main layout
self.main_layout = QVBoxLayout(self)

@@ -70,6 +63,12 @@ def __init__(self, viewer: "Viewer", items: list[dict]):

self.main_layout.addWidget(self.scroll_area)

@property
def viewer(self):
from compas_viewer import Viewer

return Viewer()

def clear_layout(self, layout):
"""Clear all widgets from the layout."""
while layout.count():
@@ -85,6 +84,7 @@ def clear_layout(self, layout):
def update(self):
"""Update the layout with the latest object settings."""
self.clear_layout(self.scroll_layout)
self.setting_layout = SettingLayout(viewer=self.viewer, items=self.items, type="obj_setting")
self.setting_layout.generate_layout()

if len(self.setting_layout.widgets) != 0:
8 changes: 4 additions & 4 deletions src/compas_viewer/components/sceneform.py
Original file line number Diff line number Diff line change
@@ -36,16 +36,16 @@ class Sceneform(QTreeWidget):

def __init__(
self,
columns: list[dict],
items: list[dict],
column_editable: Optional[list[bool]] = None,
show_headers: bool = True,
callback: Optional[Callable] = None,
):
super().__init__()
self.columns = columns
self.columns = items
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))
self.column_editable = (column_editable or [False]) + [False] * (len(self.columns) - len(column_editable or [False]))
self.setColumnCount(len(self.columns))
self.setHeaderLabels(col["title"] for col in self.columns)
self.setHeaderHidden(not show_headers)
self.setSelectionMode(QTreeWidget.SingleSelection)
4 changes: 3 additions & 1 deletion src/compas_viewer/config.py
Original file line number Diff line number Diff line change
@@ -250,13 +250,15 @@ class SidebarConfig(ConfigBase):
items: list[dict] = field(
default_factory=lambda: [
{
"area": "splitter",
"type": "Sceneform",
"columns": [
"items": [
{"title": "Name", "type": "label", "text": lambda obj: obj.name},
{"title": "Show", "type": "checkbox", "checked": lambda obj: obj.show, "action": lambda obj, checked: setattr(obj, "show", checked)},
],
},
{
"area": "tab",
"type": "ObjectSetting",
"items": [
{"title": "Name", "items": [{"type": "text_edit", "action": lambda obj: obj.name}]},
77 changes: 46 additions & 31 deletions src/compas_viewer/ui/sidebar.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,42 @@
from typing import TYPE_CHECKING
from typing import Callable
from typing import Optional

from PySide6 import QtCore
from PySide6.QtWidgets import QSplitter
from PySide6.QtWidgets import QTabWidget
from PySide6.QtWidgets import QWidget

from compas_viewer.components import Sceneform
from compas_viewer.components.objectsetting import ObjectSetting

if TYPE_CHECKING:
from .ui import UI

type_registry = {
"Sceneform": Sceneform,
"ObjectSetting": ObjectSetting,
}


class SideBarRight:
def __init__(self, ui: "UI", show: bool, items: list[dict[str, Callable]]) -> None:
self.ui = ui
self.widget = QSplitter(QtCore.Qt.Orientation.Vertical)
self.widget.setChildrenCollapsible(True)
self._tab_widget: Optional[QTabWidget] = None
self.show = show
self.hide_widget = True
self.items = items

def add_items(self) -> None:
if not self.items:
return

for item in self.items:
itemtype = item.get("type", None)

if itemtype == "Sceneform":
columns = item.get("columns", None)
if columns is None:
raise ValueError("Please setup config for Sceneform")
self.sceneform = Sceneform(columns=columns)
self.widget.addWidget(self.sceneform)

elif itemtype == "ObjectSetting":
items = item.get("items", None)
if items is None:
raise ValueError("Please setup config for ObjectSetting")
self.object_setting = ObjectSetting(viewer=self.ui.viewer, items=items)
self.widget.addWidget(self.object_setting)

self.show_sceneform = True
self.show_objectsetting = True
# add widgets manualy to avoide multiple emits signals from QTabWidget
self.update_widgets = []

def update(self):
self.widget.update()
for widget in self.widget.children():
widget.update()
@property
def tab_widget(self):
if self._tab_widget is None:
self._tab_widget = QTabWidget(self.widget)
return self._tab_widget

@property
def show(self):
@@ -59,16 +48,42 @@ def show(self, value: bool):

@property
def show_sceneform(self):
return self.sceneform.isVisible()
return getattr(self, "Sceneform", QWidget()).isVisible()

@show_sceneform.setter
def show_sceneform(self, value: bool):
self.sceneform.setVisible(value)
getattr(self, "Sceneform", QWidget()).setVisible(value)

@property
def show_objectsetting(self):
return self.object_setting.isVisible()
return getattr(self, "ObjectSetting", QWidget()).isVisible()

@show_objectsetting.setter
def show_objectsetting(self, value: bool):
self.object_setting.setVisible(value)
getattr(self, "ObjectSetting", QWidget()).setVisible(value)

def add_items(self) -> None:
if not self.items:
return

for item in self.items:
area = item.get("area", None)
itemtype = item.get("type", None)
items = item.get("items", None)

if itemtype in type_registry:
if items is None:
raise ValueError(f"Please setup config for {itemtype} widget")
widget = type_registry[itemtype](items=items)
# set the attribute dynamically
setattr(self, itemtype, widget)
if area == "tab":
self.tab_widget.addTab(widget, itemtype)
elif area == "splitter":
self.widget.addWidget(widget)
self.update_widgets.append(widget)

def update(self):
self.widget.update()
for widget in self.update_widgets:
widget.update()