From c760ef926a595a0e916853b47dbc5788bcf45e3e Mon Sep 17 00:00:00 2001 From: charlesstrauss Date: Sun, 12 May 2024 20:19:15 -0600 Subject: [PATCH] huge refactor? --- .gitignore | 1 + .tmp.blend1 | Bin 867808 -> 822272 bytes install-pynodes.py | 18 +- pynodes/__init__.py | 483 ++++++++++++-------------- pynodes/nodes/AnyBaseNode.py | 64 ++++ pynodes/nodes/AnyNode.py | 141 ++++++++ pynodes/nodes/PythonBaseNode.py | 10 +- pynodes/nodes/PythonNode.py | 114 +----- pynodes/nodes/PythonNodeGroupNodes.py | 2 +- pynodes/nodes/__init__.py | 7 +- pynodes/reflection.py | 0 11 files changed, 436 insertions(+), 404 deletions(-) create mode 100644 pynodes/nodes/AnyBaseNode.py create mode 100644 pynodes/nodes/AnyNode.py create mode 100644 pynodes/reflection.py diff --git a/.gitignore b/.gitignore index 650638a..ea90230 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +env_managment/envs pynodes.zip .tmp.blend test.blend diff --git a/.tmp.blend1 b/.tmp.blend1 index 7067b4dacc6160b3d0add1714828d994827d057d..b310328715a887806b51da2e2b920263d11586e4 100644 GIT binary patch delta 89439 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 p_00I4S^k|O;Y~{6y+!u^r{3 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%0DUmpr4@QAA91?LNrtT)9E8U@sKAz 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 K*K<3?pZ)(Iq7xYa diff --git a/install-pynodes.py b/install-pynodes.py index d992436..b9ab036 100644 --- a/install-pynodes.py +++ b/install-pynodes.py @@ -2,25 +2,9 @@ import os sys.path.append(os.path.split(__file__)[0]) -print('installing environment into blender') - -# import env_managment.envs.pytorch_blender as pytorch_blender -# pytorch_blender.register() -import env_managment.envs.tensorflow_blender as tensorflow_blender -tensorflow_blender.register() - -print('installed environment into blender') - -import tensorflow -import tensorflow_datasets - -print('imported environment packages') - import pynodes import pynodes.nodes -print('registering pynodes') - pynodes.register() -print('pynodes was registered') +print('pynodes was imported successfully') diff --git a/pynodes/__init__.py b/pynodes/__init__.py index 184c306..7fcd52d 100644 --- a/pynodes/__init__.py +++ b/pynodes/__init__.py @@ -12,7 +12,17 @@ import bpy from bpy.types import NodeTree, Node, NodeSocket -import numpy as np +from bpy.types import ( + Operator, + PropertyGroup, +) +from bpy.props import ( + BoolProperty, + CollectionProperty, + EnumProperty, + FloatVectorProperty, + StringProperty, +) # TODO ideas: # 1. autorename sockets based off of return type, numpy array could show dtype, shape @@ -24,34 +34,10 @@ class PythonCompositorTree(NodeTree): # Optional identifier string. If not explicitly defined, the python class name is used. bl_idname = 'PythonCompositorTreeType' # Label for nice name display - bl_label = "Python Compositor Tree" + bl_label = "PyNodes Compositor Tree" # Icon identifier bl_icon = 'NODETREE' -def get_node_execution_scope(): - ''' - Returns the scope to be used when evaluating python node inputs. - Quickly get a bunch of constants, can be extracted and modified to - have more constants or packages later. Note that many python - builtins are already in the scope by default, such as int or str. - ''' - import numpy as np - import bpy - import os - import sys - # tensorflow, ffmpeg, gmic qt, osl, PIL... - return { - 'pi':np.pi, - 'tau':np.pi*2, - 'e':np.e, - 'np':np, - 'bpy':bpy, - 'os':os, - 'sys':sys - } - -node_execution_scope = get_node_execution_scope() - # Custom socket type class AbstractPyObjectSocket(NodeSocket): # Description string @@ -60,7 +46,7 @@ class AbstractPyObjectSocket(NodeSocket): # for storing the inputs and outputs of nodes without overriding default_value # we can't change value of _value_, but we can set what it points to if its a list - _value_ = [{},]#: bpy.props.PointerProperty(type=bpy.types.Object, name='value', description='Pointer to object contained by the socket') + # _value_ = [{},]#: bpy.props.PointerProperty(type=bpy.types.Object, name='value', description='Pointer to object contained by the socket') # blender properties have to be wrapped in this so they are inherited in a way that blender properties can access them class Properties: @@ -83,15 +69,6 @@ def argvalue_updated(self): def node_updated(self): pass - def get_value(self): - if (self.is_linked or self.is_output) and self.identifier in self._value_[0]: - return self._value_[0][self.identifier] - else: - return self.argvalue # eval(self.argvalue, node_execution_scope) - - def set_value(self, value): - self._value_[0][self.identifier] = value - def hide_text_input(self): self.argvalue_hidden = True @@ -119,200 +96,6 @@ class PyObjectSocket(AbstractPyObjectSocket.Properties, AbstractPyObjectSocket): # Label for nice name display bl_label = "Python Object Socket" -class AbstractPyObjectVarArgSocket(AbstractPyObjectSocket.Properties, AbstractPyObjectSocket): - # Description string - '''PyNodes node socket type for variable arguments (varargs, *args)''' - - class Properties: - socket_index : bpy.props.IntProperty( - name = 'socket_index', - default = -1 - ) - - socket_index_valid : bpy.props.BoolProperty( - name = 'socket_index_valid', - default = True - ) - - def __init__(self): - super().__init__() - self.name = self.identifier - if not self.socket_index_valid: - self.node.inputs.move(self.node.inputs.find(self.identifier), self.socket_index) - self.socket_index_valid = True - - # var args nodes automatically remove or add more of themselves as they are used - def node_updated(self): - self.update() - - def argvalue_updated(self): - self.update() - - def socket_init(self): - pass - - def update(self): - - if self.is_output: - socket_collection = self.node.outputs - else: - socket_collection = self.node.inputs - - emptypins = 0 - # count the number of non-linked, empty sibling vararg pins - for i in socket_collection: - if i.bl_idname == self.bl_idname: - if i.is_empty(): - emptypins+=1 - last_empty_socket = i - last_socket = i - - # there is at least one other empty non-linked one (other than self) - if emptypins > 1: - # remove self if empty and not linked - self.node.unsubscribe_to_update(last_empty_socket.node_updated) - socket_collection.remove(last_empty_socket) - # create new pin if there is not enough - elif emptypins < 1: - new_socket = socket_collection.new( - self.bl_idname, - '', - identifier=self.identifier - ) - - if last_socket.socket_index==-1: - new_socket.socket_index = socket_collection.find(last_socket.identifier)+1 - else: - new_socket.socket_index = last_socket.socket_index+1 - new_socket.socket_index_valid = False - - new_socket.socket_init() - -class PyObjectVarArgSocket(AbstractPyObjectVarArgSocket.Properties, AbstractPyObjectVarArgSocket): - # Description string - '''PyNodes node socket type for variable arguments (varargs, *args)''' - # Optional identifier string. If not explicitly defined, the python class name is used. - bl_idname = 'PyObjectVarArgSocketType' - # Label for nice name display - bl_label = 'Expanding Socket' - - def __init__(self): - super().__init__() - # pin shape - self.display_shape = 'DIAMOND' - -# class PyObjectVarArgSocket(AbstractPyObjectSocket.Properties, AbstractPyObjectSocket): -# # Description string -# '''Python node socket type for variable arguments (varargs, *args)''' -# # Optional identifier string. If not explicitly defined, the python class name is used. -# bl_idname = 'PyObjectVarArgSocketType' -# # Label for nice name display -# bl_label = 'Python *args Object Socket' -# -# def __init__(self): -# super().__init__() -# # pin shape -# self.display_shape = 'DIAMOND' -# self.name = self.identifier -# # socket must be indexible using name. -# # therefore force name to be unique like identifier -# self.node.subscribe_to_update(self.node_updated) -# # subscribe socket to node update events -# -# # var args nodes automatically remove or add more of themselves as they are used -# def node_updated(self): -# # print('node_updated', self.node, self) -# self.update() -# -# def argvalue_updated(self): -# # print('argvalue_updated', self.node, self) -# self.update() -# -# def update(self): -# emptyvarargpins = 0 -# # count the number of non-linked, empty sibling vararg pins -# for input in self.node.inputs: -# if input.bl_idname == PyObjectVarArgSocket.bl_idname: -# if input.is_empty(): -# emptyvarargpins+=1 -# -# # there is at least one other empty non-linked one (other than self) -# if emptyvarargpins > 1: -# # remove self if empty and not linked -# self.node.unsubscribe_to_update(self) -# self.node.inputs.remove(self) -# # create new pin if there is not enough -# elif emptyvarargpins < 1: -# self.node.inputs.new(PyObjectVarArgSocket.bl_idname, '*arg') - -# class PyObjectKwArgSocket(AbstractPyObjectVarArgSocket.Properties, AbstractPyObjectVarArgSocket): -# # Description string -# '''PyNodes socket type for variable arguments (varargs, *args)''' -# # Optional identifier string. If not explicitly defined, the python class name is used. -# bl_idname = 'PyObjectKwArgSocket' -# # Label for nice name display -# bl_label = 'Expanding Socket for Optional Attributes' -# -# attribute : bpy.props.StringProperty( -# name='Attribute', -# description="An attribute to set.", -# update=lambda s,c:s.node.update() -# ) -# -# attribute_collection : bpy.props.CollectionProperty( -# name='Attribute Selector', -# type=bpy.types.PropertyGroup -# ) -# -# def update_attribute_collection(self): -# self.attribute_collection.clear() -# for a in self.get_display_attributes(): -# self.attribute_collection.add().name = a -# -# def node_updated(self): -# super().node_updated() -# self.update_attribute_collection() -# -# def socket_init(self): -# super().socket_init() -# self.update_attribute_collection() -# -# def get_display_attributes(self): -# unused_attributes = self.node.unused_attributes() -# if not self.attribute is None: -# unused_attributes+=[self.attribute,] -# unused_attributes = sorted(unused_attributes) -# return unused_attributes -# -# def draw(self, context, layout, node, text): -# if text!='': -# layout.label(text=text) -# layout.prop_search(self, 'attribute', self, 'attribute_collection', text='') -# if not self.is_linked and not self.is_output: -# layout.prop(self, 'argvalue', text='') -# -# def __init__(self): -# super().__init__() -# # pin shape -# self.display_shape = 'SQUARE' - -class PyObjectKwArgSocket(AbstractPyObjectSocket.Properties, AbstractPyObjectSocket): - # Description string - '''Python node socket type for keyword argumnets''' - # Optional identifier string. If not explicitly defined, the python class name is used. - bl_idname = 'PyObjectKWArgSocketType' - # Label for nice name display - bl_label = 'Python *kwargs Object Socket' - - def __init__(self): - super().__init__() - # pin shape - self.display_shape = 'SQUARE' - - # method to set the default value defined by the kwargs - def set_default(self, value): - self.argvalue = value - ### Node Categories ### # Node categories are a python system for automatically # extending the Add menu, toolbar panels and search operator. @@ -334,43 +117,57 @@ class PythonCompositorOperator(bpy.types.Operator): def poll(cls, context): return context.space_data.tree_type == PythonCompositorTree.bl_idname -# Have to add grouping behavior manually: - -def group_make(self, new_group_name): - self.node_tree = bpy.data.node_groups.new(new_group_name, PythonCompositorTree.bl_idname) - self.group_name = self.node_tree.name - - nodes = self.node_tree.nodes - inputnode = nodes.new('PyNodesGroupInputsNode') - outputnode = nodes.new('PyNodesGroupOutputsNode') - inputnode.location = (-300, 0) - outputnode.location = (300, 0) - return self.node_tree - +def get_override(area_type): + for window in bpy.context.window_manager.windows: + screen = window.screen + + for area in screen.areas: + if area.type == area_type: + for region in area.regions: + if region.type == 'WINDOW': + override = {'window': window, + 'screen': screen, + 'area': area, + 'region': region, + 'blend_data': bpy.context.blend_data} + + return override + +# Have to add node grouping behavior manually: class PyNodesGroupEdit(PythonCompositorOperator): bl_idname = "node.pynodes_group_edit" - bl_label = "edits an pynodes node group" + bl_label = "edits a pynodes node group" group_name : bpy.props.StringProperty(default='Node Group') - + + def group_make(self, node, new_group_name): + self.node_tree = bpy.data.node_groups.new(new_group_name, PythonCompositorTree.bl_idname) + self.group_name = self.node_tree.name + + nodes = self.node_tree.nodes + inputnode = nodes.new('PyNodesGroupInputsNode') + outputnode = nodes.new('PyNodesGroupOutputsNode') + inputnode.location = (-300, 0) + outputnode.location = (300, 0) + return self.node_tree + def execute(self, context): node = context.active_node + parent_tree_name = node.id_data.name ng = bpy.data.node_groups print(self.group_name) - group_node = ng.get(self.group_name) - if not group_node: - group_node = group_make(node, new_group_name=self.group_name) - + node_group = ng.get(self.group_name) + if not node_group: + node_group = self.group_make(node, new_group_name=self.group_name) + bpy.ops.node.pynodes_switch_layout(layout_name=self.group_name) -# print(context.space_data, context.space_data.node_tree) -# context.space_data.node_tree = ng[self.group_name] # does the same # by switching, space_data is now different - parent_tree_name = node.id_data.name + # parent_tree_name = node.id_data.name path = context.space_data.path - path.clear() + path.clear() #? path.append(ng[parent_tree_name]) # below the green opacity layer path.append(ng[self.group_name]) # top level @@ -428,17 +225,17 @@ def execute(self, context): return {'CANCELLED'} return {'FINISHED'} -class NODE_MT_add_test_node_tree(PythonCompositorOperator): - """Programmatically create node tree for testing, if it dosen't already exist.""" - bl_idname = "node.add_test_node_tree" - bl_label = "Add test node tree." +class NODE_MT_add_python_node(PythonCompositorOperator): + """Allow user to search for the exact function they wish to run in a node.""" + bl_idname = "node.add_python_node" + bl_label = "Add python node by search" bl_options = {'REGISTER', 'UNDO'} def execute(self, context): if bpy.data.node_groups.get('Python Node Tree Test', False)==False: bpy.ops.node.new_node_tree( - type='PythonCompositorTreeType', + type=PythonCompositorTree.bl_idname, name='Python Node Tree Test' ) test_node_tree = bpy.data.node_groups.get('Python Node Tree Test', False) @@ -456,6 +253,168 @@ def add_test_node_tree(self, context): NODE_MT_add_test_node_tree.bl_idname, text="Add Test Node Tree") +# NodeAddOperator, NodeSetting from https://raw.githubusercontent.com/blender/blender/main/scripts/startup/bl_operators/node.py + +class NodeSetting(PropertyGroup): + value: StringProperty( + name="Value", + description="Python expression to be evaluated " + "as the initial node setting", + default="", + ) + +# Base class for node "Add" operators. +class NodeAddOperator: + + use_transform: BoolProperty( + name="Use Transform", + description="Start transform operator after inserting the node", + default=False, + ) + settings: CollectionProperty( + name="Settings", + description="Settings to be applied on the newly created node", + type=NodeSetting, + options={'SKIP_SAVE'}, + ) + + @staticmethod + def store_mouse_cursor(context, event): + space = context.space_data + tree = space.edit_tree + + # convert mouse position to the View2D for later node placement + if context.region.type == 'WINDOW': + # convert mouse position to the View2D for later node placement + space.cursor_location_from_region( + event.mouse_region_x, event.mouse_region_y) + else: + space.cursor_location = tree.view_center + + # Deselect all nodes in the tree. + @staticmethod + def deselect_nodes(context): + space = context.space_data + tree = space.edit_tree + for n in tree.nodes: + n.select = False + + def create_node(self, context, node_type): + space = context.space_data + tree = space.edit_tree + + try: + node = tree.nodes.new(type=node_type) + except RuntimeError as ex: + self.report({'ERROR'}, str(ex)) + return None + + for setting in self.settings: + # XXX catch exceptions here? + value = eval(setting.value) + node_data = node + node_attr_name = setting.name + + # Support path to nested data. + if '.' in node_attr_name: + node_data_path, node_attr_name = node_attr_name.rsplit(".", 1) + node_data = node.path_resolve(node_data_path) + + try: + setattr(node_data, node_attr_name, value) + except AttributeError as ex: + self.report( + {'ERROR_INVALID_INPUT'}, + tip_("Node has no attribute %s") % setting.name) + print(str(ex)) + # Continue despite invalid attribute + + node.select = True + tree.nodes.active = node + node.location = space.cursor_location + return node + + @classmethod + def poll(cls, context): + space = context.space_data + # needs active node editor and a tree to add nodes to + return (space and (space.type == 'NODE_EDITOR') and + space.edit_tree and not space.edit_tree.library) + + # Default invoke stores the mouse position to place the node correctly + # and optionally invokes the transform operator + def invoke(self, context, event): + self.store_mouse_cursor(context, event) + result = self.execute(context) + + if self.use_transform and ('FINISHED' in result): + # removes the node again if transform is canceled + bpy.ops.node.translate_attach_remove_on_cancel('INVOKE_DEFAULT') + + return result + +# NODE_OT_add_node from https://raw.githubusercontent.com/jesterKing/blender/143ccc8c44cbd1a630c4f02d8c6eb26e26c63757/blender/release/scripts/startup/bl_operators/node.py + +class NODE_OT_add_search_pynodes(NodeAddOperator, Operator): + '''Add a node to the active tree''' + bl_idname = "node.add_search_pynodes" + bl_label = "Search and Add Node" + bl_options = {'REGISTER', 'UNDO'} + bl_property = "node_item" + + _enum_item_hack = [] + + # Create an enum list from node items + def node_enum_items(self, context): + enum_items = NODE_OT_add_search._enum_item_hack + enum_items.clear() + + + + return enum_items + + # Look up the item based on index + def find_node_item(self, context): + node_item = int(self.node_item) + for index, item in enumerate(nodeitems_utils.node_items_iter(context)): + if index == node_item: + return item + return None + + node_item = EnumProperty( + name="Node Type", + description="Node type", + items=node_enum_items, + ) + + def execute(self, context): + item = self.find_node_item(context) + + # no need to keep + self._enum_item_hack.clear() + + if item: + # apply settings from the node item + for setting in item.settings.items(): + ops = self.settings.add() + ops.name = setting[0] + ops.value = setting[1] + + n = self.create_node(context, 'AnyNode') + n.api_endpoint_string = item.label + + if self.use_transform: + bpy.ops.transform.translate('INVOKE_DEFAULT', remove_on_cancel=True) + + return {'FINISHED'} + else: + return {'CANCELLED'} + + def invoke(self, context, event): + self.store_mouse_cursor(context, event) + # Delayed execution in the search popup + context.window_manager.invoke_search_popup(self) + return {'CANCELLED'} from pynodes import registry from pynodes import helpers @@ -468,24 +427,20 @@ def register(): # register the essentials to building a PythonNode register_class(PythonCompositorTree) register_class(PyObjectSocket) - register_class(PyObjectVarArgSocket) - register_class(PyObjectKwArgSocket) register_class(PyNodesGroupEdit) register_class(PyNodesTreePathParent) register_class(PyNodesSwitchToLayout) bpy.types.NODE_MT_node.append(pynodes_group_edit) - register_class(NODE_MT_add_test_node_tree) - bpy.types.NODE_MT_node.append(add_test_node_tree) + # register_class(NODE_MT_add_test_node_tree) + # bpy.types.NODE_MT_node.append(add_test_node_tree) def unregister(): registry.unregisterAll() unregister_class(PythonCompositorTree) unregister_class(PyObjectSocket) - unregister_class(PyObjectVarArgSocket) - unregister_class(PyObjectKwArgSocket) unregister_class(PyNodesGroupEdit) unregister_class(PyNodesTreePathParent) diff --git a/pynodes/nodes/AnyBaseNode.py b/pynodes/nodes/AnyBaseNode.py new file mode 100644 index 0000000..86a5113 --- /dev/null +++ b/pynodes/nodes/AnyBaseNode.py @@ -0,0 +1,64 @@ +import pynodes +from pynodes import nodes +import numpy as np +import bpy +import networkx as nx + +from pynodes.nodes.AnyNode import BlenderPythonAPIEndpoint + +def blender_python_kernel(code): + eval(code) + +class AnyBaseNode(nodes.PythonBaseNode.Properties, nodes.PythonBaseNode): + # === Basics === + # Description string + '''Execute connected AnyNodes''' + # Optional identifier string. If not explicitly defined, the python class name is used. + bl_idname = 'AnyBaseNode' + # Label for nice name display + bl_label = "Execute connected AnyNodes" + + _cache_ = [{}] # immutable lookup of node names to variables names in some kernel + + def init(self, context): + super().init(context) + self.inputs.new(pynodes.PyObjectSocket.bl_idname, "Result") + + def run(self): + G = nx.DiGraph() + + # BFS to find all connected nodes, add connectsion to dependency graph G + leaves = [] + branches = [self] + while len(branches)>0: + for branch in branches: + for input_socket in branch.inputs: + if len(input_socket.links)==0: + continue + node = input_socket.links[0].from_socket.node + G.add_edge(node.name, branch.name) + leaves.append(node) + branches = leaves + leaves = [] + print(len(branches), len(leaves)) + + # use the dependency graph to determine execution order of the nodes + print(G.edges) + # if nx.is_directed_acyclic_graph(G)>0: + # raise Exception('Cycles detected in node graph!') + + # should be a Directed Acyclic Graph + print(list(nx.topological_sort(G))) + + while len(G)>0: + + # ignoring all caching, and just recomputing whole graph upon every run + + leaves = [x for x in G.nodes() if G.out_degree(x)==0 and G.in_degree(x)==1] + + for leave in leaves: + api = BlenderPythonAPIEndpoint(leave.api_endpoint_string) + # get parameters, and composite an execution of the function with corresponding filled in variables from kernel + +from pynodes import registry +registry.registerNodeType(AnyBaseNode) diff --git a/pynodes/nodes/AnyNode.py b/pynodes/nodes/AnyNode.py new file mode 100644 index 0000000..6585932 --- /dev/null +++ b/pynodes/nodes/AnyNode.py @@ -0,0 +1,141 @@ +import pynodes +from pynodes import nodes +import time +import bpy +import inspect +import traceback + +class APIEndpointInterface: + + def get_signature(): + pass + + def get_documentation(): + pass + +class BlenderPythonAPIEndpoint(APIEndpointInterface): + + def __init__(self, api_endpoint_string): + self.api_endpoint_string = api_endpoint_string + self.api = eval(self.api_endpoint_string) + + def get_signature(self): + try: + return inspect.signature(self.api) + except: + return inspect.Signature( + [ + inspect.Parameter('unknown_posarg', inspect.Parameter.VAR_POSITIONAL, default=inspect.Parameter.empty, annotation=inspect.Parameter.empty), + inspect.Parameter('unknown_keyarg', inspect.Parameter.VAR_KEYWORD, default=inspect.Parameter.empty, annotation=inspect.Parameter.empty) + ], + return_annotation=inspect.Signature.empty + ) + + def get_documentation(self): + return inspect.getdoc(self.api) + +class AnyNode(nodes.PythonNode): + # === Basics === + # Description string + '''A node which takes on the form of the specified API.''' + # Optional identifier string. If not explicitly defined, the python class name is used. + bl_idname = 'AnyNode' + # Label for nice name display + bl_label = "Any Node" + + api_endpoint_string : bpy.props.StringProperty( + name='', + description="Callable API endpoint.", + update=lambda s,c:s.update_api(c) + ) + + api_documentation : bpy.props.StringProperty( + description='Documentation for the API endpoint.' + ) + + # Additional buttons displayed on the node. + def draw_buttons(self, context, layout): + layout.prop(self, "api_endpoint_string") + + # Detail buttons in the sidebar. + # If this function is not defined, the draw_buttons function is used instead + def draw_buttons_ext(self, context, layout): + # maybe add a label to say its the api string + + layout.prop(self, "api_endpoint_string") + + layout.label(text=self.api_documentation) # show the documentation for this api + # perhaps make this multiline + + def init(self, context): + super().init(context) + + def run(self): + super().run(eval(self.label)) + + def update(self): + print(f'node {self.name} updated') + + def update_api(self, context): + # can force the api string to be auto-corrected, etc. + self.label = self.api_endpoint_string + self.set_no_color() + + try: + api = BlenderPythonAPIEndpoint(self.api_endpoint_string) + except: + self.set_color([1,0,0]) + print(traceback.format_exc()) + return + + try: + self.api_documentation = api.get_documentation() + except: + self.api_documentation = 'No documentation found!' + print(traceback.format_exc()) + + self.inputs.clear() + self.outputs.clear() + + try: + sig = api.get_signature() + includes_varargs = False + includes_kwargs = False + for param_name in sig.parameters: + param = sig.parameters[param_name] + + if param.kind==inspect.Parameter.POSITIONAL_OR_KEYWORD or param.kind==inspect.Parameter.POSITIONAL_ONLY: + if param.annotation!=inspect.Parameter.empty: + key = param_name + ': ' + param.annotation + else: + key = param_name + + self.inputs.new(pynodes.PyObjectSocket.bl_idname, key) + + if param.default!=inspect.Parameter.empty: + self.inputs[key].argvalue = str(param.default) + + elif param.kind==inspect.Parameter.VAR_POSITIONAL: + includes_varargs = True + else: + includes_kwargs = True + + if includes_varargs: + self.inputs.new(pynodes.PyObjectSocket.bl_idname, '*args') + if includes_kwargs: + self.inputs.new(pynodes.PyObjectSocket.bl_idname, '**kwargs') + + if sig.return_annotation!=inspect.Signature.empty: + self.outputs.new(pynodes.PyObjectSocket.bl_idname, sig.return_annotation) + else: + self.outputs.new(pynodes.PyObjectSocket.bl_idname, 'Any') + except: + self.set_color([1,0,0]) + print(traceback.format_exc()) + # self.inputs.clear() + # self.outputs.clear() + return + + +from pynodes import registry +registry.registerNodeType(AnyNode) diff --git a/pynodes/nodes/PythonBaseNode.py b/pynodes/nodes/PythonBaseNode.py index 9656555..0fb8edb 100644 --- a/pynodes/nodes/PythonBaseNode.py +++ b/pynodes/nodes/PythonBaseNode.py @@ -15,7 +15,7 @@ def poll(cls, context): return space.type == 'NODE_EDITOR' def execute(self, context): - context.node.compute_output() + context.node.run() return {'FINISHED'} from pynodes import registry @@ -37,14 +37,6 @@ def mark_dirty(self): self.is_current = False super().mark_dirty() - # def compute_output(self): - # print('compute_output run') - # try: - # super().compute_output() - # except nodes.PythonNode.PythonNodeRunError as e: - # print('Python Nodes caught the following error:') - # traceback.print_exc() - def draw_buttons(self, context, layout): layout.operator('node.evaluate_python_node_tree', icon='PLAY') diff --git a/pynodes/nodes/PythonNode.py b/pynodes/nodes/PythonNode.py index 18a7595..d8bf0e6 100644 --- a/pynodes/nodes/PythonNode.py +++ b/pynodes/nodes/PythonNode.py @@ -3,6 +3,8 @@ import bpy import numpy as np +from pynodes import PythonCompositorTree + # Mix-in class for all custom nodes in this tree type. # Defines a poll function to enable instantiation. class PythonCompositorTreeNode: @@ -40,42 +42,6 @@ class PythonNode(ColorfulNode, PythonCompositorTreeNode): # Icon identifier bl_icon = 'SCRIPT' - # class Properties: - is_dirty = True - # bpy.props.BoolProperty( - # name='dirty', - # default=True - # ) - - class PythonNodeRunError(Exception): - ''' - raised when a node has an error. - ''' - def __init__(self, node, e): - self.node = node - self.ex = e - super().__init__( - f'Exception raised by node {node.bl_idname}:\n{e}' - ) - - def mark_dirty(self): - ''' - Propogate to all downstream nodes that this nodes is not up to date. - ''' - self.is_dirty = True - self.set_no_color() - for k in self.outputs.keys(): - out = self.outputs[k] - if out.is_linked: - for o in out.links: - node = o.to_socket.node - if not node is self and o.is_valid and not node.get_dirty(): - node.mark_dirty() - - - def get_dirty(self): - return self.is_dirty - def is_connected_to_base(self): ''' Return true if this node is connected to a node which is a "base" node. @@ -88,84 +54,10 @@ def is_connected_to_base(self): return True return False - def run(self): - ''' - Do whatever calculations this node does. Take input from self.get_input - and set outputs using self.set_output. - ''' - pass - - def get_input(self, k, default_func=lambda:None): - ''' - Called by run to get value stored behind socket. - ''' - v = self.inputs[k] - if v.is_linked and len(v.links)>0 and v.links[0].is_valid: - o = v.links[0].from_socket - value = o.get_value() - if value is None or o.node.get_dirty(): - o.node.compute_output() - value = o.get_value(); - return value - v.set_value(None) - return v.get_value() - - def set_output(self, k, v): - self.outputs[k].set_value(v) - - update_subscribers = [] - - def subscribe_to_update(self, callback): - self.update_subscribers.append(callback) - - def unsubscribe_to_update(self, callback): - if callback in self.update_subscribers: - self.update_subscribers.remove(callback) - def update(self): ''' Called when the node tree changes. ''' - if not self.get_dirty(): - self.mark_dirty() - # mark node, and downstream nodes as dirty/not current - for socket in self.inputs: + for socket in self.inputs: # TODO: check that socket is updatable socket.node_updated() - # call all socket callbacks and other subscribers - for callback in self.update_subscribers: - try: - callback() - except Exception as e: - traceback.print_exc() - self.unsubscribe_to_update(callback) - - - def interrupt_execution(self, e): - raise PythonNode.PythonNodeRunError(self, e) - - def compute_output(self): - # print(self, 'compute_output') - try: - if self.get_dirty(): - self.set_color([0.0, 0.0, 0.5]) - self.run() - self.is_dirty = False - self.propagate() - self.set_color([0.0, 0.5, 0.0]) - except Exception as e: - # self.mark_dirty() - self.set_color([0.5, 0.0, 0.0]) - traceback.print_exc() - self.interrupt_execution(e) - - def propagate(self): - ''' - Pass this nodes outputs to nodes linked to outputs. Call update_value on - them after passing. - ''' - for out in self.outputs: - if out.is_linked: - for o in out.links: - if o.is_valid: - o.to_socket.set_value(out.get_value()) diff --git a/pynodes/nodes/PythonNodeGroupNodes.py b/pynodes/nodes/PythonNodeGroupNodes.py index f8398ef..4fbe5ba 100644 --- a/pynodes/nodes/PythonNodeGroupNodes.py +++ b/pynodes/nodes/PythonNodeGroupNodes.py @@ -7,7 +7,7 @@ from pynodes import registry from bpy.utils import register_class -class PythonNodeGroupIOSocket(pynodes.AbstractPyObjectVarArgSocket.Properties, pynodes.AbstractPyObjectVarArgSocket): +class PythonNodeGroupIOSocket(pynodes.AbstractPyObjectSocket.Properties, pynodes.AbstractPyObjectSocket): ''' PyNodes node socket type for group input and output nodes''' # Optional identifier string. If not explicitly defined, the python class name is used. bl_idname = 'PythonNodeGroupIOSocket' diff --git a/pynodes/nodes/__init__.py b/pynodes/nodes/__init__.py index c537063..b33833b 100644 --- a/pynodes/nodes/__init__.py +++ b/pynodes/nodes/__init__.py @@ -9,6 +9,9 @@ from pynodes.nodes.PythonShowArrayShapeBaseNode import PythonShowArrayShapeBaseNode from pynodes.nodes.PythonNodeGroupNodes import * -from pynodes.nodes.AutoNodeTypeAdder import add_all_globals +from pynodes.nodes.AnyNode import AnyNode +from pynodes.nodes.AnyBaseNode import AnyBaseNode -add_all_globals() +# from pynodes.nodes.AutoNodeTypeAdder import add_all_globals + +# add_all_globals() diff --git a/pynodes/reflection.py b/pynodes/reflection.py new file mode 100644 index 0000000..e69de29