How to make a thumbnail activating a class blender 2.80

(Peetie) #1

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.

0 Likes

(Peetie) #2

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()'
0 Likes

(Peetie) #3

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.

Probably I have to dive into;

bpy.data.windows_managers (or WindowManager(ID))
bpy.utils.previews
template_icon_view

Ehj, quite frustrated at the moment.

0 Likes

(mkbreuer) #4

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’

0 Likes

(Peetie) #5

I have seen it yes, but then I thought I better look for another approach because:

  • It dynamically scans the folder / fill the previews while I have a fixed set.
  • I didn’t understand how then, once a preview is selected, to activate a class/function.

Maybe I can dive into that again, finding out how to trigger something once a thumnails has been selected.

Ah, I was looking for what IMAGE_EDITOR is now, couldn’t find it. Thanks!

0 Likes

(mkbreuer) #6

Maybe you can transfer this one:


This is a very simple addon for 2.79.

0 Likes

(Peetie) #7

@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.

0 Likes

(Peetie) #8

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.

0 Likes