diff --git a/__init__.py b/__init__.py index 59d5dbc47b4b7304760ff94fde5a006e89f36f6b..6bf49731cbff67941c001d2bc4144a3a191b0251 100755 --- a/__init__.py +++ b/__init__.py @@ -92,7 +92,7 @@ utils_modules = [ "text_editor_submenu", "text_editor_plugins", # UI operators and tools "sv_panels_tools", "sv_gist_tools", "sv_IO_panel_tools", "sv_load_zipped_blend", - "monad", "sv_help", + "monad", "sv_help", "sv_default_macros", "sv_macro_utils", "sv_extra_search", #"loadscript", "debug_script", "sv_update_utils" ] diff --git a/nodes/generator/random_vector_mk2.py b/nodes/generator/random_vector_mk2.py index cd5d06fc7d2ba169837d315566d82b0df5953cba..906d560fd879dc14d3aa250e6e6789aa39547d3f 100644 --- a/nodes/generator/random_vector_mk2.py +++ b/nodes/generator/random_vector_mk2.py @@ -25,7 +25,7 @@ from sverchok.data_structure import updateNode, match_long_repeat class RandomVectorNodeMK2(bpy.types.Node, SverchCustomTreeNode): - ''' Random Vectors with len=1 MK2, Unit Vectors''' + ''' Random unit Vectors''' bl_idname = 'RandomVectorNodeMK2' bl_label = 'Random Vector MK2' bl_icon = 'RNDCURVE' diff --git a/nodes/generators_extended/profile.py b/nodes/generators_extended/profile.py index a3a785a63b0ce17fa99471c9e3bb17007bc7db94..306c834b17b9627cfeab695fe86bf07f9e08ad4e 100644 --- a/nodes/generators_extended/profile.py +++ b/nodes/generators_extended/profile.py @@ -514,6 +514,8 @@ class SvPrifilizer(bpy.types.Operator): class SvProfileNode(bpy.types.Node, SverchCustomTreeNode): ''' + svg-like 2d profiles /// + SvProfileNode generates one or more profiles / elevation segments using; assignments, variables, and a string descriptor similar to SVG. @@ -521,6 +523,7 @@ class SvProfileNode(bpy.types.Node, SverchCustomTreeNode): - sockets with no input are automatically 0, not None - The longest input array will be used to extend the shorter ones, using last value repeat. ''' + bl_idname = 'SvProfileNode' bl_label = 'Profile Parametric' bl_icon = 'OUTLINER_OB_EMPTY' diff --git a/nodes/matrix/apply_and_join.py b/nodes/matrix/apply_and_join.py index fdaeb8f86f0bd03fe2c363be7153c9215796450f..6a765f35c74400e62452a4ad246b7f7120edd899 100644 --- a/nodes/matrix/apply_and_join.py +++ b/nodes/matrix/apply_and_join.py @@ -25,8 +25,12 @@ from sverchok.utils.sv_mesh_utils import mesh_join class SvMatrixApplyJoinNode(bpy.types.Node, SverchCustomTreeNode): - ''' Multiply vectors on matrices with several objects in output, - and process edges & faces too ''' + ''' + M * verts (optional join) /// + + Multiply vectors on matrices with several objects in output, + and process edges & faces too + ''' bl_idname = 'SvMatrixApplyJoinNode' bl_label = 'Matrix Apply' bl_icon = 'OUTLINER_OB_EMPTY' diff --git a/nodes/text/debug_print.py b/nodes/text/debug_print.py index 738e515656ca86c4b75c20cad933e93b5baac9ea..064fce391a68ab38cb519265791291093080dd2d 100644 --- a/nodes/text/debug_print.py +++ b/nodes/text/debug_print.py @@ -24,7 +24,7 @@ from sverchok.data_structure import multi_socket, updateNode class SvDebugPrintNode(bpy.types.Node, SverchCustomTreeNode): - ''' SvDebugPrintNode ''' + ''' print socket data to terminal ''' bl_idname = 'SvDebugPrintNode' bl_label = 'Debug print' bl_icon = 'OUTLINER_OB_EMPTY' diff --git a/nodes/vector/axis_input_mk2.py b/nodes/vector/axis_input_mk2.py index e8b03f227e4bf63264c0de00586172d0a2a481f7..6a772221df7fd5e1283646c10eb973a67ca19473 100644 --- a/nodes/vector/axis_input_mk2.py +++ b/nodes/vector/axis_input_mk2.py @@ -24,10 +24,10 @@ from sverchok.data_structure import updateNode class SvAxisInputNodeMK2(bpy.types.Node, SverchCustomTreeNode): - ''' Generator for X, Y or Z axis. ''' + ''' axis input ''' bl_idname = 'SvAxisInputNodeMK2' - bl_label = 'Vector X | Y | Z' + bl_label = 'Vector X/Y/Z' bl_icon = 'MANIPUL' m = [("-1", "-1", "", 0), ("0", "0", "", 1), ("1", "1", "", 2)] diff --git a/nodes/vector/color_in.py b/nodes/vector/color_in.py index ad96c4f98d29f12192e0a87aeb86ffc97a57c6e1..b9dcf66b8e9873b2475a5185f140c453e26ef2b0 100644 --- a/nodes/vector/color_in.py +++ b/nodes/vector/color_in.py @@ -36,7 +36,7 @@ def fprop_generator(**altprops): class SvColorsInNode(bpy.types.Node, SverchCustomTreeNode): - ''' Generator for Color data , color combine''' + ''' rgb(a) ---> color /// Generator for Color data''' bl_idname = 'SvColorsInNode' bl_label = 'Color in' sv_icon = 'SV_COMBINE_IN' diff --git a/nodes/vector/color_out.py b/nodes/vector/color_out.py index d09e9616f9470f6ea3a6446730f25f8f629f77cb..7aa1b8d6f78443609a33eb93becfa309e19491b2 100644 --- a/nodes/vector/color_out.py +++ b/nodes/vector/color_out.py @@ -31,7 +31,7 @@ nodule_color = (0.899, 0.8052, 0.0, 1.0) class SvColorsOutNode(bpy.types.Node, SverchCustomTreeNode): - ''' Generator for Color data , color separate''' + ''' color ---> rgb(a) /// Generator for Color data''' bl_idname = 'SvColorsOutNode' bl_label = 'Color Out' sv_icon = 'SV_COMBINE_OUT' diff --git a/ui/nodeview_keymaps.py b/ui/nodeview_keymaps.py index 838b1e482b39b026be3bb9155077ea3b677fa68d..6c6961293fdf31839448b76e278b7453675e0c3f 100644 --- a/ui/nodeview_keymaps.py +++ b/ui/nodeview_keymaps.py @@ -51,6 +51,11 @@ def add_keymap(): kmi.properties.name = "NODEVIEW_MT_Dynamic_Menu" nodeview_keymaps.append((km, kmi)) + # TAB | enter or exit monad depending on selection and edit_tree type + kmi = km.keymap_items.new('node.sv_extra_search', 'RIGHT_BRACKET', 'PRESS', shift=True) + nodeview_keymaps.append((km, kmi)) + + def remove_keymap(): diff --git a/utils/sv_default_macros.py b/utils/sv_default_macros.py new file mode 100644 index 0000000000000000000000000000000000000000..03fdac46073223f333de6071d8ad92b09d5c363f --- /dev/null +++ b/utils/sv_default_macros.py @@ -0,0 +1,111 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import os +import webbrowser + +import bpy + +from sverchok.utils.sv_update_utils import sv_get_local_path + + +macros = { + "> obj vd": { + 'display_name': "active_obj into objlite + vdmk2", + 'file': 'macro', + 'ident': ['verbose_macro_handler', 'obj vd']}, + "> objs vd": { + 'display_name': "multi obj in + vdmk2", + 'file':'macro', + 'ident': ['verbose_macro_handler', 'objs vd']}, + "> zen": { + 'display_name': "zen of Sverchok", + 'file':'macro', + 'ident': ['verbose_macro_handler', 'zen']}, + "> sn petal": { + 'display_name': "load snlite w/ petalsine", + 'file':'macro', + 'ident': ['verbose_macro_handler', 'sn petal']} +} + +sv_types = {'SverchCustomTreeType', 'SverchGroupTreeType'} + + +class DefaultMacros(): + + @classmethod + def ensure_nodetree(cls, operator, context): + ''' + if no active nodetree + add new empty node tree, set fakeuser immediately + ''' + if not context.space_data.tree_type in sv_types: + print('not running from a sv nodetree') + return + + if not hasattr(context.space_data.edit_tree, 'nodes'): + msg_one = 'going to add a new empty node tree' + msg_two = 'added new node tree' + print(msg_one) + operator.report({"WARNING"}, msg_one) + ng_params = {'name': 'NodeTree', 'type': 'SverchCustomTreeType'} + ng = bpy.data.node_groups.new(**ng_params) + ng.use_fake_user = True + context.space_data.node_tree = ng + operator.report({"WARNING"}, msg_two) + + @classmethod + def verbose_macro_handler(cls, operator, context, term): + + cls.ensure_nodetree(operator, context) + + tree = context.space_data.edit_tree + nodes, links = tree.nodes, tree.links + + if term == 'obj vd': + obj_in_node = nodes.new('SvObjInLite') + obj_in_node.dget() + vd_node = nodes.new('ViewerNode2') + vd_node.location = obj_in_node.location.x + 180, obj_in_node.location.y + + links.new(obj_in_node.outputs[0], vd_node.inputs[0]) + links.new(obj_in_node.outputs[2], vd_node.inputs[1]) + links.new(obj_in_node.outputs[3], vd_node.inputs[2]) + elif term == 'objs vd': + obj_in_node = nodes.new('SvObjectsNodeMK3') + obj_in_node.get_objects_from_scene(operator) + vd_node = nodes.new('ViewerNode2') + vd_node.location = obj_in_node.location.x + 180, obj_in_node.location.y + + links.new(obj_in_node.outputs[0], vd_node.inputs[0]) + links.new(obj_in_node.outputs[2], vd_node.inputs[1]) + links.new(obj_in_node.outputs[3], vd_node.inputs[2]) + elif term == 'zen': + full_url_term = 'https://blenderpython.tumblr.com/post/91951323209/zen-of-sverchok' + webbrowser.open(full_url_term) + elif term == 'sn petal': + snlite = nodes.new('SvScriptNodeLite') + # set location of snlite based on mouse? + + sv_path = os.path.dirname(sv_get_local_path()[0]) + snlite_template_path = os.path.join(sv_path, 'node_scripts', 'SNLite_templates') + fullpath = os.path.join(snlite_template_path, 'petal_sine.py') + + txt = bpy.data.texts.load(fullpath) + snlite.script_name = os.path.basename(txt.name) + snlite.load() diff --git a/utils/sv_extra_search.py b/utils/sv_extra_search.py new file mode 100644 index 0000000000000000000000000000000000000000..f5a5d354da01d455cdc95c6601b740f4e18b2618 --- /dev/null +++ b/utils/sv_extra_search.py @@ -0,0 +1,175 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import os +import importlib.util as getutil +import bpy + +import sverchok +import nodeitems_utils +from sverchok.menu import make_node_cats +from sverchok.ui.sv_icons import custom_icon +from sverchok.utils.sv_default_macros import macros, DefaultMacros +from nodeitems_utils import _node_categories + +# pylint: disable=c0326 + +sv_tree_types = {'SverchCustomTreeType', 'SverchGroupTreeType'} +node_cats = make_node_cats() +addon_name = sverchok.__name__ + +loop = {} +loop_reverse = {} +local_macros = {} +ddir = lambda content: [n for n in dir(content) if not n.startswith('__')] + + +def format_item(k, v): + return k + " | " + v['display_name'] + +def format_macro_item(k, v): + return '< ' + k.replace('_', ' ') + " | " + slice_docstring(v) + +def slice_docstring(desc): + if '///' in desc: + desc = desc.strip().split('///')[0] + return desc + +def ensure_short_description(description): + ''' the font is not fixed width, it makes litle sense to calculate chars ''' + hardcoded_maxlen = 20 + if description: + if len(description) > hardcoded_maxlen: + description = description[:hardcoded_maxlen] + description = ' | ' + description + return description + +def ensure_valid_show_string(item): + nodetype = getattr(bpy.types, item[0]) + loop_reverse[nodetype.bl_label] = item[0] + description = slice_docstring(nodetype.bl_rna.description).strip() + return nodetype.bl_label + ensure_short_description(description) + +def function_iterator(module_file): + for name in ddir(module_file): + obj = getattr(module_file, name) + if callable(obj): + yield name, obj.__doc__ + +def get_main_macro_module(fullpath): + if os.path.exists(fullpath): + print('--- first time getting sv_macro_module --- ') + spec = getutil.spec_from_file_location("macro_module.name", fullpath) + macro_module = getutil.module_from_spec(spec) + spec.loader.exec_module(macro_module) + local_macros['sv_macro_module'] = macro_module + return macro_module + +def fx_extend(idx, datastorage, filepath): + + datafiles = os.path.join(bpy.utils.user_resource('DATAFILES', path='sverchok', create=True)) + fullpath = os.path.join(datafiles, filepath) + + # load from previous obtained module, else get from fullpath. + macro_module = local_macros.get('sv_macro_module') + if not macro_module: + macro_module = get_main_macro_module(fullpath) + if not macro_module: + return + + for func_name, func_descriptor in function_iterator(macro_module): + datastorage.append((func_name, format_macro_item(func_name, func_descriptor), '', idx)) + idx +=1 + + +def gather_items(): + fx = [] + idx = 0 + for _, node_list in node_cats.items(): + for item in node_list: + if item[0] in {'separator', 'NodeReroute'}: + continue + + fx.append((str(idx), ensure_valid_show_string(item), '', idx)) + idx += 1 + + for k, v in macros.items(): + fx.append((k, format_item(k, v), '', idx)) + idx += 1 + + fx_extend(idx, fx, 'user_macros/macros.py') + + return fx + + +def item_cb(self, context): + return loop.get('results') + + +class SvExtraSearch(bpy.types.Operator): + """ Extra Search library """ + bl_idname = "node.sv_extra_search" + bl_label = "Extra Search" + bl_property = "my_enum" + + my_enum = bpy.props.EnumProperty(items=item_cb) + + def bl_idname_from_bl_label(self, context): + macro_result = loop['results'][int(self.my_enum)] + bl_label = macro_result[1].split(' | ')[0].strip() + return loop_reverse[bl_label] + + def execute(self, context): + # print(context.space_data.cursor_location) (in nodeview space) + # self.report({'INFO'}, "Selected: %s" % self.my_enum) + if self.my_enum.isnumeric(): + macro_bl_idname = self.bl_idname_from_bl_label(self) + DefaultMacros.ensure_nodetree(self, context) + bpy.ops.node.sv_macro_interpretter(macro_bl_idname=macro_bl_idname) + else: + macro_reference = macros.get(self.my_enum) + + if macro_reference: + handler, term = macro_reference.get('ident') + getattr(DefaultMacros, handler)(self, context, term) + + elif hasattr(local_macros['sv_macro_module'], self.my_enum): + func = getattr(local_macros['sv_macro_module'], self.my_enum) + func(self, context) + + return {'FINISHED'} + + def invoke(self, context, event): + context.space_data.cursor_location_from_region(event.mouse_region_x, event.mouse_region_y) + loop['results'] = gather_items() + wm = context.window_manager + wm.invoke_search_popup(self) + return {'FINISHED'} + + +classes = [SvExtraSearch,] + + +def register(): + for class_name in classes: + bpy.utils.register_class(class_name) + + +def unregister(): + for class_name in classes: + bpy.utils.unregister_class(class_name) diff --git a/utils/sv_macro_utils.py b/utils/sv_macro_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..2f027a84afc09ec2440eb0d77e4f0b19905ba0f6 --- /dev/null +++ b/utils/sv_macro_utils.py @@ -0,0 +1,96 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import re + +import bpy +from bpy.props import StringProperty + +import sverchok + + +# pylint: disable=w0141 +# pylint: disable=w0123 + + + +def convert_string_to_settings(arguments): + + # expects (varname=value,....) + # for example (selected_mode="int", fruits=20, alama=[0,0,0]) + def deform_args(**args): + return args + + unsorted_dict = eval('deform_args{arguments}'.format(**vars()), locals(), locals()) + pattern = r'(\w+\s*)=' + results = re.findall(pattern, arguments) + return [(varname, unsorted_dict[varname]) for varname in results] + + +class SvMacroInterpretter(bpy.types.Operator): + """ Launch menu item as a macro """ + bl_idname = "node.sv_macro_interpretter" + bl_label = "Sverchok check for new minor version" + bl_options = {'REGISTER'} + + macro_bl_idname = StringProperty() + settings = StringProperty() + + def create_node(self, context, node_type): + space = context.space_data + tree = space.edit_tree + + # select only the new node + for n in tree.nodes: + n.select = False + + node = tree.nodes.new(type=node_type) + + if self.settings: + settings = convert_string_to_settings(self.settings) + for name, value in settings: + try: + setattr(node, name, value) + except AttributeError as e: + self.report({'ERROR_INVALID_INPUT'}, "Node has no attribute " + name) + print(str(e)) + + node.select = True + tree.nodes.active = node + node.location = space.cursor_location + return node + + + def execute(self, context): + self.create_node(context, self.macro_bl_idname) + bpy.ops.node.translate_attach_remove_on_cancel('INVOKE_DEFAULT') + return {'FINISHED'} + + +classes = (SvMacroInterpretter,) + + +def register(): + for class_name in classes: + bpy.utils.register_class(class_name) + + +def unregister(): + for class_name in classes: + bpy.utils.unregister_class(class_name) +