Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Menu presets: automatic application. #4856

Merged
merged 2 commits into from
Jan 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 32 additions & 28 deletions settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
from bpy.types import AddonPreferences
from bpy.props import BoolProperty, FloatVectorProperty, EnumProperty, IntProperty, FloatProperty, StringProperty

from sverchok.ui.utils import message_on_layout
from sverchok.ui.utils import (
MENU_TYPE_DEFAULT, MENU_TYPE_SVERCHOK, MENU_TYPE_USER,
get_sverchok_menu_presets_directory, get_user_menu_presets_directory,
get_menu_preset_path,
datafiles,
message_on_layout
)

"""Don't import other Sverchok modules here"""

Expand All @@ -26,18 +32,6 @@
draw_extra_addons = None
apply_theme, rebuild_color_cache, color_callback = [None] * 3

MENU_TYPE_DEFAULT = '__DEFAULT__'
MENU_TYPE_SVERCHOK = '__SVERCHOK__'
MENU_TYPE_USER = '__USER__'

datafiles = join(bpy.utils.user_resource('DATAFILES', path='sverchok', create=True))

def get_sverchok_menu_presets_directory():
return join(dirname(__file__), 'menus')

def get_user_menu_presets_directory():
return join(datafiles, 'menus')

def get_params(prop_names_and_fallbacks, direct=False):
"""
This function returns an object which you can use the . op on.
Expand Down Expand Up @@ -207,14 +201,7 @@ class SvOverwriteMenuFile(bpy.types.Operator):

def execute(self, context):
target_menu_file = join(datafiles, 'index.yaml')
preset_type, preset_name = self.preset_path.split(os.sep)
if preset_type == MENU_TYPE_DEFAULT:
directory = dirname(__file__)
elif preset_type == MENU_TYPE_SVERCHOK:
directory = get_sverchok_menu_presets_directory()
else: # MENU_TYPE_USER
directory = get_user_menu_presets_directory()
preset_path = join(directory, preset_name)
preset_path = get_menu_preset_path(self.preset_path)
shutil.copy(preset_path, target_menu_file)
self.report({'INFO'}, f"Menu preset {preset_path} saved as {target_menu_file}. Please restart Blender to see effect.")
return {'FINISHED'}
Expand Down Expand Up @@ -460,19 +447,36 @@ def get_menu_presets(self, context):
items = get_menu_presets,
)

menu_usage_options = [
('SVERCHOK', "Use preset file", "Original menu preset file will be used. So the menu will be updated automatically when the preset is updated within Sverchok distribution", 0),
('COPY', "Use local copy of preset file", "Menu preset file will be copied under your datafiles directory. You may edit it manually without touching the preset file in Sverchok directory. But the responsibility of updating the menu when a node is added into Sverchok is yours", 1)
]

menu_preset_usage : EnumProperty(
name = "Application mode",
description = "Menu preset application mode",
items = menu_usage_options,
default = 'SVERCHOK'
)

def general_tab(self, layout):
col = layout.row().column()
col_split = col.split(factor=0.5)
col1 = col_split.column()

row = col1.row()
split = row.split(factor=0.85)
split_prop = split.column()
split_prop.prop(self, 'menu_preset')
split_op = split.column()
op = split_op.operator(SvOverwriteMenuFile.bl_idname, text="Set")
op.preset_path = self.menu_preset
box = col1.box()
box.label(text = "Menu presets:")
menu_col = box.column()
menu_col.prop(self, 'menu_preset', text='Preset')
if self.menu_preset_usage == 'COPY':
menu_split = menu_col.split(factor=0.8)
split_prop = menu_split.column()
split_prop.prop(self, 'menu_preset_usage', text='')
op_split = menu_split.column()
op = op_split.operator(SvOverwriteMenuFile.bl_idname, text="Copy")
op.preset_path = self.menu_preset
else:
menu_col.prop(self, 'menu_preset_usage', text='')

col1.prop(self, "external_editor", text="Ext Editor")
col1.prop(self, "real_sverchok_path", text="Src Directory")
Expand Down
6 changes: 3 additions & 3 deletions ui/add_nodes_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def node_search_update(self, context):
return

categories = defaultdict(list)
for cat in sm.add_node_menu.walk_categories():
for cat in sm.get_add_node_menu().walk_categories():
for add_node in cat:
if not isinstance(add_node, sm.AddNode):
continue
Expand All @@ -52,7 +52,7 @@ def node_search_update(self, context):

def select_category_update(self, context):
cat_name = context.scene.sv_add_node_panel_settings.selected_category
for cat in sm.add_node_menu.walk_categories():
for cat in sm.get_add_node_menu().walk_categories():
if cat.menu_cls.__name__ == cat_name:
items = [n for n in cat if isinstance(n, (sm.AddNode, sm.Separator))]
AddNodeToolPanel._items = items
Expand Down Expand Up @@ -121,7 +121,7 @@ class AddNodePanelSettings(bpy.types.PropertyGroup):
def categories(self, context):
# this should be a function because new categories can be added
# by Sverchok's extensions after the registration
for i, category in enumerate(sm.add_node_menu.walk_categories()):
for i, category in enumerate(sm.get_add_node_menu().walk_categories()):
if any(isinstance(add_node, sm.AddNode) for add_node in category):
identifier = category.menu_cls.__name__
yield identifier, category.name, category.name, i
Expand Down
4 changes: 2 additions & 2 deletions ui/color_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from sverchok.utils.logging import debug
import sverchok
from sverchok.utils.handle_blender_data import BlTrees
from sverchok.ui.nodeview_space_menu import add_node_menu
from sverchok.ui.nodeview_space_menu import get_add_node_menu

colors_cache = {}

Expand Down Expand Up @@ -100,7 +100,7 @@ def sv_colors_definition():
sv_node_colors = default_theme
sv_cats_node = {}

for cat in add_node_menu.walk_categories():
for cat in get_add_node_menu().walk_categories():
for elem in cat:
if not hasattr(elem, 'bl_idname'):
continue
Expand Down
38 changes: 32 additions & 6 deletions ui/nodeview_space_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@
from sverchok.ui.sv_icons import node_icon, icon, get_icon_switch
from sverchok.ui import presets
from sverchok.ui.presets import apply_default_preset
from sverchok.ui.utils import get_menu_preset_path
from sverchok.utils.context_managers import sv_preferences
from sverchok.utils import yaml_parser
from sverchok.utils.modules_inspection import iter_classes_from_module
from sverchok.utils.logging import info, debug


"""
Expand Down Expand Up @@ -466,13 +469,36 @@ def draw(self, context):
for elem in self.draw_data:
elem.draw(self.layout)

