2
$\begingroup$

Is it possible to make a button that creates more buttons when you click on it? I was thinking that maybe it'd be a good idea to make extra buttons that store extra info. or functions that you can easily access. I did some searching and found this, but it seems hard to use, and it doesn't seem like you can delete or replace items in a CollectionProperty (actually you can edit items, as @Jerryno said). I've seen how the Surface Panel in the Cycles Materials Tab creates more buttons when I add nodes to it, so I'm wondering if I could do that for myself.

$\endgroup$
4
$\begingroup$

Of course you can delete or replace items from Collection property. Here are functions available to it:

add(
as_bytes(
clear(
data
find(
foreach_get(
foreach_set(
get(
id_data
items(
keys(
move(
path_from_id(
remove(
rna_type(
update(
values(

So lets say we have this: bpy.context.scene.collection Collection property with some items:

bpy.context.scene.collection.remove(5)    # will remove 6th element
bpy.context.scene.collection.move(2,3)    # will swap 3rd and 4th

# replace 5th element with a new one - just assign it new values:
bpy.context.scene.collection[4].name = "New name"
bpy.context.scene.collection[4].value = 42

So the script should work like this:

  • your Add button will add entries to the collection property
  • the UI will draw a button for each item of the collection property
  • every generated button will call the same operator, but will pass it a unique identifier
  • the operator does different things based on the identifier property

An example:

import bpy

class SceneItems(bpy.types.PropertyGroup):
    value = bpy.props.IntProperty()

class AddButtonOperator(bpy.types.Operator):
    bl_idname = "scene.add_button_operator"
    bl_label = "Add Button"

    def execute(self, context):
        id = len(context.scene.collection)
        new = context.scene.collection.add()
        new.name = str(id)
        new.value = id
        return {'FINISHED'}

class ButtonOperator(bpy.types.Operator):
    bl_idname = "scene.button_operator"
    bl_label = "Button"

    id = bpy.props.IntProperty()

    def execute(self, context):
        print("Pressed button ", self.id)
        return {'FINISHED'}

class FancyPanel(bpy.types.Panel):
    bl_label = "Fancy Panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'

    def draw(self, context):
        self.layout.operator("scene.add_button_operator")
        for item in context.scene.collection:
               self.layout.operator("scene.button_operator", text="Button #"+item.name).id = item.value

bpy.utils.register_class(SceneItems)
bpy.utils.register_class(AddButtonOperator)
bpy.utils.register_class(ButtonOperator)
bpy.utils.register_class(FancyPanel)

bpy.types.Scene.collection = bpy.props.CollectionProperty(type=SceneItems)

enter image description here

Edit @Lars comment: How can I have multiple input fields for each new entry, i.e. having an Integer input field next to each button that will deliver the Value to the buttons operator once the button is pressed?

You display the values of the Collection property items in the UI, and the user can change them. They get passed to the operator:

def draw(self, context):
    self.layout.operator("scene.add_button_operator")
    for item in context.scene.collection:
       row = self.layout.row(align=True)
       row.prop(item, "value")
       row.operator("scene.button_operator", text="Button #"+item.name).id = item.value

enter image description here

$\endgroup$
  • $\begingroup$ You're right. I didn't see that haha. So that must also mean draw() is like a smart function then, it's called multiple times when the addon is enabled, and it'll be called if the collection updates. How did you find those list of functions for the collection? I tried calling print(dir(bpy.types.Scene.collection)) at the end of the script and print(dir(context.scene.collection)) in an execute(), but I could only get functions with underscores, and the best I could get was add(), clear(), move(), and remove(). Also, the documentation link you showed me only shows collection.add(). $\endgroup$ – DragonautX Jul 9 '16 at 22:21
  • $\begingroup$ @DragonautX The script console will list you all the available options when you hit ctrl-spacebar. So if you end something with dot (.), you get all the functions. It is very handy. $\endgroup$ – Jaroslav Jerryno Novotny Jul 9 '16 at 22:29
  • $\begingroup$ I tried in the console test = bpy.props.CollectionProperty() and did the dot thing with test. I could only get count() and index() out of that. Even after seeing that was a tuple, I tried the dot thing on the elements, and the best I could get was from the empty dictionary in the last element. I still can't match what you have. What did you put in the console to get that? $\endgroup$ – DragonautX Jul 9 '16 at 22:49
  • 1
    $\begingroup$ @DragonautX i.stack.imgur.com/w2ao7.png, you don't have to register the property like I did, if it exist from running the script $\endgroup$ – Jaroslav Jerryno Novotny Jul 9 '16 at 22:58
  • $\begingroup$ That makes it a lot clearer. Thanks! Basically, I can best test it by writing it in the console exactly like in the script haha. So the context in execute() is the same as bpy.context, if I can get the same functions you got from the image you showed me. And hey, that's cool, so I found from this I just need to rename the script right to import the script. Alright, again, thanks! This helped me a lot! $\endgroup$ – DragonautX Jul 9 '16 at 23:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.