forked from bartvdbraak/blender
f68145011f
This adds the ability to switch between different application-configurations without interfering with Blender's normal operation. This commit doesn't include any templates, so its mostly to allow collaboration for the Blender 101 project and other custom configurations. Application templates can be installed & selected from the file menu. Other details: - The `bl_app_template_utils` module handles template activation (similar to `addon_utils`). - The `bl_app_override` module is a general module to assist scripts overriding parts of Blender in reversible way. See docs: https://docs.blender.org/manual/en/dev/advanced/app_templates.html See patch: D2565
168 lines
4.7 KiB
Python
168 lines
4.7 KiB
Python
# ##### 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()
|