[Bf-blender-cvs] [d30f664] master: Expose PreviewImage & custom icons to py API.

Bastien Montagne noreply at git.blender.org
Mon May 11 16:38:20 CEST 2015


Commit: d30f664c0438e8378c79d5beb114b1338d0e1d94
Author: Bastien Montagne
Date:   Mon May 11 16:29:12 2015 +0200
Branches: master
https://developer.blender.org/rBd30f664c0438e8378c79d5beb114b1338d0e1d94

Expose PreviewImage & custom icons to py API.

This commit mainly:

* Exposes PreviewImage struct in RNA, including ways for user to set images data.
* Adds a new kind of PreviewImage, using a file path and IMB_thumb to get image.
* Adds a new kind of custom icon using PreviewImage, unrelated to ID previews system.
* Adds a python API (utils.previews) to allow python scripts to access those custom previews/icons.

Note that loading image from files' thumbnails is done when needed (deferred loading), not
when defining the custom preview/icon.

WARNING: for release addons who would want to use this, please keep it to a strict minimum, really needed level.
We do not want our UI to explode under hundreds of different flashy icons!

For more info, see also the release notes of Blender 2.75 (http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.75/Addons)
and the example/templates featured with Blender.

Patch by Campbell (ideasman42), Inês (brita) and Bastien (mont29).

Differential Revision: https://developer.blender.org/D1255

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

M	release/scripts/modules/bpy/utils/__init__.py
A	release/scripts/modules/bpy/utils/previews.py
A	release/scripts/templates_py/ui_previews_custom_icon.py
A	release/scripts/templates_py/ui_previews_dynamic_enum.py
M	source/blender/blenkernel/BKE_icons.h
M	source/blender/blenkernel/intern/icons.c
M	source/blender/blenkernel/intern/image.c
M	source/blender/blenkernel/intern/lamp.c
M	source/blender/blenkernel/intern/material.c
M	source/blender/blenkernel/intern/texture.c
M	source/blender/blenkernel/intern/world.c
M	source/blender/editors/include/ED_render.h
M	source/blender/editors/include/UI_interface_icons.h
M	source/blender/editors/interface/interface.c
M	source/blender/editors/interface/interface_icons.c
M	source/blender/editors/interface/interface_intern.h
M	source/blender/editors/interface/interface_templates.c
M	source/blender/editors/render/render_preview.c
M	source/blender/editors/render/render_update.c
M	source/blender/editors/space_image/image_ops.c
M	source/blender/imbuf/IMB_thumbs.h
M	source/blender/imbuf/intern/thumbs.c
M	source/blender/makesdna/DNA_ID.h
M	source/blender/makesrna/RNA_access.h
M	source/blender/makesrna/intern/rna_ID.c
M	source/blender/makesrna/intern/rna_brush.c
M	source/blender/makesrna/intern/rna_main_api.c
M	source/blender/python/intern/CMakeLists.txt
M	source/blender/python/intern/bpy.c
A	source/blender/python/intern/bpy_utils_previews.c
A	source/blender/python/intern/bpy_utils_previews.h
M	source/blender/windowmanager/intern/wm_init_exit.c

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

diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py
index 5f235ae..fa97714 100644
--- a/release/scripts/modules/bpy/utils/__init__.py
+++ b/release/scripts/modules/bpy/utils/__init__.py
@@ -38,6 +38,7 @@ __all__ = (
     "unregister_manual_map",
     "make_rna_paths",
     "manual_map",
+    "previews",
     "resource_path",
     "script_path_user",
     "script_path_pref",
diff --git a/release/scripts/modules/bpy/utils/previews.py b/release/scripts/modules/bpy/utils/previews.py
new file mode 100644
index 0000000..4e8adf8
--- /dev/null
+++ b/release/scripts/modules/bpy/utils/previews.py
@@ -0,0 +1,137 @@
+# ##### 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 compliant>
+
+"""
+This module contains utility functions to handle custom previews.
+
+It behaves as a high-level 'cached' previews manager.
+
+This allows addons to generate their own previews, and use them as icons in UI widgets
+('icon_value' of UILayout functions).
+"""
+
+__all__ = (
+    "new",
+    "remove",
+    )
+
+import _bpy
+_utils_previews = _bpy._utils_previews
+del _bpy
+
+
+_uuid_open = set()
+
+
+# High-level previews manager.
+# not accessed directly
+class _BPyImagePreviewCollection(dict):
+    """
+    Dict-like class of previews.
+    """
+
+    # Internal notes:
+    # - keys in the dict are stored by name
+    # - values are instances of bpy.types.ImagePreview
+    # - Blender's internal 'PreviewImage' struct uses 'self._uuid' prefix.
+
+    def __init__(self):
+        super().__init__()
+        self._uuid = hex(id(self))
+        _uuid_open.add(self._uuid)
+
+    def __del__(self):
+        if self._uuid not in _uuid_open:
+            return
+
+        raise ResourceWarning(
+                "<%s id=%s[%d]>: left open, remove with "
+                "'bpy.utils.previews.remove()'" %
+                (self.__class__.__name__, self._uuid, len(self)))
+        self.close()
+
+    def _gen_key(self, name):
+        return ":".join((self._uuid, name))
+
+    def new(self, name):
+        if name in self:
+            raise KeyException("key %r already exists")
+        p = self[name] = _utils_previews.new(
+                self._gen_key(name))
+        return p
+    new.__doc__ = _utils_previews.new.__doc__
+
+    def load(self, name, path, path_type, force_reload=False):
+        if name in self:
+            raise KeyException("key %r already exists")
+        p = self[name] = _utils_previews.load(
+                self._gen_key(name), path, path_type, force_reload)
+        return p
+    load.__doc__ = _utils_previews.load.__doc__
+
+    def release(self, name):
+        p = self.pop(name, None)
+        if p is not None:
+            _utils_previews.release(self._gen_key(name))
+    release.__doc__ = _utils_previews.release.__doc__
+
+    def clear(self):
+        for name in self.keys():
+            _utils_previews.release(self._gen_key(name))
+        super().clear()
+
+    def close(self):
+        self.clear()
+        _uuid_open.remove(self._uuid)
+
+    def __delitem__(self, key):
+        return self.release(key)
+
+    def __repr__(self):
+        return "<%s id=%s[%d], %s>" % (
+                self.__class__.__name__,
+                self._uuid,
+                len(self),
+                super().__repr__())
+
+
+def new():
+    """
+    Return a new preview collection.
+    """
+
+    return _BPyImagePreviewCollection()
+
+
+def remove(p):
+    """
+    Remove the specified previews collection.
+    """
+    p.close()
+
+
+# don't complain about resources on exit (only unregister)
+import atexit
+
+def exit_clear_warning():
+    del _BPyImagePreviewCollection.__del__
+
+atexit.register(exit_clear_warning)
+del atexit, exit_clear_warning
diff --git a/release/scripts/templates_py/ui_previews_custom_icon.py b/release/scripts/templates_py/ui_previews_custom_icon.py
new file mode 100644
index 0000000..defa2d2
--- /dev/null
+++ b/release/scripts/templates_py/ui_previews_custom_icon.py
@@ -0,0 +1,82 @@
+# This sample script demonstrates how to place a custom icon on a button or
+# menu entry.
+#
+# IMPORTANT NOTE: if you run this sample, there will be no icon in the button
+# You need to replace the image path with a real existing one.
+# For distributable addons, it is recommended to place the icons inside the
+# addon folder and access it relative to the py script file for portability
+#
+#
+# Other use cases for UI-previews:
+# - provide a fixed list of previews to select from
+# - provide a dynamic list of preview (eg. calculated from reading a directory)
+#
+# For the above use cases, see the template 'ui_previews_dynamic_enum.py"
+
+
+import os
+import bpy
+
+
+class PreviewsExamplePanel(bpy.types.Panel):
+    """Creates a Panel in the Object properties window"""
+    bl_label = "Previews Example Panel"
+    bl_idname = "OBJECT_PT_previews"
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+    bl_context = "object"
+
+    def draw(self, context):
+        layout = self.layout
+        pcoll = preview_collections["main"]
+
+        row = layout.row()
+        my_icon = pcoll.get("my_icon")
+        row.operator("render.render", icon_value=my_icon.icon_id)
+
+        # my_icon.icon_id can be used in any UI function that accepts
+        # icon_value # try also setting text=""
+        # to get an icon only operator button
+
+
+# We can store multiple preview collections here,
+# however in this example we only store "main"
+preview_collections = {}
+
+
+def register():
+
+    # Note that preview collections returned by bpy.utils.previews
+    # are regular py objects - you can use them to store custom data.
+    import bpy.utils.previews
+    pcoll = bpy.utils.previews.new()
+
+    # path to the folder where the icon is
+    # the path is calculated relative to this py file inside the addon folder
+    my_icons_dir = os.path.join(os.path.dirname(__file__), "icons")
+
+    # load a preview thumbnail of a file and store in the previews collection
+    pcoll.load(
+        # identifier
+        "my_icon",
+        # path to image
+        os.path.join(my_icons_dir, "icon-image.png"),
+        # file type to generate preview from. others are: MOVIE, FONT, BLEND
+        'IMAGE')
+
+    preview_collections["main"] = pcoll
+
+    bpy.utils.register_class(PreviewsExamplePanel)
+
+
+def unregister():
+
+    for pcoll in preview_collections.values():
+        bpy.utils.previews.remove(pcoll)
+    preview_collections.clear()
+
+    bpy.utils.unregister_class(PreviewsExamplePanel)
+
+
+if __name__ == "__main__":
+    register()
diff --git a/release/scripts/templates_py/ui_previews_dynamic_enum.py b/release/scripts/templates_py/ui_previews_dynamic_enum.py
new file mode 100644
index 0000000..1603df0
--- /dev/null
+++ b/release/scripts/templates_py/ui_previews_dynamic_enum.py
@@ -0,0 +1,136 @@
+# This sample script demonstrates a dynamic EnumProperty with custom icons.
+# The EnumProperty is populated dynamically with thumbnails of the contents of
+# a chosen directory in 'enum_previews_from_directory_items'.
+# Then, the same enum is displayed with different interfaces. Note that the
+# generated icon previews do not have Blender IDs, which means that they can
+# not be used with UILayout templates that require IDs,
+# such as template_list and template_ID_preview.
+#
+# Other use cases:
+# - make a fixed list of enum_items instead of calculating them in a function
+# - generate isolated thumbnails to use as custom icons in buttons
+#   and menu items
+#
+# For custom icons, see the template "ui_previews_custom_icon.py".
+#
+# For distributable addons, it is recommended to place the icons inside the
+# addon directory and access it relative to the py script file for portability:
+#
+#    os.path.join(os.path.dirname(__file__), "images")
+
+
+import os
+import bpy
+
+
+def enum_previews_from_directory_items(self, context):
+    """EnumProperty callback"""
+    wm = context.window_manager
+
+    enum_items = []
+    directory = wm.my_previews_dir
+
+    # Get the preview collection (defined in register func).
+    pcoll = preview_collections["main"]
+
+    if directory == pcoll.my_previews_dir:
+        return pcoll.my_previews
+
+    print("Scanning directory: %s" % directory)
+
+    if directory and os.path.exists(directory):
+        # Scan the directory for png files
+        image_paths = []
+        for fn in  os.listdir(directory):
+            if fn.lower().endswith(".png"):
+                image_paths.append(fn)
+
+        for i, name in enumerate(image_paths):
+            # generates a thumbnail preview for a file.
+            # Also works with previews for 'MOVIE', 'BLEND' and 'FONT'
+            filepath = os.path.join(directory, name)
+            thumb = pcoll.load(filepath, filepath, 'IMAGE')
+            # enum item: (identifier, name, description, icon, number)
+            enum_items.append((name, name, name, thumb.icon_id, i))
+
+    pcoll.my_previews = enum_items
+    pcoll.my_previews_dir = directory
+    return pcoll.my_previews
+
+
+class PreviewsExamplePanel(bpy.types.Panel):
+    """Creates a Panel in the Object properties window"""
+    bl_label = "Previews Example Panel"
+    bl_idname = "OBJECT_PT_previews"
+    bl_space_type = 'PROPERTIES'
+    bl_region_type = 'WINDOW'
+    bl_context = "object"
+
+    def draw(self, context):
+        layout = self.layout
+        wm = context.window_manager
+
+        row = lay

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list