Is it possible to script a custom UI feature where you have a list of items, and you can add or subtract from the list using [+] or [-] buttons?
I would like each item to be able to store information.
Thanks in advance!
Is it possible to script a custom UI feature where you have a list of items, and you can add or subtract from the list using [+] or [-] buttons?
I would like each item to be able to store information.
Thanks in advance!
Yes you can, with a custom UIList, template_list and a few operators:
import bpy
from bpy.props import *
sire_items = (
('s', "Scale", ""),
('i', "Inset", ""),
('r', "Rotate", ""),
('e', "Extrude", "")
)
sire_items_dict = {}
for identifier, label, description in sire_items:
sire_items_dict[identifier] = label
class SireStep(bpy.types.PropertyGroup):
name = StringProperty()
trans = EnumProperty(items=sire_items) # default 's' required?
val = FloatProperty(min=0.001, soft_max=1000, default=1.0, precision=3)
rot = EnumProperty(items=(
('cw', 'CW', ''),
('ccw', 'CCW', '')),
name="Rotation" # name required if enum expanded, or labels will be missing
)
other = EnumProperty(items=(
('in', 'In', ''),
('out', 'Out', '')),
name="Other"
)
class Sire(bpy.types.PropertyGroup):
type_add = EnumProperty(
items=sire_items,
name = "some name",
default = 's'
)
steps = CollectionProperty(type=SireStep, name="Sire Step")
index = IntProperty() # min/max/default?
class SIRE_UL_steplist(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
# draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
if self.layout_type in {'DEFAULT', 'COMPACT'}:
row = layout.row(align=True)
row.label(item.name)
row.prop(item, "val", text="")
sub = row.row(align=True)
sub.scale_x = 0.4
if item.name == sire_items_dict['r']:
sub.prop(item, "rot", expand=True)
else:
sub.prop(item, "other", expand=True) # if not expanded, use text=""
# 'GRID' layout type should be as compact as possible (typically a single icon!).
elif self.layout_type in {'GRID'}:
layout.alignment = 'CENTER'
layout.label("", icon_value=icon)
# ------ operator 0 ------ add item to collection
class VIEW3D_OT_sire_step_add(bpy.types.Operator):
bl_idname = 'view3d.sire_step_add'
bl_label = 'Add'
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
@classmethod
def poll(cls, context):
return (context.active_object and
context.active_object.type == 'MESH' and
context.mode == 'EDIT_MESH')
def execute(self, context):
steps = context.scene.sire.steps
step = steps.add()
step.name = sire_items_dict[context.scene.sire.type_add]
context.scene.sire.index = len(steps) - 1
return {'FINISHED'}
# ------ operator 1 ------ remove item from collection
class VIEW3D_OT_sire_step_remove(bpy.types.Operator):
bl_idname = 'view3d.sire_step_remove'
bl_label = 'Remove'
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
@classmethod
def poll(cls, context):
return (context.active_object and
context.active_object.type == 'MESH' and
context.mode == 'EDIT_MESH' and
len(context.scene.sire.steps) > 0)
def execute(self, context):
idx = context.scene.sire.index
move_idx = len(context.scene.sire.steps) - 1 == idx
context.scene.sire.steps.remove(idx)
if move_idx:
context.scene.sire.index -= 1
return {'FINISHED'}
# ------ operator 2 ------ move item up
class VIEW3D_OT_sire_step_moveup(bpy.types.Operator):
bl_idname = 'view3d.sire_step_moveup'
bl_label = 'Move up'
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
@classmethod
def poll(cls, context):
return (context.active_object and
context.active_object.type == 'MESH' and
context.mode == 'EDIT_MESH' and
len(context.scene.sire.steps) > 0 and
context.scene.sire.index > 0)
def draw(self, context):
layout = self.layout
def execute(self, context):
idx = context.scene.sire.index
context.scene.sire.steps.move(idx, idx-1)
context.scene.sire.index -= 1
return {'FINISHED'}
# ------ operator 3 ------ move item down
class VIEW3D_OT_sire_step_movedown(bpy.types.Operator):
bl_idname = 'view3d.sire_step_movedown'
bl_label = 'Move down'
bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
@classmethod
def poll(cls, context):
return (context.active_object and
context.active_object.type == 'MESH' and
context.mode == 'EDIT_MESH' and
len(context.scene.sire.steps) > 0 and
context.scene.sire.index < len(context.scene.sire.steps) - 1)
def execute(self, context):
idx = context.scene.sire.index
context.scene.sire.steps.move(idx, idx+1)
context.scene.sire.index += 1
return {'FINISHED'}
class VIEW3D_PT_sire_steplist(bpy.types.Panel):
"""Tooltip"""
bl_label = "SIRE"
bl_idname = "VIEW3D_PT_sire_steplist"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "scene"
def draw(self, context):
layout = self.layout
layout.prop(context.scene.sire, "type_add", expand=True)
row = layout.row()
row.template_list("SIRE_UL_steplist", "", context.scene.sire, "steps", context.scene.sire, "index", rows=4, maxrows=10)
col = row.column(align=True)
col.operator("view3d.sire_step_add", text="", icon="ZOOMIN")
col.operator("view3d.sire_step_remove", text="", icon="ZOOMOUT")
col.separator()
col.operator('view3d.sire_step_moveup', text='', icon='TRIA_UP')
col.operator('view3d.sire_step_movedown', text='', icon='TRIA_DOWN')
def register():
bpy.utils.register_module(__name__)
bpy.types.Scene.sire = PointerProperty(type=Sire)
def unregister():
bpy.utils.unregister_module(__name__)
del bpy.types.Scene.sire
if __name__ == "__main__":
register()
RUn that script, select a mesh object and go to editmode, see scene tab in properties editor.
Lol wow!
I hope you copy-pasted this, and didn’t code for 3 hours for my sake
Thanks! I’ll definitely check it out!
copied from something I coded for another thread on this forum