def node_replacement_menu(self, context, layout): """ Draw menu items with node replacement operators. This is called from `rclick_menu()' method by default. Items are defined by `replacement_nodes' class property. Expected format is replacement_nodes = [ (new_node_bl_idname, inputs_mapping_dict, outputs_mapping_dict) ] where new_node_bl_idname is bl_idname of replacement node class, inputs_mapping_dict is a dictionary mapping names of inputs of this node to names of inputs to new node, and outputs_mapping_dict is a dictionary mapping names of outputs of this node to names of outputs of new node. inputs_mapping_dict and outputs_mapping_dict can be None. """ if hasattr(self, "replacement_nodes"): for bl_idname, inputs_mapping, outputs_mapping in self.replacement_nodes: node_class = get_node_class_reference(bl_idname) text = "Replace with {}".format(node_class.bl_label) op = layout.operator("node.sv_replace_node", text=text) op.old_node_name = self.name op.new_bl_idname = bl_idname set_inputs_mapping(op, inputs_mapping) set_outputs_mapping(op, outputs_mapping)
def node_replacement_menu(self, context, layout): """ Draw menu items with node replacement operators. This is called from `rclick_menu()' method by default. Items are defined by `replacement_nodes' class property. Expected format is replacement_nodes = [ (new_node_bl_idname, inputs_mapping_dict, outputs_mapping_dict) ] where new_node_bl_idname is bl_idname of replacement node class, inputs_mapping_dict is a dictionary mapping names of inputs of this node to names of inputs to new node, and outputs_mapping_dict is a dictionary mapping names of outputs of this node to names of outputs of new node. inputs_mapping_dict and outputs_mapping_dict can be None. """ if hasattr(self, "replacement_nodes"): for bl_idname, inputs_mapping, outputs_mapping in self.replacement_nodes: node_class = get_node_class_reference(bl_idname) if node_class: text = "Replace with {}".format(node_class.bl_label) op = layout.operator("node.sv_replace_node", text=text) op.old_node_name = self.name op.new_bl_idname = bl_idname set_inputs_mapping(op, inputs_mapping) set_outputs_mapping(op, outputs_mapping) else: self.error("Can't build replacement menu: no such node class: %s",bl_idname)
def layout_draw_categories(layout, node_details): for node_info in node_details: if node_info[0] == 'separator': layout.separator() continue if not node_info: print(repr(node_info), 'is incomplete, or unparsable') continue bl_idname = node_info[0] node_ref = get_node_class_reference(bl_idname) if hasattr(node_ref, "bl_label"): layout_params = dict(text=node_ref.bl_label, **node_icon(node_ref)) elif bl_idname == 'NodeReroute': layout_params = dict(text='Reroute') else: continue node_op = draw_add_node_operator(layout, bl_idname, params=layout_params)
def make_categories(): original_categories = make_node_cats() node_cats = juggle_and_join(original_categories) node_cats = include_submenus(node_cats) node_categories = [] node_count = 0 for category, nodes in node_cats.items(): name_big = "SVERCHOK_" + category.replace(' ', '_') node_items = [] for item in nodes: nodetype = item[0] if is_submenu_call(nodetype): continue rna = get_node_class_reference(nodetype) if not rna and not nodetype == 'separator': logger.info( "Node `%s' is not available (probably due to missing dependencies).", nodetype) else: node_item = SverchNodeItem.new(nodetype) node_items.append(node_item) if node_items: node_categories.append( SverchNodeCategory(name_big, category, items=node_items)) node_count += len(nodes) node_categories.append( SverchNodeCategory("SVERCHOK_MONAD", "Monad", items=sv_group_items)) SverchNodeItem.new('SvMonadInfoNode') return node_categories, node_count, original_categories
def draw_add_node_operator(layout, nodetype, label=None, icon_name=None, params=None): """ Draw node adding operator button. This is to be used both in Shift-A menu and in T panel. """ default_context = bpy.app.translations.contexts.default node_class = get_node_class_reference(nodetype) if node_class is None: info("cannot locate node class: %s", nodetype) return node_rna = node_class.bl_rna if label is None: if hasattr(node_rna, 'bl_label'): label = node_rna.bl_label elif nodetype == "NodeReroute": label = "Reroute" else: label = node_rna.name if params is None: params = dict(text=label) params['text_ctxt'] = default_context if icon_name is not None: params.update(**icon(icon_name)) else: params.update(**node_icon(node_rna)) add = layout.operator("node.sv_add_" + get_node_idname_for_operator(nodetype), **params) add.type = nodetype add.use_transform = True return add
def gather_items(context): fx = [] idx = 0 for _, node_list in node_cats.items(): for item in node_list: if item[0] in {'separator', 'NodeReroute'}: continue nodetype = get_node_class_reference(item[0]) if not nodetype: continue docstring = ensure_valid_show_string(nodetype) if not docstring: continue fx.append((str(idx), docstring, '', idx)) idx += 1 for k, v in macros.items(): fx.append((k, format_item(k, v), '', idx)) idx += 1 gather_extra_nodes(idx, fx, context) fx_extend(idx, fx) return fx
def draw_add_node_operator(layout, nodetype, label=None, icon_name=None, params=None): """ Draw node adding operator button. This is to be used both in Shift-A menu and in T panel. """ default_context = bpy.app.translations.contexts.default node_rna = get_node_class_reference(nodetype).bl_rna if label is None: if hasattr(node_rna, 'bl_label'): label = node_rna.bl_label elif nodetype == "NodeReroute": label = "Reroute" else: label = node_rna.name if params is None: params = dict(text=label) params['text_ctxt'] = default_context if icon_name is not None: params.update(**icon(icon_name)) else: params.update(**node_icon(node_rna)) add = layout.operator("node.sv_add_" + get_node_idname_for_operator(nodetype), **params) add.type = nodetype add.use_transform = True return add
def make_categories(): original_categories = make_node_cats() node_cats = juggle_and_join(original_categories) node_cats = include_submenus(node_cats) node_categories = [] node_count = 0 nodes_not_enabled = defaultdict(list) for category, nodes in node_cats.items(): name_big = "SVERCHOK_" + category.replace(' ', '_') node_items = [] for item in nodes: nodetype = item[0] if is_submenu_call(nodetype): continue rna = get_node_class_reference(nodetype) if not rna and not nodetype == 'separator': nodes_not_enabled[category].append(nodetype) else: node_item = SverchNodeItem.new(nodetype) node_items.append(node_item) if node_items: node_categories.append( SverchNodeCategory(name_big, category, items=node_items)) node_count += len(nodes) # logger.info(f"The following nodes are not enabled (probably due to missing dependencies)\n{strformated_tree(nodes_not_enabled)}") temp_details['not_enabled_nodes'] = nodes_not_enabled return node_categories, node_count, original_categories
def make_categories(): original_categories = make_node_cats() node_cats = juggle_and_join(original_categories) node_categories = [] node_count = 0 for category, nodes in node_cats.items(): name_big = "SVERCHOK_" + category.replace(' ', '_') node_items = [] for item in nodes: nodetype = item[0] rna = get_node_class_reference(nodetype) if not rna and not nodetype == 'separator': info( "Node `%s' is not available (probably due to missing dependencies).", nodetype) else: node_item = SverchNodeItem.new(nodetype) node_items.append(node_item) if node_items: node_categories.append( SverchNodeCategory(name_big, category, items=node_items)) node_count += len(nodes) node_categories.append( SverchNodeCategory("SVERCHOK_GROUPS", "Groups", items=sv_group_items)) return node_categories, node_count, original_categories
def category_has_nodes(cat_name): cat = node_cats[cat_name] for item in cat: rna = get_node_class_reference(item[0]) if rna and not item[0] == 'separator': return True return False
def layout_draw_categories(layout, node_details): for node_info in node_details: if node_info[0] == 'separator': layout.separator() continue if not node_info: print(repr(node_info), 'is incomplete, or unparsable') continue bl_idname = node_info[0] # this is a node bl_idname that can be registered but shift+A can drop it from showing. if bl_idname == 'ScalarMathNode': continue node_ref = get_node_class_reference(bl_idname) if hasattr(node_ref, "bl_label"): layout_params = dict(text=node_ref.bl_label, **node_icon(node_ref)) elif bl_idname == 'NodeReroute': layout_params = dict(text='Reroute',icon_value=custom_icon('SV_REROUTE')) else: continue node_op = draw_add_node_operator(layout, bl_idname, params=layout_params)
def layout_draw_categories(layout, node_details): for node_info in node_details: if node_info[0] == 'separator': layout.separator() continue if not node_info: print(repr(node_info), 'is incomplete, or unparsable') continue bl_idname = node_info[0] # this is a node bl_idname that can be registered but shift+A can drop it from showing. if bl_idname == 'ScalarMathNode': continue node_ref = get_node_class_reference(bl_idname) if hasattr(node_ref, "bl_label"): layout_params = dict(text=node_ref.bl_label, **node_icon(node_ref)) elif bl_idname == 'NodeReroute': layout_params = dict(text='Reroute') else: continue node_op = draw_add_node_operator(layout, bl_idname, params=layout_params)
def draw(self, context): if context is None: return space = context.space_data if not space: return ntree = space.edit_tree if not ntree: return layout = self.layout monad_node_ops(self, layout, context) if ntree.bl_idname == "SverchGroupTreeType": draw_add_node_operator(layout, "SvMonadInfoNode") layout.separator() for monad in context.blend_data.node_groups: if monad.bl_idname != "SverchGroupTreeType": continue if monad.name == ntree.name: continue # make sure class exists cls_ref = get_node_class_reference(monad.cls_bl_idname) if cls_ref and monad.cls_bl_idname and monad.cls_bl_idname: op = layout.operator('node.add_node', text=monad.name) op.type = monad.cls_bl_idname op.use_transform = True
def add_nodes_to_sv(): index = nodes_index() for _, items in index: for item in items: nodetype = item[1] rna = get_node_class_reference(nodetype) if not rna and nodetype != 'separator': info("Node `%s' is not available (probably due to missing dependencies).", nodetype) else: SverchNodeItem.new(nodetype)
def get_node_idname_for_operator(nodetype): """Select valid bl_idname for node to create node adding operator bl_idname.""" rna = get_node_class_reference(nodetype) if not rna: raise Exception("Can't find registered node {}".format(nodetype)) if hasattr(rna, 'bl_idname'): return rna.bl_idname.lower() elif nodetype == "NodeReroute": return "node_reroute" else: return rna.name.lower()
def add_prop_from(self, socket): """ Add a property if possible """ other = socket.other cls = get_node_class_reference(self.cls_bl_idname) cls_dict = cls.__dict__ if cls else {} if other.prop_name: prop_name = other.prop_name # prop_func, prop_dict = getattr(other.node.rna_type, prop_name, ("", {})) # <-- in 2.79 prop_func, prop_dict = other.node.__annotations__.get( prop_name, ("", {})) if prop_func.__name__ == "FloatProperty": self.get_current_as_default(prop_dict, other.node, prop_name) prop_settings = self.float_props.add() elif prop_func.__name__ == "IntProperty": self.get_current_as_default(prop_dict, other.node, prop_name) prop_settings = self.int_props.add() elif prop_func.__name__ == "FloatVectorProperty": info( "FloatVectorProperty ignored (normal behaviour since day one). prop_func: %s, prop_dict: %s.", prop_func, prop_dict) return None # for now etc else: # no way to handle it return None # print('dict') # pprint.pprint(prop_dict) new_name = generate_name(prop_name, cls_dict) prop_settings.prop_name = new_name prop_settings.set_settings(prop_dict) socket.prop_name = new_name return new_name elif hasattr(other, "prop_type"): if "float" in other.prop_type: prop_settings = self.float_props.add() elif "int" in other.prop_type: prop_settings = self.int_props.add() else: return None new_name = generate_name(make_valid_identifier(other.name), cls_dict) prop_settings.prop_name = new_name prop_settings.set_settings({"name": other.name}) socket.prop_name = new_name return new_name return None
def idname_draw(self, context): if not displaying_sverchok_nodes(context): return layout = self.layout node = context.active_node if not node: return bl_idname = node.bl_idname box = layout.box() col = box.column(align=False) col.scale_y = 0.9 row = col.row(align=True) colom = row.column(align=True) colom.scale_x = 3 colom.label(text=bl_idname + ':') colom = row.column(align=True) colom.operator('node.copy_bl_idname', text='', icon='COPY_ID').name = bl_idname # show these anyway, can fail and let us know.. row = col.row(align=True) row.label(text='Help & Docs:') row = col.row(align=True) row.operator('node.view_node_help', text='Online').kind = 'online' row.operator('node.view_node_help', text='Offline').kind = 'offline' row.operator('node.view_node_help', text='Github').kind = 'github' #, icon='GHOST' col.separator() # view the source of the current node ( warning, some nodes rely on more than one file ) row = col.row(align=True) row.label(text='Edit Source:') row = col.row(align=True) row.operator('node.sv_view_node_source', text='Externally').kind = 'external' row.operator('node.sv_view_node_source', text='Internally').kind = 'internal' if hasattr(node, 'replacement_nodes'): box = col.box() box.label(text="Replace with:") for new_bl_idname, inputs_mapping, outputs_mapping in node.replacement_nodes: node_class = get_node_class_reference(new_bl_idname) text = node_class.bl_label op = box.operator("node.sv_replace_node", text=text) op.old_node_name = node.name op.new_bl_idname = new_bl_idname set_inputs_mapping(op, inputs_mapping) set_outputs_mapping(op, outputs_mapping) row = col.row(align=True) op = row.operator('node.sv_replace_node', text='Re-Create Node') op.old_node_name = node.name op.new_bl_idname = bl_idname
def update_cls(self): """ create or update the corresponding class reference """ if not all((self.input_node, self.output_node)): error("Monad %s not set up correctly", self.name) return None cls_dict = {} if not self.cls_bl_idname: # the monad cls_bl_idname needs to be unique and cannot change monad_base_name = make_valid_identifier(self.name) monad_itentifier = id(self) ^ random.randint(0, 4294967296) cls_name = "SvGroupNode{}_{}".format(monad_base_name, monad_itentifier) # set the unique name for the class, depending on context this might fail # then we cannot do the setup of the class properly so abandon try: self.cls_bl_idname = cls_name except Exception: return None else: cls_name = self.cls_bl_idname self.verify_props() cls_dict["bl_idname"] = cls_name cls_dict["bl_label"] = self.name cls_dict["input_template"] = self.generate_inputs() cls_dict["output_template"] = self.generate_outputs() self.make_props(cls_dict) # done with setup old_cls_ref = get_node_class_reference(cls_name) bases = (SvGroupNodeExp, Node, SverchCustomTreeNode) cls_ref = type(cls_name, bases, cls_dict) if old_cls_ref: sverchok.utils.unregister_node_class(old_cls_ref) sverchok.utils.register_node_class(cls_ref) return cls_ref
def add_node(self, tree): class SvSingleSocketNode(): bl_idname = 'SvSingleSocketNode' bl_label = 'DO NOT USE' bl_icon = 'MOD_CURVE' # [x] define new node prop_dict = self.get_prop_dict() bases = (SvSingleSocketNode, Node, SverchCustomTreeNode) prop_func = IntProperty if self.new_prop_type == "int" else FloatProperty # [x] -- add prop if needed (only when inputs) cls_dict = {} cls_dict['__annotations__'] = {} cls_dict['__annotations__'][self.new_prop_name] = prop_func( **prop_dict) cls_name = SvSingleSocketNode.bl_idname cls_ref = type(cls_name, bases, cls_dict) # [x] register new node (but unregister first if needed..) old_cls_ref = get_node_class_reference(cls_name) if old_cls_ref: unregister_node_class(old_cls_ref) register_node_class(cls_ref) # [x] add node property_node = tree.nodes.new(cls_name) # [x] -- add socket to node (add both in the template) io_sockets = getattr(property_node, self.kind) if self.kind == 'outputs': io_sockets.new(self.socket_type, self.new_prop_name) else: io_sockets.new(self.socket_type, prop_dict['name']).prop_name = prop_dict['name'] # [x] link node io_node = tree.input_node if self.kind == 'inputs' else tree.output_node out2in = self.kind == 'inputs' AB_LINK = (io_node.outputs[-1], io_sockets[-1]) if out2in else (io_sockets[-1], io_node.inputs[-1]) tree.links.new(*AB_LINK) # [x] unlink, remove node # tree.links.remove(*AB_LINK) tree.nodes.remove(property_node) # [x] unregister node unregister_node_class(cls_ref)
def add_prop_from(self, socket): """ Add a property if possible """ other = socket.other cls = get_node_class_reference(self.cls_bl_idname) cls_dict = cls.__dict__ if cls else {} if other.prop_name: prop_name = other.prop_name prop_func, prop_dict = getattr(other.node.rna_type, prop_name, ("", {})) if prop_func.__name__ == "FloatProperty": self.get_current_as_default(prop_dict, other.node, prop_name) prop_settings = self.float_props.add() elif prop_func.__name__ == "IntProperty": self.get_current_as_default(prop_dict, other.node, prop_name) prop_settings = self.int_props.add() elif prop_func.__name__ == "FloatVectorProperty": info("FloatVectorProperty ignored (normal behaviour since day one). prop_func: %s, prop_dict: %s.", prop_func, prop_dict) return None # for now etc else: # no way to handle it return None # print('dict') # pprint.pprint(prop_dict) new_name = generate_name(prop_name, cls_dict) prop_settings.prop_name = new_name prop_settings.set_settings(prop_dict) socket.prop_name = new_name return new_name elif hasattr(other, "prop_type"): if "float" in other.prop_type: prop_settings = self.float_props.add() elif "int" in other.prop_type: prop_settings = self.int_props.add() else: return None new_name = generate_name(make_valid_identifier(other.name), cls_dict) prop_settings.prop_name = new_name prop_settings.set_settings({"name": other.name}) socket.prop_name = new_name return new_name return None
def get_category_items(self, context): category_items = None category_items = [(GENERAL, "General", "Uncategorized presets", 0)] node_category_items = [] for idx, category in enumerate(get_category_names()): node_class = get_node_class_reference(category) if node_class: title = "/Node/ {}".format(node_class.bl_label) node_category_items.append((category, title, category, idx+1)) else: title = category category_items.append((category, title, category, idx+1)) include_node_categories = not hasattr(self, 'include_node_categories') or self.include_node_categories if node_category_items and include_node_categories: category_items = category_items + [None] + node_category_items return category_items
def sv_group_items(context): """ Based on the built in node_group_items in the blender distrubution somewhat edited to fit. """ if context is None: return space = context.space_data if not space: return ntree = space.edit_tree if not ntree: return yield NodeItemCustom(draw=draw_node_ops) def contains_group(nodetree, group): if nodetree == group: return True else: for node in nodetree.nodes: if node.bl_idname in node_tree_group_type.values( ) and node.node_tree is not None: if contains_group(node.node_tree, group): return True return False if ntree.bl_idname == "SverchGroupTreeType": yield NodeItem("SvMonadInfoNode", "Monad Info") for monad in context.blend_data.node_groups: if monad.bl_idname != "SverchGroupTreeType": continue # make sure class exists cls_ref = get_node_class_reference(monad.cls_bl_idname) if cls_ref and monad.cls_bl_idname: yield NodeItem(monad.cls_bl_idname, monad.name) elif monad.cls_bl_idname: monad_cls_template_dict = { "cls_bl_idname": "str('{}')".format(monad.cls_bl_idname) } yield NodeItem("SvMonadGenericNode", monad.name, monad_cls_template_dict)
def make_menu(): menu = [] index = nodes_index() for category, items in index: identifier = "IFCSVERCHOK_" + category.replace(" ", "_") node_items = [] for item in items: nodetype = item[1] rna = get_node_class_reference(nodetype) if not rna: info( "Node `%s' is not available (probably due to missing dependencies).", nodetype) else: node_item = SverchNodeItem.new(nodetype) node_items.append(node_item) if node_items: cat = SverchNodeCategory(identifier, category, items=node_items) menu.append(cat) return menu
def layout_draw_categories(layout, category_name, node_details): global menu_class_by_title for node_info in node_details: if node_info[0] == 'separator': layout.separator() continue if not node_info: print(repr(node_info), 'is incomplete, or unparsable') continue bl_idname = node_info[0] if is_submenu_call(bl_idname): submenu_title = get_submenu_call_name(bl_idname) menu_title = compose_submenu_name(category_name, bl_idname) menu_class = menu_class_by_title[menu_title] layout.menu(menu_class.__name__, text=submenu_title) continue # this is a node bl_idname that can be registered but shift+A can drop it from showing. if bl_idname == 'ScalarMathNode': continue node_ref = get_node_class_reference(bl_idname) if hasattr(node_ref, "bl_label"): layout_params = dict(text=node_ref.bl_label, **node_icon(node_ref)) elif bl_idname == 'NodeReroute': layout_params = dict(text='Reroute', icon_value=custom_icon('SV_REROUTE')) else: continue node_op = draw_add_node_operator(layout, bl_idname, params=layout_params)
def sv_group_items(context): """ Based on the built in node_group_items in the blender distrubution somewhat edited to fit. """ if context is None: return space = context.space_data if not space: return ntree = space.edit_tree if not ntree: return yield NodeItemCustom(draw=draw_node_ops) def contains_group(nodetree, group): if nodetree == group: return True else: for node in nodetree.nodes: if node.bl_idname in node_tree_group_type.values() and node.node_tree is not None: if contains_group(node.node_tree, group): return True return False if ntree.bl_idname == "SverchGroupTreeType": yield NodeItem("SvMonadInfoNode", "Monad Info") for monad in context.blend_data.node_groups: if monad.bl_idname != "SverchGroupTreeType": continue # make sure class exists cls_ref = get_node_class_reference(monad.cls_bl_idname) if cls_ref and monad.cls_bl_idname: yield NodeItem(monad.cls_bl_idname, monad.name) elif monad.cls_bl_idname: monad_cls_template_dict = {"cls_bl_idname": "str('{}')".format(monad.cls_bl_idname)} yield NodeItem("SvMonadGenericNode", monad.name, monad_cls_template_dict)
def make_categories(): menu_cats = [] index = nodes_index() for category, items in index: identifier = "SVERCHOK_OPEN3D_" + category.replace(' ', '_') node_items = [] for item in items: nodetype = item[1] rna = get_node_class_reference(nodetype) if not rna and nodetype != 'separator': info("Node `%s' is not available (probably due to missing dependencies).", nodetype) else: node_item = SverchNodeItem.new(nodetype) node_items.append(node_item) if node_items: cat = SverchNodeCategory( identifier, category, items=node_items ) menu_cats.append(cat) return menu_cats
def idname_draw(self, context): if not displaying_sverchok_nodes(context): return layout = self.layout node = context.active_node ntree = node.id_data if not node: return bl_idname = node.bl_idname box = layout.box() col = box.column(align=False) col.scale_y = 0.9 row = col.row(align=True) colom = row.column(align=True) colom.scale_x = 3 colom.label(text=bl_idname + ':') colom = row.column(align=True) colom.operator('node.copy_bl_idname', text='', icon='COPY_ID').name = bl_idname if node_supports_presets(node): box = col.box() box.label(text="Presets:") box.menu("SV_MT_LoadPresetMenu") save_row = box.row() save = save_row.operator(SvSaveSelected.bl_idname, text="Save Node Preset", icon='SOLO_ON') save.id_tree = ntree.name save.category = node.bl_idname save.save_defaults = True selected_nodes = [node for node in ntree.nodes if node.select] save_row.enabled = len(selected_nodes) == 1 # show these anyway, can fail and let us know.. row = col.row(align=True) row.label(text='Help & Docs:') row = col.row(align=True) row.operator('node.view_node_help', text='Online').kind = 'online' row.operator('node.view_node_help', text='Offline').kind = 'offline' row.operator('node.view_node_help', text='Github').kind = 'github' #, icon='GHOST' col.separator() # view the source of the current node ( warning, some nodes rely on more than one file ) row = col.row(align=True) row.label(text='Edit Source:') row = col.row(align=True) row.operator('node.sv_view_node_source', text='Externally').kind = 'external' row.operator('node.sv_view_node_source', text='Internally').kind = 'internal' if hasattr(node, 'replacement_nodes'): box = col.box() box.label(text="Replace with:") for new_bl_idname, inputs_mapping, outputs_mapping in node.replacement_nodes: node_class = get_node_class_reference(new_bl_idname) text = node_class.bl_label op = box.operator("node.sv_replace_node", text=text) op.old_node_name = node.name op.new_bl_idname = new_bl_idname set_inputs_mapping(op, inputs_mapping) set_outputs_mapping(op, outputs_mapping) row = col.row(align=True) op = row.operator('node.sv_replace_node', text='Re-Create Node') op.old_node_name = node.name op.new_bl_idname = bl_idname
def get_node_class(self): return get_node_class_reference(self.nodetype)
def ensure_valid_show_string(item): # nodetype = getattr(bpy.types, item[0]) nodetype = get_node_class_reference(item[0]) loop_reverse[nodetype.bl_label] = item[0] description = nodetype.bl_rna.get_shorthand() return nodetype.bl_label + ensure_short_description(description)
def add_prop_from(self, socket): """ Add a property if possible """ other = socket.other cls = get_node_class_reference(self.cls_bl_idname) cls_dict = cls.__dict__ if cls else {} local_debug = False # reference_obj_id = cls.instances[0] # print(reference_obj_id.__annotations__) try: monad_prop_names = self.get_stored_prop_names() has_monad_prop_names = True print(monad_prop_names) except: print('no prop names yet in : add_prop_from call') has_monad_prop_names = False if other.prop_name: prop_name = other.prop_name prop_func, prop_dict = other.node.__annotations__.get(prop_name, ("", {})) if 'attr' in prop_dict: prop_dict.pop('attr') # this we store in prop_name anyway if 'update' in prop_dict: """ the node may be doing a tonne of stuff in a wrapped update, but because this property will be on a shell node (the monad outside) we can replace it with a reference to updateNode. i think this is a sane thing to ensure. """ prop_dict['update'] = updateNode if not 'name' in prop_dict: """ name is used exclusively for displaying name on the slider or label most properties will have this defined anyway, but just in case. """ regex = re.compile('[^a-z A-Z0-9]') prop_dict['name'] = regex.sub('', prop_name) print(f"monad: generated name for property function: {prop_name} -> {prop_dict['name']}") if local_debug: print("prop_func:", prop_func) # tells us the kind of property to make print("prop_dict:", prop_dict) # tells us the attributes of the property print("prop_name:", prop_name) # tells the socket / slider ui which prop to display # # and its associated 'name' attribute from the prop_dict if prop_func.__name__ == "FloatProperty": self.get_current_as_default(prop_dict, other.node, prop_name) prop_settings = self.float_props.add() prop_name_prefix = f"floats_{len(self.float_props)}_" elif prop_func.__name__ == "IntProperty": self.get_current_as_default(prop_dict, other.node, prop_name) prop_settings = self.int_props.add() prop_name_prefix = f"ints_{len(self.int_props)}_" elif prop_func.__name__ == "FloatVectorProperty": info("FloatVectorProperty ignored (normal behaviour since day one). prop_func: %s, prop_dict: %s.", prop_func, prop_dict) return None else: # no way to handle it return None if other.node.bl_idname == "SvNumberNode": if "float" in prop_name: prop_dict['min'] = other.node.float_min prop_dict['max'] = other.node.float_max elif "int" in prop_name: prop_dict['min'] = other.node.int_min prop_dict['max'] = other.node.int_max new_name = prop_name_prefix + prop_name if has_monad_prop_names: new_name = ensure_unique(monad_prop_names, new_name) prop_settings.prop_name = new_name prop_settings.set_settings(prop_dict) socket.prop_name = new_name return new_name elif hasattr(other, "prop_type"): # if you are seeing errors with this and the other.node.bl_idname is not scriptnodelite # the fix will be here somewhere. print(f'{other.node} = other.node') print(f'{other.prop_type} = other.prop_type') if not any(substring in other.prop_type for substring in ["float", "int"]): return None if "float" in other.prop_type: prop_settings = self.float_props.add() prop_name_prefix = f"floats_{len(self.float_props)}_" elif "int" in other.prop_type: prop_settings = self.int_props.add() prop_name_prefix = f"ints_{len(self.int_props)}_" new_name = prop_name_prefix + other.name if has_monad_prop_names: new_name = ensure_unique(monad_prop_names, new_name) # this name will be used as the attr name of the newly generated property for the shellnode # essentially this is # __annotations__[prop_name] = new property function prop_settings.prop_name = new_name custom_prop_dict = { "name": nice_ui_name(other.name), "update": updateNode } # there are other nodes that use this technique, if other.node.bl_idname == "SvScriptNodeLite": prop_list = other.node.float_list if "float" in other.prop_type else other.node.int_list default = prop_list[other.prop_index] custom_prop_dict["default"] = default prop_settings.set_settings(custom_prop_dict) socket.prop_name = new_name return new_name return None
def draw(self, context): layout = self.layout if len(context.space_data.path) > 1: layout.label(text="Is not supported inside node groups") return ntree = context.space_data.node_tree panel_props = ntree.preset_panel_properties layout.prop(panel_props, 'manage_mode', toggle=True, icon='PREFERENCES') needle = None if not panel_props.manage_mode: row = layout.row(align=True) row.prop(panel_props, "search_text", text="") row.operator("node.sv_reset_preset_search", icon="X", text="") needle = panel_props.search_text if not panel_props.search_text: layout.prop(panel_props, 'category', text='') row = layout.row() op = row.operator('node.sv_save_selected', text="Save Preset", icon='SOLO_ON') op.id_tree = ntree.name op.category = panel_props.category op.is_node_preset = False selected_nodes = [node for node in ntree.nodes if node.select] can_save_preset = len(selected_nodes) > 0 category_node_class = get_node_class_reference(op.category) if category_node_class is not None: if len(selected_nodes) == 1: selected_node = selected_nodes[0] can_save_preset = can_save_preset and hasattr( selected_node, 'bl_idname') and selected_node.bl_idname == op.category else: can_save_preset = False row.enabled = can_save_preset layout.separator() presets = get_presets(panel_props.category, search=needle) layout.separator() if panel_props.manage_mode: col = layout.column(align=True) col.operator("node.sv_preset_from_gist", icon='URL').category = panel_props.category col.operator("node.sv_preset_from_file", icon='IMPORT').category = panel_props.category col.operator('node.sv_preset_category_new', icon='NEWFOLDER') if panel_props.category != GENERAL: remove = col.operator('node.sv_preset_category_remove', text="Delete category {}".format( panel_props.category), icon='CANCEL') remove.category = panel_props.category if len(presets): layout.label(text="Manage presets:") for preset in presets: name = preset.name row = layout.row(align=True) row.label(text=name) gist = row.operator('node.sv_preset_to_gist', text="", icon='URL') gist.preset_name = name gist.category = panel_props.category export = row.operator('node.sv_preset_to_file', text="", icon="EXPORT") export.preset_name = name export.category = panel_props.category if not preset.standard: rename = row.operator('node.sv_preset_props', text="", icon="GREASEPENCIL") rename.old_name = name rename.old_category = panel_props.category rename.allow_change_category = (category_node_class is None) if rename.allow_change_category: rename.new_category = rename.old_category delete = row.operator('node.sv_preset_delete', text="", icon='CANCEL') delete.preset_name = name delete.category = panel_props.category else: layout.label(text="You do not have any presets") layout.label( text="under `{}` category.".format(panel_props.category)) layout.label(text="You can import some presets") layout.label(text="from Gist or from file.") else: if len(presets): layout.label(text="Use preset:") draw_presets_ops(layout, panel_props.category, ntree.name, presets) elif needle: layout.label(text="There are no presets matching") layout.label(text="the search terms.") else: layout.label(text="You do not have any presets") layout.label( text="under `{}` category.".format(panel_props.category)) layout.label(text="Select some nodes and") layout.label(text="Use the `Save Preset' button.")