Looking for example script to help me understand UI, props and operators better...

Well, I am much better in understanding stuff when I got the code to dissect in front of me. In the API wiki there are some examples, some non-functional and just code snipplets and I feel generally lost.

Maybe some good soul has a moment to write me a random script frame of following specification:

Panel in Toolshelf in all Contexts.
3 dunnow, float properties I can set, which set the position of an object updating in realtime.
One operator calling a custom function.

In theory I got an idea how to do it, but applying it is another story.
I know that the panel has to be a derived class of panel and that it has to be of space_type VIEW_3D and region_type “TOOLS”.
I also know that I have to derive operators from bpy.types.Operator.

I’ve looked into a few scripts like looptools and can’t really figure it out.

I am not sure how to properly choose the bl_idname.
I figured out how to register multiple classes, but somehow with the wrong bl_idname it’s not working properly. For some reason I can’t even get the “pingpong” example from the wiki to run properly, the operator will not appear.
I am also not sure how to interlink the class for properties and the class for the operators with the class for the layout.

It all seems easy and clear when I read the individual references, but not anymore when I have to put it together :slight_smile:

So maybe someone has a moment of charity this weekend, I’d appreciate it.

A thanks a ton, your example seem to be just what I was looking for. I’ll check it out as soon as I got time again.
The templates themselves are rather clear, but combining them I usually failed. That’s where I started, that along with “show source” and reading into other peoples scripts but the complexity the templates miss is then too much in those scripts. :slight_smile:

Given the situation that I want my properties more to be “globals” (i know thats rough’n’dirty) rather than registering them to each and every object, where do you register them to?

It really depends upon what your script is going to do. If it is a modeling tool, then users generally attach variables as custom properties to the scene. Quite frankly, I don’t program for anything except the object context. My scripts are generally animation based so I place them in objects.

The link Atom provide is a great example. Here’s another small one that shows a simple setup of what I think you’re looking for. I tried to comment it to explain parts.

import bpy
# Import the property module 
from bpy.props import FloatProperty, EnumProperty, IntProperty, BoolProperty, FloatVectorProperty



# Class to create custom properties
class CreateMyProperties(bpy.types.PropertyGroup):
	# These are just samples
    myprop_Bool = BoolProperty(name="Boolean", default=False, description="Boolean Property")
    myprop_Int = IntProperty(name="Int Property", min=0, max=64, default=12, description="Int Property")
    # These are used in the operator
    myprop_Floatx = FloatProperty(name="x", min=-10.0, max=10.0, default=0, description="Float Property x")
    myprop_Floaty = FloatProperty(name="y", min=-10.0, max=10.0, default=0, description="Float Property y")
    myprop_Floatz = FloatProperty(name="z", min=-10.0, max=10.0, default=0, description="Float Property z")

############################
## Draw panel in Toolbar
############################
class AddMyPanel(bpy.types.Panel):
    bl_label = "My Panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_context = 'objectmode'
    # bl_context = 'editmode'
    # bl_context = 'object'

    def draw(self, context):
        layout = self.layout
        myprops = bpy.context.window_manager.myprops

        # Create aligned column with two operators
        col = self.layout.column()
        col.label(text="Existing Operator")
        col.operator("render.render", text="Render Image", icon='RENDER_STILL')  # Built in operator, same as render button in props panel
        col.separator()
        col.label(text="Custom Operator Button")
        col.operator("object.mycustomoperator", text="The Custom Move Operator", icon="PLAY")  # This is the bl_idname

        row = layout.row(align=True) # switch from columns to row, keep row aligned together
        row.prop(myprops, "myprop_Floatx")
        row.prop(myprops, "myprop_Floaty")
        row.prop(myprops, "myprop_Floatz")

        row = layout.row()  # sameprops, just NOT aligned
        row.prop(myprops, "myprop_Floatx")
        row.prop(myprops, "myprop_Floaty")
        row.prop(myprops, "myprop_Floatz")

        row = layout.row()
        row.label(text="these props are just for show")
        row = layout.row()  # Show the other props that do nothing
        row.prop(myprops, "myprop_Bool")
        row.prop(myprops, "myprop_Int")


##################################################################
## Make custom operator that runs from button
##################################################################

