Skip to content

Commit 041348e

Browse files
committed
refactored node deletion undo logic
1 parent a5707a1 commit 041348e

File tree

5 files changed

+108
-34
lines changed

5 files changed

+108
-34
lines changed

NodeGraphQt/base/commands.py

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -154,27 +154,14 @@ def __init__(self, graph, node):
154154
self.scene = graph.scene()
155155
self.model = graph.model
156156
self.node = node
157-
self.inputs = []
158-
self.outputs = []
159157
self.node_parent = node.parent()
160158

161-
if hasattr(self.node, 'inputs'):
162-
input_ports = self.node.input_ports()
163-
self.inputs = [(p, p.connected_ports()) for p in input_ports]
164-
if hasattr(self.node, 'outputs'):
165-
output_ports = self.node.output_ports()
166-
self.outputs = [(p, p.connected_ports()) for p in output_ports]
167-
168159
def undo(self):
169160
self.model.nodes[self.node.id] = self.node
170161
self.scene.addItem(self.node.view)
171-
[port.connect_to(p) for port, connected_ports in self.inputs for p in connected_ports]
172-
[port.connect_to(p) for port, connected_ports in self.outputs for p in connected_ports]
173162
self.node.set_parent(self.node_parent)
174163

175164
def redo(self):
176-
[port.disconnect_from(p) for port, connected_ports in self.inputs for p in connected_ports]
177-
[port.disconnect_from(p) for port, connected_ports in self.outputs for p in connected_ports]
178165
self.model.nodes.pop(self.node.id)
179166
self.node.delete()
180167

@@ -328,6 +315,49 @@ def redo(self):
328315
self.source.view.disconnect_from(self.target.view)
329316

330317

318+
class PortLockedCmd(QtWidgets.QUndoCommand):
319+
"""
320+
Port locked command.
321+
322+
Args:
323+
port (NodeGraphQt.Port): node port.
324+
"""
325+
def __init__(self, port):
326+
QtWidgets.QUndoCommand.__init__(self)
327+
self.setText('lock port "{}"'.format(port.name()))
328+
self.port = port
329+
330+
def undo(self):
331+
self.port.model.locked = False
332+
self.port.view.locked = False
333+
334+
def redo(self):
335+
self.port.model.locked = True
336+
self.port.view.locked = True
337+
338+
339+
class PortUnlockedCmd(QtWidgets.QUndoCommand):
340+
"""
341+
Port unlocked command.
342+
343+
Args:
344+
port (NodeGraphQt.Port): node port.
345+
"""
346+
347+
def __init__(self, port):
348+
QtWidgets.QUndoCommand.__init__(self)
349+
self.setText('unlock port "{}"'.format(port.name()))
350+
self.port = port
351+
352+
def undo(self):
353+
self.port.model.locked = True
354+
self.port.view.locked = True
355+
356+
def redo(self):
357+
self.port.model.locked = False
358+
self.port.view.locked = False
359+
360+
331361
class PortVisibleCmd(QtWidgets.QUndoCommand):
332362
"""
333363
Port visibility command.

NodeGraphQt/base/graph.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from .factory import NodeFactory
1616
from .menu import NodeGraphMenu, NodesMenu
1717
from .model import NodeGraphModel
18-
from .node import NodeObject, BaseNode, BackdropNode
18+
from .node import NodeObject, BackdropNode, BaseNode
1919
from .port import Port
2020
from ..constants import (
2121
URI_SCHEME, URN_SCHEME,
@@ -1002,14 +1002,26 @@ def delete_node(self, node):
10021002
'node must be a instance of a NodeObject.'
10031003
if node is self.root_node():
10041004
return
1005-
self.nodes_deleted.emit([node.id])
1005+
1006+
node_id = node.id
1007+
self._undo_stack.beginMacro('delete node: "{}"'.format(node.name()))
1008+
if isinstance(node, BaseNode):
1009+
for p in node.input_ports():
1010+
if p.locked():
1011+
p.set_locked(False, connected_ports=False)
1012+
p.clear_connections()
1013+
for p in node.output_ports():
1014+
if p.locked():
1015+
p.set_locked(False, connected_ports=False)
1016+
p.clear_connections()
1017+
10061018
if isinstance(node, SubGraph):
1007-
self._undo_stack.beginMacro('delete sub graph')
10081019
self.delete_nodes(node.children())
10091020
self._undo_stack.push(NodeRemovedCmd(self, node))
1010-
self._undo_stack.endMacro()
1011-
else:
1012-
self._undo_stack.push(NodeRemovedCmd(self, node))
1021+
1022+
self._undo_stack.push(NodeRemovedCmd(self, node))
1023+
self._undo_stack.endMacro()
1024+
self.nodes_deleted.emit([node_id])
10131025

10141026
def delete_nodes(self, nodes):
10151027
"""
@@ -1018,14 +1030,29 @@ def delete_nodes(self, nodes):
10181030
Args:
10191031
nodes (list[NodeGraphQt.BaseNode]): list of node instances.
10201032
"""
1033+
if not nodes:
1034+
return
10211035
if not self._editable:
10221036
return
1037+
node_ids = [n.id for n in nodes]
10231038
root_node = self.root_node()
1024-
self.nodes_deleted.emit([n.id for n in nodes])
10251039
self._undo_stack.beginMacro('delete nodes')
1026-
[self.delete_nodes(n.children()) for n in nodes if isinstance(n, SubGraph)]
1027-
[self._undo_stack.push(NodeRemovedCmd(self, n)) for n in nodes if n is not root_node]
1040+
for node in nodes:
1041+
if isinstance(node, BaseNode):
1042+
for p in node.input_ports():
1043+
if p.locked():
1044+
p.set_locked(False, connected_ports=False)
1045+
p.clear_connections()
1046+
for p in node.output_ports():
1047+
if p.locked():
1048+
p.set_locked(False, connected_ports=False)
1049+
p.clear_connections()
1050+
if isinstance(node, SubGraph):
1051+
self.delete_nodes(node.children())
1052+
if node is not root_node:
1053+
self._undo_stack.push(NodeRemovedCmd(self, node))
10281054
self._undo_stack.endMacro()
1055+
self.nodes_deleted.emit(node_ids)
10291056

10301057
def delete_pipe(self, pipe):
10311058
self._on_connection_changed([(pipe.input_port, pipe.output_port)], [])

NodeGraphQt/base/port.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/usr/bin/python
22
from .commands import (PortConnectedCmd,
33
PortDisconnectedCmd,
4+
PortLockedCmd,
5+
PortUnlockedCmd,
46
PortVisibleCmd,
57
NodeInputConnectedCmd,
68
NodeInputDisconnectedCmd)
@@ -162,8 +164,12 @@ def set_locked(self, state=False, connected_ports=True):
162164
state (Bool): port lock state.
163165
connected_ports (Bool): apply to lock state to connected ports.
164166
"""
165-
self.model.locked = state
166-
self.__view.locked = state
167+
graph = self.node().graph
168+
undo_stack = graph.undo_stack()
169+
if state:
170+
undo_stack.push(PortLockedCmd(self))
171+
else:
172+
undo_stack.push(PortUnlockedCmd(self))
167173
if connected_ports:
168174
for port in self.connected_ports():
169175
port.set_locked(state, connected_ports=False)
@@ -272,6 +278,25 @@ def disconnect_from(self, port=None):
272278
ports = {p.type_(): p for p in [self, port]}
273279
graph.port_disconnected.emit(ports[IN_PORT], ports[OUT_PORT])
274280

