Skip to content

Commit 59de5c1

Browse files
committed
initial support sub graph
1 parent f1f4251 commit 59de5c1

File tree

11 files changed

+479
-46
lines changed

11 files changed

+479
-46
lines changed

NodeGraphQt/base/commands.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,17 @@ def __init__(self, graph, node, pos=None):
125125
self.model = graph.model
126126
self.node = node
127127
self.pos = pos
128+
self.node_parent = node.parent()
128129

129130
def undo(self):
130131
self.pos = self.pos or self.node.pos()
131132
self.model.nodes.pop(self.node.id)
132-
self.node.view.delete()
133+
self.node.delete()
133134

134135
def redo(self):
135136
self.model.nodes[self.node.id] = self.node
136137
self.viewer.add_node(self.node.view, self.pos)
138+
self.node.set_parent(self.node_parent)
137139

138140

139141
class NodeRemovedCmd(QtWidgets.QUndoCommand):
@@ -153,6 +155,7 @@ def __init__(self, graph, node):
153155
self.node = node
154156
self.inputs = []
155157
self.outputs = []
158+
self.node_parent = node.parent()
156159

157160
if hasattr(self.node, 'inputs'):
158161
input_ports = self.node.inputs().values()
@@ -164,18 +167,15 @@ def __init__(self, graph, node):
164167
def undo(self):
165168
self.model.nodes[self.node.id] = self.node
166169
self.scene.addItem(self.node.view)
167-
for port, connected_ports in self.inputs:
168-
[port.connect_to(p) for p in connected_ports]
169-
for port, connected_ports in self.outputs:
170-
[port.connect_to(p) for p in connected_ports]
170+
[port.connect_to(p) for port, connected_ports in self.inputs for p in connected_ports]
171+
[port.connect_to(p) for port, connected_ports in self.outputs for p in connected_ports]
172+
self.node.set_parent(self.node_parent)
171173

172174
def redo(self):
173-
for port, connected_ports in self.inputs:
174-
[port.disconnect_from(p) for p in connected_ports]
175-
for port, connected_ports in self.outputs:
176-
[port.disconnect_from(p) for p in connected_ports]
175+
[port.disconnect_from(p) for port, connected_ports in self.inputs for p in connected_ports]
176+
[port.disconnect_from(p) for port, connected_ports in self.outputs for p in connected_ports]
177177
self.model.nodes.pop(self.node.id)
178-
self.node.view.delete()
178+
self.node.delete()
179179

180180

181181
class NodeInputConnectedCmd(QtWidgets.QUndoCommand):

NodeGraphQt/base/graph.py

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from .factory import NodeFactory
1414
from .menu import NodeGraphMenu, NodesMenu
1515
from .model import NodeGraphModel
16-
from .node import NodeObject, BaseNode
16+
from .node import NodeObject, BaseNode, SubGraph
1717
from .port import Port
1818
from ..constants import (DRAG_DROP_ID,
1919
PIPE_LAYOUT_CURVED,
@@ -128,11 +128,12 @@ def __init__(self, parent=None):
128128
self._viewer = NodeViewer()
129129
self._node_factory = NodeFactory()
130130
self._undo_stack = QtWidgets.QUndoStack(self)
131+
self._current_node_space = None
131132

132133
tab = QtWidgets.QShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Tab), self._viewer)
133134
tab.activated.connect(self._toggle_tab_search)
134135
self._viewer.need_show_tab_search.connect(self._toggle_tab_search)
135-
136+
136137
self._wire_signals()
137138
self.widget.setAcceptDrops(True)
138139

@@ -223,6 +224,8 @@ def _on_node_double_clicked(self, node_id):
223224
"""
224225
node = self.get_node_by_id(node_id)
225226
self.node_double_clicked.emit(node)
227+
if isinstance(node,SubGraph):
228+
node.enter()
226229

227230
def _on_node_selected(self, node_id):
228231
"""
@@ -745,6 +748,9 @@ def format_color(clr):
745748
if pos:
746749
node.model.pos = [float(pos[0]), float(pos[1])]
747750

751+
# set node parent
752+
node.set_parent(self._current_node_space)
753+
748754
node.update()
749755

750756
undo_cmd = NodeAddedCmd(self, node, node.model.pos)
@@ -782,6 +788,24 @@ def add_node(self, node, pos=None):
782788
node.update()
783789
self._undo_stack.push(NodeAddedCmd(self, node, pos))
784790

791+
def set_node_space(self, node):
792+
"""
793+
Set the node space of the node graph.
794+
795+
Args:
796+
node (NodeGraphQt.SubGraph): node object.
797+
"""
798+
self._current_node_space = node
799+
800+
def get_node_space(self):
801+
"""
802+
Get the node space of the node graph.
803+
804+
Returns:
805+
node (NodeGraphQt.SubGraph): node object or None.
806+
"""
807+
return self._current_node_space
808+
785809
def delete_node(self, node):
786810
"""
787811
Remove the node from the node graph.
@@ -793,6 +817,8 @@ def delete_node(self, node):
793817
'node must be a instance of a NodeObject.'
794818
self.nodes_deleted.emit([node.id])
795819
self._undo_stack.push(NodeRemovedCmd(self, node))
820+
if isinstance(node,SubGraph):
821+
self.delete_nodes(node.children())
796822

797823
def delete_nodes(self, nodes):
798824
"""
@@ -803,6 +829,7 @@ def delete_nodes(self, nodes):
803829
"""
804830
self.nodes_deleted.emit([n.id for n in nodes])
805831
self._undo_stack.beginMacro('delete nodes')
832+
[self.delete_nodes(n.children()) for n in nodes if isinstance(n,SubGraph)]
806833
[self._undo_stack.push(NodeRemovedCmd(self, n)) for n in nodes]
807834
self._undo_stack.endMacro()
808835

@@ -866,7 +893,7 @@ def get_node_by_id(self, node_id=None):
866893
Returns:
867894
NodeGraphQt.NodeObject: node object.
868895
"""
869-
return self._model.nodes.get(node_id)
896+
return self._model.nodes.get(node_id, None)
870897

