Skip to content

Commit ff2b6d4

Browse files
committed
improve and perfect topological sort functions
1 parent 8fdcb0c commit ff2b6d4

File tree

15 files changed

+160
-97
lines changed

15 files changed

+160
-97
lines changed

NodeGraphQt/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,17 @@ def __init__(self):
8989
from .pkg_info import __license__ as LICENSE
9090

9191
# functions
92-
from .base.utils import setup_context_menu
92+
from .base.utils import setup_context_menu, \
93+
topological_sort_by_up, topological_sort_by_down, \
94+
update_node_down_stream, update_node_up_stream,\
95+
update_nodes_by_up, update_nodes_by_down
9396

9497
# widgets
9598
from .widgets.node_tree import NodeTreeWidget
9699
from .widgets.properties_bin import PropertiesBinWidget
97100
from .widgets.node_publish_widget import NodePublishWidget
98101

102+
99103
__version__ = VERSION
100104
__all__ = [
101105
'BackdropNode',
@@ -114,5 +118,11 @@ def __init__(self):
114118
'setup_context_menu',
115119
'NodePublishWidget',
116120
'SubGraph',
121+
'topological_sort_by_up',
122+
'topological_sort_by_down',
123+
'update_node_up_stream',
124+
'update_node_down_stream',
125+
'update_nodes_by_up',
126+
'update_nodes_by_down',
117127
]
118128

NodeGraphQt/base/graph.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -839,8 +839,9 @@ def set_node_space(self, node):
839839
Args:
840840
node (NodeGraphQt.SubGraph): node object.
841841
"""
842-
if node is self._current_node_space:
842+
if node is self._current_node_space or not isinstance(node, SubGraph):
843843
return
844+
844845
if self._current_node_space is not None:
845846
self._current_node_space.exit()
846847

NodeGraphQt/base/node.py

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
NodeIntEdit,
2121
NodeCheckBox,
2222
NodeFilePath)
23+
from .utils import update_node_down_stream
2324

2425

2526
class classproperty(object):
@@ -1060,35 +1061,21 @@ def on_input_disconnected(self, in_port, out_port):
10601061
"""
10611062
return
10621063

1063-
def update_streams(self, *args):
1064+
def update_stream(self):
10641065
"""
1065-
Update all nodes joined by pipes to this.
1066+
Update node down stream.
10661067
"""
1067-
nodes = []
1068-
trash = []
1069-
1070-
for port, nodeList in self.connected_output_nodes().items():
1071-
nodes.extend(nodeList)
1072-
1073-
while nodes:
1074-
node = nodes.pop()
1075-
if node.disabled():
1076-
continue
1077-
if node not in trash:
1078-
trash.append(node)
1079-
1080-
for port, nodeList in node.connected_output_nodes().items():
1081-
nodes.extend(nodeList)
1082-
1083-
if not node.connected_output_nodes():
1084-
try:
1085-
node.run()
1086-
except Exception as error:
1087-
print("Error Update Streams: %s" % str(error))
1068+
update_node_down_stream(self)
10881069

10891070
def run(self):
10901071
"""
1091-
Node evaluation logics.
1072+
Node evaluation logic.
1073+
"""
1074+
return
1075+
1076+
def when_disabled(self):
1077+
"""
1078+
Node evaluation logic when node has been disabled.
10921079
"""
10931080
return
10941081

NodeGraphQt/base/utils.py

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from distutils.version import LooseVersion
33

44
from .. import QtGui, QtCore
5-
from .node import SubGraph
65
from ..constants import (PIPE_LAYOUT_CURVED,
76
PIPE_LAYOUT_STRAIGHT,
87
PIPE_LAYOUT_ANGLE)
@@ -250,9 +249,7 @@ def _fit_to_selection(graph):
250249
def _jump_in(graph):
251250
nodes = graph.selected_nodes()
252251
if nodes:
253-
node = nodes[0]
254-
if isinstance(node, SubGraph):
255-
graph.set_node_space(node)
252+
graph.set_node_space(nodes[0])
256253

257254

258255
def _jump_out(graph):
@@ -312,7 +309,7 @@ def _has_output_node(node):
312309
return False
313310

314311

315-
def _build_graph(start_nodes):
312+
def _build_down_stream_graph(start_nodes):
316313
graph = {}
317314
for node in start_nodes:
318315
output_nodes = get_output_nodes(node)
@@ -325,19 +322,26 @@ def _build_graph(start_nodes):
325322
graph[n] = nodes
326323
_output_nodes.extend(nodes)
327324
output_nodes = _output_nodes
328-
329325
return graph
330326

