123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- # ##### 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 '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):
- 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
- """
- 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 avoids redundant
- # disable/enable for all add-ons on 'File -> New'
- 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.preferences.app_template
- if _bpy.app.debug_python:
- print("bl_app_template_utils.reset('%s')" % template_id)
- # TODO reload_scripts
- activate(template_id)
|