2

Appologies firstly, I'm very new to python and this site.

I have a small code that I am trying to create, to add an option on my panel addon.

The code is to select all collections in Blender and generate a Enum list, and then to populate a second Enum list with the objects within, the second list changing with the input from the first.

I plan to use the code once final to make the selection in the second menu active and show it in viewport, and to hide everything else.

So far, I have the following, but I'm pretty sure that this code will end up crashing blender as I'm pretty sure it re-writes the enum array with each redraw.

import os
import bpy
from bpy.types import (Panel, Operator)

class OBJECT_PT_Test(Panel):

bl_label = "Panel"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "Example"

def draw(self, context):
    layout = self.layout

    row = layout.row(align=True)
    row.prop(context.scene, "test_cat", text="")

    row = layout.row(align=True)
    row.prop(context.scene, "test_obj", text="")

def test_cat(self, context):

Enum_items = []

for collection in bpy.data.collections:
    item = (collection.name, collection.name, "")
    Enum_items.append(item)   
return Enum_items
#print (Enum_items) 

def test_obj(self, context):

obj_items = []

for obj in bpy.data.collections[bpy.context.scene.test_cat].objects:
    obj_item = (obj.name, obj.name, "")
    obj_items.append(obj_item)   
return obj_items
#print (obj_item)  

def register(): bpy.utils.register_class(OBJECT_PT_Test) bpy.types.Scene.test_cat = bpy.props.EnumProperty(name= "Category Name", description= "Category Desc", items=test_cat) bpy.types.Scene.test_obj = bpy.props.EnumProperty(name= "Object Name", description= "Object Desc", items=test_obj)

def unregister(): del bpy.types.Scene.test_cat del bpy.types.Scene.test_obj bpy.utils.unregister_class(OBJECT_PT_Test)

if name == "main": register()

Any advice would be greatly appreciated and taken on board

Kev Thomas
  • 23
  • 7

1 Answers1

3

Even this approach is not that beginner-friendly and there are a few concepts involved, I'd suggest use a PointerProperty for each selection instead. In case there are many collections and objects in the scene each enum gets crowded quickly and makes it difficult for the user to choose from these rather long lists.

Demo on a PointerProperty for collections as well as a PointerProperty for the objects of the selected collection using its provided poll callback function:

import bpy
from bpy.props import PointerProperty

def filter_callback(self, object): return object.name in self.my_collection.objects.keys()

class TEST_PT_layout_panel(bpy.types.Panel): bl_label = "My Panel" bl_category = "Test Panel" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "scene"

def draw(self, context):
    scene = context.scene
    layout = self.layout

    col = layout.column()
    col.prop(scene, "my_collection")

    col = layout.column()
    col.enabled = True if scene.my_collection else False
    col.prop(scene, "my_collection_objects")


def register():

bpy.types.Scene.my_collection = PointerProperty(
    name="Collection",
    type=bpy.types.Collection)
bpy.types.Scene.my_collection_objects = PointerProperty(
    name="Object",
    type=bpy.types.Object,
    poll=filter_callback)

bpy.utils.register_class(TEST_PT_layout_panel)


def unregister():

bpy.utils.unregister_class(TEST_PT_layout_panel)
del bpy.types.Scene.my_collection
del bpy.types.Collection.my_collection_objects

if name == "main": register()


As @batFINGER mentioned in the comments, you can also register a custom pointer to each collection type which allows to get the object reference via associated collection:

import bpy
from bpy.props import PointerProperty

def filter_callback(self, object): return self in object.users_collection

class TEST_PT_layout_panel(bpy.types.Panel): bl_label = "My Panel" bl_category = "Test Panel" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "scene"

def draw(self, context):
    scene = context.scene
    layout = self.layout

    col = layout.column()
    col.prop(scene, "my_collection")

    if scene.my_collection:
        col.prop(scene.my_collection, "my_active_object")


def register():

bpy.types.Scene.my_collection = PointerProperty(
    name="Collection",
    type=bpy.types.Collection)
bpy.types.Collection.my_active_object = PointerProperty(
    name="Object",
    type=bpy.types.Object,
    poll=filter_callback)

bpy.utils.register_class(TEST_PT_layout_panel)


def unregister():

bpy.utils.unregister_class(TEST_PT_layout_panel)
del bpy.types.Scene.my_collection
del bpy.types.Collection.my_active_object

if name == "main": register()


Related

brockmann
  • 12,613
  • 4
  • 50
  • 93
  • 1
    Suggest a minor change to bpy.types.Collection.active_object = PointerProperty(type=Object) in which case can set "object of interest in collection" for each. – batFINGER Aug 13 '21 at 09:34
  • Thanks @batFINGER. Disadvantage is that data argument of Layout.prop() call cannot be None so an additional additional check is needed... However, it feels closely associated with the collection, beautiful. – brockmann Aug 13 '21 at 10:27
  • NP. Would consider using and polling on context.collection Do you know of a template ID to set the context collection|? eg can C.scene.my_collection = C.scene.collection in the console to set collection of interest (btw Watson IMO never use "my_" like in Mycroft or Mycrosoft lol) to the scene collection. The other question is how to add base collections as as an option to the selection. – batFINGER Aug 13 '21 at 10:38
  • "Can not edit built-in name" lol. Need to investigate @batFINGER – brockmann Aug 13 '21 at 10:47
  • one more thing... perhaps poll for latter could be return self in object.users_collection – batFINGER Aug 13 '21 at 10:48
  • Interesting catch, no idea how to add the master collection @batFINGER – brockmann Aug 13 '21 at 13:26
  • Many Thanks @brockmann and batFINGER, appreciate the time you have both taken to show me the correct way to approach this. – Kev Thomas Aug 13 '21 at 15:22
  • No problem, glad it helps @KevThomas – brockmann Aug 13 '21 at 19:37