331327

332-
def topological_sort(start_nodes=[], all_nodes=[]):
333-
if not start_nodes:
334-
start_nodes = [n for n in all_nodes if not _has_input_node(n)]
335-
if not start_nodes:
336-
return []
337-
if not [n for n in start_nodes if _has_output_node(n)]:
338-
return start_nodes
328+
def _build_up_stream_graph(start_nodes):
329+
graph = {}
330+
for node in start_nodes:
331+
input_nodes = get_input_nodes(node)
332+
graph[node] = input_nodes
333+
while input_nodes:
334+
_input_nodes = []
335+
for n in input_nodes:
336+
if n not in graph:
337+
nodes = get_input_nodes(n)
338+
graph[n] = nodes
339+
_input_nodes.extend(nodes)
340+
input_nodes = _input_nodes
341+
return graph
342+
339343

340-
graph = _build_graph(start_nodes)
344+
def _sort_nodes(graph, start_nodes, reverse=True):
341345
if not graph:
342346
return []
343347

@@ -357,6 +361,57 @@ def dfs(graph, start_node):
357361
visit[start_node] = True
358362
dfs(graph, start_node)
359363

360-
sorted_nodes.reverse()
364+
if reverse:
365+
sorted_nodes.reverse()
361366

362367
return sorted_nodes
368+
369+
370+
def topological_sort_by_down(start_nodes=[], all_nodes=[]):
371+
if not start_nodes:
372+
start_nodes = [n for n in all_nodes if not _has_input_node(n)]
373+
if not start_nodes:
374+
return []
375+
if not [n for n in start_nodes if _has_output_node(n)]:
376+
return start_nodes
377+
378+
graph = _build_down_stream_graph(start_nodes)
379+
380+
return _sort_nodes(graph, start_nodes, True)
381+
382+
383+
def topological_sort_by_up(start_nodes=[], all_nodes=[]):
384+
if not start_nodes:
385+
start_nodes = [n for n in all_nodes if not _has_output_node(n)]
386+
if not start_nodes:
387+
return []
388+
if not [n for n in start_nodes if _has_input_node(n)]:
389+
return start_nodes
390+
391+
graph = _build_up_stream_graph(start_nodes)
392+
393+
return _sort_nodes(graph, start_nodes, False)
394+
395+
396+
def _update_nodes(nodes):
397+
for node in nodes:
398+
if node.disabled():
399+
node.when_disabled()
400+
else:
401+
node.run()
402+
403+
404+
def update_node_down_stream(node):
405+
_update_nodes(topological_sort_by_down(start_nodes=[node]))
406+
407+
408+
def update_node_up_stream(node):
409+
_update_nodes(topological_sort_by_up(start_nodes=[node]))
410+
411+
412+
def update_nodes_by_down(nodes):
413+
_update_nodes(topological_sort_by_down(all_nodes=nodes))
414+
415+
416+
def update_nodes_by_up(nodes):
417+
_update_nodes(topological_sort_by_up(all_nodes=nodes))

example_auto_nodes.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@
55
from NodeGraphQt import NodeGraph, setup_context_menu
66
from NodeGraphQt import QtWidgets, QtCore, PropertiesBinWidget, \
77
NodeTreeWidget, BackdropNode, NodePublishWidget
8-
from NodeGraphQt.base.utils import topological_sort
98
import os
109
import sys
1110
import inspect
1211
import importlib
1312
from example_auto_nodes import AutoNode, ModuleNode, \
14-
SubGraphNode, Publish, RootNode
13+
SubGraphNode, Publish, RootNode, update_nodes
1514

1615

1716
def get_nodes_from_folder(folder_path):
@@ -52,7 +51,7 @@ def get_published_nodes_from_folder(folder_path):
5251

5352

5453
def cook_node(graph, node):
55-
node.cook(forceCook=True)
54+
node.update_stream(forceCook=True)
5655

5756

5857
def print_functions(graph, node):
@@ -87,11 +86,7 @@ def publish_node(graph, node):
8786

8887

8988
def cook_nodes(nodes):
90-
nodes = topological_sort(all_nodes=nodes)
91-
for node in nodes:
92-
node.cook(stream=True)
93-
if node.error():
94-
break
89+
update_nodes(nodes)
9590

9691

9792
if __name__ == '__main__':

example_auto_nodes/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
from .node_base.module_node import ModuleNode
33
from .node_base.subgraph_node import SubGraphNode, RootNode
44
from .subgraph_nodes import Publish
5+
from .node_base.utils import update_node_down_stream, update_nodes

