diff --git a/NodeGraphQt/base/node.py b/NodeGraphQt/base/node.py index 854cc727..55c6fe5a 100644 --- a/NodeGraphQt/base/node.py +++ b/NodeGraphQt/base/node.py @@ -853,7 +853,7 @@ def update_streams(self, *args): for port, nodeList in node.connected_output_nodes().items(): nodes.extend(nodeList) - if not nodes: + if not node.connected_output_nodes(): try: node.run() except Exception as error: diff --git a/example.nodes b/example.nodes new file mode 100644 index 00000000..104ba0b8 --- /dev/null +++ b/example.nodes @@ -0,0 +1,175 @@ +{ + "nodes":{ + "0x15095e08":{ + "type_":"Viewers.DataViewerNode", + "icon":null, + "name":"Output", + "color":[ + 13, + 18, + 23, + 255 + ], + "border_color":[ + 74, + 84, + 85, + 255 + ], + "text_color":[ + 255, + 255, + 255, + 180 + ], + "disabled":false, + "selected":true, + "width":170, + "height":91.0, + "pos":[ + 149.0, + -146.0 + ], + "custom":{ + "data":"" + } + }, + "0x15126848":{ + "type_":"Inputs.BoolInputNode", + "icon":null, + "name":"Bool", + "color":[ + 13, + 18, + 23, + 255 + ], + "border_color":[ + 74, + 84, + 85, + 255 + ], + "text_color":[ + 255, + 255, + 255, + 180 + ], + "disabled":false, + "selected":true, + "width":170, + "height":103.0, + "pos":[ + -413.0, + -212.0 + ], + "custom":{ + "out":true, + "combo":"True" + } + }, + "0x15140308":{ + "type_":"Logics.BooleanNode", + "icon":null, + "name":"Boolean", + "color":[ + 13, + 18, + 23, + 255 + ], + "border_color":[ + 74, + 84, + 85, + 255 + ], + "text_color":[ + 255, + 255, + 255, + 180 + ], + "disabled":false, + "selected":true, + "width":170, + "height":103.0, + "pos":[ + -143.0, + -149.0 + ], + "custom":{ + "out":null, + "funcs":"and" + } + }, + "0x1514a4c8":{ + "type_":"Inputs.BoolInputNode", + "icon":null, + "name":"Bool 1", + "color":[ + 13, + 18, + 23, + 255 + ], + "border_color":[ + 74, + 84, + 85, + 255 + ], + "text_color":[ + 255, + 255, + 255, + 180 + ], + "disabled":false, + "selected":true, + "width":170, + "height":103.0, + "pos":[ + -411.25, + -61.5 + ], + "custom":{ + "out":true, + "combo":"True" + } + } + }, + "connections":[ + { + "in":[ + "0x15095e08", + "data" + ], + "out":[ + "0x15140308", + "out" + ] + }, + { + "out":[ + "0x15126848", + "out" + ], + "in":[ + "0x15140308", + "a" + ] + }, + { + "in":[ + "0x15140308", + "b" + ], + "out":[ + "0x1514a4c8", + "out" + ] + } + ] +} \ No newline at end of file diff --git a/example_auto_nodes/module_nodes.py b/example_auto_nodes/module_nodes.py index 7531db13..915266a6 100644 --- a/example_auto_nodes/module_nodes.py +++ b/example_auto_nodes/module_nodes.py @@ -2,7 +2,6 @@ get_functions_from_module, get_functions_from_type) -import math import os import sys import random @@ -10,19 +9,11 @@ import numpydoc.docscrape import inspect -# add basic math functions to math library -math.add = lambda x, y: x + y -math.sub = lambda x, y: x - y -math.mul = lambda x, y: x * y -math.div = lambda x, y: x / y - - -def get_value(self,index): - return self[index] - - -def get_item(self,index): - return self.items()[index] +import example_auto_nodes.wrappers.math as _math +import example_auto_nodes.wrappers.list as _list +import example_auto_nodes.wrappers.dict as _dict +import example_auto_nodes.wrappers.str as _str +import example_auto_nodes.wrappers.tuple as _tuple class MathModuleNode(ModuleNode): @@ -36,7 +27,7 @@ class MathModuleNode(ModuleNode): # set the initial default node name. NODE_NAME = 'math module' - module_functions = get_functions_from_type(math) + module_functions = get_functions_from_type(_math) def __init__(self): super(MathModuleNode, self).__init__(float, float) @@ -162,8 +153,8 @@ class StringFunctionsNode(ModuleNode): # set the initial default node name. NODE_NAME = 'String Functions' - module_functions = get_functions_from_type(str) - module_functions["get value"] = get_value + module_functions = get_functions_from_type(_str) + def __init__(self): super(StringFunctionsNode, self).__init__() @@ -180,8 +171,7 @@ class ListFunctionsNode(ModuleNode): # set the initial default node name. NODE_NAME = 'List Functions' - module_functions = get_functions_from_type(list) - module_functions["get value"] = get_value + module_functions = get_functions_from_type(_list) def __init__(self): super(ListFunctionsNode, self).__init__() @@ -198,8 +188,7 @@ class DictFunctionsNode(ModuleNode): # set the initial default node name. NODE_NAME = 'Dict Functions' - module_functions = get_functions_from_type(list) - module_functions["get item"] = get_item + module_functions = get_functions_from_type(_dict) def __init__(self): super(DictFunctionsNode, self).__init__() @@ -216,8 +205,7 @@ class TupleFunctionsNode(ModuleNode): # set the initial default node name. NODE_NAME = 'Tuple Functions' - module_functions = get_functions_from_type(tuple) - module_functions["get value"] = get_value + module_functions = get_functions_from_type(_tuple) def __init__(self): super(TupleFunctionsNode, self).__init__() diff --git a/example_auto_nodes/wrappers/__init__.py b/example_auto_nodes/wrappers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/example_auto_nodes/wrappers/dict.py b/example_auto_nodes/wrappers/dict.py new file mode 100644 index 00000000..5775e3fa --- /dev/null +++ b/example_auto_nodes/wrappers/dict.py @@ -0,0 +1,45 @@ +def clear(self): + self.clear() + return self + + +def copy(self): + return self.copy() + + +def fromkeys(self, iterable, value=None): + return self.fromkeys(iterable, value) + + +def get(self, key, default=None): + return self.get(key, default) + + +def items(self): + return list(self.items()) + + +def keys(self): + return list(self.keys()) + + +def pop(self, index): + return self.pop(index) + + +def popitem(self): + return self.popitem() + + +def setdefault(self, key, default=None): + self.setdefault(key, default) + return self + + +def update(self, e): + self.update(e) + return self + + +def values(self): + return list(self.values()) diff --git a/example_auto_nodes/wrappers/list.py b/example_auto_nodes/wrappers/list.py new file mode 100644 index 00000000..50a8daae --- /dev/null +++ b/example_auto_nodes/wrappers/list.py @@ -0,0 +1,52 @@ +def append(self, object): + self.append(object) + return self + + +def clear(self): + self.clear() + return self + + +def copy(self): + return self.copy() + + +def count(self, value): + return self.count(value) + + +def extend(self, iterable): + self.extend(iterable) + return self + +def index(self, value): + return self.index(value) + + +def insert(self, index, obj): + self.insert(index, obj) + return self + + +def pop(self, index=-1): + return self.pop(index) + + +def remove(self, value): + return self.remove(value) + + +def reverse(self): + self.reverse() + return self + + +def sort(self): + self.sort() + return self + + +# custom functions +def get(self, index): + return self.__getitem__(index) diff --git a/example_auto_nodes/wrappers/math.py b/example_auto_nodes/wrappers/math.py new file mode 100644 index 00000000..c0e4cc66 --- /dev/null +++ b/example_auto_nodes/wrappers/math.py @@ -0,0 +1,52 @@ +from math import * + +_pi = pi +_e = e +_tau = tau +_inf = inf +_nan = nan + + +# add contants as functions + +def pi(): + print('pi', _pi) + return _pi + + +def e(): + print('e', _e) + return _e + + +def tau(): + print('tau', _tau) + return _tau + + +def inf(): + print('inf', _inf) + return _inf + + +def nan(): + print('nan', _nan) + return _nan + + +# add basic math functions to math library + +def add(x, y): + return x + y + + +def sub(x, y): + return x - y + + +def mul(x, y): + return x * y + + +def div(x, y): + return x / y diff --git a/example_auto_nodes/wrappers/str.py b/example_auto_nodes/wrappers/str.py new file mode 100644 index 00000000..8c23726d --- /dev/null +++ b/example_auto_nodes/wrappers/str.py @@ -0,0 +1,171 @@ + +def capitalize(self): + return self.capitalize() + + +def casefold(self): + return self.casefold() + + +def center(self, width, fillchar=" "): + return self.center(width, fillchar) + + +def count(self, sub, start=0, end=-1): + return self.count(sub, start, end) + + +def encode(self, encoding, errors): + return self.encode(encoding, errors) + + +def endswith(self, suffix, start=0, end=-1): + return self.endswith(suffix, start, end) + + +def expandtabs(self, tabsize=8): + return self.expandtabs(tabsize) + + +def find(self, sub, start=0, end=-1): + return self.find(sub, start, end) + + +def format(self, *args, **kwargs): + return self.format(*args, **kwargs) + + +def index(self, sub, start=0, end=-1): + return self.index(sub, start, end) + + +def isalnum(self): + return self.isalnum() + + +def isalpha(self): + return self.isalpha() + + +def isascii(self): + return self.isascii() + + +def isdecimal(self): + return self.isdecimal() + + +def isdigit(self): + return self.isdigit() + + +def isidentifier(self): + return self.isidentifier() + + +def islower(self): + return self.islower() + + +def isnumeric(self): + return self.isnumeric() + + +def isprintable(self): + return self.isprintable() + + +def isspace(self): + return self.isspace() + + +def istitle(self): + return self.istitle() + + +def isupper(self): + return self.isupper() + + +def join(self, iterable): + return self.join(iterable) + + +def ljust(self, width, fillchar=" "): + return self.ljust(width, fillchar) + + +def lower(self): + return self.lower() + + +def lstrip(self, chars=" "): + return self.lstrip(chars) + + +def partition(self, sep): + return self.partition(sep) + + +def replace(self, old, new, count=-1): + return self.replace(old, new, count) + + +def rfind(self, sub, start=0, end=-1): + return self.rfind(sub, start, end) + + +def rindex(self, sub, start=0, end=-1): + return self.rindex(sub, start, end) + + +def rjust(self, width, fillchar=" "): + return self.rjust(width, fillchar) + + +def rpartition(self, sep): + return self.rpartition(sep) + + +def rsplit(self, sep, maxsplit=-1): + return self.rsplit(sep, maxsplit) + + +def rstrip(self, chars=" "): + return self.rstrip(chars) + + +def split(self, sep, maxsplit=-1): + return self.split(sep, maxsplit) + + +def splitlines(self, keepends): + return self.splitlines(keepends) + + +def startswith(self, prefix, start=0, end=-1): + return self.startswith(prefix, start, end) + + +def strip(self, chars=" "): + return self.strip(chars) + + +def swapcase(self): + return self.swapcase() + + +def title(self): + return self.title() + + +def translate(self, table): + return self.translate(table) + + +def upper(self): + return self.upper() + + +def zfill(self, width): + return self.zfill(width) diff --git a/example_auto_nodes/wrappers/tuple.py b/example_auto_nodes/wrappers/tuple.py new file mode 100644 index 00000000..1a8af60c --- /dev/null +++ b/example_auto_nodes/wrappers/tuple.py @@ -0,0 +1,11 @@ +def count(self, value): + return self.count(value) + + +def index(self, value): + return self.index(value) + + +# custom functions +def get(self, index): + return self.__getitem__(index) diff --git a/example_math_nodes.py b/example_math_nodes.py index efb23ac2..c6206380 100644 --- a/example_math_nodes.py +++ b/example_math_nodes.py @@ -40,37 +40,7 @@ def show_nodes_list(node): graph.node_double_clicked.connect(show_nodes_list) # registered nodes. - for n in Nodes: - graph.register_node(n) - - mathNodeA = graph.create_node('Math.MathFunctionsNode', - name='Math Functions A', - color='#0a1e20', - text_color='#feab20', - pos=[-250, 70]) - - mathNodeB = graph.create_node('Math.MathFunctionsNode', - name='Math Functions B', - color='#0a1e20', - text_color='#feab20', - pos=[-250, -70]) - - mathNodeC = graph.create_node('Math.MathFunctionsNode', - name='Math Functions C', - color='#0a1e20', - text_color='#feab20', - pos=[0, 0]) - - inputANode = graph.create_node('Inputs.DataInputNode', - name='Input A', - pos=[-500, -50]) - - inputBNode = graph.create_node('Inputs.DataInputNode', - name='Input B', - pos=[-500, 50]) - - outputNode = graph.create_node('Viewers.DataViewerNode', - name='Output', - pos=[250, 0]) + [graph.register_node(n) for n in Nodes] + graph.load_session('example.nodes') app.exec_() diff --git a/example_nodes/input_nodes.py b/example_nodes/input_nodes.py index b1a4c733..1bc7cf53 100644 --- a/example_nodes/input_nodes.py +++ b/example_nodes/input_nodes.py @@ -27,7 +27,7 @@ class TickTimeNode(BaseNode): def __init__(self): super(TickTimeNode, self).__init__() self.add_output('out') - self.add_text_input('out', 'Data Input', text='0', tab='widgets') + self.add_text_input('out', 'Ticks', text='0', tab='widgets') self.view.widgets['out'].value_changed.connect(self.update_streams) self.timer = QtCore.QTimer() @@ -66,3 +66,22 @@ def run(self): else: print('No existe %s' % path) self.set_property('output', '') + + +class BoolInputNode(BaseNode): + """ + Input Bool data. + """ + + __identifier__ = 'Inputs' + NODE_NAME = 'Bool' + + def __init__(self): + super(BoolInputNode, self).__init__() + self.add_output('out') + self.create_property('out', None) + self.add_combo_menu('combo', 'Bool value', items=['True', 'False'], tab='widgets') + self.view.widgets['combo'].value_changed.connect(self.update_streams) + + def run(self): + self.set_property('out', eval(self.get_property('combo'))) \ No newline at end of file diff --git a/example_nodes/logic_nodes.py b/example_nodes/logic_nodes.py new file mode 100644 index 00000000..6ea49a30 --- /dev/null +++ b/example_nodes/logic_nodes.py @@ -0,0 +1,110 @@ +from NodeGraphQt import BaseNode, QtCore + + +class IfNode(BaseNode): + """ + if node. + """ + + __identifier__ = 'Logics' + NODE_NAME = 'If' + + def __init__(self): + super(IfNode, self).__init__() + self.condition = self.add_input('condition') + self._then = self.add_input('then') + self._else = self.add_input('else') + self.add_output('out') + self.create_property('out', None) + + def run(self): + for port in self.condition.connected_ports(): + port.node().run() + if port.node().get_property(port.name()): + result = self._then + else: + result = self._else + + for port in result.connected_ports(): + port.node().run() + self.set_property('out', port.node().get_property(port.name())) + break + + def on_input_connected(self, to_port, from_port): + """Override node callback method.""" + from_port.node().run() + self.set_property(to_port.name(), from_port.node().get_property(from_port.name())) + self.update_streams() + + def on_input_disconnected(self, to_port, from_port): + """Override node callback method.""" + self.set_property('out', None) + + +class BooleanNode(BaseNode): + """ + Boolean Logic funtions node. + """ + + __identifier__ = 'Logics' + + NODE_NAME = 'Boolean' + + logics = {'and': 'a and b', + 'or': 'a or b', + 'xor': '(not a and b) or (a and not b)', + 'not': 'not a'} + + def __init__(self): + super(BooleanNode, self).__init__() + self.a = self.add_input('a') + self.b = self.add_input('b') + self.add_output('out') + self.create_property('out', None) + self.add_combo_menu('funcs', + 'Functions', + items=list(self.logics.keys()), + tab='widgets') + + self.func = self.logics['and'] + # switch math function type + self.view.widgets['funcs'].value_changed.connect(self.addFunction) + self.view.widgets['funcs'].value_changed.connect(self.update_streams) + + def addFunction(self, prop, func): + """ + Create inputs based on math functions arguments. + """ + self.func = self.logics[func] + if self.b.visible() and not 'b' in self.func: + self.b.set_visible(False) + elif not self.b.visible(): + self.b.set_visible(True) + + def run(self): + a = None + b = None + + for port in self.a.connected_ports(): + port.node().run() + a = port.node().get_property(port.name()) + + for port in self.b.connected_ports(): + port.node().run() + b = port.node().get_property(port.name()) + + if a is None or (b is None and 'b' in self.func): + raise Exception("No inputs!") + + self.set_property('out', eval(self.func)) + + def on_input_connected(self, to_port, from_port): + """Override node callback method.""" + from_port.node().run() + result = from_port.node().get_property(from_port.name()) + self.set_property(to_port.name(), result) + self.update_streams() + + def on_input_disconnected(self, to_port, from_port): + """Override node callback method.""" + self.set_property('out', None) diff --git a/example_nodes/math_node.py b/example_nodes/math_node.py index 65969ec2..22e867a3 100644 --- a/example_nodes/math_node.py +++ b/example_nodes/math_node.py @@ -1,19 +1,8 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -import math +import example_nodes.wrappers.math as math import inspect from functools import partial - -# add basic math functions to math library -math.add = lambda x, y: x + y -math.sub = lambda x, y: x - y -math.mul = lambda x, y: x * y -math.div = lambda x, y: x / y - -# Transform float to functions -for constant in ['pi', 'e', 'tau', 'inf', 'nan']: - setattr(math, constant, partial(lambda x: x, getattr(math, constant))) - from NodeGraphQt import BaseNode diff --git a/example_nodes/util_nodes.py b/example_nodes/util_nodes.py new file mode 100644 index 00000000..9ff490b5 --- /dev/null +++ b/example_nodes/util_nodes.py @@ -0,0 +1,130 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +import inspect +from NodeGraphQt import BaseNode + + +class ObjectWrapperNode(BaseNode): + """ + Take an object from the input port and wrappe it. + """ + + # set a unique node identifier. + __identifier__ = 'Util' + + # set the initial default node name. + NODE_NAME = 'Object Wrapper' + + def __init__(self): + super(ObjectWrapperNode, self).__init__() + self.selfPort = self.add_input('self') + self.create_property('self', None) + + def buildNode(self): + obj = self.get_property('self') + if obj: + self.set_name(obj.__class__.__name__.capitalize()) + else: + self.set_name('Object Wrapper (None)') + + # switch math function type + if 'methods' not in self.view.widgets: + self.add_combo_menu('methods', + 'Methods', + items=dir(obj), + tab='widgets') + + self.view.widgets['methods'].value_changed.connect( + self.addFunction) + self.view.widgets['methods'].value_changed.connect( + self.update_streams) + self.add_output('output') + self.create_property('output', None) + else: + comboBox = self.view.widgets['methods'].widget + comboBox.clear() + comboBox.addItems(dir(obj)) + + def addFunction(self, prop, func): + """ + Create inputs based on math functions arguments. + """ + self.funcName = func + obj = self.get_property('self') + func = getattr(obj, self.funcName) + dataFunc = inspect.signature(func) + + for arg in dataFunc.args: + if not self.has_property(arg): + inPort = self.add_input(arg) + self.create_property(arg, None) + + for inPort in self._inputs: + if inPort.name() in dataFunc.args: + if not inPort.visible(): + inPort.set_visible(True) + else: + inPort.set_visible(False) + + def getSelf(self): + for from_port in self.selfPort.connected_ports(): + return from_port.node().get_property(from_port.name()) + + def run(self): + """ + Evaluate all entries, pass them as arguments of the + chosen mathematical function. + """ + obj = self.getSelf() + if not obj: + return + + for to_port in self.input_ports(): + if to_port.visible() == False: + continue + + from_ports = to_port.connected_ports() + if not from_ports: + raise Exception('Port %s not connected!' % to_port.name(), + to_port) + + for from_port in from_ports: + if from_port.name() == 'self': + obj = from_port.node().get_property(from_port.name()) + continue + from_port.node().run() + data = from_port.node().get_property(from_port.name()) + self.set_property(to_port.name(), data) + + self.func = getattr(obj, self.funcName) + + try: + # Execute math function with arguments. + data = self.func(*[ + self.get_property(inport.name()) for inport in self._inputs + if inport.visible() and inport.name() != 'self' + ]) + + self.set_property('output', data) + except KeyError as error: + print("An input is missing! %s" % str(error)) + except TypeError as error: + print("Error evaluating function: %s" % str(error)) + + def on_input_connected(self, to_port, from_port): + """Override node callback method.""" + from_port.node().run() + outValue = from_port.node().get_property(from_port.name()) + self.set_property(to_port.name(), outValue) + + if to_port.name() == 'self': + self.buildNode() + self.update_streams() + + def on_input_disconnected(self, to_port, from_port): + """Override node callback method.""" + self.set_property('self', None) + self.set_property('output', None) + comboBox = self.view.widgets['methods'].widget + comboBox.clear() + self.update_streams() diff --git a/example_nodes/wrappers/__init__.py b/example_nodes/wrappers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/example_nodes/wrappers/math.py b/example_nodes/wrappers/math.py new file mode 100644 index 00000000..d2dd475a --- /dev/null +++ b/example_nodes/wrappers/math.py @@ -0,0 +1,53 @@ +from math import * + +_pi = pi +_e = e +_tau = tau +_inf = inf +_nan = nan + +# add contants as functions + + +def pi(): + print('pi', _pi) + return _pi + + +def e(): + print('e', _e) + return _e + + +def tau(): + print('tau', _tau) + return _tau + + +def inf(): + print('inf', _inf) + return _inf + + +def nan(): + print('nan', _nan) + return _nan + + +# add basic math functions to math library + + +def add(x, y): + return x + y + + +def sub(x, y): + return x - y + + +def mul(x, y): + return x * y + + +def div(x, y): + return x / y