# 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 scripts, it is recommended to place the icons inside the # script 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""" enum_items = [] if context is None: return enum_items wm = context.window_manager 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. filepath = os.path.join(directory, name) icon = pcoll.get(name) if not icon: thumb = pcoll.load(name, filepath, 'IMAGE') else: thumb = pcoll[name] enum_items.append((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 = layout.row() row.prop(wm, "my_previews_dir") row = layout.row() row.template_icon_view(wm, "my_previews") row = layout.row() row.prop(wm, "my_previews") # We can store multiple preview collections here, # however in this example we only store "main" preview_collections = {} def register(): from bpy.types import WindowManager from bpy.props import ( StringProperty, EnumProperty, ) WindowManager.my_previews_dir = StringProperty( name="Folder Path", subtype='DIR_PATH', default="" ) WindowManager.my_previews = EnumProperty( items=enum_previews_from_directory_items, ) # Note that preview collections returned by bpy.utils.previews # are regular Python objects - you can use them to store custom data. # # This is especially useful here, since: # - It avoids us regenerating the whole enum over and over. # - It can store enum_items' strings # (remember you have to keep those strings somewhere in py, # else they get freed and Blender references invalid memory!). import bpy.utils.previews pcoll = bpy.utils.previews.new() pcoll.my_previews_dir = "" pcoll.my_previews = () preview_collections["main"] = pcoll bpy.utils.register_class(PreviewsExamplePanel) def unregister(): from bpy.types import WindowManager del WindowManager.my_previews for pcoll in preview_collections.values(): bpy.utils.previews.remove(pcoll) preview_collections.clear() bpy.utils.unregister_class(PreviewsExamplePanel) if __name__ == "__main__": register()