diff options
Diffstat (limited to 'release/scripts/startup')
-rw-r--r-- | release/scripts/startup/bl_operators/node.py | 194 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_node.py | 43 |
2 files changed, 131 insertions, 106 deletions
diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index bc0224db765..9839e0ee092 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -100,75 +100,55 @@ class NODE_OT_add_node(NodeAddOperator, Operator): return result -# XXX These node item lists should actually be generated by a callback at -# operator execution time (see node_type_items below), -# using the active node tree from the context. -# Due to a difficult bug in bpy this is not possible -# (item list memory gets freed too early), -# so for now just copy the static item lists to these global variables. -# -# In the custom_nodes branch, the static per-tree-type node items are replaced -# by a single independent type list anyway (with a poll function to limit node -# types to the respective trees). So this workaround is only temporary. - -# lazy init -node_type_items_dict = {} - -# Prefixes used to distinguish base node types and node groups -node_type_prefix = 'NODE_' -node_group_prefix = 'GROUP_' - - -# Generate a list of enum items for a given node class -# Copy existing type enum, adding a prefix to distinguish from node groups -# Skip the base node group type, -# node groups will be added below for all existing group trees -def node_type_items(node_class): - return [(node_type_prefix + item.identifier, item.name, item.description) - for item in node_class.bl_rna.properties['type'].enum_items - if item.identifier != 'GROUP'] - - -# Generate items for node group types -# Filter by the given tree_type -# Node group trees don't have a description property yet -# (could add this as a custom property though) -def node_group_items(tree_type): - return [(node_group_prefix + group.name, group.name, '') - for group in bpy.data.node_groups if group.type == tree_type] +def node_classes_iter(base=bpy.types.Node): + """ + Yields all true node classes by checking for the is_registered_node_type classmethod. + Node types can use specialized subtypes of bpy.types.Node, which are not usable + nodes themselves (e.g. CompositorNode). + """ + if base.is_registered_node_type(): + yield base + for subclass in base.__subclasses__(): + for node_class in node_classes_iter(subclass): + yield node_class + + +def node_class_items_iter(node_class, context): + identifier = node_class.bl_rna.identifier + # XXX Checking for explicit group node types is stupid. + # This should be replaced by a generic system of generating + # node items via callback. + # Group node_tree pointer should also use a poll function to filter the library list, + # but cannot do that without a node instance here. A node callback could just use the internal poll function. + if identifier in {'ShaderNodeGroup', 'CompositorNodeGroup', 'TextureNodeGroup'}: + tree_idname = context.space_data.edit_tree.bl_idname + for group in bpy.data.node_groups: + if group.bl_idname == tree_idname: + yield (group.name, "", {"node_tree":group}) # XXX empty string should be replaced by description from tree + else: + yield (node_class.bl_rna.name, node_class.bl_rna.description, {}) -# Returns the enum item list for the edited tree in the context -def node_type_items_cb(self, context): +def node_items_iter(context): snode = context.space_data if not snode: - return () + return tree = snode.edit_tree if not tree: - return () - - # Lists of basic node types for each - if not node_type_items_dict: - node_type_items_dict.update({ - 'SHADER': node_type_items(bpy.types.ShaderNode), - 'COMPOSITING': node_type_items(bpy.types.CompositorNode), - 'TEXTURE': node_type_items(bpy.types.TextureNode), - }) - - # XXX Does not work correctly, see comment above - ''' - return [(item.identifier, item.name, item.description, item.value) - for item in - tree.nodes.bl_rna.functions['new'].parameters['type'].enum_items] - ''' - - if tree.type in node_type_items_dict: - return node_type_items_dict[tree.type] + node_group_items(tree.type) - else: - return () + return + + for node_class in node_classes_iter(): + if node_class.poll(tree): + for item in node_class_items_iter(node_class, context): + yield (node_class,) + item + + +# Create an enum list from node class items +def node_type_items_cb(self, context): + return [(str(index), item[1], item[2]) for index, item in enumerate(node_items_iter(context))] -class NODE_OT_add_search(Operator): +class NODE_OT_add_search(NodeAddOperator, Operator): '''Add a node to the active tree''' bl_idname = "node.add_search" bl_label = "Search and Add Node" @@ -182,55 +162,48 @@ class NODE_OT_add_search(Operator): items=node_type_items_cb, ) - _node_type_items_dict = None + def execute(self, context): + for index, item in enumerate(node_items_iter(context)): + if str(index) == self.type: + node = self.create_node(context, item[0].bl_rna.identifier) + for prop,value in item[3].items(): + setattr(node, prop, value) + break + return {'FINISHED'} - def create_node(self, context): - space = context.space_data - tree = space.edit_tree + 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'} - # Enum item identifier has an additional prefix to - # distinguish base node types from node groups - item = self.type - if item.startswith(node_type_prefix): - # item means base node type - node = tree.nodes.new(type=item[len(node_type_prefix):]) - elif item.startswith(node_group_prefix): - # item means node group type - node = tree.nodes.new( - type='GROUP', - group=bpy.data.node_groups[item[len(node_group_prefix):]]) - else: - return None - for n in tree.nodes: - if n == node: - node.select = True - tree.nodes.active = node - else: - node.select = False - node.location = space.cursor_location - return node +# Simple basic operator for adding a node without further initialization +class NODE_OT_add_node(NodeAddOperator, bpy.types.Operator): + '''Add a node to the active tree''' + bl_idname = "node.add_node" + bl_label = "Add Node" - @classmethod - def poll(cls, context): - space = context.space_data - # needs active node editor and a tree to add nodes to - return (space.type == 'NODE_EDITOR' and space.edit_tree) + type = StringProperty(name="Node Type", description="Node type") def execute(self, context): - self.create_node(context) + node = self.create_node(context, self.type) return {'FINISHED'} - def invoke(self, context, event): - space = context.space_data - v2d = context.region.view2d - # convert mouse position to the View2D for later node placement - space.cursor_location = v2d.region_to_view(event.mouse_region_x, - event.mouse_region_y) +class NODE_OT_add_group_node(NodeAddOperator, bpy.types.Operator): + '''Add a group node to the active tree''' + bl_idname = "node.add_group_node" + bl_label = "Add Group Node" - context.window_manager.invoke_search_popup(self) - return {'CANCELLED'} + type = StringProperty(name="Node Type", description="Node type") + grouptree = StringProperty(name="Group tree", description="Group node tree name") + + def execute(self, context): + node = self.create_node(context, self.type) + node.node_tree = bpy.data.node_groups[self.grouptree] + + return {'FINISHED'} class NODE_OT_collapse_hide_unused_toggle(Operator): @@ -261,3 +234,24 @@ class NODE_OT_collapse_hide_unused_toggle(Operator): socket.hide = hide return {'FINISHED'} + + +class NODE_OT_tree_path_parent(Operator): + '''Go to parent node tree''' + bl_idname = "node.tree_path_parent" + bl_label = "Parent Node Tree" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + space = context.space_data + # needs active node editor and a tree + return (space.type == 'NODE_EDITOR' and len(space.path) > 1) + + def execute(self, context): + space = context.space_data + + space.path.pop() + + return {'FINISHED'} + diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 1865b049a03..e739c5ea5d4 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -44,8 +44,8 @@ class NODE_HT_header(Header): row.menu("NODE_MT_node") layout.prop(snode, "tree_type", text="", expand=True) - - if snode.tree_type == 'SHADER': + + if snode.tree_type == 'ShaderNodeTree': if scene.render.use_shading_nodes: layout.prop(snode, "shader_type", text="", expand=True) @@ -65,7 +65,7 @@ class NODE_HT_header(Header): if snode_id: layout.prop(snode_id, "use_nodes") - elif snode.tree_type == 'TEXTURE': + elif snode.tree_type == 'TextureNodeTree': layout.prop(snode, "texture_type", text="", expand=True) if id_from: @@ -76,7 +76,7 @@ class NODE_HT_header(Header): if snode_id: layout.prop(snode_id, "use_nodes") - elif snode.tree_type == 'COMPOSITING': + elif snode.tree_type == 'CompositorNodeTree': layout.prop(snode_id, "use_nodes") layout.prop(snode_id.render, "use_free_unused_nodes", text="Free Unused") layout.prop(snode, "show_backdrop") @@ -84,6 +84,13 @@ class NODE_HT_header(Header): row = layout.row(align=True) row.prop(snode, "backdrop_channels", text="", expand=True) layout.prop(snode, "use_auto_render") + + else: + # Custom node tree is edited as independent ID block + layout.template_ID(snode, "node_tree", new="node.new_node_tree") + + layout.prop(snode, "pin", text="") + layout.operator("node.tree_path_parent", text="", icon='FILE_PARENT') layout.separator() @@ -182,6 +189,7 @@ class NODE_MT_node(Menu): layout.operator("node.group_edit") layout.operator("node.group_ungroup") layout.operator("node.group_make") + layout.operator("node.group_insert") layout.separator() @@ -208,7 +216,7 @@ class NODE_PT_properties(Panel): @classmethod def poll(cls, context): snode = context.space_data - return snode.tree_type == 'COMPOSITING' + return snode.tree_type == 'CompositorNodeTree' def draw_header(self, context): snode = context.space_data @@ -237,7 +245,7 @@ class NODE_PT_quality(bpy.types.Panel): @classmethod def poll(cls, context): snode = context.space_data - return snode.tree_type == 'COMPOSITING' and snode.node_tree is not None + return snode.tree_type == 'CompositorNodeTree' and snode.node_tree is not None def draw(self, context): layout = self.layout @@ -276,5 +284,28 @@ class NODE_MT_node_color_specials(Menu): layout.operator("node.node_copy_color", icon='COPY_ID') +class NODE_UL_interface_sockets(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + socket = item + color = socket.draw_color(context) + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + row = layout.row(align=True) + + # inputs get icon on the left + if socket.in_out == 'IN': + row.template_node_socket(color) + + row.label(text=socket.name, icon_value=icon) + + # outputs get icon on the right + if socket.in_out == 'OUT': + row.template_node_socket(color) + + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + layout.template_node_socket(color) + + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) |