[Bf-blender-cvs] [f68145011f] master: WM: Application Templates

Campbell Barton noreply at git.blender.org
Sat Mar 25 00:04:24 CET 2017


Commit: f68145011fd46d0e22145363e5b5d9a704a912df
Author: Campbell Barton
Date:   Sat Mar 25 09:29:51 2017 +1100
Branches: master
https://developer.blender.org/rBf68145011fd46d0e22145363e5b5d9a704a912df

WM: Application Templates

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

===================================================================

A	release/scripts/modules/bl_app_override/__init__.py
A	release/scripts/modules/bl_app_override/helpers.py
A	release/scripts/modules/bl_app_template_utils.py
M	release/scripts/modules/bpy/utils/__init__.py
M	release/scripts/startup/bl_operators/wm.py
M	release/scripts/startup/bl_ui/space_info.py
M	release/scripts/startup/bl_ui/space_userpref.py
M	source/blender/blenkernel/BKE_appdir.h
M	source/blender/blenkernel/BKE_blender.h
M	source/blender/blenkernel/intern/appdir.c
M	source/blender/blenkernel/intern/blender.c
M	source/blender/makesdna/DNA_userdef_types.h
M	source/blender/makesrna/intern/rna_userdef.c
M	source/blender/windowmanager/intern/wm_files.c
M	source/blender/windowmanager/intern/wm_init_exit.c
M	source/blender/windowmanager/intern/wm_operators.c
M	source/blender/windowmanager/wm_files.h

===================================================================

diff --git a/release/scripts/modules/bl_app_override/__init__.py b/release/scripts/modules/bl_app_override/__init__.py
new file mode 100644
index 0000000000..efd7c525e6
--- /dev/null
+++ b/release/scripts/modules/bl_app_override/__init__.py
@@ -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)
diff --git a/release/scripts/modules/bl_app_override/helpers.py b/release/scripts/modules/bl_app_override/helpers.py
new file mode 100644
index 0000000000..981039e8dd
--- /dev/null
+++ b/release/scripts/modules/bl_app_override/helpers.py
@@ -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=(
+    

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list