Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
A commented template for making simple UI in blender using the bpy python API
#import the bpy module to access blender API
import bpy
#WARNING: this is written and tested for blender 2.79
#blender 2.8 and newer will likely have a different python API
#create a property group, this is REALLY needed so that operators
#AND the UI can access, display and expose it to the user to change
#in here we will have all properties(variables) that is neccessary
class CustomPropertyGroup(bpy.types.PropertyGroup):
#NOTE: read documentation about 'props' to see them and their keyword arguments
#builtin float (variable)property that blender understands
float_slider = bpy.props.FloatProperty(name='float value', soft_min=0, soft_max=10)
#builtin integer (variable)property
int_slider = bpy.props.IntProperty(name='int value', soft_min=0, soft_max=10)
#builting boolean (variable)property
bool_toggle = bpy.props.BoolProperty(name='bool toggle')
#builting string (variable)property
string_field = bpy.props.StringProperty(name='string field')
#create a panel (class) by deriving from the bpy Panel, this be the UI
class CustomToolShelf(bpy.types.Panel):
#variable for determining which view this panel will be in
bl_space_type = 'VIEW_3D'
#this variable tells us where in that view it will be drawn
bl_region_type = 'TOOLS'
#this variable is a label/name that is displayed to the user
bl_label = 'Custom Tool Shelf'
#this context variable tells when it will be displayed, edit mode, object mode etc
bl_context = 'objectmode'
#category is esentially the main UI element, the panels inside it are
#collapsible dropdown menus drawn under a category
#you can add your own name, or an existing one and it will be drawn accordingly
bl_category = 'Custom'
#now we define a draw method, in it we can tell what elements we want to draw
#in this new space we created, buttons, toggles etc.
def draw(self, context):
#shorten the self.layout to just layout for convenience
layout = self.layout
#add a button to it, which is called an operator, its a little tricky to do it but...
#first argument is a string with the operator name to be invoked
#in example 'bpy.ops.mesh.primitive_cube_add()' is the function we want to invoke
#so we invoke it by name 'mesh.primitive_cube_add'
#then the rest are keyword arguments based on documentation
#NOTE: for custom operations, you need to define and register an operator with
#custom name, and then call it by that custom name as we did here
layout.operator('mesh.primitive_cube_add', text = 'Add new cube')
#the custom operator that we just made will go here as a new button
layout.operator('custom.simple_op', text = 'Simple Op')
#add multiple items on the same line, like a column layout, from left to right
subrow = layout.row(align=True)
#the complex operator will be draw on the left, as a button
subrow.operator('custom.complex_op', text = 'Complex Op')
#the property will be drawn next to it on the right, as an adjustible slider thing
subrow.prop(context.scene.custom_props, 'float_slider')
#add a label to the UI
layout.label('v Testing layout, does nothing bellow this v')
#add a new row with multiple elements in a column
subrow = layout.row(align=True)
#add a toggle
subrow.prop(context.scene.custom_props, 'bool_toggle')
#add an int slider
subrow.prop(context.scene.custom_props, 'int_slider')
#add a custom text field in the usual layout
layout.prop(context.scene.custom_props, 'string_field')
#NOTE: for more layout things see the types.UILayout in the documentation
#in order to make a button do custom behavior we need to register and make an operator, a basic
#custom operator that does not take any property and just runs is easily made like so
class CustomSimpleOperator(bpy.types.Operator):
#the id variable by which we can invoke the operator in blender
#usually its good practice to have SOMETHING.other_thing as style so we can group
#many id's together by SOMETHING and we have less chance of overriding existing op's
bl_idname = 'custom.simple_op'
#this is the label that essentially is the text displayed on the button
bl_label = 'Simple Op'
#these are the options for the operator, this one makes it not appear
#in the search bar and only accessible by script, useful
#NOTE: it's a list of strings in {} braces, see blender documentation on types.operator
bl_options = {'INTERNAL'}
#this is needed to check if the operator can be executed/invoked
#in the current context, useful for some but not for this example
def poll(cls, context):
#check the context here
return context.object is not None
#this is the cream of the entire operator class, this one's the function that gets
#executed when the button is pressed
def execute(self, context):
#just do the logic here
#this is a report, it pops up in the area defined in the word
#in curly braces {} which is the first argument, second is the actual displayed text{'INFO'}, "The custom operator actually worked!")
#return value tells blender wether the operation finished sueccessfully
#needs to be in curly braces also {}
return {'FINISHED'}
#this is a more complex operator, it will take a property value and
#then use it for computation of some kind
class CustomComplexOperator(bpy.types.Operator):
#add an id to be able to access it
bl_idname = 'custom.complex_op'
#add label to show up on the button
bl_label = 'Complex Op'
#make it internal so we can't search for it
#make it check if it can run in the context
def poll(cls, context):
#check the context here
return context.object is not None
#here we can define how the operator itself is drawn to the screne
#that means we can add toggles, sliders etc and be able to acess their
#set values in the code execution
#NOTE: this is automaticly done by default, if you have defined it
#then it will be used, this gives more control over the layout
#def draw(self, context):
#TODO: could not get it working so far, would like to make it work on tools shelf
#invoke runs before execute, it is useful to (run background tasts???)
#run code that sets up or reads values neccessary for script execution
#it is a little more involved then a simple operator
#NOTE: look at types.operator documentation for more information
#def invoke(self, context):
#execute method for... executing... this... on call(button press) (after invoke)
def execute(self, context):
#shorthand to reach properties of self
props =
#shorthand to scene
scene = context.scene
#this sends a report showing the set value of the slider{'INFO'}, "The value of the slider: " + str(scene.custom_props.float_slider))
#return value that tells blender we finished without failure
return {'FINISHED'}
#this is the addon info for when you choose to install it
#NOTE: for more information, see addon tutorial in the documentation
"name":"Ui test addon",
#this function is called on plugin loading(installing), adding class definitions into blender
#to be used, drawed and called
def register():
#register property group class
#this one especially, it adds the property group class to the scene context (instantiates it)
bpy.types.Scene.custom_props = bpy.props.PointerProperty(type=CustomPropertyGroup)
#register the classes with the correct function
#same as register but backwards, deleting references
def unregister():
#delete the custom property pointer
#NOTE: this is different from its accessor, as that is a read/write only
#to delete this we have to delete its pointer, just like how we added it
del bpy.types.Scene.custom_props
#now we can continue to unregister classes normally
#NOTE: during testing if this addon was installed from a file then that current version
#of that file will be copied over to the blender addons directory
#if you want to see what changes occour you HAVE TO REINSTALL from the new file for it to register
#a quick line to autorun the script from the text editor when we hit 'run script'
if __name__ == '__main__':
Copy link

tin2tin commented Aug 16, 2019

I changed a few things to make it work in 2.80:

Copy link

AzureDVBB commented Aug 16, 2019

Wow I nearly forgot about this code snippet i did back then. I'm glad if it was helpful to you! @tin2tin

Copy link

tin2tin commented Aug 16, 2019

I've been looking at how to improve the Text Editor and ease newcomers encounter with coding add-ons: The included Blender py templates are not very comprehensive, and a well documented piece of code like yours might be very helpful and enlightening for people to study.

I don't know what the process is for getting new templates included in Blender, but maybe it's just a question of submitting it on as a patch? Would you be interested in something like this?

(The registering of classes properly have to be converted into the class registering style of 2.80)

Copy link

AzureDVBB commented Aug 17, 2019

Hmm.. I'll give it a go. Going to make a repo for it and bring over my Node Tree thing too, going to be an interesting dive into the new way of doing things after over a year! But I'll see if I can add more comments then code to the examples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment