Skip to content

Commit 8b00c8c

Browse files
authored
Merge pull request #2953 from alicevision/dev/rename_node
[ui] Rename node
2 parents 35e0e38 + 9dde23e commit 8b00c8c

File tree

10 files changed

+316
-8
lines changed

10 files changed

+316
-8
lines changed

meshroom/common/core.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,22 @@ def add(self, obj):
5050
assert key is not None
5151
assert key not in self._objects
5252
self._objects[key] = obj
53+
54+
def rename(self, oldKey: str, newKey: str):
55+
""" Rename an element in the dict model
56+
57+
Args:
58+
oldKey (str): Previous key name of the element to replace.
59+
newKey (str): New key name to insert in the model.
60+
61+
Raises:
62+
KeyError: if the new name is already used.
63+
"""
64+
if newKey in self._objects.keys():
65+
raise KeyError(f"Key {newKey} is already in use in {self}")
66+
obj = self._objects[oldKey]
67+
self._objects[newKey] = obj
68+
del self._objects[oldKey]
5369

5470
def pop(self, key):
5571
assert key in self._objects

meshroom/common/qt.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,24 @@ def replace(self, i, obj):
167167
self._objects[i] = obj
168168
self.dataChanged.emit(self.index(i), self.index(i), [])
169169

170+
def rename(self, oldKey: str, newKey: str):
171+
""" Rename an element in the model
172+
173+
Args:
174+
oldKey (str): Previous key name of the element to replace.
175+
newKey (str): New key name to insert in the model.
176+
177+
Raises:
178+
KeyError: if the new name is already used.
179+
"""
180+
if newKey in self._objectByKey.keys():
181+
raise KeyError(f"Key {newKey} is already in use in {self}")
182+
obj = self._objectByKey[oldKey]
183+
index = self.indexOf(obj)
184+
self._objectByKey[newKey] = obj
185+
del self._objectByKey[oldKey]
186+
self.dataChanged.emit(self.index(index), self.index(index), [])
187+
170188
def move(self, fromIndex, toIndex):
171189
""" Moves the item at index position from to index position to
172190
and notifies any views.

meshroom/core/graph.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,30 @@ def addNode(self, node, uniqueName=None):
513513
node._applyExpr()
514514
return node
515515

516+
def renameNode(self, node: Node, newName: str):
517+
""" Rename a node in the Node Graph.
518+
If the proposed name is already assigned to a node then it will create a unique name
519+
520+
Args:
521+
node (Node): Node to rename.
522+
newName (str): New name of the node.
523+
"""
524+
# Handle empty string
525+
if not newName:
526+
return
527+
if node.getLocked():
528+
logging.warning(f"Cannot rename node {node} because of the locked status")
529+
return
530+
usedNames = {n._name for n in self._nodes if n != node}
531+
# Make sure we rename to an available name
532+
if newName in usedNames:
533+
newName = self._createUniqueNodeName(newName, usedNames)
534+
# Rename in the dict model
535+
self._nodes.rename(node._name, newName)
536+
# Finally rename the node name property and notify Qt
537+
node._name = newName
538+
node.nodeNameChanged.emit()
539+
516540
def copyNode(self, srcNode: Node, withEdges: bool=False):
517541
"""
518542
Get a copy instance of a node outside the graph.

