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

Support display and connection of attributes that are part of GroupAttributes #2544

Open
wants to merge 7 commits into
base: fix/minorUiFixes
Choose a base branch
from
48 changes: 47 additions & 1 deletion meshroom/core/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ def __init__(self, node, attributeDesc, isOutput, root=None, parent=None):
self._description = attributeDesc.description
self._invalidate = False if self._isOutput else attributeDesc.invalidate

self._exposed = attributeDesc.exposed
self._depth = 0
if root is not None:
current = self
while current.root is not None:
self._depth += 1
if current.root.exposed != self._exposed:
self._exposed = current.root.exposed
current = current.root

# invalidation value for output attributes
self._invalidationValue = ""

Expand All @@ -77,6 +87,12 @@ def node(self):
def root(self):
return self._root() if self._root else None

def getDepth(self):
return self._depth

def getExposed(self):
return self._exposed

def getName(self):
""" Attribute name """
return self._name
Expand Down Expand Up @@ -346,7 +362,7 @@ def _applyExpr(self):
elif self.isInput and Attribute.isLinkExpression(v):
# value is a link to another attribute
link = v[1:-1]
linkNode, linkAttr = link.split('.')
linkNode, linkAttr = link.split('.', 1)
try:
g.addEdge(g.node(linkNode).attribute(linkAttr), self)
except KeyError as err:
Expand Down Expand Up @@ -413,6 +429,33 @@ def updateInternals(self):
# Emit if the enable status has changed
self.setEnabled(self.getEnabled())

def getFlattenedChildren(self):
""" Return a list of all the attributes that refer to this instance as their parent through
the 'root' property. If no such attribute exist, return an empty list. The depth difference is not
taken into account in the list, which is thus always flat. """
attributes = ListModel(parent=self)
if isinstance(self._value, str):
# String/File attributes are iterable but cannot have children: immediately rule that case out
return attributes

try:
# If self._value is not iterable, then it is not an attribute that can have children
iter(self._value)
except TypeError:
return attributes

for attribute in self._value:
if not isinstance(attribute, Attribute):
# Handle ChoiceParam values, which contained in a list hence iterable, but are string
continue
attributes.add(attribute)
if isinstance(attribute, ListAttribute) or isinstance(attribute, GroupAttribute):
# Handle nested ListAttributes and GroupAttributes
flattened = attribute.getFlattenedChildren()
for i in flattened:
attributes.add(i)
return attributes

name = Property(str, getName, constant=True)
fullName = Property(str, getFullName, constant=True)
fullNameToNode = Property(str, getFullNameToNode, constant=True)
Expand All @@ -425,6 +468,7 @@ def updateInternals(self):
type = Property(str, getType, constant=True)
baseType = Property(str, getType, constant=True)
isReadOnly = Property(bool, _isReadOnly, constant=True)
exposed = Property(bool, getExposed, constant=True)

# Description of the attribute
descriptionChanged = Signal()
Expand Down Expand Up @@ -454,6 +498,8 @@ def updateInternals(self):
validValueChanged = Signal()
validValue = Property(bool, getValidValue, setValidValue, notify=validValueChanged)
root = Property(BaseObject, root.fget, constant=True)
depth = Property(int, getDepth, constant=True)
flattenedChildren = Property(BaseObject, getFlattenedChildren, constant=True)


def raiseIfLink(func):
Expand Down
11 changes: 10 additions & 1 deletion meshroom/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -1678,9 +1678,18 @@ def attributeDescFromName(refAttributes, name, value, strict=True):
attrDesc = next((d for d in refAttributes if d.name == name), None)
if attrDesc is None:
return None

# We have found a description, and we still need to
# check if the value matches the attribute description.
#

# If it is a GroupAttribute, all attributes within the group should be matched individually
# so that links that can be correctly evaluated
if isinstance(attrDesc, desc.GroupAttribute):
for k, v in value.items():
if CompatibilityNode.attributeDescFromName(attrDesc.groupDesc, k, v, strict=True) is None:
return None
return attrDesc

