Skip to content

Commit 949a737

Browse files
author
Leandro Inocencio
committed
Add example of math node with a basic evaluation system.
1 parent f43b915 commit 949a737

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed

example_math_nodes.py

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
import os
4+
import inspect
5+
from functools import partial
6+
7+
from NodeGraphQt import (NodeGraph,
8+
BaseNode,
9+
setup_context_menu)
10+
from NodeGraphQt import QtWidgets, QtCore, PropertiesBinWidget, NodeTreeWidget
11+
12+
13+
def update_streams(node):
14+
"""
15+
Update all nodes joined by pipes
16+
"""
17+
nodes = []
18+
trash = []
19+
20+
for port, nodeList in node.connected_output_nodes().items():
21+
nodes.extend(nodeList)
22+
23+
while nodes:
24+
node = nodes.pop()
25+
if node not in trash:
26+
trash.append(node)
27+
28+
for port, nodeList in node.connected_output_nodes().items():
29+
nodes.extend(nodeList)
30+
31+
if not nodes:
32+
try:
33+
node.run()
34+
except Exception as error:
35+
print("Error Update Streams: %s" % str(error))
36+
37+
38+
class DataInputNode(BaseNode):
39+
"""
40+
Input node data.
41+
"""
42+
43+
__identifier__ = 'com.chantasticvfx'
44+
NODE_NAME = 'Input Numbers'
45+
46+
def __init__(self):
47+
super(DataInputNode, self).__init__()
48+
self.output = self.add_output('out')
49+
self.add_text_input('out', 'Data Input', text='4', tab='widgets')
50+
51+
def run(self):
52+
return
53+
54+
55+
def add(a, b):
56+
return a + b
57+
58+
59+
def sub(a, b):
60+
return a - b
61+
62+
63+
def mul(a, b):
64+
return a * b
65+
66+
67+
def div(a, b):
68+
return a / b
69+
70+
71+
# create a dict with all function
72+
funcs = {'add': add, 'sub': sub, 'mul': mul, 'div': div}
73+
74+
75+
class MathFunctionsNode(BaseNode):
76+
"""
77+
Math functions node.
78+
79+
"""
80+
81+
# set a unique node identifier.
82+
__identifier__ = 'com.chantasticvfx'
83+
84+
# set the initial default node name.
85+
NODE_NAME = 'Functions node'
86+
87+
def __init__(self):
88+
super(MathFunctionsNode, self).__init__()
89+
self.set_color(25, 58, 51)
90+
self.add_combo_menu('functions', 'Functions',
91+
items=funcs.keys(), tab='widgets')
92+
# switch math function type
93+
self.view.widgets['functions'].value_changed.connect(self.addFunction)
94+
self.set_property('functions', 'add')
95+
self.view.widgets['functions'].value_changed.connect(
96+
partial(update_streams, self))
97+
self.output = self.add_output('output')
98+
self.create_property(self.output.name(), None)
99+
100+
def addFunction(self, prop, func):
101+
"""
102+
Create inputs based on math functions arguments.
103+
"""
104+
self.func = funcs[func] # add, sub, mul, div
105+
106+
dataFunc = inspect.getargspec(self.func)
107+
108+
for arg in dataFunc.args:
109+
if not self.has_property(arg):
110+
self.add_input(arg)
111+
self.create_property(arg, None)
112+
113+
def run(self):
114+
"""
115+
Evaluate all entries, pass them as arguments of the
116+
chosen mathematical function.
117+
"""
118+
for to_port in self.input_ports():
119+
from_ports = to_port.connected_ports()
120+
if not from_ports:
121+
raise Exception('Port %s not connected!' % to_port.name())
122+
123+
for from_port in from_ports:
124+
from_port.node().run()
125+
data = from_port.node().get_property(from_port.name())
126+
self.set_property(to_port.name(), int(data))
127+
128+
try:
129+
# Execute math function with arguments.
130+
output = self.func(*[self.get_property(prop)
131+
for prop in self.properties()['inputs']])
132+
self.set_property('output', output)
133+
except KeyError as error:
134+
print("An input is missing! %s" % str(error))
135+
except TypeError as error:
136+
print("Error evaluating function: %s" % str(error))
137+
138+
def on_input_connected(self, to_port, from_port):
139+
"""Override node callback method."""
140+
self.set_property(to_port.name(), from_port.node().run())
141+
update_streams(self)
142+
143+
def on_input_disconnected(self, to_port, from_port):
144+
"""Override node callback method."""
145+
self.set_property('output', None)
146+
update_streams(self)
147+
148+
149+
class DataViewerNode(BaseNode):
150+
__identifier__ = 'com.chantasticvfx'
151+
NODE_NAME = 'Result View'
152+
153+
def __init__(self):
154+
super(DataViewerNode, self).__init__()
155+
self.input = self.add_input('in data')
156+
self.add_text_input('data', 'Data Viewer', tab='widgets')
157+
158+
def run(self):
159+
"""Evaluate input to show it."""
160+
for source in self.input.connected_ports():
161+
from_node = source.node()
162+
try:
163+
from_node.run()
164+
except Exception as error:
165+
print("%s no inputs connected: %s" % (from_node.name(), str(error)))
166+
self.set_property('data', None)
167+
return
168+
value = from_node.get_property(source.name())
169+
self.set_property('data', str(value))
170+
171+
def on_input_connected(self, to_port, from_port):
172+
"""Override node callback method"""
173+
self.run()
174+
175+
def on_input_disconnected(self, to_port, from_port):
176+
"""Override node callback method"""
177+
self.set_property('data', None)
178+
179+
180+
if __name__ == '__main__':
181+
app = QtWidgets.QApplication([])
182+
183+
# create node graph.
184+
graph = NodeGraph()
185+
186+
# set up default menu and commands.
187+
setup_context_menu(graph)
188+
189+
# widget used for the node graph.
190+
graph_widget = graph.widget
191+
graph_widget.resize(1100, 800)
192+
graph_widget.show()
193+
194+
# show the properties bin when a node is "double clicked" in the graph.
195+
properties_bin = PropertiesBinWidget(node_graph=graph)
196+
properties_bin.setWindowFlags(QtCore.Qt.Tool)
197+
198+
def show_prop_bin(node):
199+
if not properties_bin.isVisible():
200+
properties_bin.show()
201+
graph.node_double_clicked.connect(show_prop_bin)
202+
203+
# show the nodes list when a node is "double clicked" in the graph.
204+
node_tree = NodeTreeWidget(node_graph=graph)
205+
206+
def show_nodes_list(node):
207+
if not node_tree.isVisible():
208+
node_tree.update()
209+
node_tree.show()
210+
graph.node_double_clicked.connect(show_nodes_list)
211+
212+
# registered nodes.
213+
reg_nodes = [DataInputNode, DataViewerNode, MathFunctionsNode]
214+
215+
for n in reg_nodes:
216+
graph.register_node(n)
217+
218+
my_node = graph.create_node('com.chantasticvfx.MathFunctionsNode',
219+
name='Math Functions A',
220+
color='#0a1e20',
221+
text_color='#feab20',
222+
pos=[-250, 70])
223+
224+
my_node = graph.create_node('com.chantasticvfx.MathFunctionsNode',
225+
name='Math Functions B',
226+
color='#0a1e20',
227+
text_color='#feab20',
228+
pos=[-250, -70])
229+
230+
my_node = graph.create_node('com.chantasticvfx.MathFunctionsNode',
231+
name='Math Functions C',
232+
color='#0a1e20',
233+
text_color='#feab20',
234+
pos=[0, 0])
235+
236+
inputANode = graph.create_node('com.chantasticvfx.DataInputNode',
237+
name='Input A',
238+
pos=[-500, -50])
239+
240+
inputBNode = graph.create_node('com.chantasticvfx.DataInputNode',
241+
name='Input B',
242+
pos=[-500, 50])
243+
244+
outputNode = graph.create_node('com.chantasticvfx.DataViewerNode',
245+
name='Output',
246+
pos=[250, 0])
247+
248+
app.exec_()

0 commit comments

Comments
 (0)