forked from bartvdbraak/blender
a80c1e50bc
Enum's itemf callback can be called without context in some cases (UI, doc generation...). Python's enum properties did not handle this at all - it's kind of odd this did not cause more trouble and wasn't notice earlier... Probably dynamic enums using context are not much used in py code. Note about nodes: those are heavy users of dynamic enum with context. Now, we expect `NodeCategory.poll()` and `NodeItem.poll()` to always be called with a valid context (since when there is no context available, we can assume `poll()` is always True). `NodeCategory.items()`, however, must accept NULL context, so if you use custom `items` callable for your custom node categories, you may need to update it (as was done here for builtin `node_group_items()`).
138 lines
4.0 KiB
Python
138 lines
4.0 KiB
Python
# 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)
|
|
thumb = pcoll.load(filepath, filepath, 'IMAGE')
|
|
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()
|