# If it is a serialized link expression (no proper value to set/evaluate)
if Attribute.isLinkExpression(value):
return attrDesc
Expand Down
3 changes: 2 additions & 1 deletion meshroom/ui/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,8 @@ def duplicateNodesFrom(self, nodes):
def canExpandForLoop(self, currentEdge):
""" Check if the list attribute can be expanded by looking at all the edges connected to it. """
listAttribute = currentEdge.src.root
if not listAttribute:
# Check that the parent is indeed a ListAttribute (it could be a GroupAttribute, for instance)
if not listAttribute or not isinstance(listAttribute, ListAttribute):
return False
srcIndex = listAttribute.index(currentEdge.src)
allSrc = [e.src for e in self._graph.edges.values()]
Expand Down
6 changes: 4 additions & 2 deletions meshroom/ui/qml/GraphEditor/AttributeItemDelegate.qml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ RowLayout {
function updateAttributeLabel() {
background.color = attribute.validValue ? Qt.darker(palette.window, 1.1) : Qt.darker(Colors.red, 1.5)

console.warn("about to enter if")
if (attribute.desc) {
var tooltip = ""
if (!attribute.validValue && attribute.desc.errorMessage !== "")
tooltip += "<i><b>Error: </b>" + Format.plainToHtml(attribute.desc.errorMessage) + "</i><br><br>"
tooltip += "<b> " + attribute.desc.name + ":</b> " + attribute.type + "<br>" + Format.plainToHtml(attribute.desc.description)
tooltip += "<b> " + attribute.fullName + ":</b> " + attribute.type + "<br>" + Format.plainToHtml(attribute.desc.description)

console.warn(attribute.fullName)
parameterTooltip.text = tooltip
}
}
Expand Down Expand Up @@ -81,7 +83,7 @@ RowLayout {
var tooltip = ""
if (!object.validValue && object.desc.errorMessage !== "")
tooltip += "<i><b>Error: </b>" + Format.plainToHtml(object.desc.errorMessage) + "</i><br><br>"
tooltip += "<b>" + object.desc.name + ":</b> " + attribute.type + "<br>" + Format.plainToHtml(object.description)
tooltip += "<b>" + object.fullName + ":</b> " + attribute.type + "<br>" + Format.plainToHtml(object.description)
return tooltip
}
visible: parameterMA.containsMouse
Expand Down
63 changes: 44 additions & 19 deletions meshroom/ui/qml/GraphEditor/AttributePin.qml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ RowLayout {

property var nodeItem
property var attribute
property bool expanded: false
property bool readOnly: false
/// Whether to display an output pin for input attribute
property bool displayOutputPinForInput: true
Expand All @@ -24,33 +25,53 @@ RowLayout {
outputAnchor.y + outputAnchor.height / 2)

readonly property bool isList: attribute && attribute.type === "ListAttribute"
readonly property bool isGroup: attribute && attribute.type === "GroupAttribute"
readonly property bool isChild: attribute && attribute.root

signal childPinCreated(var childAttribute, var pin)
signal childPinDeleted(var childAttribute, var pin)

signal pressed(var mouse)
signal edgeAboutToBeRemoved(var input)

signal clicked()

objectName: attribute ? attribute.name + "." : ""
layoutDirection: Qt.LeftToRight
spacing: 3

ToolTip {
text: attribute.name + ": " + attribute.type
text: attribute.fullName + ": " + attribute.type
visible: nameLabel.hovered

y: nameLabel.y + nameLabel.height
x: nameLabel.x
}

function updatePin(isSrc, isVisible)
{
function updatePin(isSrc, isVisible) {
if (isSrc) {
innerOutputAnchor.linkEnabled = isVisible
} else {
innerInputAnchor.linkEnabled = isVisible
}
}

function updateLabel() {
var label = ""
var expandedGroup = expanded ? "-" : "+"
if (attribute && attribute.label !== undefined) {
label = attribute.label
if (isGroup && attribute.isOutput) {
label = label + " " + expandedGroup
} else if (isGroup && !attribute.isOutput) {
label = expandedGroup + " " + label
}
}
return label
}

onExpandedChanged: {
nameLabel.text = updateLabel()
}

// Instantiate empty Items for each child attribute
Expand Down Expand Up @@ -160,17 +181,16 @@ RowLayout {
drag.smoothed: false
enabled: !root.readOnly
anchors.fill: parent
// use the same negative margins as DropArea to ease pin selection
// Use the same negative margins as DropArea to ease pin selection
anchors.margins: inputDropArea.anchors.margins
anchors.leftMargin: inputDropArea.anchors.leftMargin
anchors.rightMargin: inputDropArea.anchors.rightMargin
onPressed: {
root.pressed(mouse)
}
onReleased: {
inputDragTarget.Drag.drop()
}
hoverEnabled: true

onPressed: root.pressed(mouse)
onReleased: inputDragTarget.Drag.drop()
onClicked: root.clicked()

hoverEnabled: root.visible
}

Edge {
Expand All @@ -186,7 +206,6 @@ RowLayout {
}



// Attribute name
Item {
id: nameContainer
Expand All @@ -199,17 +218,22 @@ RowLayout {
id: nameLabel

enabled: !root.readOnly
visible: true
property bool hovered: (inputConnectMA.containsMouse || inputConnectMA.drag.active || inputDropArea.containsDrag || outputConnectMA.containsMouse || outputConnectMA.drag.active || outputDropArea.containsDrag)
text: (attribute && attribute.label) !== undefined ? attribute.label : ""
text: root.updateLabel()
elide: hovered ? Text.ElideNone : Text.ElideMiddle
width: hovered ? contentWidth : parent.width
font.pointSize: 7
font.italic: isChild ? true : false
horizontalAlignment: attribute && attribute.isOutput ? Text.AlignRight : Text.AlignLeft
anchors.right: attribute && attribute.isOutput ? parent.right : undefined
rightPadding: 0
color: {
if ((object.hasOutputConnections || object.isLink) && !object.enabled) return Colors.lightgrey
return hovered ? palette.highlight : palette.text
if ((object.hasOutputConnections || object.isLink) && !object.enabled)
return Colors.lightgrey
else if (hovered)
return palette.highlight
return palette.text
}
}
}
Expand All @@ -236,8 +260,8 @@ RowLayout {
anchors.fill: parent
anchors.margins: 2
color: {
if (object.enabled && (outputConnectMA.containsMouse || outputConnectMA.drag.active ||
(outputDropArea.containsDrag && outputDropArea.acceptableDrop)))
if (modelData.enabled && (outputConnectMA.containsMouse || outputConnectMA.drag.active ||
(outputDropArea.containsDrag && outputDropArea.acceptableDrop)))
return Colors.sysPalette.highlight
return Colors.sysPalette.text
}
Expand Down Expand Up @@ -309,15 +333,16 @@ RowLayout {
// Move the edge's tip straight to the the current mouse position instead of waiting after the drag operation has started
drag.smoothed: false
anchors.fill: parent
// use the same negative margins as DropArea to ease pin selection
// Use the same negative margins as DropArea to ease pin selection
anchors.margins: outputDropArea.anchors.margins
anchors.leftMargin: outputDropArea.anchors.leftMargin
anchors.rightMargin: outputDropArea.anchors.rightMargin

onPressed: root.pressed(mouse)
onReleased: outputDragTarget.Drag.drop()
onClicked: root.clicked()

hoverEnabled: true
hoverEnabled: root.visible
}

Edge {
Expand Down
Loading
Loading