Doc:2.6/Manual/Extensions/Python/Properties

提供: wiki
< Doc:2.6‎ | Manual‎ | Extensions‎ | Python
2018年6月29日 (金) 03:47時点におけるYamyam (トーク | 投稿記録)による版 (1版 をインポートしました)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
移動先: 案内検索

Properties, ID-Properties and their differences


There are several ways to define custom properties in Blender using Python. One of them is to add a new property to an existing datablock. Another is to define a new ID-property for a datablock. This might seem to be the same, but there are some differences. So when to use what?

This document assumes that the reader has a basic knowledge of Python and Blender. Any code examples can be tested successfully in the default scene. Any user-interface elements that are created will be displayed in the properties sidebar of the 3d-view (toggle with N-key). As they show up at the bottom of this bar, it might be useful to collapse the panels above it (like Transform, View, etc.)


Properties


Basics

By default these are the normal properties that Blender itself uses. For instance, an object has a location property. This is a vector which contains information on the location of the object. This property can be both read and written. Other properties, like for instance users are read-only.

import bpy

ob = bpy.context.active_object  # get the active object
print("Location:", ob.location) # print its location
ob.location = [1,1,1]           # set it to a new location

print("Users:", ob.users)       # print its number of users
#ob.users = 3                   # this will raise an error

Custom properties

You can also add a new property yourself. The first thing you need to decide is the property type: a boolean, float, string, etc. Here is the full list. Simply add the new property (a string in this case) by using:

bpy.types.Object.myProperty = bpy.props.StringProperty()

This adds the property myProperty to all objects.

Once you've added the property you can interact with it just like it were any other normal property.

ob = bpy.context.active_object      # get the active object
bpy.types.Object.foo = bpy.props.StringProperty()       # add a new property, called "foo"
ob.foo = "bar"                      # assign a value to the property
print("foo:", ob.foo)               # prints "foo: bar"
print("foo:", ob['foo'])            # also prints "foo: bar"

class myPanel(bpy.types.Panel):     # panel to display new property
    bl_space_type = "VIEW_3D"       # show up in: 3d-window
    bl_region_type = "UI"           # show up in: properties panel
    bl_label = "My Panel"           # name of the new panel
    
    def draw(self, context):
        # display value of "foo", of the active object
        self.layout.prop(bpy.context.active_object, "foo")

bpy.utils.register_class(myPanel)   # register panel

Spaces

Blender3D FreeTip.png
If you have spaces in your property name you can't call it with ob.foo bar
Use getattr(ob, "foo bar") instead

One thing to keep in mind is that after saving and reloading your blend-file, your new property will have disappeared. So print(ob.foo) won't work anymore. Any object that had a value assigned to the property will still have it available though, but only as an ID-property. For more information on those, look further down this page.
To make the values you assigned in a previous session available again, you need to redefine the property. So after reopening the blend-file you need to rerun:

bpy.types.Object.myProperty = bpy.props.StringProperty()

This will also reassign the values to the property that were assigned to it in a previous session (they are retrieved from the corresponding ID-properties), so you don't need to reassign those. print(ob.foo) will still return bar

Dynamic custom properties

The previous method will work fine for cases in which you wish to store static information on certain datablocks, but won't be of much help when you want the property to be dynamic. In that case you need to assign a function to the property. The way to do this is to use python's built-in property() function. For example:

bpy.types.Lamp.foo = property(bar)

A further difference is that dynamic custom properties aren't stored when saving a blend-file. Not even as an ID-property.

Below is a full example of creating a new dynamic property, called distance

import bpy #needed in a script-text-window!
def fget(self):                                 # custom get function
    """Distance from origin"""                  # description of property
    loc = self.location                         # location of object
    distance = loc.length                       # distance from origin
    return distance                             # return value

def fset(self, value):                          # custom set function
    if self.location.length < 1E-6:             # if object is at origin
        self.location = [1, 0, 0]               # direction to move in
    self.location.length = value                # set distance from origin

bpy.types.Object.distance = property(fget, fset)# assign function to property

ob = bpy.context.active_object                  # get the active object
print(ob.distance)                              # print distance to the console
ob.distance = 2                                 # set the distance

