[Bf-blender-cvs] [2a7c66d9af] app-templates: Add install operator for templates

Campbell Barton noreply at git.blender.org
Fri Mar 24 23:12:18 CET 2017


Commit: 2a7c66d9afc130b19631700bf247c3d3586ee777
Author: Campbell Barton
Date:   Sat Mar 25 09:09:33 2017 +1100
Branches: app-templates
https://developer.blender.org/rB2a7c66d9afc130b19631700bf247c3d3586ee777

Add install operator for templates

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

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

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

diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 42f1e723d1..5393015229 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -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"
@@ -1917,10 +1931,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 +1967,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 +2019,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 +2037,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 +2072,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 +2169,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 +2200,124 @@ 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 addon_utils
+        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 +2327,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 +2371,4 @@ classes = (
     WM_OT_sysinfo,
     WM_OT_theme_install,
     WM_OT_url_open,
-)
\ No newline at end of file
+)
diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py
index c536c61627..a7b518dfd2 100644
--- a/release/scripts/startup/bl_ui/space_info.py
+++ b/release/scripts/startup/bl_ui/space_info.py
@@ -137,7 +137,7 @@ class INFO_MT_file(Menu):
                 ).app_template = app_template
             del app_template
 
-            layout.menu("USERPREF_MT_app_templates", icon='FILE_BLEND')
+        layout.menu("USERPREF_MT_app_templates", icon='FILE_BLEND')
 
         layout.separator()
 
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 5c06b79001..f4e2cf006b 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -94,7 +94,7 @@ 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):
+    def draw_ex(self, context, *, use_splash=False, use_default=False, use_install=False):
         import os
 
         layout = self.layout
@@ -129,8 +129,14 @@ class USERPREF_MT_app_templates(Menu):
             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)
+        self.draw_ex(context, use_splash=False, use_default=True, use_install=True)
 
 
 class USERPREF_MT_templates_splash(Menu):




More information about the Bf-blender-cvs mailing list