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.
- 1,306
- 1
- 12
- 18
2 Answers
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)
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
- 51,077
- 7
- 129
- 218
I made an update of the script given by Jaroslav Jerryno Novotny to work in 2.93
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.newbutton)
new = context.scene.newbutton.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.newbutton:
row = self.layout.row(align=True)
row.prop(item, "value")
row.operator("scene.button_operator", text="Button #"+item.name).id = item.value
classes = (
SceneItems,
AddButtonOperator,
ButtonOperator,
FancyPanel,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.newbutton = bpy.props.CollectionProperty(type = SceneItems)
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
if name == "main":
register()
- 307
- 2
- 11


print(dir(bpy.types.Scene.collection))at the end of the script andprint(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(). – DragonautX Jul 09 '16 at 22:21test = 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? – DragonautX Jul 09 '16 at 22:49