Skip to content

Commit 2e2fbec

Browse files
committed
AnySet : Add UI to edit items
1 parent 1d89d80 commit 2e2fbec

5 files changed

Lines changed: 634 additions & 0 deletions

File tree

meshroom/ui/components/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ def registerTypes():
99
from meshroom.ui.components.geom2D import Geom2D
1010
from meshroom.ui.components.scriptEditor import PySyntaxHighlighter
1111
from meshroom.ui.components.logLinesModel import LogLinesModel, LogLevelEnum
12+
from meshroom.ui.components.anySetEditor import NodeAttributeEditor
1213
from meshroom.core.node import ChunkIndexEnum
1314

1415
qmlRegisterType(EdgeMouseArea, "GraphEditor", 1, 0, "EdgeMouseArea")
@@ -20,6 +21,7 @@ def registerTypes():
2021
qmlRegisterType(CsvData, "DataObjects", 1, 0, "CsvData")
2122
qmlRegisterType(LogLinesModel, "DataObjects", 1, 0, "LogLinesModel")
2223
qmlRegisterType(PySyntaxHighlighter, "ScriptEditor", 1, 0, "PySyntaxHighlighter")
24+
qmlRegisterType(NodeAttributeEditor, "GraphEditor", 1, 0, "NodeAttributeEditor")
2325

2426
qmlRegisterSingletonType(Geom2D, "Meshroom.Helpers", 1, 0, "Geom2D")
2527
qmlRegisterSingletonType(LogLevelEnum, "DataObjects", 1, 0, "LogLevelEnum")
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
AnySet attributes editor: Provide list model and helper to edit a AnySet attribute items
5+
"""
6+
7+
from PySide6.QtCore import (
8+
QObject,
9+
Slot,
10+
Signal,
11+
Property,
12+
QAbstractListModel,
13+
Qt,
14+
QModelIndex
15+
)
16+
17+
18+
EXPOSED_ATTR_TYPES = ["StringParam", "File", "IntParam", "FloatParam", "BoolParam"]
19+
20+
21+
class AttributesSetModel(QAbstractListModel):
22+
TypeRole = Qt.UserRole + 1 # Attribute type
23+
NameRole = Qt.UserRole + 2 # Attribute name
24+
LabelRole = Qt.UserRole + 3 # Attribute label
25+
DescriptionRole = Qt.UserRole + 4 # Attribute description
26+
ValueRole = Qt.UserRole + 5 # Attribute value
27+
ObjectRole = Qt.UserRole + 6 # Attribute value
28+
29+
def __init__(self, parent=None):
30+
super().__init__(parent)
31+
self._items = []
32+
33+
def roleNames(self):
34+
return {
35+
self.TypeRole: b"type",
36+
self.NameRole: b"name",
37+
self.LabelRole: b"label",
38+
self.DescriptionRole: b"description",
39+
self.ValueRole: b"value",
40+
self.ObjectRole: b"attributeObject",
41+
}
42+
43+
def rowCount(self, parent=QModelIndex()):
44+
return len(self._items)
45+
46+
def data(self, index, role):
47+
if not index.isValid() or not (0 <= index.row() < len(self._items)):
48+
return None
49+
item = self._items[index.row()]
50+
if role == self.TypeRole:
51+
return item.get("type", "String")
52+
if role == self.NameRole:
53+
return item.get("name", "")
54+
if role == self.LabelRole:
55+
return item.get("label", "")
56+
if role == self.DescriptionRole:
57+
return item.get("description", "")
58+
if role == self.ValueRole:
59+
return item.get("value", "")
60+
if role == self.ObjectRole:
61+
return item.get("attributeObject", "")
62+
return None
63+
64+
def reset(self, items):
65+
self.beginResetModel()
66+
self._items = list(items)
67+
self.endResetModel()
68+
69+
def appendItem(self, item):
70+
self.beginInsertRows(QModelIndex(), len(self._items), len(self._items))
71+
self._items.append(item)
72+
self.endInsertRows()
73+
74+
def removeByName(self, name):
75+
for i, item in enumerate(self._items):
76+
if item.get("name") == name:
77+
self.beginRemoveRows(QModelIndex(), i, i)
78+
del self._items[i]
79+
self.endRemoveRows()
80+
return True
81+
return False
82+
83+
84+
class NodeAttributeEditor(QObject):
85+
def __init__(self, parent=None):
86+
super().__init__(parent)
87+
self._node = None
88+
self._attribute = None
89+
self._model = AttributesSetModel(self)
90+
91+
def refresh(self):
92+
"""Reload the model from the node's current inputs."""
93+
if self._node is None:
94+
self._model.reset([])
95+
else:
96+
self._model.reset(self.listInputs())
97+
self.inputsChanged.emit()
98+
99+
def getNode(self):
100+
return self._node
101+
102+
def setNode(self, node):
103+
if self._node is node:
104+
return
105+
self._node = node
106+
self.nodeChanged.emit()
107+
self.refresh()
108+
109+
def getAttribute(self):
110+
return self._attribute
111+
112+
def setAttribute(self, attribute):
113+
if self._attribute is attribute:
114+
return
115+
self._attribute = attribute
116+
self.attributeChanged.emit()
117+
self.refresh()
118+
119+
@Slot(str, str, str, str, str)
120+
def addInput(self, attrType, name, label, description, value):
121+
"""Add a new input attribute on the node using params dict."""
122+
if not name:
123+
self.errorOccurred.emit("Input name cannot be empty")
124+
return
125+
params = {
126+
"type": attrType,
127+
"name": name,
128+
"label": label,
129+
"description": description,
130+
"value": value,
131+
}
132+
try:
133+
insertIndex = len(list(self._attribute._value))
134+
self._attribute.insertAttribute(params, index=insertIndex)
135+
self._model.appendItem(params)
136+
except Exception as e:
137+
self.errorOccurred.emit(str(e))
138+
139+
@Slot(str)
140+
def removeInput(self, inputName):
141+
"""Remove the input attribute named inputName from the node."""
142+
try:
143+
for attr in list(self._attribute._value):
144+
if attr.name == inputName:
145+
self._attribute.removeAttribute(attr)
146+
self._model.removeByName(inputName)
147+
except Exception as e:
148+
self.errorOccurred.emit(str(e))
149+
150+
def listInputs(self):
151+
"""List inputs (build the list model displayed in the UI)."""
152+
inputs = list(self._attribute.value)
153+
inputsParams = []
154+
for i in range(len(inputs)):
155+
input_ = inputs[i]
156+
inputParams = {
157+
"type": input_._desc.__class__.__name__,
158+
"name": input_.name,
159+
"label": input_.label,
160+
"description": input_._desc.description,
161+
"value": input_.value,
162+
"attributeObject": input_,
163+
}
164+
inputsParams.append(inputParams)
165+
return inputsParams
166+
167+
attrTypes = Property(list, lambda _: EXPOSED_ATTR_TYPES, constant=True)
168+
inputsChanged = Signal()
169+
errorOccurred = Signal(str)
170+
nodeChanged = Signal()
171+
node = Property(QObject, getNode, setNode, notify=nodeChanged)
172+
attributeChanged = Signal()
173+
attribute = Property(QObject, getAttribute, setAttribute, notify=attributeChanged)
174+
inputsModel = Property(QObject, lambda self: self._model, constant=True)

meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,34 @@ RowLayout {
993993
}
994994
}
995995

996+
Component {
997+
id: groupAttrsEditorComponent
998+
Dialog {
999+
id: dialog
1000+
property alias node: editor.node
1001+
property alias attribute: editor.attribute
1002+
1003+
parent: Overlay.overlay
1004+
modal: true
1005+
focus: true
1006+
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
1007+
anchors.centerIn: parent
1008+
title: "Manage Group Attributes"
1009+
standardButtons: Dialog.Close
1010+
1011+
NodeAttributeEditor {
1012+
id: editor
1013+
}
1014+
1015+
CustomAttributesEditor {
1016+
nodeAttributeEditor: editor
1017+
palette: Colors.sysPalette
1018+
}
1019+
1020+
onClosed: destroy()
1021+
}
1022+
}
1023+
9961024
Component {
9971025
id: groupAttributeComponent
9981026
ColumnLayout {
@@ -1051,6 +1079,22 @@ RowLayout {
10511079
onDoubleClicked: function(mouse) { root.doubleClicked(mouse, root.attribute) }
10521080
}
10531081
}
1082+
1083+
ToolButton {
1084+
enabled: attribute.type == "AnySet"
1085+
visible: enabled
1086+
text: MaterialIcons.edit
1087+
font.family: MaterialIcons.fontFamily
1088+
font.pointSize: 10
1089+
padding: 2
1090+
onClicked: {
1091+
var groupAttrEditor = groupAttrsEditorComponent.createObject(root, {
1092+
'node': _currentScene.selectedNode,
1093+
'attribute': attribute,
1094+
})
1095+
groupAttrEditor.open()
1096+
}
1097+
}
10541098
}
10551099

10561100
Component.onCompleted: {

0 commit comments

Comments
 (0)