As example I made this simplified addon. Instead of having buttons, I want to show it thumbnails. Once a thumbnail is chosen it should activate a class (ADD_OT_cube1 or class ADD_OT_cube2). Here the example with just buttons:
import bpy
# Here comes bl_info
# Add cube 1 to scene
class ADD_OT_cube1(bpy.types.Operator):
bl_idname = 'add.cube1'
bl_description = 'Adds a cube. Blue, small.'
bl_category = 'SuperCube'
bl_label = 'Add Blue Cube'
def execute(self, context):
path = os.path.dirname(__file__) + "/ojbects/cubes.blend\\Collection\\"
object_name = "cube_01"
bpy.ops.wm.append(filename=object_name, directory=path)
return {"FINISHED"}
# Add cube 2 to scene
class ADD_OT_cube2(bpy.types.Operator):
bl_idname = 'add.cube2'
bl_description = 'Adds a cube. Red, Eating a banana.'
bl_category = 'SuperCube'
bl_label = 'Add Grazy Cube'
def execute(self, context):
path = os.path.dirname(__file__) + "/ojbects/cubes.blend\\Collection\\"
object_name = "cube_02"
bpy.ops.wm.append(filename=object_name, directory=path)
return {"FINISHED"}
# The menu in the N-Panel
class ADD_MT_menu(bpy.types.Menu):
bl_label = "Add Cubes"
bl_idname = "ADD_MT_menu"
def draw(self, context):
layout = self.layout
layout.operator("add.cube1")
layout.operator("add.cube2")
# Register Classes
classes = (
ADD_OT_cube1,
ADD_OT_cube2,
ADD_MT_menu)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
def unregister():
from bpy.utils import unregister_class
for cls in classes:
unregister_class(cls)
if __name__ == "__main__":
register()
I hope you can help me out.
There is the ui_previews_dynamic_enum.py template that comes with blender (which I am struggling with to understand i fully) but I don’t need the dynamic part since my previews are already defined. And I wonder how I can make it so that a selection of the thumbnail activates ADD_OT_cube1 or ADD_OT_cube1
And the template ui_previews_custom_icon.py is not what I am looking for because I have more than 20 thumbnails. That would end up in 20 big buttons I suppose.
Oh well, Found a tutorial that shows what I need on: https://www.youtube.com/watch?time_continue=1139&v=cnz02CCRThQ but there is no download. So I typed it over from the video, and tried to port it over to 2.80 here the results so far:
(Don’t run it, because it gives some nasty errors)
import os
import bpy
import bpy.utils.previews
def get_image(filepath):
name = os.path.basename(filepath)
if name not in bpy.data.images:
bpy.ops.image.open(filepath = filepath)
return bpy.data.images(name)
def open_image(self, context):
image = get_image(self.reference_images_enum)
for area in bpy.context.screen.areas :
if area.type == 'IMAGE_EDITOR' :
area.spaces.active.image = image
def get_previews_collection(self, context):
images = []
custom_icons = preview_collections["main"]
directory = "C:\Icons"
i = 0
if directory == custom_icons.my_previews_dir:
return custom_icons.my_previews
for file in os.listdir(directory):
if file.endswitch(".png"):
fullpath = os.path.join(directory, file)
custom_icons.load(file, fullpath, 'IMAGE')
images.append((fullpath, file, fullpath, custom_icons(file).icon_id, i))
i+=1
custom_icons.my_previews = images
custom_icons.my_previews_dir = directory
return custom_icons.my_previews
class ReferenceManagerPanel(bpy.types.Panel):
""" A customer Panel in the viewport toolbar """
bl_label = "Reference Images"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
def draw(self, context):
layout = self.layout
row = layout.row
row.template_icon_view(context.window_manager, "reference_images_enum")
row = layout.row
row.prop(context.window_manager, "reference_images_enum")
preview_collections = {}
def register():
bpy.types.WindowManager.reference_images_enum = bpy.props.EnumProperty(
items = get_previews_collection,
update = open_image
)
preview_collection = bpy.utils.previews.new()
preview_collection.my_previews_dir = ""
preview_collection.my_previews = ()
preview_collections['main'] = preview_collection
bpy.utils.register_class(ReferenceManagerPanel)
def unregister():
del bpy.types.Scene.reference_images_enum
for preview_collection in preview_collections.values():
bpy.utils.previews.remove(preview_collection)
preview_collections.clear()
bpy.utils.unregister_class(ReferenceManagerPanel)
if __name__ == "__main__":
register()
The error with this script:
Exception ignored in: <function ImagePreviewCollection.__del__ at 0x00000285630A2510>
Traceback (most recent call last):
File "C:\Users\Flatron\Downloads\blender-280-April_15\blender-2.80.0-git.860a9f979d60-windows64\2.80\scripts\modules\bpy\utils\previews.py", line 79, in __del__
f"{self!r}: left open, remove with "
ResourceWarning: <ImagePreviewCollection id=0x285689d9678[0], <super: <class 'ImagePreviewCollection'>, <ImagePreviewCollection object>>>: left open, remove with 'bpy.utils.previews.remove()'
Exception ignored in: <function ImagePreviewCollection.__del__ at 0x00000285630A2510>
Traceback (most recent call last):
File "C:\Users\Flatron\Downloads\blender-280-April_15\blender-2.80.0-git.860a9f979d60-windows64\2.80\scripts\modules\bpy\utils\previews.py", line 79, in __del__
f"{self!r}: left open, remove with "
ResourceWarning: <ImagePreviewCollection id=0x285689d99e8[0], <super: <class 'ImagePreviewCollection'>, <ImagePreviewCollection object>>>: left open, remove with 'bpy.utils.previews.remove()'
Trying to figure out how it works by analysing Simple Asset Manager. Here some notes:
Simple Asset Manager:
Found in def execute_instert(context, link):
- selected_preview = bpy.data.windows_managers["WinMan"].asset_manager_prevs
- append_element(selected_preview, link) , refers to function to append_elements.
Or:
in Class SAM_ReplaceMaterialButton(bpy.types.Operator):
Is an example of a class that performs action depending on what preview is chosen.
selected_preview = wm.asset_manager_prevs
files = append_material(selected_preview)
gen_thumbnails(image_paths, enum_items, pcoll, empty_path, directory):
is a function that seems to fill the previews.
it is using pcoll: pcoll.load(filepath, imgpath, 'IMAGE')
enum_previews_from_directory_items(self, context):
A function that seems to manage previews in to a collection or something.
related to: preferences, directory, pcoll,
returns pcoll.asset_manager_prevs
def SAM_UI(self.context):
This is a function that makes the UI where previews are shown. Interesting is:
- row.template_icon_view(wm, "asset_manager_prevs", show_labels=True)
class SAM_panel(bpy.types.Panel):
Is a class that uses the previous function to show UI
class SAM_Popup(bpy.types.Operator):
Seems to be the same but then a popup?
def register():
WindowManager.asset_manager_prev_dir = StringProperty(
name="Folder Path",
subtype='DIR_PATH',
default="")
WindowManager.asset_manager_prevs = EnumProperty(
items=enum_preview_from_directory_items
pcoll = bpy.utils.previews.new()
pcoll.asset_manager_prev_dir = ""
pcoll.asset_manager_prevs = ""
preview_collections["main"] = pcoll
bpy.types.Scene.asset_manager = PointerProperty(
type=SimpleAssetManager)
bpy.types.VIEW3D_MT_add.append(SAM_button)
def unregister():
to unregister related as in register.
I thought it would be relatively easy to get thumbnails.
Have you used the template from 2.80 : ui_previews_dynamic_enum.py ?
And bpy.context.area.type = ‘IMAGE_EDITOR’ is now bpy.context.area.ui_type == ‘VIEW’
@mkbreuer
Thanks, that looks a bit easier to analyse then the bigger addons. And it does exactly what I need. I am not a hero yet in coding so that I easily can convert it to 2.8, but relieving that I am looking in the right direction now. I hope I can manage it in a few hours.
I didn’t manage it yet to convert (even the simple addon) to 2.80. But I managed to get the reference manager (typed it over from video) at least showing up now after a bit more tweaks:
import os
import bpy
import bpy.utils.previews
def get_image(filepath):
name = os.path.basename(filepath)
if name not in bpy.data.images:
bpy.ops.image.open(filepath = filepath)
return bpy.data.images(name)
def open_image(self, context):
image = get_image(self.reference_images_enum)
for area in bpy.context.screen.areas :
if area.ui_type == 'VIEW' :
area.spaces.active.image = image
def get_previews_collection(self, context):
images = []
custom_icons = preview_collections["main"]
directory = "C:\Icons"
i = 0
if directory == custom_icons.my_previews_dir:
return custom_icons.my_previews
for file in os.listdir(directory):
if file.endswith(".png"):
fullpath = os.path.join(directory, file)
custom_icons.load(file, fullpath, 'IMAGE')
images.append(( fullpath, file, fullpath, custom_icons(file).icon_id, i))
i+=1
custom_icons.my_previews = images
custom_icons.my_previews_dir = directory
return custom_icons.my_previews
class ReferenceManagerPanel(bpy.types.Panel):
""" A custom Panel in the viewport toolbar """
bl_label = "Reference Images"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
def draw(self, context):
layout = self.layout
row = layout.row()
row.template_icon_view(context.window_manager, "reference_images_enum")
row = layout.row()
row.prop(context.window_manager, "reference_images_enum")
preview_collections = {}
def register():
bpy.types.WindowManager.reference_images_enum = bpy.props.EnumProperty(
items = get_previews_collection,
update = open_image
)
preview_collection = bpy.utils.previews.new()
preview_collection.my_previews_dir = ""
preview_collection.my_previews = ()
preview_collections['main'] = preview_collection
bpy.utils.register_class(ReferenceManagerPanel)
def unregister():
del bpy.types.Scene.reference_images_enum
for preview_collection in preview_collections.values():
bpy.utils.previews.remove(preview_collection)
preview_collections.clear()
bpy.utils.unregister_class(ReferenceManagerPanel)
if __name__ == "__main__":
register()
But I get the error :
Traceback (most recent call last):
File "\ReferenceTemplateManager_04.py", line 29, in get_previews_collection
File "C:\Users\Flatron\Downloads\blender-280-April_15\blender-2.80.0-git.860a9f979d60-windows64\2.80\scripts\modules\bpy\utils\previews.py", line 97, in load
raise KeyError("key {name!r} already exists")
KeyError: 'key {name!r} already exists'
File "\ReferenceTemplateManager_04.py", line 17, in get_previews_collection
I hope someone can see what the issue it. If I just got this running then I very happy.
Thanks,
That seems to be the same solution Stephan gave on Simple python fix for script
For both the solutions is that it’s checks if the icon is already loaded or not.