871898
def get_node_by_name(self, name):
872899
"""
@@ -1009,6 +1036,9 @@ def _deserialize(self, data, relative_pos=False, pos=None):
10091036
self.add_node(node, n_data.get('pos'))
10101037
node.set_graph(self)
10111038

1039+
# set node parent
1040+
[node.set_parent_id(node.parent_id) for node in nodes.values()]
1041+
10121042
# build the connections.
10131043
for connection in data.get('connections', []):
10141044
nid, pname = connection.get('in', ('', ''))
@@ -1064,6 +1094,10 @@ def save_session(self, file_path):
10641094
file_path (str): path to the saved node layout.
10651095
"""
10661096
serliazed_data = self._serialize(self.all_nodes())
1097+
node_space = self.get_node_space()
1098+
if node_space is not None:
1099+
node_space = node_space.id
1100+
serliazed_data['graph'] = {'node_space':node_space,'pipe_layout':self._viewer.get_pipe_layout()}
10671101
file_path = file_path.strip()
10681102
with open(file_path, 'w') as file_out:
10691103
json.dump(serliazed_data, file_out, indent=2, separators=(',', ':'))
@@ -1104,6 +1138,12 @@ def import_session(self, file_path):
11041138
return
11051139

11061140
self._deserialize(layout_data)
1141+
node_space_id = layout_data['graph']['node_space']
1142+
1143+
# deserialize graph data
1144+
self.set_node_space(self.get_node_by_id(node_space_id))
1145+
self._viewer.set_pipe_layout(layout_data['graph']['pipe_layout'])
1146+
11071147
self._undo_stack.clear()
11081148
self._model.session = file_path
11091149
self.session_changed.emit(file_path)
@@ -1126,6 +1166,17 @@ def copy_nodes(self, nodes=None):
11261166
return True
11271167
return False
11281168

1169+
def cut_nodes(self, nodes=None):
1170+
"""
1171+
Cut nodes to the clipboard.
1172+
1173+
Args:
1174+
nodes (list[NodeGraphQt.BaseNode]): list of nodes (default: selected nodes).
1175+
"""
1176+
nodes = nodes or self.selected_nodes()
1177+
self.copy_nodes(nodes)
1178+
self.delete_nodes(nodes)
1179+
11291180
def paste_nodes(self):
11301181
"""
11311182
Pastes nodes copied from the clipboard.
@@ -1140,6 +1191,9 @@ def paste_nodes(self):
11401191
self.clear_selection()
11411192
nodes = self._deserialize(serial_data, relative_pos=True)
11421193
[n.set_selected(True) for n in nodes]
1194+
# set node parent
1195+
[n.set_parent(self._current_node_space) for n in nodes]
1196+
11431197
self._undo_stack.endMacro()
11441198

11451199
def duplicate_nodes(self, nodes):

NodeGraphQt/base/model.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ def __init__(self):
5858
self.text_color = (255, 255, 255, 180)
5959
self.disabled = False
6060
self.selected = False
61+
self.visible = True
62+
self.parent_id = None
6163
self.width = 100.0
6264
self.height = 80.0
6365
self.pos = [0.0, 0.0]
@@ -214,10 +216,14 @@ def to_dict(self):
214216
'type': 'com.chantasticvfx.FooNode',
215217
'selected': False,
216218
'disabled': False,
219+
'visible': True,
220+
'parent_id' : None,
217221
'inputs': {
218222
<port_name>: {<node_id>: [<port_name>, <port_name>]}},
219223
'outputs': {
220224
<port_name>: {<node_id>: [<port_name>, <port_name>]}},
225+
'input_ports': [<port_name>, <port_name>],
226+
'output_ports': [<port_name>, <port_name>],
221227
'width': 0.0,
222228
'height: 0.0,
223229
'pos': (0.0, 0.0),
@@ -230,11 +236,15 @@ def to_dict(self):
230236

231237
inputs = {}
232238
outputs = {}
239+
input_ports = []
240+
output_ports = []
233241
for name, model in node_dict.pop('inputs').items():
242+
input_ports.append(name)
234243
connected_ports = model.to_dict['connected_ports']
235244
if connected_ports:
236245
inputs[name] = connected_ports
237246
for name, model in node_dict.pop('outputs').items():
247+
output_ports.append(name)
238248
connected_ports = model.to_dict['connected_ports']
239249
if connected_ports:
240250
outputs[name] = connected_ports
@@ -243,6 +253,9 @@ def to_dict(self):
243253
if outputs:
244254
node_dict['outputs'] = outputs
245255

256+
node_dict['input_ports'] = input_ports
257+
node_dict['output_ports'] = output_ports
258+
246259
custom_props = node_dict.pop('_custom_prop', {})
247260

248261
if custom_props:

0 commit comments

Comments
 (0)