add_node_menu = None

def setup_add_menu():
global add_node_menu
datafiles = Path(bpy.utils.user_resource('DATAFILES', path='sverchok', create=True))

datafiles = Path(bpy.utils.user_resource('DATAFILES', path='sverchok', create=True))
menu_file = datafiles / 'index.yaml'
if not menu_file.exists():
default_menu_file = Path(__file__).parents[1] / 'index.yaml'
shutil.copy(default_menu_file, menu_file)
add_node_menu = Category.from_config(yaml_parser.load(menu_file), 'All Categories', icon_name='RNA')

with sv_preferences() as prefs:
if prefs is None:
raise Exception("Internal error: Sverchok preferences are not initialized yet at the moment of loading the menu")
if prefs.menu_preset_usage == 'COPY':
default_menu_file = get_menu_preset_path(prefs.menu_preset)
menu_file = datafiles / 'index.yaml'
use_preset_copy = True
else:
menu_file = get_menu_preset_path(prefs.menu_preset)
use_preset_copy = False

if use_preset_copy and not menu_file.exists():
info(f"Applying menu preset {default_menu_file} at startup")
shutil.copy(default_menu_file, menu_file)
debug(f"Using menu preset file: {menu_file}")
add_node_menu = Category.from_config(yaml_parser.load(menu_file), 'All Categories', icon_name='RNA')

def get_add_node_menu():
global add_node_menu
if add_node_menu is None:
setup_add_menu()
return add_node_menu
Comment on lines +497 to +501
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't agree less that the function represents functionality of the module. If it returns Category instances than it can/should be a class method of the Category. The same is about setup_add_menu.

Also I don't like a laze concept here and in general - it makes things more complicated than they could be. It probably would be valid to use if initialization of the menu was really in arbitrary place in code. I assume you don't know from where the menu is called what leads to treating code as a black box / magic. From my knowledge this is not good approach.

I had a look at the places and it seems that the menu is only used in panels and operators what means that if you need the settings to be available during initialization of the menu it's possible to initialize the menu in the register function.

def register():
    add_menu = Category.generate_main_menu()
    add_menu.register()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will not have objections if you rewrite it according to your suggestions :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm too tired to rewrite somebody code.


class AddNodeOp(bl_operators.node.NodeAddOperator):
extra_description: StringProperty()
Expand Down Expand Up @@ -658,7 +684,7 @@ def register():
for class_name in classes:
bpy.utils.register_class(class_name)
bpy.types.NODE_MT_add.append(sv_draw_menu)
add_node_menu.register()
get_add_node_menu().register()


def unregister():
Expand Down
4 changes: 2 additions & 2 deletions ui/sv_extra_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from sverchok.utils.logging import error
from sverchok.utils.docstring import SvDocstring
from sverchok.utils.sv_default_macros import macros, DefaultMacros
from sverchok.ui.nodeview_space_menu import add_node_menu
from sverchok.ui.nodeview_space_menu import get_add_node_menu


addon_name = sverchok.__name__
Expand Down Expand Up @@ -101,7 +101,7 @@ def gather_items(context):
fx = []
idx = 0

for cat in add_node_menu.walk_categories():
for cat in get_add_node_menu().walk_categories():
for item in cat:
if not hasattr(item, 'bl_idname'):
continue
Expand Down
11 changes: 7 additions & 4 deletions ui/sv_panel_display_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from sverchok.utils.logging import debug
from collections import namedtuple

from sverchok.ui.nodeview_space_menu import add_node_menu
from sverchok.ui.nodeview_space_menu import get_add_node_menu

_node_category_cache = {} # cache for the node categories
_spawned_nodes = {} # cache for the spawned nodes
Expand Down Expand Up @@ -115,14 +115,14 @@ def cache_node_categories():
if _node_category_cache:
return

categories = [c.name for c in add_node_menu.walk_categories()]
categories = [c.name for c in get_add_node_menu().walk_categories()]

_node_category_cache["categories"] = {}
_node_category_cache["categories"]["names"] = list(categories)
_node_category_cache["categories"]["names"].append("All")
_node_category_cache["categories"]["All"] = {}
_node_category_cache["categories"]["All"]["nodes"] = []
for cat in add_node_menu.walk_categories():
for cat in get_add_node_menu().walk_categories():
nodes = [n.bl_idname for n in cat if hasattr(n, 'bl_idname')]
nodes = list(filter(lambda node: should_display_node(node), nodes))
_node_category_cache["categories"][cat.name] = {}
Expand Down Expand Up @@ -251,7 +251,10 @@ def navigate_category(self, direction):
def category_items(self, context):
''' Get the items to display in the category enum property '''
cache_node_categories()
return _node_category_cache["categories"]["items"]
if _node_category_cache:
return _node_category_cache["categories"]["items"]
else:
return []

def arrange_nodes(self, context):
''' Arrange the nodes in current category (using bin-packing) '''
Expand Down
29 changes: 29 additions & 0 deletions ui/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@
#
# ##### END GPL LICENSE BLOCK #####

import os
from os.path import dirname, basename, join
from pathlib import Path
import textwrap

import bpy

def message_on_layout(layout, text, width=140, **kwargs):
box = layout.box()
lines = textwrap.wrap(text, width=width, **kwargs)
Expand All @@ -29,3 +34,27 @@ def enum_split(layout, node, prop_name, label, factor):
enum_row = layout.split(factor=factor, align=False)
enum_row.label(text=label)
enum_row.prop(node, prop_name, text="")

MENU_TYPE_DEFAULT = '__DEFAULT__'
MENU_TYPE_SVERCHOK = '__SVERCHOK__'
MENU_TYPE_USER = '__USER__'

datafiles = join(bpy.utils.user_resource('DATAFILES', path='sverchok', create=True))

def get_sverchok_menu_presets_directory():
return Path(__file__).parents[1] / 'menus'

def get_user_menu_presets_directory():
return join(datafiles, 'menus')

def get_menu_preset_path(preset_path):
preset_type, preset_name = preset_path.split(os.sep)
if preset_type == MENU_TYPE_DEFAULT:
directory = Path(__file__).parents[1]
elif preset_type == MENU_TYPE_SVERCHOK:
directory = get_sverchok_menu_presets_directory()
else: # MENU_TYPE_USER
directory = get_user_menu_presets_directory()
preset_path = join(directory, preset_name)
return preset_path