[ui] Coloring a Node: Added ColorSelector to the Graph Editor

The color selector in the Graph Editor provides a quick way to color nodes with predefined palette of colors.

Added Command to allow the Coloring to be undone and redone using QUndoStack
This commit is contained in:
waaake 2024-11-25 09:22:14 +05:30
parent 58a9b74a72
commit d77b7a398a
5 changed files with 213 additions and 0 deletions

View file

@ -205,6 +205,54 @@ class DuplicateNodesCommand(GraphCommand):
self.graph.removeNode(duplicate) self.graph.removeNode(duplicate)
class UpdateNodeColorCommand(GraphCommand):
""" Command representing the work for Coloring nodes in the Graph.
"""
def __init__(self, graph, nodes, color, parent=None):
""" Command Constructor.
Args:
graph (meshroom.core.Graph): Current Graph instance.
nodes (list<Node>): Array of nodes.
color (str): Hex code for the color.
Keyword Args:
parent (QObject): Parent QObject instance.
"""
super(UpdateNodeColorCommand, self).__init__(graph, parent)
self._nodes = nodes
self._color = color
# Existing colors
# Map <Node: str>
# Holds the existing color of the node which can be applied on the node back in case of an undo
self._colorMap = {}
self.setText("Update Selected Node Color")
def redoImpl(self) -> bool:
""" Redo implementation.
"""
for node in self._nodes:
# Update the existing color for the node in the map
self._colorMap[node] = node.color
# Now update the color of the node with the provided one
node.color = self._color
return True
def undoImpl(self):
""" Undo Implementation.
"""
with GraphModification(self.graph):
# Revert the color for the nodes
for node in self._nodes:
# Get the color which was saved for the node
node.color = self._colorMap.get(node)
class PasteNodesCommand(GraphCommand): class PasteNodesCommand(GraphCommand):
""" """
Handle node pasting in a Graph. Handle node pasting in a Graph.

View file

@ -952,6 +952,17 @@ class UIGraph(QObject):
""" Select all the nodes the depend on 'node'. """ """ Select all the nodes the depend on 'node'. """
self.selectNodes(self._graph.dfsOnDiscover(startNodes=[node], reverse=True, dependenciesOnly=True)[0]) self.selectNodes(self._graph.dfsOnDiscover(startNodes=[node], reverse=True, dependenciesOnly=True)[0])
@Slot(str)
def updateNodeColor(self, color):
""" Sets the Provided color on the selected Nodes.
Args:
color (str): Hex code of the color to be set on the nodes.
"""
# Update the color attribute of the nodes which are currently selected
with self.groupedGraphModification("Update Color for Select Nodes"):
self.push(commands.UpdateNodeColorCommand(self._graph, list(self._selectedNodes), color))
@Slot(QObject, QObject) @Slot(QObject, QObject)
def boxSelect(self, selection, draggable): def boxSelect(self, selection, draggable):
""" """

View file

@ -0,0 +1,133 @@
import QtQuick
import QtQuick.Controls
import Utils 1.0
import MaterialIcons 2.2
/**
* ColorSelector is a color picker based on a set of predefined colors.
* It takes the form of a ToolButton that pops-up its palette when pressed.
*/
MaterialToolButton {
id: root
text: MaterialIcons.palette
// Internal property holding when the popup remains visible and when is it toggled off
property var isVisible: false
property var colors: [
"#FF6600",
"#FFAD7D",
"#FFF82F",
"#FFFB8B",
"#BDFF07",
"#E8FF97",
"#0BFFCA",
"#8EFFF7",
"#1158FF",
"#77A7FF",
"#9318FF",
"#EAACFF",
"#B61518",
"#FF989A",
]
// When a color gets selected/choosen
signal colorSelected(var color)
// Toggles the visibility of the popup
onPressed: toggle()
function toggle() {
/*
* Toggles the visibility of the color palette.
*/
if (!isVisible) {
palettePopup.open()
isVisible = true
}
else {
palettePopup.close()
isVisible = false
}
}
// Popup for the color palette
Popup {
id: palettePopup
// The popup will not get closed unless explicitly closed
closePolicy: Popup.NoAutoClose
// Bounds
padding: 4
width: (root.height * 4) + (padding * 4)
// center the current color
y: -height
x: -width + root.width + padding
// Layout of the Colors
Grid {
// Allow only 4 columns and all the colors can be adjusted in row multiples of 4
columns: 4
spacing: 2
anchors.centerIn: parent
// Default -- Reset Colour button
ToolButton {
id: defaultButton
padding: 0
width: root.height
height: root.height
// Emit no color so the graph sets None as the color of the Node
onClicked: {
root.colorSelected("")
}
background: Rectangle {
color: "#FFFFFF"
// display border of current/selected item
border.width: defaultButton.hovered ? 1 : 0
border.color: "#000000"
// Another Rectangle
Rectangle {
color: "#FF0000"
width: parent.width + 8
height: 2
anchors.centerIn: parent
rotation: 135 // Diagonally creating a Red line from bottom left to top right
}
}
}
// Colors palette
Repeater {
model: root.colors
// display each color as a ToolButton with a custom background
delegate: ToolButton {
padding: 0
width: root.height
height: root.height
// Emit the model data as the color to update
onClicked: {
colorSelected(modelData)
}
// Model color as the background of the button
background: Rectangle {
color: modelData
// display border of current/selected item
border.width: hovered ? 1 : 0
border.color: "#000000"
}
}
}
}
}
}

View file

@ -1,6 +1,7 @@
module Controls module Controls
ColorChart 1.0 ColorChart.qml ColorChart 1.0 ColorChart.qml
ColorSelector 1.0 ColorSelector.qml
ExpandableGroup 1.0 ExpandableGroup.qml ExpandableGroup 1.0 ExpandableGroup.qml
FloatingPane 1.0 FloatingPane.qml FloatingPane 1.0 FloatingPane.qml
Group 1.0 Group.qml Group 1.0 Group.qml

View file

@ -136,6 +136,8 @@ Item {
Keys.onPressed: function(event) { Keys.onPressed: function(event) {
if (event.key === Qt.Key_F) { if (event.key === Qt.Key_F) {
fit() fit()
} else if (event.key === Qt.Key_C) {
colorSelector.toggle()
} else if (event.key === Qt.Key_Delete) { } else if (event.key === Qt.Key_Delete) {
if (event.modifiers === Qt.AltModifier) { if (event.modifiers === Qt.AltModifier) {
uigraph.removeNodesFrom(uigraph.selectedNodes) uigraph.removeNodesFrom(uigraph.selectedNodes)
@ -1048,6 +1050,24 @@ Item {
} }
} }
} }
// Separator
Rectangle {
Layout.fillHeight: true
Layout.margins: 2
implicitWidth: 1
color: activePalette.window
}
ColorSelector {
id: colorSelector
Layout.minimumWidth: colorSelector.width
// When a Color is selected
onColorSelected: (color)=> {
uigraph.updateNodeColor(color)
}
}
} }
} }