class OBJECT_OT_mycustomoperator(bpy.types.Operator):
    bl_idname = "object.mycustomoperator"
    bl_label = "My Custom Operator"
    bl_description = "My Operator description"
    bl_options = {'REGISTER', 'UNDO'}


    @classmethod
    def poll(cls, context):  # what context does the operator work in
        # return (context.object and context.object.type in 'MESH')  # Only work on a mesh object
        return (context.object)  # Work only a object is selected (any object)

    def invoke(self, context, event):
        import bpy

        # Run through each selected object and convert to to a curved object
        myobj = bpy.context.object
        myprops = bpy.context.window_manager.myprops

        myobj.location.x = myprops.myprop_Floatx  # Change location from properties
        myobj.location.y = myprops.myprop_Floaty
        myobj.location.z = myprops.myprop_Floatz
        
        return{"FINISHED"}

##################################################################
### Define Classes to register
##################################################################

# store all my classes in a list to easily register/unregister
classes = [
    CreateMyProperties,
    AddMyPanel,
    OBJECT_OT_mycustomoperator
    ]

def register():
    for c in classes:
        bpy.utils.register_class(c)
    bpy.types.WindowManager.myprops = bpy.props.PointerProperty(type=CreateMyProperties)  # store my props

def unregister():
    for c in classes:
        bpy.utils.unregister_class(c)
    del bpy.types.WindowManager.myprops
if __name__ == "__main__":
    register()

Here are three major areas into which I tend to categorize parts of my scripts:
Operators, Properties, and Ui.
Depending on your needs, any single item of which can be made to entirely suffice. (Achieve goal using only ops, only props, or only ui) Usually a combination of all three, but not always necessary.

You can have operators, with no properties, and no ui.

You can have properties – and make use of them w/o necessarily needing an interface or full-fledged operators.

You could have an interface, such as a panel or menu, display whatever properties you wish, without needing to define any of said properties.

Thus I see these (at least) three distinct areas – The Operators, The Properties, and The User Interface.

Its best to keep them clear in your head while you work on them, lest tasks get mis-delegated to unsuited items.

Add to the list of code-areas, what I call ‘The Idiom’. It should be familiar by now:


def register():
    bpy.utils.register_module(__name__)

def unregister():
    bpy.utils.unregister_module(__name__)

if __name__ == '__main__':
    register()

[\code]

Incidentally, I don't keep a lot of code snippets around.  Besides this one.

So, in continuing to break standard blender scripting down into discrete areas, we have the registration area, at the bottom, and of course at the very top, the lines where you import bpy and optionally, other modules.

That gives us:

- Imports
- Properties
- Operators
- User Interface
- Registration



Remarks on 1 (importing)
There are a lot of different ways to do it, but the line:
from . import foo
seems to work for me, assuming foo.py lives in the same folder as the script doing the importing.

Remarks on 5 (Registration)
Things *can* get messy here, as a sign that you may be doing it wrong.  To keep it clean, group properties together so you can register them all at once.


class foo(bpy.types.PropertyGroup):
bar = bpy.props.BoolProperty()
baz = bpy.props.IntProperty()

def register():

bpy.types.Object.foo = bpy.props.PointerProperty(type=foo)



When you have a lot of properties, this method keeps the registration area from getting cluttered.

Remarks on 2 (Properties)
The update handlers are extremely useful.  Their definitions obviously group with the properties they accompany, prefixed to the properties area.
Properties?  Properties of what?  Thise is a common issue.  Yes, of course you can give tons of things properties, and you must use common sense in deciding what goes where.  You can assign properties to the scene, objects, OPERATORS and...  Well my point is that AFAIK, one doesn't give properties to UI classes.  ( read: bpy.types.Panel )

Remarks on 4: (User Interface)
Not a lot of logic goes here.  It is just 'layout'.  You may use if blocks to control what gets drawn, but a @poll class method is better suited for that type of logic.
You'll see a lot of:

def draw(self,context):
layout = self.layout
row = layout.row()
row.prop(context.scene.foo,‘baz’)


-- or --

def draw(self,context):
self.layout.row().prop(context.scene.foo,‘baz’)



For testing little bits of interface code in the console, I occasionally use a dirty little one-liner lambda and skip all the extras:


bpy.types.INFO_HT_header.append(lambda self,context:self.layout.prop(context.active_object,‘name’))



Functions such as these do not need to be registered, nor will they persist from session to session.

Remarks on 3 (Operators)
Operators can have properties and they can define their own interface code.

In basic form, skipping the Importation and Registration:

class foo(bpy.types.Operator):
bl_label = ‘foo’
bl_idname = ‘bar.foo’
def execute(self,context):
return {‘FINISHED’}



So you have a foo class.  Nothing happening yet.  Simply defining a class does nothing.  Should do nothing.  Register the class with blender and what happens?  Look in bpy.types for your answer, and since this is an operator, bpy.ops also.

