Doc:JA/2.6/Manual/Extensions/Python/Properties
目次
プロパティとID プロパティおよび両者の違い
Python を使って Blender でカスタムプロパティを定義する方法はいくつもあります。その一つは、既存のデータブロックに新たなプロパティを追加することです。もう一つはデータブロックに新たな IDプロパティを定義することです。両者は同じに見えるかもしれませんが、違いがあります。どのように使い分けるのでしょう?
この記事は読者が Python と Blender の基礎知識を持っていることを想定しています。コード例はデフォルトシーンで問題なく試すことができます。作成されるユーザーインタフェース要素は 3Dビューのプロパティサイドバー(Nキーでトグル)に表示されるでしょう。このバーの最下部に表示されるので、それより上にあるパネル(Transform、View など)を折りたたんでおくと便利かもしれません。
プロパティ
基礎
デフォルトで Blender 自身が使う通常のプロパティがあります。例えばオブジェクトは location プロパティを持っています。これはオブジェクトの位置情報を含むベクターです。このプロパティは読み書きが可能です。他のプロパティ、例えば users などは読み取り専用です。
import bpy
ob = bpy.context.active_object # アクティブなオブジェクトを得る
print("Location:", ob.location) # 位置を表示
ob.location = [1,1,1] # 新たな位置を設定
print("Users:", ob.users) # user 数を表示
#ob.users = 3 # これはエラーを起こすでしょう
カスタムプロパティ
新たなプロパティを自分で追加することもできます。始めに決める必要があるのはプロパティの型です: boolean、float、string など。ここに 全ての一覧 があります。次のようにして新たなプロパティ(この例では string)を単に追加してください:
bpy.types.Object.myProperty = bpy.props.StringProperty()
これで全てのオブジェクトにプロパティ myProperty が追加されます。
一旦プロパティを追加すれば他の通常プロパティのように働きかけることができます。
ob = bpy.context.active_object # アクティブなオブジェクトを得る
bpy.types.Object.foo = bpy.props.StringProperty() # "foo" という名の新たなプロパティを追加
ob.foo = "bar" # プロパティに値を代入
print("foo:", ob.foo) # "foo: bar" を表示
print("foo:", ob['foo']) # これも "foo: bar" を表示
class myPanel(bpy.types.Panel): # 新プロパティを表示するパネル
bl_space_type = "VIEW_3D" # 3D ウィンドウに表示
bl_region_type = "UI" # プロパティパネルに表示
bl_label = "My Panel" # 新たなパネルの名前
def draw(self, context):
# アクティブなオブジェクトの "foo" の値を表示
self.layout.prop(bpy.context.active_object, "foo")
bpy.utils.register_class(myPanel) # パネルを登録
忘れずにおきたいのは保存して blend ファイルを再読み込みしたとき、追加した新たなプロパティは消えてしまうであろうことです。このため print(ob.foo) はもう動作しないでしょう。プロパティに値を代入されたオブジェクトはすべて、プロパティを維持していますが、ID プロパティとしてのみ利用可能になります。詳細はこのページのさらに下をご覧ください。
前回のセッションで代入した値を再び利用可能にするには、プロパティを再定義する必要があります。blend ファイルを開き直したら、次を再実行する必要があります:
bpy.types.Object.myProperty = bpy.props.StringProperty()
これで、前回のセッションで代入した値が代入しなおされるので(値は対応する IDプロパティから取り戻されたのです)、自分で再度代入する必要はありません。 print(ob.foo) はやはり bar を返すでしょう。
動的なカスタムプロパティ
前の方法は静的な情報を決まったデータブロックに格納したい場合にはうまく動作しますが、動的なプロパティがほしいときにはそうならないでしょう。この場合プロパティに関数を代入する必要があります。このためには python の 組み込みの property() 関数を使います。例えば:
bpy.types.Lamp.foo = property(bar)
大きな違いは、動的なカスタムプロパティは blend ファイルの保存時に格納されないことです。ID プロパティとしても保存されません。
次は distance という名前の新たな動的プロパティを作る完全な例です。
import bpy #スクリプトテキストウィンドウで必要!
def fget(self): # カスタム get 関数
"""Distance from origin""" # プロパティ説明
loc = self.location # オブジェクトの場所
distance = loc.length # 原点からの距離
return distance # 値を返します
def fset(self, value): # カスタム set 関数
if self.location.length < 1E-6: # オブジェクトが原点にあれば
self.location = [1, 0, 0] # 動かす方向
self.location.length = value # 原点から離します
bpy.types.Object.distance = property(fget, fset)# プロパティに関数を代入
ob = bpy.context.active_object # アクティブオブジェクトを取得
print(ob.distance) # 距離をコンソールに表示
ob.distance = 2 # 距離を設定
class myPanel(bpy.types.Panel): # 新プロパティ表示用パネル
bl_space_type = "VIEW_3D" # 3D ウィンドウに表示
bl_region_type = "UI" # プロパティパネルに表示
bl_label = "My Panel" # 新パネルの名前
def draw(self, context):
# アクティブオブジェクトの "距離" を表示
self.layout.label(text=str(bpy.context.active_object.distance))
bpy.utils.register_class(myPanel) # パネルを登録
作成したプロパティを異なる blend ファイル間で(保存して再読み込みしたあとでも)利用可能にするには、プロパティを ../.blender/scripts/modules/bpy_types.py (.. は blender のインストールフォルダです)に追加する必要があります。
プロパティを追加したいクラスを見つけて、プロパティを定義する関数を単に追加してください。構文に関して小さな違いが2つあります。1つ目は decorator の利用です。2つ目は(この結果として)関数名がプロパティ名として使われることです。
次の例は上と同じですが、ここでは bpy_types.py に追加しました。
@property # decorator
def distance(self): # 属性名
"""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プロパティ
基礎
ID プロパティは別々のデータブロックに代入されるプロパティです。データブロックは ID 型のサブクラスであるものに限られます。例えば Lamp、Meshe、Object、 WindowManager です。詳細は 全一覧 をご覧ください。新たな ID プロパティを作るのはとても簡単です:
ob = bpy.context.active_object # アクティブオブジェクトを得る
ob["foo"] = "bar" # ID プロパティを作って代入
print(ob["foo"]) # "bar" を表示
# print(ob.foo) # foo は属性ではないため動作しません
print(ob.items()) # ob のすべての ID プロパティを表示
ID プロパティの作成と、そこへの値の代入は一度にできます。ID プロパティはデータブロックに dictionary として保管されます。
代入可能なデータブロックの型の他に、ID プロパティは注意すべきもう一つの制限を持ちます。ID プロパティに代入できるのは文字列、整数、浮動小数点数、リストだけです。リストには浮動小数点数と整数のみを含むことができます。
参照
ID プロパティには blend ファイルが保存されたときにその中に保管されるという利点があり、見失いにくくなります。見失いにくくなる主な理由は、デフォルトでユーザーインタフェースに表示されることです。妥当なカスタムプロパティパネルに表示されます。ID プロパティを lamp に追加したなら、このプロパティがLamp ウィンドウのカスタムプロパティパネルに表示されるでしょう。このため通常ユーザーにとっても、値の変更がとても簡単になります。新たな ID プロパティを python を使わず追加するのにも使えます。
UILayout.prop() を使って ID プロパティを表示するには、単純に "foo" をプロパティとして渡すことはできず、1項目のリストを文字列で渡す必要があります。'["foo"]' のようにします。
class myPanel(bpy.types.Panel): # 新プロパティを表示するパネル
bl_space_type = "VIEW_3D" # 3D ウィンドウに表示
bl_region_type = "UI" # プロパティパネルに表示
bl_label = "My Panel" # 新パネルの名前
def draw(self, context):
# アクティブオブジェクトの "foo" ID プロパティを表示
self.layout.prop(bpy.context.active_object, '["foo"]')
bpy.utils.register_class(myPanel) # パネルを登録
これは、アクティブオブジェクトが ID プロパティを持たない場合には動作しないことに注意してください。したがってエラーを起こす代わりにコード内に代替処理を用意してください。
ここまでは ID プロパティはとてもわかりやすいものでしたが、ID プロパティにパラメータを渡したくなったとき、多少扱いにくくなります。例えば整数の最小値と最大値です。これらのパラメータは _RNA_UI と呼ばれる特殊なプロパティに保管されます。これは他の ID プロパティと同じようにアクセスでき、他の ID プロパティをキーに、パラメータを値とした dictionary です。
ob = bpy.context.active_object # アクティブなオブジェクトを得る
ob["foo"] = 7 # ID プロパティを作る
ob["_RNA_UI"] = {"foo": {"min":3, "max":42}} # パラメータを設定
スクリプト例を短くするため単に _RNA_UI を上書きして既存のパラメータを消去しています。通常のスクリプトでは新たなパラメータ用に新たなキーを挿入したほうがよいでしょう。
まとめ
プロパティ (静的) |
プロパティ (動的) |
IDプロパティ | |
---|---|---|---|
作成 | bpy.types.Object.foo = bpy.props.StringProperty() | bpy.types.Object.foo = property(bar) | ob["foo"] = "bar" |
定義対象 | 同じ型のすべてのデータブロック | 同じ型のすべてのデータブロック | 単独のデータブロック |
値代入 | ob.foo = "bar" | ob.foo = "bar" | ob["foo"] = "bar" |
受け付ける値 | プロパティ のどれでも | 関数 | 浮動小数点数、整数、リスト*、文字列 |
.blend への保管 | する** | しない | する |
* リストは浮動小数点数と整数のみを含むことができます。
** プロパティに代入された値のあるデータブロックは、その値を ID プロパティとして保存します。