class myPanel(bpy.types.Panel):                 # panel to display new property
    bl_space_type = "VIEW_3D"                   # show up in: 3d-window
    bl_region_type = "UI"                       # show up in: properties panel
    bl_label = "My Panel"                       # name of the new panel
    
    def draw(self, context):
        # display "distance" of the active object
        self.layout.label(text=str(bpy.context.active_object.distance))

bpy.utils.register_class(myPanel)               # register panel

To have your property available across different blend-files (even after saving and reloading), you'll have to add your property in ../.blender/scripts/modules/bpy_types.py (where .. is the folder in which blender is installed). Find the class to which you wish to add your property and simply add the functions that define your property. There are two small differences, concerning the syntax. The first one is the use of a decorator. The second one (resulting from this) is that the function name is used as the property name.
Here's the same example as above, but now added to bpy_types.py

@property                           # decorator
def distance(self):                 # attribute name
    """Distance from origin"""
    loc = self.location
    distance = loc.length
    return distance

@distance.setter                    # decorator
def distance(self, value):
    if self.location.length < 1E-6:
        self.location = [1, 0, 0]
    self.location.length = value

ID-Properties


Basics

ID-properties are properties that are assigned to individual datablocks. And not just any datablock, but only datablocks that are subclassed from the ID-type. Examples are Lamps, Meshes, Objects and WindowManagers. Look at the full list for more information about ID subclasses.

Creating a new ID-property is very easy:

ob = bpy.context.active_object  # get the active object
ob["foo"] = "bar"               # create and assign an ID-property

print(ob["foo"])                # prints "bar"
# print(ob.foo)                 # won't work, as foo isn't an attribute
print(ob.items())               # prints all ID-properties of ob

Creating the ID-property and assigning it a value is all done at the same time. The ID-properties are stored as a dictionary on the datablock.

Besides the types of datablocks they can be assigned to, ID-properties have another limitation to be aware of. You can only assign strings, integers, floats, and lists to an ID-property. And the lists are limited, because they may only contain floats and integers.

Referencing

Crouch-custom-properties.png

ID-properties have the advantages that they are stored in a blend-file when it is saved, and that they are easy to keep track of. The main reason they are easy to track, is that by default they are shown in the user interface. They show up in the appropriate Custom Properties panel. So if you add an ID-property to a lamp, it will show up in the Custom Properties panel in the Lamp window. This makes it very easy to change their values, even for normal users. This can also be used to add new ID-properties, without having to use python.

To display an ID-property using UILayout.prop(), you can't simply pass "foo" as the property, but you need to pass a one-item list as a string. So '["foo"]'

class myPanel(bpy.types.Panel):     # panel to display new property
    bl_space_type = "VIEW_3D"       # show up in: 3d-window
    bl_region_type = "UI"           # show up in: properties panel
    bl_label = "My Panel"           # name of the new panel
    
    def draw(self, context):
        # display "foo" ID-property, of the active object
        self.layout.prop(bpy.context.active_object, '["foo"]')

bpy.utils.register_class(myPanel)   # register panel

Note that this won't work if the active object hasn't got the ID-property, so make sure that you have a fallback in your code instead of raising an error.

So far ID-properties were pretty straightforward, but it gets a bit more tricky when you wish to set parameters for the ID-property. For instance the minimum and maximum value of an integer. These parameters are stored in a special property called _RNA_UI It can be accessed like any other ID-property and is a dictionary with the other ID-properties as key and their parameters as values.

ob = bpy.context.active_object               # get the active object
ob["foo"] = 7                                # create the ID-property
ob["_RNA_UI"] = {"foo": {"min":3, "max":42}} # set parameters

To keep this example script short it simply overwrites _RNA_UI and thus erases any already existing parameters. In normal scripts it's better to insert a new key for your new parameters.

Summary


Property
(static)
Property
(dynamic)
ID-Property
Creation bpy.types.Object.foo = bpy.props.StringProperty() bpy.types.Object.foo = property(bar) ob["foo"] = "bar"
Declared for all datablocks of same type all datablocks of same type single datablock
Assigning values ob.foo = "bar" ob.foo = "bar" ob["foo"] = "bar"
Accepted values Any property Functions Floats, integers, lists*, strings
Stored in .blend Yes** No Yes

* List may only contain floats and integers.
** Datablocks that have a value assigned to the property, save that value as an ID-property.