-
-
Save tin2tin/ce4696795ad918448dfbad56668ed4d5 to your computer and use it in GitHub Desktop.
| #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 = 'UI' | |
| #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 = 'View' | |
| #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(text="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 | |
| @classmethod | |
| 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 | |
| self.report({'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 | |
| bl_options={'INTERNAL'} | |
| #make it check if it can run in the context | |
| @classmethod | |
| 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 = self.properties | |
| #shorthand to scene | |
| scene = context.scene | |
| #this sends a report showing the set value of the slider | |
| self.report({'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 | |
| bl_info={ | |
| "name":"Ui test addon", | |
| "category":"tests" | |
| } | |
| #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 | |
| bpy.utils.register_class(CustomPropertyGroup) | |
| #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 | |
| bpy.utils.register_class(CustomSimpleOperator) | |
| bpy.utils.register_class(CustomComplexOperator) | |
| bpy.utils.register_class(CustomToolShelf) | |
| #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 | |
| bpy.utils.unregister_class(CustomPropertyGroup) | |
| bpy.utils.unregister_class(CustomSimpleOperator) | |
| bpy.utils.unregister_class(CustomComplexOperator) | |
| bpy.utils.unregister_class(CustomToolShelf) | |
| #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__': | |
| register() |
This is exactly what I have been searching for! Thank you so much for putting this together, it also helped me with with obtaining a better grasp of using the UI elements. Good work!
@sirdavid32 Works like a charm even in Blender 2.82. Really good template to learn how it works. :) To all who want to see an image: https://imgur.com/a/y5dmeGP
Awesome! Thank you for sharing!
Thank you for this. I've been searching for hours trying to find this information in an understandable format. Very nicely done.
I have one question ? How can I add float_slider to change transperency in material for example.
principledBDSF.inputs[18].default_value = 1
what I should write to get value from float slider and put it instead of 1
Greets
kickass! I've been adding gists as I go as well, thanks for this one
I would love a 2.81 template. Is it possible? Pretty please?