bpy.types.BAR_OT_foo where BAR is the uppercased name of the submodule or fake module-like class to which foo belongs, and _OT_ indicates that this is an operator type class.

bpy.ops.bar.foo where bar represents said submodule and foo is the 'operator' which 'belongs' to it.

If you wish to have more control over the naming of the class, you explicitly name your classes according to this convention, otherwise expect that blender will name registered classes according to this system.

The bl_idname 'bar.foo' contains enough information for blender to do this.  It isn't obvious that one may use whatever name one might wish, in place of bar -- reading through example scripts, the majority use 'object' or 'mesh' or 'scene' as appropriate, but if you wish to group your operators according to your own 'fake module-like class', go right ahead.  No need for the code to live all in one file or even addon, for them to all share in common one thing: their bl_idname have same prefix before the dot.  (something unique to them, after the dot.)

In your interface code, to lay out a button for an operator:
self.layout.operator('foo.bar')

To make button call with certain prop hardcoded in ui:
self.layout.operator('foo.bar').n = 1

an op with a prop:

class foo(bpy.types.Operator):
bl_label = ‘foo’
bl_idname = ‘bar.foo’
n = bpy.props.IntProperty()
def execute(self,context):
print(self.n)
return {‘FINISHED’}



For an operator to use its draw method it has to get called.  Panels do not 'draw what the operator says it wants to look like'.  Panels don't particularly care what the operator does, much less what it looks like.  Operators get buttons. There is a lot of flexibility for labels and icons and properties, but in the end there is just a button.  The fact that operators can have draw method may lead one to wonder, HOW IS DRAW?

also, WHERE IS DRAW?  there are various places where an active_operator should show up.  Foremost, the left-side pane in the 3d view which opens by the letter t.  at the bottom is where you always see the properties of the active operator.
Other spaces have similar tool panels as well.

The set of bl_options for the operator should contain 'REGISTER', first of all.  Then when the operator is run, blender will know to put it in the place where operators can draw or are drawn.  If there are any properties to the operator, they will automatically have associated ui elements created for them, so that there is no need to be explicit about how to draw.  The draw method is for when you wish to customize the display of the operator, and it doesn't work any differently from how a panel's draw method works.

Thanks for all that, I’ll read it on sunday, I just came home from training and I am beaten… literally :smiley:
And I am away all weekend. So don’t wonder if this thread bumps up with questions later this week, but seeing the wall of text you wrote I doubt there’ll be many if any.

Thanks a lot, apreciate it.

So far so good. :smiley:

Is there a way to update let’s say the position of an object in realtime while I change the location property?

To have it completely static, to have a property for each x,y and z location of a scene object [‘cube’]
Now I want the cube to move as soon as I change one of the properties…

I can’t put e.g.:

cube = bpy.data.objects[‘Cube’]
cube.rotation_euler=[myprops.myprop_Floatx,myprops.myprop_Floaty,myprops.myprop_Floatz]

Into the UI or the Props class, and in the OP class it’ll only update when I click the button… I am missing something again :confused:

Arexma,
You can choose to make a property run a function whenever that property has been changed. At the end of your property, add update=nameofyourfunction

def up_func(self, context):
    print("test")

bpy.types.Scene.testprop = bpy.props.FloatProperty(update=up_func)
bpy.context.scene.testprop = 11

So to move the x,y,z locations, create the appropriate property and function for each axis. Changing say the X axis number would then run a function that moves the object according to the property.

See more here: http://lists.blender.org/pipermail/bf-blender-cvs/2011-June/036463.html

Oh, that’s great exactly what I was looking for… it’s just awesome how all the information is out there, just hiding in plain sight :smiley:

thanks. again!

Coming along great with my project, yet another dead end.

I am looking to include custom “units” in my properties.
I know for instance that I can use (unit=“ROTATION”) for a float to display the [100°]
But what if the unit is potatoe and I want to show [100 Potatoes]? Is that even possible or do I have to work with hacking a text somewhere in there?

I don’t think Blender supports custom units for display properties.

This page shows the various UI elements and what they can display.
http://www.blender.org/documentation/blender_python_api_2_62_2/bpy.types.UILayout.html?highlight=label#bpy.types.UILayout.label

Holy cow, if I ever need an example of excessive use of hardcoded enumerators :smiley:

But I was afraid there are no custom units… makes it hard to use Blender for astrophysics… physicians love units… and astrophysicists love extraterrestial units :wink:
I’ll make a cutout stickynote to put on the screen over the UI :stuck_out_tongue:

