diff --git a/NodeGraphQt/constants.py b/NodeGraphQt/constants.py index 9f031e81..59ef50e4 100644 --- a/NodeGraphQt/constants.py +++ b/NodeGraphQt/constants.py @@ -109,7 +109,7 @@ # === ITEM CACHE MODE === # QGraphicsItem.NoCache -# QGraphicsItem.ItemCoordinateCache # QGraphicsItem.DeviceCoordinateCache +# QGraphicsItem.ItemCoordinateCache -ITEM_CACHE_MODE = QtWidgets.QGraphicsItem.ItemCoordinateCache \ No newline at end of file +ITEM_CACHE_MODE = QtWidgets.QGraphicsItem.DeviceCoordinateCache \ No newline at end of file diff --git a/NodeGraphQt/widgets/viewer.py b/NodeGraphQt/widgets/viewer.py index a3770b24..2482bae1 100644 --- a/NodeGraphQt/widgets/viewer.py +++ b/NodeGraphQt/widgets/viewer.py @@ -130,7 +130,7 @@ def _set_viewer_zoom(self, value, sensitivity=None, pos=None): self.scale(scale, scale, pos) def _set_viewer_pan(self, pos_x, pos_y): - speed = self._scene_range.width() * 0.001 + speed = self._scene_range.width() * 0.002 x = -pos_x * speed y = -pos_y * speed self._scene_range.adjust(x, y, x, y) diff --git a/example_auto_nodes.py b/example_auto_nodes.py index 1874ad2c..2f50301c 100644 --- a/example_auto_nodes.py +++ b/example_auto_nodes.py @@ -34,17 +34,19 @@ def GetNodesFromFolder(FolderPath): return nodes -def cook_node(graph,node): +def cook_node(graph, node): node.cook() -def print_functions(graph,node): +def print_functions(graph, node): for func in node.module_functions: print(func) -def toggle_auto_cook(graph,node): + +def toggle_auto_cook(graph, node): node.autoCook = not node.autoCook + if __name__ == '__main__': app = QtWidgets.QApplication([]) diff --git a/example_auto_nodes/basic_nodes.py b/example_auto_nodes/basic_nodes.py index f7da9e77..b731020e 100644 --- a/example_auto_nodes/basic_nodes.py +++ b/example_auto_nodes/basic_nodes.py @@ -1,12 +1,13 @@ from .node_base.auto_node import AutoNode + class FooNode(AutoNode): """ A node class with 2 inputs and 2 outputs. """ # unique node identifier. - __identifier__ = 'com.chantasticvfx' + __identifier__ = 'examples' # initial default node name. NODE_NAME = 'foo node' @@ -21,7 +22,8 @@ def __init__(self): # create node outputs. self.add_output('out A') self.add_output('out B') - self.set_icon("example_auto_nodes/pear.png") + self.set_icon("example_auto_nodes/icons/pear.png") + class BarNode(AutoNode): """ @@ -30,7 +32,7 @@ class BarNode(AutoNode): """ # unique node identifier. - __identifier__ = 'com.chantasticvfx' + __identifier__ = 'examples' # initial default node name. NODE_NAME = 'bar' diff --git a/example_auto_nodes/data_node.py b/example_auto_nodes/data_node.py index 3e00d0a9..3af72062 100644 --- a/example_auto_nodes/data_node.py +++ b/example_auto_nodes/data_node.py @@ -13,22 +13,27 @@ class VectorSplit(AutoNode): def __init__(self): super(VectorSplit, self).__init__() - self.defaultValue = [0.0, 0.0, 0.0] self.add_output('x') - self.create_property("x", self.defaultValue[0]) + self.create_property("x", 0.0) self.add_output('y') - self.create_property("y", self.defaultValue[1]) + self.create_property("y", 0.0) self.add_output('z') - self.create_property("z", self.defaultValue[2]) + self.create_property("z", 0.0) + self.add_output('w') + self.create_property("w", 0.0) - self.add_input("in vector",list) + self.add_input("in vector", list) + self.map = {0: "x", 1: "y", 2: "z", 3: "w"} def run(self): value = self.getInputData(0) - self.set_property("x", value[0]) - self.set_property("y", value[1]) - self.set_property("z", value[2]) + if type(value) is not list: + self.error("Input data not list") + for index, data in enumerate(value): + if index > 3: + return + self.set_property(self.map[index], data) class VectorMaker(AutoNode): @@ -42,17 +47,22 @@ class VectorMaker(AutoNode): def __init__(self): super(VectorMaker, self).__init__() - self.add_output('out') - self.create_property("out", [0, 0, 0]) - - self.add_input("x",float) - self.add_input("y",float) - self.add_input("z",float) + self.add_output('out', list) + self.create_property("out",None) - self.defaultValue = 0.0 + self.add_input("x", float) + self.add_input("y", float) + self.add_input("z", float) + self.add_input("w", float) def run(self): - self.set_property("out", [self.getInputData(0), self.getInputData(1), self.getInputData(2)]) + result = [] + for i in range(4): + data = self.getInputData(i) + if data is not None: + result.append(data) + + self.set_property("out", result) class DataConvect(AutoNode): @@ -67,7 +77,7 @@ def __init__(self): super(DataConvect, self).__init__() self.add_output('out') - self.create_property("out",None) + self.create_property("out", None) self.add_input("in data") items = ["all to int"] diff --git a/example_auto_nodes/icons/numpy.png b/example_auto_nodes/icons/numpy.png new file mode 100644 index 00000000..4812a438 Binary files /dev/null and b/example_auto_nodes/icons/numpy.png differ diff --git a/example_auto_nodes/pear.png b/example_auto_nodes/icons/pear.png similarity index 100% rename from example_auto_nodes/pear.png rename to example_auto_nodes/icons/pear.png diff --git a/example_auto_nodes/input_nodes.py b/example_auto_nodes/input_nodes.py index 716aa931..8164188a 100644 --- a/example_auto_nodes/input_nodes.py +++ b/example_auto_nodes/input_nodes.py @@ -1,8 +1,7 @@ from NodeGraphQt import QtCore from NodeGraphQt.constants import (NODE_PROP_VECTOR2, NODE_PROP_VECTOR3, - NODE_PROP_VECTOR4, - NODE_PROP_SLIDER) + NODE_PROP_VECTOR4) from .node_base.auto_node import AutoNode import os @@ -19,7 +18,7 @@ class FloatInputNode(AutoNode): def __init__(self): super(FloatInputNode, self).__init__() self.output = self.add_output('out', float) - self.add_float_input('out', 'Float Value', value=0.0,range=(-10,10)) + self.add_float_input('out', 'Float Value', value=0.0, range=(-10, 10)) class IntInputNode(AutoNode): @@ -44,7 +43,7 @@ def __init__(self): super(Vector2InputNode, self).__init__() self.output = self.add_output('out', list) self.create_property( - "out", [0, 0], widget_type=NODE_PROP_VECTOR2) + "out", [0.0, 0.0], widget_type=NODE_PROP_VECTOR2) class Vector3InputNode(AutoNode): @@ -55,7 +54,7 @@ def __init__(self): super(Vector3InputNode, self).__init__() self.output = self.add_output('out', list) self.create_property( - "out", [0, 0, 0], widget_type=NODE_PROP_VECTOR3) + "out", [0.0, 0.0, 0.0], widget_type=NODE_PROP_VECTOR3) class Vector4InputNode(AutoNode): @@ -66,7 +65,7 @@ def __init__(self): super(Vector4InputNode, self).__init__() self.output = self.add_output('out', list) self.create_property( - "out", [0, 0, 0, 0], widget_type=NODE_PROP_VECTOR4) + "out", [0.0, 0.0, 0.0, 0.0], widget_type=NODE_PROP_VECTOR4) class TickTimeNode(AutoNode): @@ -106,7 +105,6 @@ def __init__(self): self.add_output('file content', str) self.create_property('file content', "") self.add_output('file path', str) - self.add_file_input('file path', 'File Path') def run(self): @@ -119,7 +117,7 @@ def run(self): except Exception as e: self.error(e) else: - self.error('No existe %s' % path) + self.error('No exist %s' % path) self.set_property('file content', '') @@ -142,3 +140,21 @@ def __init__(self): # create QLineEdit text input widget. self.add_text_input('out', 'Text Input') + + +class BoolInputNode(AutoNode): + """ + Input Bool data. + """ + + __identifier__ = 'Inputs' + NODE_NAME = 'Bool' + + def __init__(self): + super(BoolInputNode, self).__init__() + self.add_output('out', bool) + self.create_property('out', True) + self.add_combo_menu('combo', 'Bool value', items=['True', 'False']) + + def run(self): + self.set_property('out', eval(self.get_property('combo'))) diff --git a/example_auto_nodes/logic_nodes.py b/example_auto_nodes/logic_nodes.py new file mode 100644 index 00000000..ee85b51a --- /dev/null +++ b/example_auto_nodes/logic_nodes.py @@ -0,0 +1,77 @@ +from .basic_nodes import AutoNode + + +class IfNode(AutoNode): + """ + 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): + if self.getInputData(self.condition): + result = self.getInputData(self._then) + else: + result = self.getInputData(self._else) + + self.set_property('out', result) + + +class BooleanNode(AutoNode): + """ + 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', bool) + self.b = self.add_input('b', bool) + self.add_output('out', bool) + 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) + + 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) + self.cook() + + def run(self): + a = self.getInputData(self.a) + b = self.getInputData(self.b) + + if a is None or (b is None and 'b' in self.func): + self.error("No inputs!") + return + + self.set_property('out', eval(self.func)) diff --git a/example_auto_nodes/math_node.py b/example_auto_nodes/math_node.py deleted file mode 100644 index 84b245d1..00000000 --- a/example_auto_nodes/math_node.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -import 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 -from .node_base.auto_node import AutoNode - -class MathFunctionsNode(AutoNode): - """ - Math functions node. - """ - - # set a unique node identifier. - __identifier__ = 'Math' - - # set the initial default node name. - NODE_NAME = 'Math Functions' - - mathFuncs = [func for func in dir(math) if not func.startswith('_')] - - def __init__(self): - super(MathFunctionsNode, self).__init__() - self.set_color(25, 58, 51) - self.add_combo_menu('funcs', 'Functions', items=self.mathFuncs, - tab='widgets') - - self.defaultValue = 0.0 - # switch math function type - self.view.widgets['funcs'].value_changed.connect(self.addFunction) - self.view.widgets['funcs'].value_changed.connect(self.cook) - self.add_output('output') - self.create_property('output', None) - - self.view.widgets['funcs'].widget.setCurrentIndex(2) - - def addFunction(self, prop, func): - """ - Create inputs based on math functions arguments. - """ - self.func = getattr(math, func) - dataFunc = inspect.getfullargspec(self.func) - - for arg in dataFunc.args: - if arg not in self.inputs().keys(): - inPort = self.add_input(arg,float) - - for inPort in self.input_ports(): - if inPort.name() in dataFunc.args: - if not inPort.visible(): - inPort.set_visible(True) - else: - inPort.set_visible(False) - - def run(self): - """ - Evaluate all entries, pass them as arguments of the - chosen mathematical function. - """ - args = [] - for port in self.input_ports(): - if not port.visible(): - continue - data = self.getInputData(port) - if data and (type(data) is float or type(data) is int): - args.append(data) - else: - args.append(0) - try: - # Execute math function with arguments. - data = self.func(*args) - - self.set_property('output', data) - except KeyError as error: - self.error("An input is missing! %s" % str(error)) - except TypeError as error: - self.error("Error evaluating function: %s" % str(error)) - except ValueError as error: - self.error("Error input data type: %s" % str(error)) - except Exception as error: - self.error("Error : %s" % str(error)) - -class VectorValue(AutoNode): - """ - Create a basic vector data - """ - - __identifier__ = 'Math' - NODE_NAME = 'Vector' - - def __init__(self): - super(VectorValue, self).__init__() - value = [0.0, 0.0, 0.0] - - self.add_output('out',list) - self.create_property("out", value) - - self.add_float_input('0', 'X', value=value[0], tab='widgets') - self.view.widgets['0'].value_changed.connect(lambda: self.updateValue(0)) - - self.add_float_input('1', 'Y', value=value[1], tab='widgets') - self.view.widgets['1'].value_changed.connect(lambda: self.updateValue(1)) - - self.add_float_input('2', 'Z', value=value[2], tab='widgets') - self.view.widgets['2'].value_changed.connect(lambda: self.updateValue(2)) - - self.defaultValue = value - - def updateValue(self, index): - self.get_property("out")[index] = self.get_property(str(index)) - self.cook() - - -class VectorSplit(AutoNode): - """ - Splict a vector to x,y,z - """ - - __identifier__ = 'Math' - NODE_NAME = 'Vector Split' - - def __init__(self): - super(VectorSplit, self).__init__() - self.defaultValue = [0.0, 0.0, 0.0] - - self.add_output('x') - self.create_property("x", self.defaultValue[0]) - self.add_output('y') - self.create_property("y", self.defaultValue[1]) - self.add_output('z') - self.create_property("z", self.defaultValue[2]) - - self.add_input("in vector",list) - - def run(self): - value = self.getInputData(0) - self.set_property("x", value[0]) - self.set_property("y", value[1]) - self.set_property("z", value[2]) - - -class VectorMaker(AutoNode): - """ - Create a vector by three float value - """ - - __identifier__ = 'Math' - NODE_NAME = 'Vector Maker' - - def __init__(self): - super(VectorMaker, self).__init__() - - self.add_output('out') - self.create_property("out", [0, 0, 0]) - - self.add_input("x",float) - self.add_input("y",float) - self.add_input("z",float) - - self.defaultValue = 0.0 - - def run(self): - self.set_property("out", [self.getInputData(0), self.getInputData(1), self.getInputData(2)]) diff --git a/example_auto_nodes/module_nodes.py b/example_auto_nodes/module_nodes.py index 915266a6..ce9994e3 100644 --- a/example_auto_nodes/module_nodes.py +++ b/example_auto_nodes/module_nodes.py @@ -9,11 +9,11 @@ import numpydoc.docscrape import inspect -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 +from .wrappers import math as _math +from .wrappers import list as _list +from .wrappers import dict as _dict +from .wrappers import str as _str +from .wrappers import tuple as _tuple class MathModuleNode(ModuleNode): @@ -103,6 +103,7 @@ class numpyModuleNode(ModuleNode): def __init__(self): super(numpyModuleNode, self).__init__() + self.set_icon("example_auto_nodes/icons/numpy.png") def is_function(self, obj): result = super(numpyModuleNode, self).is_function(obj) diff --git a/example_auto_nodes/node_base/auto_node.py b/example_auto_nodes/node_base/auto_node.py index 7cf1b517..fd0aeffc 100644 --- a/example_auto_nodes/node_base/auto_node.py +++ b/example_auto_nodes/node_base/auto_node.py @@ -1,9 +1,11 @@ from NodeGraphQt.base.node import BaseNode from NodeGraphQt.base.port import Port from NodeGraphQt.constants import NODE_PROP +from NodeGraphQt import QtCore import random import copy + def rand_color(seed_type): seed = id(seed_type) random.seed(seed + 10) @@ -15,9 +17,12 @@ def rand_color(seed_type): return (r, g, b, 255) -class AutoNode(BaseNode): +class AutoNode(BaseNode,QtCore.QObject): + cooked = QtCore.Signal() + def __init__(self, defaultInputType=None, defaultOutputType=None): super(AutoNode, self).__init__() + QtCore.QObject.__init__(self) self.needCook = True self._autoCook = True self._error = False @@ -52,6 +57,11 @@ def cookNextNode(self): n.cook() def getInputData(self, port): + # get input data by input Port,the type of "port" can be : + # int : Port index + # str : Port name + # Port : Port object + if type(port) is int: to_port = self.input(port) elif type(port) is str: @@ -99,18 +109,18 @@ def cook(self, forceCook=False): if self.error(): return + self.cooked.emit() self.cookNextNode() def run(self): pass - # print("RUN {} Node".format(self.name())) def on_input_connected(self, to_port, from_port): if self.checkPortType(to_port, from_port): self.cook() else: self.needCook = False - self.graph._on_connection_changed([(from_port.view, to_port.view)], []) + to_port.disconnect_from(from_port) def on_input_disconnected(self, to_port, from_port): if not self.needCook: @@ -124,6 +134,9 @@ def set_disabled(self, mode=False): self.cook() def checkPortType(self, to_port, from_port): + # None type port can connect with any other type port + # types in self.matchTypes can connect with each other + if hasattr(to_port, "DataType") and hasattr(from_port, "DataType"): if to_port.DataType is not from_port.DataType: for types in self.matchTypes: @@ -139,7 +152,6 @@ def set_property(self, name, value): if name in self.model.custom_properties.keys(): self.cook() - def set_port_type(self, port, value_type): current_port = None diff --git a/example_auto_nodes/node_base/module_node.py b/example_auto_nodes/node_base/module_node.py index cca8571a..2d80aba3 100644 --- a/example_auto_nodes/node_base/module_node.py +++ b/example_auto_nodes/node_base/module_node.py @@ -46,7 +46,6 @@ class ModuleNode(AutoNode): def __init__(self,defaultInputType=None,defaultOutputType=None): super(ModuleNode, self).__init__(defaultInputType,defaultOutputType) - self.set_color(25, 58, 51) self.add_combo_menu('funcs', 'Functions', items=list(self.module_functions.keys())) # switch math function type diff --git a/example_auto_nodes/viewer_nodes.py b/example_auto_nodes/viewer_nodes.py index 26bbf89d..775e3172 100644 --- a/example_auto_nodes/viewer_nodes.py +++ b/example_auto_nodes/viewer_nodes.py @@ -1,6 +1,5 @@ from .node_base.auto_node import AutoNode -from NodeGraphQt.constants import NODE_PROP_QTEXTEDIT -from NodeGraphQt.widgets.node_widgets import NodeLineEdit + class DataViewerNode(AutoNode): __identifier__ = 'Viewers' diff --git a/example_auto_nodes/widget_nodes.py b/example_auto_nodes/widget_nodes.py index 24611c4c..ff5f90a7 100644 --- a/example_auto_nodes/widget_nodes.py +++ b/example_auto_nodes/widget_nodes.py @@ -1,12 +1,13 @@ from .node_base.auto_node import AutoNode + class DropdownMenuNode(AutoNode): """ An example node with a embedded added QCombobox menu. """ # unique node identifier. - __identifier__ = 'com.chantasticvfx' + __identifier__ = 'examples' # initial default node name. NODE_NAME = 'menu' @@ -30,7 +31,7 @@ class CheckboxNode(AutoNode): """ # set a unique node identifier. - __identifier__ = 'com.chantasticvfx' + __identifier__ = 'examples' # set the initial default node name. NODE_NAME = 'checkbox'