example_auto_nodes/networks/example_SubGraph.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@
262262
"0x169cc6f5ec8":{
263263
"type_":"Inputs.Vector3InputNode",
264264
"icon":null,
265-
"name":"Vector 5",
265+
"name":"Vector 6",
266266
"color":[
267267
13,
268268
18,

example_auto_nodes/node_base/auto_node.py

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from NodeGraphQt import BaseNode, SubGraph, Port, QtCore
22
from NodeGraphQt.constants import NODE_PROP
3-
from NodeGraphQt.base.utils import topological_sort, get_output_nodes
3+
from . utils import update_node_down_stream
44
import traceback
55
import hashlib
66
import copy
@@ -72,12 +72,13 @@ def cookTime(self, time):
7272
self._cookTime = time
7373
self._update_tool_tip()
7474

75-
def update_streams(self):
76-
nodes = topological_sort(start_nodes=get_output_nodes(self))
77-
for node in nodes:
78-
node.cook(stream=True)
79-
if node.error():
80-
break
75+
def update_stream(self, forceCook=False):
76+
if not forceCook:
77+
if not self._autoCook or not self.needCook:
78+
return
79+
if self.graph is not None and not self.graph.auto_update:
80+
return
81+
update_node_down_stream(self)
8182

8283
def cookNextNode(self):
8384
nodes = []
@@ -124,24 +125,14 @@ def when_disabled(self):
124125
for index, out_port in enumerate(self.output_ports()):
125126
self.set_property(out_port.name(), self.getInputData(min(index, num)))
126127

127-
def cook(self, forceCook=False, stream=False):
128-
if self.disabled() and stream:
129-
self.when_disabled()
130-
return
131-
if not forceCook:
132-
if not self._autoCook or not self.needCook:
133-
return
134-
if self.graph is not None and not self.graph.auto_update:
135-
return
136-
128+
def cook(self):
137129
_tmp = self._autoCook
138130
self._autoCook = False
139131

140132
if self.error():
141133
self._close_error()
142134

143135
_start_time = time.time()
144-
145136
try:
146137
self.run()
147138
except:
@@ -156,15 +147,12 @@ def cook(self, forceCook=False, stream=False):
156147

157148
self.cooked.emit()
158149

159-
if not stream:
160-
self.update_streams()
161-
162150
def run(self):
163151
pass
164152

165153
def on_input_connected(self, to_port, from_port):
166154
if self.checkPortType(to_port, from_port):
167-
self.cook()
155+
self.update_stream()
168156
else:
169157
self.needCook = False
170158
to_port.disconnect_from(from_port)
@@ -173,7 +161,7 @@ def on_input_disconnected(self, to_port, from_port):
173161
if not self.needCook:
174162
self.needCook = True
175163
return
176-
self.cook()
164+
self.update_stream()
177165

178166
def checkPortType(self, to_port, from_port):
179167
# None type port can connect with any other type port
@@ -193,7 +181,7 @@ def set_property(self, name, value):
193181
super(AutoNode, self).set_property(name, value)
194182
self.set_port_type(name, type(value).__name__)
195183
if name in self.model.custom_properties.keys():
196-
self.cook()
184+
self.update_stream()
197185

198186
def set_port_type(self, port, data_type: str):
199187
current_port = None
@@ -253,8 +241,9 @@ def set_disabled(self, mode=False):
253241
if mode:
254242
self.when_disabled()
255243
if self.graph is None or self.graph.auto_update:
256-
self.update_streams()
257-
self.cook()
244+
self.update_stream()
245+
else:
246+
self.update_stream()
258247

259248
def _close_error(self):
260249
self._error = False

example_auto_nodes/node_base/subgraph_node.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from .auto_node import AutoNode
22
from NodeGraphQt import SubGraph
33
import json
4-
from NodeGraphQt.base.utils import topological_sort
4+
from NodeGraphQt import topological_sort_by_down
55

66

77
class SubGraphNode(AutoNode, SubGraph):
@@ -121,12 +121,14 @@ def run(self):
121121
else:
122122
start_nodes = self.sub_graph_input_nodes
123123

124-
nodes = topological_sort(start_nodes=start_nodes)
124+
nodes = topological_sort_by_down(start_nodes=start_nodes)
125125

126126
for node in nodes:
127-
node.cook(stream=True)
127+
if node.disabled():
128+
node.when_disabled()
129+
else:
130+
node.cook()
128131
if node.error():
129-
self.error(node.view.toolTip())
130132
break
131133

132134
def delete(self):

0 commit comments

Comments
 (0)