meshroom/core/node.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ def nameToLabel(self, name):
913913
Returns:
914914
str: the high-level label from the technical node name
915915
"""
916-
t, idx = name.split("_")
916+
t, idx = name.rsplit("_", 1) if "_" in name else (name, "1")
917917
return f"{t}{idx if int(idx) > 1 else ''}"
918918

919919
def getDocumentation(self):
@@ -2050,7 +2050,8 @@ def _hasDisplayableShape(self):
20502050
attr.desc.semantic == "shapeFile"), None) is not None
20512051

20522052

2053-
name = Property(str, getName, constant=True)
2053+
nodeNameChanged = Signal()
2054+
name = Property(str, getName, notify=nodeNameChanged)
20542055
defaultLabel = Property(str, getDefaultLabel, constant=True)
20552056
nodeType = Property(str, nodeType.fget, constant=True)
20562057
documentation = Property(str, getDocumentation, constant=True)

meshroom/ui/commands.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,24 @@ def undoImpl(self):
152152
self.graph.removeNode(self.nodeName)
153153

154154

155+
class RenameNodeCommand(GraphCommand):
156+
def __init__(self, graph, node, name, parent=None):
157+
""" Command to rename a node. The new name should not be used yet.
158+
"""
159+
super().__init__(graph, parent)
160+
self.node = node
161+
self.oldName = node._name
162+
self.name = name
163+
164+
def redoImpl(self):
165+
self.setText(f"Rename Node {self.oldName} to {self.name}")
166+
self.graph.renameNode(self.node, self.name)
167+
return self.node._name
168+
169+
def undoImpl(self):
170+
self.graph.renameNode(self.node, self.oldName)
171+
172+
155173
class RemoveNodeCommand(GraphCommand):
156174
def __init__(self, graph, node, parent=None):
157175
super().__init__(graph, parent)

meshroom/ui/graph.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from collections.abc import Iterable
33
import logging
44
import os
5+
import re
56
import json
67
from enum import Enum
78
from threading import Thread, Event, Lock
@@ -939,6 +940,32 @@ def addNewNode(self, nodeType, position=None, **kwargs):
939940
position = Position(position.x(), position.y())
940941
return self.push(commands.AddNodeCommand(self._graph, nodeType, position=position, **kwargs))
941942

943+
@Slot(Node, str, result=str)
944+
def renameNode(self, node: Node, newName: str):
945+
""" Triggers the node renaming.
946+
947+
In this function the last `_N` index is removed, then all special characters
948+
(everything except letters and numbers) are removed.
949+
The name uniqueness will be ensured later by adding a suffix (e.g. `_1`, `_2`, ...)
950+
951+
Labels can be used to have special characters in the displayed name.
952+
953+
Args:
954+
node (Node): Node to rename.
955+
newName (str): New name to set.
956+
957+
Returns:
958+
str: The final name of the node.
959+
"""
960+
newName = "_".join(newName.split("_")[:-1]) if "_" in newName else newName
961+
# Eliminate all characters except digits and letters
962+
newName = re.sub(r"[^0-9a-zA-Z]", "", newName)
963+
# Create unique name
964+
uniqueName = self._graph._createUniqueNodeName(newName, {n._name for n in self._graph._nodes if n != node})
965+
if not newName or uniqueName == node._name:
966+
return ""
967+
return self.push(commands.RenameNodeCommand(self._graph, node, uniqueName))
968+
942969
def moveNode(self, node: Node, position: Position):
943970
"""
944971
Move `node` to the given `position`.

meshroom/ui/qml/Controls/Panel.qml

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Page {
1616
id: root
1717

1818
property alias headerBar: headerLayout.data
19+
property Component titleComponent: null // Allow custom component for title
1920
property alias footerContent: footerLayout.data
2021
property alias icon: iconPlaceHolder.data
2122
property alias loading: loadingIndicator.running
@@ -62,12 +63,22 @@ Page {
6263
}
6364

6465
// Title
65-
Label {
66-
text: root.title
67-
elide: Text.ElideRight
68-
topPadding: m.vPadding
69-
bottomPadding: m.vPadding
66+
// Either we load the custom root.titleComponent or we just put the root.title
67+
Loader {
68+
id: titleLoader
69+
sourceComponent: root.titleComponent !== null ? root.titleComponent : defaultTitleComponent
70+
Layout.fillWidth: false
71+
}
72+
Component {
73+
id: defaultTitleComponent
74+
Label {
75+
text: root.title
76+
elide: Text.ElideRight
77+
topPadding: m.vPadding
78+
bottomPadding: m.vPadding
79+
}
7080
}
81+
7182
Item {
7283
width: 10
7384
}

meshroom/ui/qml/GraphEditor/Node.qml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ Item {
2424
property bool selected: false
2525
property bool hovered: false
2626
property bool dragging: mouseArea.drag.active
27+
/// Node label
28+
property string nodeLabel: node ? node.label : ""
2729
/// Combined x and y
2830
property point position: Qt.point(x, y)
2931
/// Styling
@@ -87,6 +89,12 @@ Item {
8789
root.x = root.node.x
8890
root.y = root.node.y
8991
}
92+
function onNameChanged() {
93+
// HACK: Make sure when the node name changes the node label is updated
94+
root.nodeLabel = ""
95+
// Restore binding to root.node.label
96+
root.nodeLabel = Qt.binding(function() { return root.node.label; })
97+
}
9098
}
9199

92100
Timer {
@@ -399,7 +407,7 @@ Item {
399407
Label {
400408
id: nodeLabel
401409
Layout.fillWidth: true
402-
text: node ? node.label : ""
410+
text: root.nodeLabel
403411
padding: 4
404412
color: root.mainSelected ? activePalette.highlightedText : activePalette.text
405413
elide: Text.ElideMiddle

0 commit comments

Comments
 (0)