How to set the selected entry in a dropdown?

Hey all,
I have scoured the internet and asked questions on discord and stack exchange but still I have no concrete answer to a seemingly simple question: How to set the selected entry in a dropdown?

Here is a simple example where I have a dropdown of all materials in the scene. I would like to set the material that is on the currently selected object to be the selected entry in the material dropdown list.

Please don’t get hung up on the specific use case here (materials) as I would need to be able to set the selected value of dropdowns in other circumstances as well.

I can set the value by typing the following into the console:
bpy.context.scene.testlist = ‘my material’

However, putting that same line into my draw method gives me an error: Writing to ID classes in this context is not allowed.

I haven’t been able to come up with any way around this. Surely, setting the selected value of a dropdown should be easy?

That is the correct way to do it, but you cant change custom properties from the draw function. This is because the draw function can be run many times a second, and updating lots of properties and their update functions could cause performance issues.

In practice, you should never need to update a property that often anyway, especially if you are exposing the property to the user to change. Instead, you can change it in the execute function of an operator for example, that is only run when it is clicked.

Also, you should unregister the custom property as well with

del bpy.types.Scene.testlist

in the unregister function.

Hope that helps :slight_smile:

Thank you for the reply. I know I could make a button and when the user clicks it will update the selected item in the dropdown but that’s not really a solution. The thing doesn’t have to be set every frame, just when the user selects a different object.

How would one make it that when you select an object, the material dropdown automatically selects the material that the current object has (without having to click any other buttons).

Is there a reason you are avoiding accessing the material from the object data?

obj = context.active_object
if len(obj.material_slots) >0:
    layout.prop(obj.material_slots[0], 'material')
    layout.label(text="-- No Material Slots on active Object --")

Hmm, that could almost work. How could I filter the material list so that only some materials are in that list?

I’d suggest just using the template list property panel layout since it has filter and sorting built in (small arrow bottom left) line would look like:
layout.template_list("MATERIAL_UL_matslots", "", obj, "material_slots", obj, "active_material_index", rows=1, maxrows=3).