Skip to content
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

tab feature #194

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
Expand Up @@ -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
Expand Down
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
Expand All @@ -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):
"""
Expand Down Expand Up @@ -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)

Expand All @@ -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():
Expand All @@ -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:
Expand Down
8 changes: 4 additions & 4 deletions src/compas_viewer/components/sceneform.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion src/compas_viewer/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}]},
Expand Down
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):
Expand All @@ -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()
Loading