@arexma,
You can just name each property appropriate, either when you create the properties themselves, or just in the UI layout.

Property group class

tool_customProp = IntProperty(name="My Name", min=0, max=64, default=12, description="Your description for tooltip")

in the UI

layout.prop(my_props, "tool_customProp", text="Different Name")

Doing that will allow you to basically give anything whatever name you want.

Yeh thanks I know now thanks to you guys, but you misunderstood me.
I don’t want to name them, they need a physical unit. For instance in astrophysics in our solar system you don’t work with m, km, miles, lightyears, you use the radius of the sun… it’s 1 Rs, or AU (astronomical unit), which is the mean distance between earth and sun, or appr. 215 Rs.

So if I have e.g. a float prop with name=“MyName” I’d have:
[ MyName 10.000 ]
I can set it to rotation, distance whatever but Blender will use what it has builtin…: °, m, km, mi, in
[ MyName 10.000 m ]
[ MyName 10.000 ° ]
but it seems it does not allow custom units like:
[ MyName 10.000 Rs ]

All I can do it seems is to make name= “MyName (Rs)” or split the column and label the right one with “Rs”
A Rather cosmetic first world problem I know. :wink:

Ahh, you could just hack it to something like this. The sticky note sounds like a terrible idea :wink:


import bpy
from bpy.props import FloatProperty

# Class to define properties
class myProperties(bpy.types.PropertyGroup):
    myprop1 = FloatProperty(name="myprop 1", min=0.0, max=100.0, default=0.1, description="myprop 1")

class myPanel(bpy.types.Panel):
    bl_label = "My Panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_context = 'objectmode'

    def draw(self, context):
        layout = self.layout
        myprops = bpy.context.window_manager.myprops

        row = layout.row(align=True)
        row.prop(myprops, "myprop1", text="")
        row.label(text="Astronauts")

        row = layout.row()
        row.label(text="Or combine them")
        row = layout.row(align=True)
        row.prop(myprops, "myprop1", text="")
        custom = "%.2f" % myprops.myprop1 + " :Astros"
        row.label(text=custom)

### Define Classes to register
classes = [myProperties, myPanel]

def register():
    for c in classes: bpy.utils.register_class(c)
    bpy.types.WindowManager.myprops = bpy.props.PointerProperty(type=myProperties)

def unregister():
    for c in classes: bpy.utils.unregister_class(c)
    del bpy.types.WindowManager.myprops
if __name__ == "__main__":
    register()

Hello, I try to do this, but with a color picker. For now I can add the color box where you can choose the color, but do not know how I can store the new color selected for use in other parts of the script

When I choose a color I can see the vector on the info pannel but I can’t figure out how grab this value into a variable. on the console I can’t see it


3 days I’ve been looking at the forum and internet and have not found the solution so I think is better ask if anyone has a suggestion or solution;)

I appreciate any help on my learning road of python :wink:

This is what I have , based on @crazycourier example:

code:
import bpy
from bpy.props import FloatVectorProperty

Class to define properties

class myProperties(bpy.types.PropertyGroup):

colProp = FloatVectorProperty(size=4,default=(0, 0, 0, 1), subtype="COLOR", min=0, max=1)

class ColPanel(bpy.types.Panel):
bl_label = “Color Panel”
bl_space_type = ‘VIEW_3D’
bl_region_type = ‘TOOLS’
bl_context = ‘objectmode’

def draw(self, context):
    layout = self.layout
    myprops = bpy.context.window_manager.myprops

    row = layout.row(align=True)
    row.prop(myprops, "colProp", text="")
    row.label(text="Color")
    
   ### 1 try to get the value###
    value=myprops.colProp 
    print (value)
      
    ### 2 try to get the value###
    #value = bpy.data.window_managers["WinMan"].myprops.colProp
    #print (value)

Define Classes to register

classes = [myProperties, ColPanel]

def register():
for c in classes: bpy.utils.register_class©
bpy.types.WindowManager.myprops = bpy.props.PointerProperty(type=myProperties)

def unregister():
for c in classes: bpy.utils.unregister_class©
del bpy.types.WindowManager.myprops

if name == “main”:
register()

Have you tried:

value=myprops.colProp[:]

the [:] tells python to iterate the contents of colProp. Since vector ‘wraps’ its data, you have to do that to access the contents. Otherwise it is pass by reference.

Yeaa , thanks you very match , this works ! .

Yes well …I try to transform an array into a list, and things like that , but resulted in an error related to the length or something that does not quite understand. Now I understand a bit more how to do it, thank you very much :wink: