Merge branch 'master' into blender2.8
This commit is contained in:
200
release/scripts/modules/bl_app_override/__init__.py
Normal file
200
release/scripts/modules/bl_app_override/__init__.py
Normal file
@@ -0,0 +1,200 @@
|
||||
# ##### 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 #####
|
||||
|
||||
# <pep8-80 compliant>
|
||||
|
||||
"""
|
||||
Module to manage overriding various parts of Blender.
|
||||
|
||||
Intended for use with 'app_templates', though it can be used from anywhere.
|
||||
"""
|
||||
|
||||
|
||||
# TODO, how to check these aren't from add-ons.
|
||||
# templates might need to un-register while filtering.
|
||||
def class_filter(cls_parent, **kw):
|
||||
whitelist = kw.pop("whitelist", None)
|
||||
blacklist = kw.pop("blacklist", None)
|
||||
kw_items = tuple(kw.items())
|
||||
for cls in cls_parent.__subclasses__():
|
||||
# same as is_registered()
|
||||
if "bl_rna" in cls.__dict__:
|
||||
if blacklist is not None and cls.__name__ in blacklist:
|
||||
continue
|
||||
if ((whitelist is not None and cls.__name__ is whitelist) or
|
||||
all((getattr(cls, attr) in expect) for attr, expect in kw_items)):
|
||||
yield cls
|
||||
|
||||
|
||||
def ui_draw_filter_register(
|
||||
*,
|
||||
ui_ignore_classes=None,
|
||||
ui_ignore_operator=None,
|
||||
ui_ignore_property=None,
|
||||
ui_ignore_menu=None,
|
||||
ui_ignore_label=None,
|
||||
):
|
||||
import bpy
|
||||
|
||||
UILayout = bpy.types.UILayout
|
||||
|
||||
if ui_ignore_classes is None:
|
||||
ui_ignore_classes = (
|
||||
bpy.types.Panel,
|
||||
bpy.types.Menu,
|
||||
bpy.types.Header,
|
||||
)
|
||||
|
||||
class OperatorProperties_Fake:
|
||||
pass
|
||||
|
||||
class UILayout_Fake(bpy.types.UILayout):
|
||||
__slots__ = ()
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
# ensure we always pass down UILayout_Fake instances
|
||||
if attr in {"row", "split", "column", "box", "column_flow"}:
|
||||
real_func = UILayout.__getattribute__(self, attr)
|
||||
|
||||
def dummy_func(*args, **kw):
|
||||
# print("wrapped", attr)
|
||||
ret = real_func(*args, **kw)
|
||||
return UILayout_Fake(ret)
|
||||
return dummy_func
|
||||
|
||||
elif attr in {"operator", "operator_menu_enum", "operator_enum"}:
|
||||
if ui_ignore_operator is None:
|
||||
return UILayout.__getattribute__(self, attr)
|
||||
|
||||
real_func = UILayout.__getattribute__(self, attr)
|
||||
|
||||
def dummy_func(*args, **kw):
|
||||
# print("wrapped", attr)
|
||||
if not ui_ignore_operator(args[0]):
|
||||
ret = real_func(*args, **kw)
|
||||
else:
|
||||
# UILayout.__getattribute__(self, "label")()
|
||||
# may need to be set
|
||||
ret = OperatorProperties_Fake()
|
||||
return ret
|
||||
return dummy_func
|
||||
|
||||
elif attr in {"prop", "prop_enum"}:
|
||||
if ui_ignore_property is None:
|
||||
return UILayout.__getattribute__(self, attr)
|
||||
|
||||
real_func = UILayout.__getattribute__(self, attr)
|
||||
|
||||
def dummy_func(*args, **kw):
|
||||
# print("wrapped", attr)
|
||||
if not ui_ignore_property(args[0].__class__.__name__, args[1]):
|
||||
ret = real_func(*args, **kw)
|
||||
else:
|
||||
ret = None
|
||||
return ret
|
||||
return dummy_func
|
||||
|
||||
elif attr == "menu":
|
||||
if ui_ignore_menu is None:
|
||||
return UILayout.__getattribute__(self, attr)
|
||||
|
||||
real_func = UILayout.__getattribute__(self, attr)
|
||||
|
||||
def dummy_func(*args, **kw):
|
||||
# print("wrapped", attr)
|
||||
if not ui_ignore_menu(args[0]):
|
||||
ret = real_func(*args, **kw)
|
||||
else:
|
||||
ret = None
|
||||
return ret
|
||||
return dummy_func
|
||||
|
||||
elif attr == "label":
|
||||
if ui_ignore_label is None:
|
||||
return UILayout.__getattribute__(self, attr)
|
||||
|
||||
real_func = UILayout.__getattribute__(self, attr)
|
||||
|
||||
def dummy_func(*args, **kw):
|
||||
# print("wrapped", attr)
|
||||
if not ui_ignore_label(args[0] if args else kw.get("text", "")):
|
||||
ret = real_func(*args, **kw)
|
||||
else:
|
||||
# ret = real_func()
|
||||
ret = None
|
||||
return ret
|
||||
return dummy_func
|
||||
else:
|
||||
return UILayout.__getattribute__(self, attr)
|
||||
# print(self, attr)
|
||||
|
||||
def operator(*args, **kw):
|
||||
return super().operator(*args, **kw)
|
||||
|
||||
def draw_override(func_orig, self_real, context):
|
||||
# simple, no wrapping
|
||||
# return func_orig(self_wrap, context)
|
||||
|
||||
class Wrapper(self_real.__class__):
|
||||
__slots__ = ()
|
||||
def __getattribute__(self, attr):
|
||||
if attr == "layout":
|
||||
return UILayout_Fake(self_real.layout)
|
||||
else:
|
||||
cls = super()
|
||||
try:
|
||||
return cls.__getattr__(self, attr)
|
||||
except AttributeError:
|
||||
# class variable
|
||||
try:
|
||||
return getattr(cls, attr)
|
||||
except AttributeError:
|
||||
# for preset bl_idname access
|
||||
return getattr(UILayout(self), attr)
|
||||
|
||||
@property
|
||||
def layout(self):
|
||||
# print("wrapped")
|
||||
return self_real.layout
|
||||
|
||||
return func_orig(Wrapper(self_real), context)
|
||||
|
||||
ui_ignore_store = []
|
||||
|
||||
for cls in ui_ignore_classes:
|
||||
for subcls in list(cls.__subclasses__()):
|
||||
if "draw" in subcls.__dict__: # don't want to get parents draw()
|
||||
|
||||
def replace_draw():
|
||||
# function also serves to hold draw_old in a local name-space
|
||||
draw_orig = subcls.draw
|
||||
|
||||
def draw(self, context):
|
||||
return draw_override(draw_orig, self, context)
|
||||
subcls.draw = draw
|
||||
|
||||
ui_ignore_store.append((subcls, "draw", subcls.draw))
|
||||
|
||||
replace_draw()
|
||||
|
||||
return ui_ignore_store
|
||||
|
||||
|
||||
def ui_draw_filter_unregister(ui_ignore_store):
|
||||
for (obj, attr, value) in ui_ignore_store:
|
||||
setattr(obj, attr, value)
|
167
release/scripts/modules/bl_app_override/helpers.py
Normal file
167
release/scripts/modules/bl_app_override/helpers.py
Normal file
@@ -0,0 +1,167 @@
|
||||
# ##### 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 #####
|
||||
|
||||
# <pep8-80 compliant>
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# AppOverrideState
|
||||
|
||||
|
||||
class AppOverrideState:
|
||||
"""
|
||||
Utility class to encapsulate overriding the application state
|
||||
so that settings can be restored afterwards.
|
||||
"""
|
||||
__slots__ = (
|
||||
# setup_classes
|
||||
"_class_store",
|
||||
# setup_ui_ignore
|
||||
"_ui_ignore_store",
|
||||
# setup_addons
|
||||
"_addon_store",
|
||||
)
|
||||
|
||||
# ---------
|
||||
# Callbacks
|
||||
#
|
||||
# Set as None, to make it simple to check if they're being overridden.
|
||||
|
||||
# setup/teardown classes
|
||||
class_ignore = None
|
||||
|
||||
# setup/teardown ui_ignore
|
||||
ui_ignore_classes = None
|
||||
ui_ignore_operator = None
|
||||
ui_ignore_property = None
|
||||
ui_ignore_menu = None
|
||||
ui_ignore_label = None
|
||||
|
||||
addon_paths = None
|
||||
addons = None
|
||||
|
||||
# End callbacks
|
||||
|
||||
def __init__(self):
|
||||
self._class_store = None
|
||||
self._addon_store = None
|
||||
self._ui_ignore_store = None
|
||||
|
||||
def _setup_classes(self):
|
||||
import bpy
|
||||
assert(self._class_store is None)
|
||||
self._class_store = self.class_ignore()
|
||||
from bpy.utils import unregister_class
|
||||
for cls in self._class_store:
|
||||
unregister_class(cls)
|
||||
|
||||
def _teardown_classes(self):
|
||||
assert(self._class_store is not None)
|
||||
|
||||
from bpy.utils import register_class
|
||||
for cls in self._class_store:
|
||||
register_class(cls)
|
||||
self._class_store = None
|
||||
|
||||
def _setup_ui_ignore(self):
|
||||
import bl_app_override
|
||||
|
||||
self._ui_ignore_store = bl_app_override.ui_draw_filter_register(
|
||||
ui_ignore_classes=(
|
||||
None if self.ui_ignore_classes is None
|
||||
else self.ui_ignore_classes()
|
||||
),
|
||||
ui_ignore_operator=self.ui_ignore_operator,
|
||||
ui_ignore_property=self.ui_ignore_property,
|
||||
ui_ignore_menu=self.ui_ignore_menu,
|
||||
ui_ignore_label=self.ui_ignore_label,
|
||||
)
|
||||
|
||||
def _teardown_ui_ignore(self):
|
||||
import bl_app_override
|
||||
bl_app_override.ui_draw_filter_unregister(
|
||||
self._ui_ignore_store
|
||||
)
|
||||
self._ui_ignore_store = None
|
||||
|
||||
def _setup_addons(self):
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys_path = []
|
||||
if self.addon_paths is not None:
|
||||
for path in self.addon_paths():
|
||||
if path not in sys.path:
|
||||
sys.path.append(path)
|
||||
|
||||
import addon_utils
|
||||
addons = []
|
||||
if self.addons is not None:
|
||||
addons.extend(self.addons())
|
||||
for addon in addons:
|
||||
addon_utils.enable(addon)
|
||||
|
||||
self._addon_store = {
|
||||
"sys_path": sys_path,
|
||||
"addons": addons,
|
||||
}
|
||||
|
||||
def _teardown_addons(self):
|
||||
import sys
|
||||
|
||||
sys_path = self._addon_store["sys_path"]
|
||||
for path in sys_path:
|
||||
# should always succeed, but if not it doesn't matter
|
||||
# (someone else was changing the sys.path), ignore!
|
||||
try:
|
||||
sys.path.remove(path)
|
||||
except:
|
||||
pass
|
||||
|
||||
addons = self._addon_store["addons"]
|
||||
import addon_utils
|
||||
for addon in addons:
|
||||
addon_utils.disable(addon)
|
||||
|
||||
self._addon_store.clear()
|
||||
self._addon_store = None
|
||||
|
||||
def setup(self):
|
||||
if self.class_ignore is not None:
|
||||
self._setup_classes()
|
||||
|
||||
if any((self.addon_paths,
|
||||
self.addons,
|
||||
)):
|
||||
self._setup_addons()
|
||||
|
||||
if any((self.ui_ignore_operator,
|
||||
self.ui_ignore_property,
|
||||
self.ui_ignore_menu,
|
||||
self.ui_ignore_label,
|
||||
)):
|
||||
self._setup_ui_ignore()
|
||||
|
||||
def teardown(self):
|
||||
if self._class_store is not None:
|
||||
self._teardown_classes()
|
||||
|
||||
if self._addon_store is not None:
|
||||
self._teardown_addons()
|
||||
|
||||
if self._ui_ignore_store is not None:
|
||||
self._teardown_ui_ignore()
|
198
release/scripts/modules/bl_app_template_utils.py
Normal file
198
release/scripts/modules/bl_app_template_utils.py
Normal file
@@ -0,0 +1,198 @@
|
||||
# ##### 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 #####
|
||||
|
||||
# <pep8-80 compliant>
|
||||
|
||||
"""
|
||||
Similar to ``addon_utils``, except we can only have one active at a time.
|
||||
|
||||
In most cases users of this module will simply call 'activate'.
|
||||
"""
|
||||
|
||||
__all__ = (
|
||||
"activate",
|
||||
"import_from_path",
|
||||
"import_from_id",
|
||||
"reset",
|
||||
)
|
||||
|
||||
import bpy as _bpy
|
||||
|
||||
# Normally matches 'user_preferences.app_template_id',
|
||||
# but loading new preferences will get us out of sync.
|
||||
_app_template = {
|
||||
"id": "",
|
||||
}
|
||||
|
||||
# instead of sys.modules
|
||||
# note that we only ever have one template enabled at a time
|
||||
# so it may not seem necessary to use this.
|
||||
#
|
||||
# However, templates may want to share between each-other,
|
||||
# so any loaded modules are stored here?
|
||||
#
|
||||
# Note that the ID here is the app_template_id , not the modules __name__.
|
||||
_modules = {}
|
||||
|
||||
|
||||
def _enable(template_id, *, handle_error=None, ignore_not_found=False):
|
||||
import os
|
||||
import sys
|
||||
from bpy_restrict_state import RestrictBlend
|
||||
|
||||
if handle_error is None:
|
||||
def handle_error(ex):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# Split registering up into 2 steps so we can undo
|
||||
# if it fails par way through.
|
||||
|
||||
# disable the context, using the context at all is
|
||||
# really bad while loading an template, don't do it!
|
||||
with RestrictBlend():
|
||||
|
||||
# 1) try import
|
||||
try:
|
||||
mod = import_from_id(template_id, ignore_not_found=ignore_not_found)
|
||||
if mod is None:
|
||||
return None
|
||||
mod.__template_enabled__ = False
|
||||
_modules[template_id] = mod
|
||||
except Exception as ex:
|
||||
handle_error(ex)
|
||||
return None
|
||||
|
||||
# 2) try run the modules register function
|
||||
try:
|
||||
mod.register()
|
||||
except Exception as ex:
|
||||
print("Exception in module register(): %r" %
|
||||
getattr(mod, "__file__", template_id))
|
||||
handle_error(ex)
|
||||
del _modules[template_id]
|
||||
return None
|
||||
|
||||
# * OK loaded successfully! *
|
||||
mod.__template_enabled__ = True
|
||||
|
||||
if _bpy.app.debug_python:
|
||||
print("\tapp_template_utils.enable", mod.__name__)
|
||||
|
||||
return mod
|
||||
|
||||
|
||||
def _disable(template_id, *, handle_error=None):
|
||||
"""
|
||||
Disables a template by name.
|
||||
|
||||
:arg template_id: The name of the template and module.
|
||||
:type template_id: string
|
||||
:arg handle_error: Called in the case of an error,
|
||||
taking an exception argument.
|
||||
:type handle_error: function
|
||||
"""
|
||||
import sys
|
||||
|
||||
if handle_error is None:
|
||||
def handle_error(ex):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
mod = _modules.get(template_id)
|
||||
|
||||
if mod and getattr(mod, "__template_enabled__", False) is not False:
|
||||
mod.__template_enabled__ = False
|
||||
|
||||
try:
|
||||
mod.unregister()
|
||||
except Exception as ex:
|
||||
print("Exception in module unregister(): %r" %
|
||||
getattr(mod, "__file__", template_id))
|
||||
handle_error(ex)
|
||||
else:
|
||||
print("\tapp_template_utils.disable: %s not %s." %
|
||||
(template_id, "disabled" if mod is None else "loaded"))
|
||||
|
||||
if _bpy.app.debug_python:
|
||||
print("\tapp_template_utils.disable", template_id)
|
||||
|
||||
|
||||
def import_from_path(path, ignore_not_found=False):
|
||||
import os
|
||||
from importlib import import_module
|
||||
base_module, template_id = path.rsplit(os.sep, 2)[-2:]
|
||||
module_name = base_module + "." + template_id
|
||||
|
||||
try:
|
||||
return import_module(module_name)
|
||||
except ModuleNotFoundError as ex:
|
||||
if ignore_not_found and ex.name == module_name:
|
||||
return None
|
||||
raise ex
|
||||
|
||||
|
||||
def import_from_id(template_id, ignore_not_found=False):
|
||||
import os
|
||||
path = next(iter(_bpy.utils.app_template_paths(template_id)), None)
|
||||
if path is None:
|
||||
if ignore_not_found:
|
||||
return None
|
||||
else:
|
||||
raise Exception("%r template not found!" % template_id)
|
||||
else:
|
||||
if ignore_not_found:
|
||||
if not os.path.exists(os.path.join(path, "__init__.py")):
|
||||
return None
|
||||
return import_from_path(path, ignore_not_found=ignore_not_found)
|
||||
|
||||
|
||||
def activate(template_id=None):
|
||||
template_id_prev = _app_template["id"]
|
||||
|
||||
# not needed but may as well avoid activating same template
|
||||
# ... in fact keep this, it will show errors early on!
|
||||
"""
|
||||
if template_id_prev == template_id:
|
||||
return
|
||||
"""
|
||||
|
||||
if template_id_prev:
|
||||
_disable(template_id_prev)
|
||||
|
||||
# Disable all addons, afterwards caller must reset.
|
||||
import addon_utils
|
||||
addon_utils.disable_all()
|
||||
|
||||
# ignore_not_found so modules that don't contain scripts don't raise errors
|
||||
mod = _enable(template_id, ignore_not_found=True) if template_id else None
|
||||
|
||||
_app_template["id"] = template_id
|
||||
|
||||
|
||||
def reset(*, reload_scripts=False):
|
||||
"""
|
||||
Sets default state.
|
||||
"""
|
||||
template_id = _bpy.context.user_preferences.app_template
|
||||
if _bpy.app.debug_python:
|
||||
print("bl_app_template_utils.reset('%s')" % template_id)
|
||||
|
||||
# TODO reload_scripts
|
||||
|
||||
activate(template_id)
|
@@ -32,6 +32,7 @@ __all__ = (
|
||||
"preset_find",
|
||||
"preset_paths",
|
||||
"refresh_script_paths",
|
||||
"app_template_paths",
|
||||
"register_class",
|
||||
"register_module",
|
||||
"register_manual_map",
|
||||
@@ -245,6 +246,12 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
|
||||
for mod in modules_from_path(path, loaded_modules):
|
||||
test_register(mod)
|
||||
|
||||
# load template (if set)
|
||||
if any(_bpy.utils.app_template_paths()):
|
||||
import bl_app_template_utils
|
||||
bl_app_template_utils.reset(reload_scripts=reload_scripts)
|
||||
del bl_app_template_utils
|
||||
|
||||
# deal with addons separately
|
||||
_initialize = getattr(_addon_utils, "_initialize", None)
|
||||
if _initialize is not None:
|
||||
@@ -356,6 +363,38 @@ def refresh_script_paths():
|
||||
_sys_path_ensure(path)
|
||||
|
||||
|
||||
def app_template_paths(subdir=None):
|
||||
"""
|
||||
Returns valid application template paths.
|
||||
|
||||
:arg subdir: Optional subdir.
|
||||
:type subdir: string
|
||||
:return: app template paths.
|
||||
:rtype: generator
|
||||
"""
|
||||
|
||||
# note: LOCAL, USER, SYSTEM order matches script resolution order.
|
||||
subdir_tuple = (subdir,) if subdir is not None else ()
|
||||
|
||||
path = _os.path.join(*(
|
||||
resource_path('LOCAL'), "scripts", "startup",
|
||||
"bl_app_templates_user", *subdir_tuple))
|
||||
if _os.path.isdir(path):
|
||||
yield path
|
||||
else:
|
||||
path = _os.path.join(*(
|
||||
resource_path('USER'), "scripts", "startup",
|
||||
"bl_app_templates_user", *subdir_tuple))
|
||||
if _os.path.isdir(path):
|
||||
yield path
|
||||
|
||||
path = _os.path.join(*(
|
||||
resource_path('SYSTEM'), "scripts", "startup",
|
||||
"bl_app_templates_system", *subdir_tuple))
|
||||
if _os.path.isdir(path):
|
||||
yield path
|
||||
|
||||
|
||||
def preset_paths(subdir):
|
||||
"""
|
||||
Returns a list of paths for a specific preset.
|
||||
|
@@ -21,7 +21,10 @@ import bpy
|
||||
import os
|
||||
from bpy.types import Operator
|
||||
from bpy.props import FloatProperty
|
||||
from mathutils import Vector, Matrix
|
||||
from mathutils import (
|
||||
Vector,
|
||||
Matrix,
|
||||
)
|
||||
|
||||
|
||||
def CLIP_spaces_walk(context, all_screens, tarea, tspace, callback, *args):
|
||||
@@ -1084,4 +1087,4 @@ classes = (
|
||||
CLIP_OT_track_settings_as_default,
|
||||
CLIP_OT_track_settings_to_track,
|
||||
CLIP_OT_track_to_empty,
|
||||
)
|
||||
)
|
||||
|
@@ -16,13 +16,15 @@
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import bpy
|
||||
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
StringProperty,
|
||||
)
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
StringProperty,
|
||||
)
|
||||
|
||||
|
||||
class SCENE_OT_freestyle_fill_range_by_selection(bpy.types.Operator):
|
||||
|
@@ -21,7 +21,10 @@
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
|
||||
from bpy.props import EnumProperty, IntProperty
|
||||
from bpy.props import (
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
)
|
||||
|
||||
|
||||
class MeshMirrorUV(Operator):
|
||||
@@ -254,4 +257,4 @@ classes = (
|
||||
MeshMirrorUV,
|
||||
MeshSelectNext,
|
||||
MeshSelectPrev,
|
||||
)
|
||||
)
|
||||
|
@@ -21,16 +21,16 @@
|
||||
import bpy
|
||||
import nodeitems_utils
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
PropertyGroup,
|
||||
)
|
||||
Operator,
|
||||
PropertyGroup,
|
||||
)
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
|
||||
|
||||
class NodeSetting(PropertyGroup):
|
||||
|
@@ -21,12 +21,12 @@
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
)
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
|
||||
|
||||
class SelectPattern(Operator):
|
||||
@@ -1054,4 +1054,4 @@ classes = (
|
||||
SubdivisionSet,
|
||||
TransformsToDeltas,
|
||||
TransformsToDeltasAnim,
|
||||
)
|
||||
)
|
||||
|
@@ -26,13 +26,14 @@ from mathutils import Vector
|
||||
def GlobalBB_LQ(bb_world):
|
||||
|
||||
# Initialize the variables with the 8th vertex
|
||||
left, right, front, back, down, up = (bb_world[7][0],
|
||||
bb_world[7][0],
|
||||
bb_world[7][1],
|
||||
bb_world[7][1],
|
||||
bb_world[7][2],
|
||||
bb_world[7][2],
|
||||
)
|
||||
left, right, front, back, down, up = (
|
||||
bb_world[7][0],
|
||||
bb_world[7][0],
|
||||
bb_world[7][1],
|
||||
bb_world[7][1],
|
||||
bb_world[7][2],
|
||||
bb_world[7][2],
|
||||
)
|
||||
|
||||
# Test against the other 7 verts
|
||||
for i in range(7):
|
||||
@@ -398,13 +399,15 @@ class AlignObjects(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
align_axis = self.align_axis
|
||||
ret = align_objects(context,
|
||||
'X' in align_axis,
|
||||
'Y' in align_axis,
|
||||
'Z' in align_axis,
|
||||
self.align_mode,
|
||||
self.relative_to,
|
||||
self.bb_quality)
|
||||
ret = align_objects(
|
||||
context,
|
||||
'X' in align_axis,
|
||||
'Y' in align_axis,
|
||||
'Z' in align_axis,
|
||||
self.align_mode,
|
||||
self.relative_to,
|
||||
self.bb_quality,
|
||||
)
|
||||
|
||||
if not ret:
|
||||
self.report({'WARNING'}, "No objects with bound-box selected")
|
||||
@@ -415,4 +418,4 @@ class AlignObjects(Operator):
|
||||
|
||||
classes = (
|
||||
AlignObjects,
|
||||
)
|
||||
)
|
||||
|
@@ -22,12 +22,12 @@ from mathutils import Vector
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
)
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
FloatVectorProperty,
|
||||
)
|
||||
|
||||
|
||||
def object_ensure_material(obj, mat_name):
|
||||
@@ -652,4 +652,4 @@ classes = (
|
||||
QuickFluid,
|
||||
QuickFur,
|
||||
QuickSmoke,
|
||||
)
|
||||
)
|
||||
|
@@ -20,8 +20,10 @@
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import IntProperty
|
||||
from bpy.props import EnumProperty
|
||||
from bpy.props import (
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
)
|
||||
|
||||
|
||||
class CopyRigidbodySettings(Operator):
|
||||
@@ -315,4 +317,4 @@ classes = (
|
||||
BakeToKeyframes,
|
||||
ConnectRigidBodies,
|
||||
CopyRigidbodySettings,
|
||||
)
|
||||
)
|
||||
|
@@ -18,7 +18,11 @@
|
||||
|
||||
# TODO <pep8 compliant>
|
||||
|
||||
from mathutils import Matrix, Vector, geometry
|
||||
from mathutils import (
|
||||
Matrix,
|
||||
Vector,
|
||||
geometry,
|
||||
)
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
|
||||
@@ -1104,4 +1108,4 @@ class SmartProject(Operator):
|
||||
|
||||
classes = (
|
||||
SmartProject,
|
||||
)
|
||||
)
|
||||
|
@@ -21,12 +21,12 @@
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import (
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
@@ -130,6 +130,20 @@ def execute_context_assign(self, context):
|
||||
return operator_path_undo_return(context, data_path)
|
||||
|
||||
|
||||
def module_filesystem_remove(path_base, module_name):
|
||||
import os
|
||||
module_name = os.path.splitext(module_name)[0]
|
||||
for f in os.listdir(path_base):
|
||||
f_base = os.path.splitext(f)[0]
|
||||
if f_base == module_name:
|
||||
f_full = os.path.join(path_base, f)
|
||||
|
||||
if os.path.isdir(f_full):
|
||||
os.rmdir(f_full)
|
||||
else:
|
||||
os.remove(f_full)
|
||||
|
||||
|
||||
class BRUSH_OT_active_index_set(Operator):
|
||||
"""Set active sculpt/paint brush from it's number"""
|
||||
bl_idname = "brush.active_index_set"
|
||||
@@ -907,7 +921,10 @@ def _wm_doc_get_id(doc_id, do_url=True, url_prefix=""):
|
||||
# an operator (common case - just button referencing an op)
|
||||
if hasattr(bpy.types, class_name.upper() + "_OT_" + class_prop):
|
||||
if do_url:
|
||||
url = ("%s/bpy.ops.%s.html#bpy.ops.%s.%s" % (url_prefix, class_name, class_name, class_prop))
|
||||
url = (
|
||||
"%s/bpy.ops.%s.html#bpy.ops.%s.%s" %
|
||||
(url_prefix, class_name, class_name, class_prop)
|
||||
)
|
||||
else:
|
||||
rna = "bpy.ops.%s.%s" % (class_name, class_prop)
|
||||
else:
|
||||
@@ -922,7 +939,10 @@ def _wm_doc_get_id(doc_id, do_url=True, url_prefix=""):
|
||||
class_name, class_prop = class_name.split("_OT_", 1)
|
||||
class_name = class_name.lower()
|
||||
if do_url:
|
||||
url = ("%s/bpy.ops.%s.html#bpy.ops.%s.%s" % (url_prefix, class_name, class_name, class_prop))
|
||||
url = (
|
||||
"%s/bpy.ops.%s.html#bpy.ops.%s.%s" %
|
||||
(url_prefix, class_name, class_name, class_prop)
|
||||
)
|
||||
else:
|
||||
rna = "bpy.ops.%s.%s" % (class_name, class_prop)
|
||||
else:
|
||||
@@ -938,9 +958,12 @@ def _wm_doc_get_id(doc_id, do_url=True, url_prefix=""):
|
||||
rna_parent = rna_parent.base
|
||||
|
||||
if do_url:
|
||||
url = ("%s/bpy.types.%s.html#bpy.types.%s.%s" % (url_prefix, class_name, class_name, class_prop))
|
||||
url = (
|
||||
"%s/bpy.types.%s.html#bpy.types.%s.%s" %
|
||||
(url_prefix, class_name, class_name, class_prop)
|
||||
)
|
||||
else:
|
||||
rna = ("bpy.types.%s.%s" % (class_name, class_prop))
|
||||
rna = "bpy.types.%s.%s" % (class_name, class_prop)
|
||||
else:
|
||||
# We assume this is custom property, only try to generate generic url/rna_id...
|
||||
if do_url:
|
||||
@@ -1087,10 +1110,10 @@ class WM_OT_properties_edit(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
from rna_prop_ui import (
|
||||
rna_idprop_ui_prop_get,
|
||||
rna_idprop_ui_prop_clear,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
rna_idprop_ui_prop_get,
|
||||
rna_idprop_ui_prop_clear,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
|
||||
data_path = self.data_path
|
||||
value = self.value
|
||||
@@ -1267,9 +1290,9 @@ class WM_OT_properties_add(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
from rna_prop_ui import (
|
||||
rna_idprop_ui_prop_get,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
rna_idprop_ui_prop_get,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
|
||||
data_path = self.data_path
|
||||
item = eval("context.%s" % data_path)
|
||||
@@ -1284,10 +1307,10 @@ class WM_OT_properties_add(Operator):
|
||||
|
||||
return prop_new
|
||||
|
||||
prop = unique_name(
|
||||
{*item.keys(),
|
||||
*type(item).bl_rna.properties.keys(),
|
||||
})
|
||||
prop = unique_name({
|
||||
*item.keys(),
|
||||
*type(item).bl_rna.properties.keys(),
|
||||
})
|
||||
|
||||
item[prop] = 1.0
|
||||
rna_idprop_ui_prop_update(item, prop)
|
||||
@@ -1327,9 +1350,9 @@ class WM_OT_properties_remove(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
from rna_prop_ui import (
|
||||
rna_idprop_ui_prop_clear,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
rna_idprop_ui_prop_clear,
|
||||
rna_idprop_ui_prop_update,
|
||||
)
|
||||
data_path = self.data_path
|
||||
item = eval("context.%s" % data_path)
|
||||
prop = self.property
|
||||
@@ -1367,7 +1390,10 @@ class WM_OT_appconfig_default(Operator):
|
||||
filepath = os.path.join(bpy.utils.preset_paths("interaction")[0], "blender.py")
|
||||
|
||||
if os.path.exists(filepath):
|
||||
bpy.ops.script.execute_preset(filepath=filepath, menu_idname="USERPREF_MT_interaction_presets")
|
||||
bpy.ops.script.execute_preset(
|
||||
filepath=filepath,
|
||||
menu_idname="USERPREF_MT_interaction_presets",
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -1387,7 +1413,10 @@ class WM_OT_appconfig_activate(Operator):
|
||||
filepath = self.filepath.replace("keyconfig", "interaction")
|
||||
|
||||
if os.path.exists(filepath):
|
||||
bpy.ops.script.execute_preset(filepath=filepath, menu_idname="USERPREF_MT_interaction_presets")
|
||||
bpy.ops.script.execute_preset(
|
||||
filepath=filepath,
|
||||
menu_idname="USERPREF_MT_interaction_presets",
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -1492,7 +1521,7 @@ class WM_OT_blenderplayer_start(Operator):
|
||||
"-g", "show_profile", "=", "%d" % gs.show_framerate_profile,
|
||||
"-g", "show_properties", "=", "%d" % gs.show_debug_properties,
|
||||
"-g", "ignore_deprecation_warnings", "=", "%d" % (not gs.use_deprecation_warnings),
|
||||
])
|
||||
])
|
||||
|
||||
# finish the call with the path to the blend file
|
||||
args.append(filepath)
|
||||
@@ -1622,10 +1651,11 @@ class WM_OT_keyconfig_export(Operator):
|
||||
|
||||
wm = context.window_manager
|
||||
|
||||
keyconfig_utils.keyconfig_export(wm,
|
||||
wm.keyconfigs.active,
|
||||
self.filepath,
|
||||
)
|
||||
keyconfig_utils.keyconfig_export(
|
||||
wm,
|
||||
wm.keyconfigs.active,
|
||||
self.filepath,
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
@@ -1890,7 +1920,10 @@ class WM_OT_theme_install(Operator):
|
||||
|
||||
try:
|
||||
shutil.copyfile(xmlfile, path_dest)
|
||||
bpy.ops.script.execute_preset(filepath=path_dest, menu_idname="USERPREF_MT_interface_theme_presets")
|
||||
bpy.ops.script.execute_preset(
|
||||
filepath=path_dest,
|
||||
menu_idname="USERPREF_MT_interface_theme_presets",
|
||||
)
|
||||
|
||||
except:
|
||||
traceback.print_exc()
|
||||
@@ -1917,10 +1950,12 @@ class WM_OT_addon_refresh(Operator):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
# Note: shares some logic with WM_OT_app_template_install
|
||||
# but not enough to de-duplicate. Fixed here may apply to both.
|
||||
class WM_OT_addon_install(Operator):
|
||||
"Install an add-on"
|
||||
bl_idname = "wm.addon_install"
|
||||
bl_label = "Install from File..."
|
||||
bl_label = "Install Add-on from File..."
|
||||
|
||||
overwrite = BoolProperty(
|
||||
name="Overwrite",
|
||||
@@ -1951,20 +1986,6 @@ class WM_OT_addon_install(Operator):
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _module_remove(path_addons, module):
|
||||
import os
|
||||
module = os.path.splitext(module)[0]
|
||||
for f in os.listdir(path_addons):
|
||||
f_base = os.path.splitext(f)[0]
|
||||
if f_base == module:
|
||||
f_full = os.path.join(path_addons, f)
|
||||
|
||||
if os.path.isdir(f_full):
|
||||
os.rmdir(f_full)
|
||||
else:
|
||||
os.remove(f_full)
|
||||
|
||||
def execute(self, context):
|
||||
import addon_utils
|
||||
import traceback
|
||||
@@ -2017,7 +2038,7 @@ class WM_OT_addon_install(Operator):
|
||||
|
||||
if self.overwrite:
|
||||
for f in file_to_extract.namelist():
|
||||
WM_OT_addon_install._module_remove(path_addons, f)
|
||||
module_filesystem_remove(path_addons, f)
|
||||
else:
|
||||
for f in file_to_extract.namelist():
|
||||
path_dest = os.path.join(path_addons, os.path.basename(f))
|
||||
@@ -2035,7 +2056,7 @@ class WM_OT_addon_install(Operator):
|
||||
path_dest = os.path.join(path_addons, os.path.basename(pyfile))
|
||||
|
||||
if self.overwrite:
|
||||
WM_OT_addon_install._module_remove(path_addons, os.path.basename(pyfile))
|
||||
module_filesystem_remove(path_addons, os.path.basename(pyfile))
|
||||
elif os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
return {'CANCELLED'}
|
||||
@@ -2070,7 +2091,10 @@ class WM_OT_addon_install(Operator):
|
||||
bpy.utils.refresh_script_paths()
|
||||
|
||||
# print message
|
||||
msg = tip_("Modules Installed from %r into %r (%s)") % (pyfile, path_addons, ", ".join(sorted(addons_new)))
|
||||
msg = (
|
||||
tip_("Modules Installed (%s) from %r into %r (%s)") %
|
||||
(", ".join(sorted(addons_new)), pyfile, path_addons)
|
||||
)
|
||||
print(msg)
|
||||
self.report({'INFO'}, msg)
|
||||
|
||||
@@ -2164,6 +2188,7 @@ class WM_OT_addon_expand(Operator):
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class WM_OT_addon_userpref_show(Operator):
|
||||
"Show add-on user preferences"
|
||||
bl_idname = "wm.addon_userpref_show"
|
||||
@@ -2194,6 +2219,123 @@ class WM_OT_addon_userpref_show(Operator):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
# Note: shares some logic with WM_OT_addon_install
|
||||
# but not enough to de-duplicate. Fixes here may apply to both.
|
||||
class WM_OT_app_template_install(Operator):
|
||||
"Install an application-template"
|
||||
bl_idname = "wm.app_template_install"
|
||||
bl_label = "Install Template from File..."
|
||||
|
||||
overwrite = BoolProperty(
|
||||
name="Overwrite",
|
||||
description="Remove existing template with the same ID",
|
||||
default=True,
|
||||
)
|
||||
|
||||
filepath = StringProperty(
|
||||
subtype='FILE_PATH',
|
||||
)
|
||||
filter_folder = BoolProperty(
|
||||
name="Filter folders",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
filter_python = BoolProperty(
|
||||
name="Filter python",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
filter_glob = StringProperty(
|
||||
default="*.py;*.zip",
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
import traceback
|
||||
import zipfile
|
||||
import shutil
|
||||
import os
|
||||
|
||||
pyfile = self.filepath
|
||||
|
||||
path_app_templates = bpy.utils.user_resource(
|
||||
'SCRIPTS', os.path.join("startup", "bl_app_templates_user"),
|
||||
create=True,
|
||||
)
|
||||
|
||||
if not path_app_templates:
|
||||
self.report({'ERROR'}, "Failed to get add-ons path")
|
||||
return {'CANCELLED'}
|
||||
|
||||
if not os.path.isdir(path_app_templates):
|
||||
try:
|
||||
os.makedirs(path_app_templates, exist_ok=True)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
app_templates_old = set(os.listdir(path_app_templates))
|
||||
|
||||
# check to see if the file is in compressed format (.zip)
|
||||
if zipfile.is_zipfile(pyfile):
|
||||
try:
|
||||
file_to_extract = zipfile.ZipFile(pyfile, 'r')
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return {'CANCELLED'}
|
||||
|
||||
if self.overwrite:
|
||||
for f in file_to_extract.namelist():
|
||||
module_filesystem_remove(path_app_templates, f)
|
||||
else:
|
||||
for f in file_to_extract.namelist():
|
||||
path_dest = os.path.join(path_app_templates, os.path.basename(f))
|
||||
if os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
return {'CANCELLED'}
|
||||
|
||||
try: # extract the file to "bl_app_templates_user"
|
||||
file_to_extract.extractall(path_app_templates)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return {'CANCELLED'}
|
||||
|
||||
else:
|
||||
path_dest = os.path.join(path_app_templates, os.path.basename(pyfile))
|
||||
|
||||
if self.overwrite:
|
||||
module_filesystem_remove(path_app_templates, os.path.basename(pyfile))
|
||||
elif os.path.exists(path_dest):
|
||||
self.report({'WARNING'}, "File already installed to %r\n" % path_dest)
|
||||
return {'CANCELLED'}
|
||||
|
||||
# if not compressed file just copy into the addon path
|
||||
try:
|
||||
shutil.copyfile(pyfile, path_dest)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
return {'CANCELLED'}
|
||||
|
||||
app_templates_new = set(os.listdir(path_app_templates)) - app_templates_old
|
||||
|
||||
# in case a new module path was created to install this addon.
|
||||
bpy.utils.refresh_script_paths()
|
||||
|
||||
# print message
|
||||
msg = (
|
||||
tip_("Template Installed (%s) from %r into %r") %
|
||||
(", ".join(sorted(app_templates_new)), pyfile, path_app_templates)
|
||||
)
|
||||
print(msg)
|
||||
self.report({'INFO'}, msg)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
wm.fileselect_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
classes = (
|
||||
BRUSH_OT_active_index_set,
|
||||
WM_OT_addon_disable,
|
||||
@@ -2203,6 +2345,7 @@ classes = (
|
||||
WM_OT_addon_refresh,
|
||||
WM_OT_addon_remove,
|
||||
WM_OT_addon_userpref_show,
|
||||
WM_OT_app_template_install,
|
||||
WM_OT_appconfig_activate,
|
||||
WM_OT_appconfig_default,
|
||||
WM_OT_blenderplayer_start,
|
||||
@@ -2246,4 +2389,4 @@ classes = (
|
||||
WM_OT_sysinfo,
|
||||
WM_OT_theme_install,
|
||||
WM_OT_url_open,
|
||||
)
|
||||
)
|
||||
|
@@ -127,6 +127,18 @@ class INFO_MT_file(Menu):
|
||||
layout.operator("wm.save_homefile", icon='SAVE_PREFS')
|
||||
layout.operator("wm.read_factory_settings", icon='LOAD_FACTORY')
|
||||
|
||||
if any(bpy.utils.app_template_paths()):
|
||||
app_template = context.user_preferences.app_template
|
||||
if app_template:
|
||||
layout.operator(
|
||||
"wm.read_factory_settings",
|
||||
text="Load Factory Template Settings",
|
||||
icon='LOAD_FACTORY',
|
||||
).app_template = app_template
|
||||
del app_template
|
||||
|
||||
layout.menu("USERPREF_MT_app_templates", icon='FILE_BLEND')
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator_context = 'INVOKE_AREA'
|
||||
|
@@ -90,6 +90,63 @@ class USERPREF_MT_interaction_presets(Menu):
|
||||
draw = Menu.draw_preset
|
||||
|
||||
|
||||
class USERPREF_MT_app_templates(Menu):
|
||||
bl_label = "Application Templates"
|
||||
preset_subdir = "app_templates"
|
||||
|
||||
def draw_ex(self, context, *, use_splash=False, use_default=False, use_install=False):
|
||||
import os
|
||||
|
||||
layout = self.layout
|
||||
|
||||
# now draw the presets
|
||||
layout.operator_context = 'EXEC_DEFAULT'
|
||||
|
||||
if use_default:
|
||||
props = layout.operator("wm.read_homefile", text="Default")
|
||||
props.use_splash = True
|
||||
props.app_template = ""
|
||||
layout.separator()
|
||||
|
||||
template_paths = bpy.utils.app_template_paths()
|
||||
|
||||
# expand template paths
|
||||
app_templates = []
|
||||
for path in template_paths:
|
||||
for d in os.listdir(path):
|
||||
if d.startswith(("__", ".")):
|
||||
continue
|
||||
template = os.path.join(path, d)
|
||||
if os.path.isdir(template):
|
||||
# template_paths_expand.append(template)
|
||||
app_templates.append(d)
|
||||
|
||||
for d in sorted(app_templates):
|
||||
props = layout.operator(
|
||||
"wm.read_homefile",
|
||||
text=bpy.path.display_name(d),
|
||||
)
|
||||
props.use_splash = True
|
||||
props.app_template = d;
|
||||
|
||||
if use_install:
|
||||
layout.separator()
|
||||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
props = layout.operator("wm.app_template_install")
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
self.draw_ex(context, use_splash=False, use_default=True, use_install=True)
|
||||
|
||||
|
||||
class USERPREF_MT_templates_splash(Menu):
|
||||
bl_label = "Startup Templates"
|
||||
preset_subdir = "templates"
|
||||
|
||||
def draw(self, context):
|
||||
USERPREF_MT_app_templates.draw_ex(self, context, use_splash=True, use_default=True)
|
||||
|
||||
|
||||
class USERPREF_MT_appconfigs(Menu):
|
||||
bl_label = "AppPresets"
|
||||
preset_subdir = "keyconfig"
|
||||
@@ -110,7 +167,17 @@ class USERPREF_MT_splash(Menu):
|
||||
|
||||
split = layout.split()
|
||||
row = split.row()
|
||||
row.label("")
|
||||
|
||||
if any(bpy.utils.app_template_paths()):
|
||||
row.label("Template:")
|
||||
template = context.user_preferences.app_template
|
||||
row.menu(
|
||||
"USERPREF_MT_templates_splash",
|
||||
text=bpy.path.display_name(template) if template else "Default",
|
||||
)
|
||||
else:
|
||||
row.label("")
|
||||
|
||||
row = split.row()
|
||||
row.label("Interaction:")
|
||||
|
||||
@@ -1485,6 +1552,8 @@ classes = (
|
||||
USERPREF_HT_header,
|
||||
USERPREF_PT_tabs,
|
||||
USERPREF_MT_interaction_presets,
|
||||
USERPREF_MT_templates_splash,
|
||||
USERPREF_MT_app_templates,
|
||||
USERPREF_MT_appconfigs,
|
||||
USERPREF_MT_splash,
|
||||
USERPREF_MT_splash_footer,
|
||||
|
@@ -19,7 +19,11 @@
|
||||
# <pep8 compliant>
|
||||
import bpy
|
||||
import nodeitems_utils
|
||||
from nodeitems_utils import NodeCategory, NodeItem, NodeItemCustom
|
||||
from nodeitems_utils import (
|
||||
NodeCategory,
|
||||
NodeItem,
|
||||
NodeItemCustom,
|
||||
)
|
||||
|
||||
|
||||
# Subclasses for standard node types
|
||||
|
Reference in New Issue
Block a user