281+
def clear_connections(self):
282+
"""
283+
Disconnect from all pipe connections and emit the
284+
:attr:`NodeGraph.port_disconnected` signals from the node graph.
285+
"""
286+
if self.locked():
287+
err = 'Can\'t clear connections because port "{}" is locked.'
288+
raise PortError(err.format(self.name()))
289+
290+
if not self.connected_ports():
291+
return
292+
293+
graph = self.node().graph
294+
undo_stack = graph.undo_stack()
295+
undo_stack.beginMacro('"{}" clear connections')
296+
for cp in self.connected_ports():
297+
self.disconnect_from(cp)
298+
undo_stack.endMacro()
299+
275300
@property
276301
def color(self):
277302
return self.__view.color

NodeGraphQt/qgraphics/node_base.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -740,11 +740,6 @@ def get_widget(self, name):
740740
def has_widget(self, name):
741741
return name in self._widgets.keys()
742742

743-
def delete(self):
744-
[port.delete() for port, text in self._input_items.items()]
745-
[port.delete() for port, text in self._output_items.items()]
746-
super(NodeItem, self).delete()
747-
748743
def from_dict(self, node_dict):
749744
super(NodeItem, self).from_dict(node_dict)
750745
widgets = node_dict.pop('widgets', {})

NodeGraphQt/qgraphics/port.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,11 @@ def mouseReleaseEvent(self, event):
131131
super(PortItem, self).mouseReleaseEvent(event)
132132

133133
def hoverEnterEvent(self, event):
134-
self.hovered = True
134+
self._hovered = True
135135
super(PortItem, self).hoverEnterEvent(event)
136136

137137
def hoverLeaveEvent(self, event):
138-
self.hovered = False
138+
self._hovered = False
139139
super(PortItem, self).hoverLeaveEvent(event)
140140

141141
def viewer_start_connection(self):
@@ -253,9 +253,6 @@ def port_type(self):
253253
def port_type(self, port_type):
254254
self._port_type = port_type
255255

256-
def delete(self):
257-
[pipe.delete() for pipe in self.connected_pipes]
258-
259256
def connect_to(self, port):
260257
if not port:
261258
for pipe in self.connected_pipes:

0 commit comments

Comments
 (0)