【Blender 2.8 アドオン開発】004 Hello Blender AddOn
前回までで、ザックリとですが Blender 内のデータ構造、レイアウト構造、API の使い方、調べ方を説明しました。
今回から実際にアドオンを作っていきたいと思います。
今回はプログラミングの慣例にならいまずは、「Hello AddOn」をしましょう。
アドオンとは
Blender において、アドオンとは追加機能、拡張機能のことを指します。
Blender にはユーザが作法にのっとって作成した機能を受け入れる仕組みが備わっています。
つまり、アドオンを作るということは、
- 追加する機能を作成する
- Blender が機能を追加できるようにする
という2つの実装が必要になってきます。
機能の作成
まず、コンソールウィンドウを表示して、「Scripting」レイアウトで新規テキストを作成してください。
詳しい操作は第1回を参照してください。
続いて、以下のコードを書いてください。
import bpy class TUTORIAL_OT_HELLOADDON(bpy.types.Operator): bl_idname = "tutorial.helloaddon" bl_label = "HelloAddOn" def execute(self, context): print("Hello AddOn") return {'FINISHED'} bpy.utils.register_class(TUTORIAL_OT_HELLOADDON)
このスクリプトの説明をする前に、実行してみましょう。
テキストエリア右上の「Run Script」ボタンを押してください。
続いて Pythonコンソールに
bpy.ops.tutorial.helloaddon()
と、入力してエンターを押してください。
コンソールウィンドウにHello Addon
と表示されたら成功です。
キーボードの「F3」を押して出てくるサーチメニューから実行もできます。この場合は「HelloAddOn」で検索すれば出てきます。
このスクリプトでは新たな機能を定義し、Blender へ登録し、使える状態へしました。
では、スクリプトの解説をしていきす。
まずは、bpy
をインポートするところから全ては始まります。
機能を定義
bpy.ops
へ登録されていることからもわかるように、今回作成した機能はオペレーターです。
Blender へ登録するオペレーターはbpy.types.Operator
を継承したクラスとして定義します。
まず、クラスの命名規則について触れます。
今回はオペレータークラスだけですが、Blender で用意されたいくつかのクラスを継承してクラスを定義する場合、命名規則が存在します。
リファレンスによると[A-Z][A-Z0-9_]*_{Separator}_[A-Za-z0-9_]+
なのですが、ちょっと解りにくいので説明しますと。
XXX_{Seperator}_YYY
とした場合、
XXX
の部分は1文字目は大文字の英字(AからZ)2文字目以降は大文字の英字と数字とアンダースコア(AからZと0から9と_)で記載します。
YYY
の部分は1文字目から大文字の英字と数字とアンダースコア(AからZと0から9と_)で記載します。
XXX
とYYY
は最低1文字は必要です。
{Separator}
の部分は継承元のクラスによって変わります。
継承元 | Separator |
---|---|
Operator | OT |
Panel | PT |
Header | HT |
MENU | MT |
UIList | UL |
今回のスクリプトでいうとbpy.types.Operator
を継承しているのでOT
を Separator としてTUTORIAL_OT_HELLOADDON
です。
もちろんTUTORIAL_OT_HelloAddOn
やTUTORIAL_OT_hello_add_on
としても大丈夫です。
命名規則に従わない場合、現時点で Warning が出て、将来的にエラーとなるらしいので命名規則は守りましょう。
また、いくつか命名規則に従わなくても大丈夫なパターンがあるようですが、覚えるのがメンドクサイので順守しましょう。
では中身に触れていきましょう。
オペレータークラスは変数bl_idname
bl_label
と実行部分となるメンバー関数execute
を定義しなければエラーとなります。
bl_idname
bl_idname = "tutorial.helloaddon"
bl_idname
は ID を指定するのですが、
- 一意でなければならない
- 小文字でなければならない
{name space}.(ピリオド){operator name}
の形式でなければならない
という規約があります。
すでに登録されているクラスとカブっていなければ、クラス名にある_OT_
の両サイドを小文字にして使用していれば問題はありません。
tutorial.helloaddon
を例にとると、bpy.ops
のtutorial
グループ(name space)へhelloaddon
という形で登録されます。
このtutorial
をobject
とした場合bpy.ops.object
グループへhelloaddon
が登録されます。
今回はtutorial
としましたが、本来は機能の内容によって既存のグループへ登録したほうが親切ではあります。また、クラス名とbl_idname
は違っていてもいいのですが、同じにしておくとなにかと間違えにくいので同じにしておきましょう。
bl_label
bl_label = "HelloAddOn"
bl_label
は表示名を指定します。特に規定はないです。サーチメニューへはここで指定した名前が使われます。
execute
def execute(self, context): print("Hello AddOn") return {'FINISHED'}
コアとなるexecute
メンバー関数です。
Blender から実行を指示されると最終的にこの関数が実行されます。
この関数は引数にself
とcontext
を取り、戻り値として状態結果の列挙体(enum)を返します。
self | 自身のインスタンス(レシーバー)です。 |
context | bpy.context と同義で現在の状態を保持しています。ですが、第3回でも触れたオーバーライドされた Context を指定されている場合など、 bpy.context で取得できる内容と、引数で渡ってきたcontext の内容は違う場合があります。ですので、関数内で現在の状態を参照したい場合は引数の Context を使用するようにしましょう。 |
return | その処理が正常に終わったのか、実行がキャンセルされたのか、という状態結果を定義されている列挙体で返します。 この列挙体は RUNNING_MODAL CANCELLED FINISHED PASS_THROUGH INTERFACE が定義されています。とはいえ、この列挙体を意識しないといけないのは継続して処理を実行する Modal 実行が絡んだ時ですので、今回の要に execute メンバー関数だけの場合は関数が完了したという意味のFINISHED だけ覚えておけばひとまず問題ありません。 |
このexecute
メンバー関数内で Blender を使う上で便利な機能を書いていくことになります。
以上でオペレータークラスの定義は終了です。
Blender へ登録
定義したオペレータークラスを Blender へ登録します。
登録には Blender API のbpy.utils.register_class
にクラスを指定して使用します。
クラス定義を更新して「Run Script」した場合、もう一度登録がされるのですが、
- 同じ
bl_idname
だった場合上書きされます。 - 違う
bl_idname
だった場合は新たに登録されて、以前登録していたオペレーターはそのまま残り、実行もできます。
登録を解除したい場合は、スクリプトを保存して、Blender を再起動しましょう。
登録を解除する API としてbpy.utils.unregister_class()
というのがあります。
これは指定したクラスを登録解除するというものですが、bpy.utils.register_class()
と置き換えても動きません。それは、登録されているクラスと登録解除しようとしているクラスが同じものか判らないかららしいのです。
ですが、
bpy.utils.register_class(XXX) bpy.utils.unregister_class(XXX)
と、いうふうに連続して書くと登録解除できたりします。
出来るのですが、これが Blender 的に正しいのかちょっと疑問ですので僕自身は避けています。
とはいえ、これで機能の作成は終わりました。
ですが、Blender を再起動したら登録は解除されてしまいます。
もちろん、スクリプトを読み込んでもう一度走らせれば登録はできますが、Blender を起動するたびにこの操作をするのは億劫です。
そこで、アドオンという仕組みが出てきます。
端的に言って、この仕組みは「インストールされているアドオンで指定してある機能を Blender へ登録する」というものです。
最小のアドオン
# # bl_info # bl_info = { "name": "HelloAddOn", "description": "This AddOn is first AddOn", "author": "memoteu", "version": (1, 0, 0, 0), "blender": (2, 80, 0), "support": "TESTING", "category": "Tutorial", "location": "", "warning": "", "wiki_url": "", "tracker_url": "" } # # register # def register(): print("regist addon") # # unregister # def unregister(): print("unregist addon") # # AddOn Entry # if __name__ == "__main__": register()
このスクリプトが Blender アドオンの最小コードになります。
では、コードの説明をしていきます。
bl_info
このbl_info
へアドオンの説明を設定すると、アドオンをインストールした際 Blender が読み取り、追加されます。
逆を言えばこのbl_info
がないとアドオンとみなされずインストールすることは出来ません。
各項目については Blender アドオンで検索したら様々なサイトで説明がされていますが、一応書いておきます。
項目 | 型 | 説明 |
---|---|---|
name | 文字列 | アドオンの名前 |
description | 文字列 | アドオンの説明文 |
author | 文字列 | アドオン作成者名 |
version | 整数タプル | アドオンのバージョンをタプルで指定。桁数の上限は不明ですが、4桁はいけました。 バージョンが 1.0 なら (1, 0) 2.10.1 なら(2, 10, 1) と指定する。 |
blender | 3桁整数のタプル | アドオンが動作する Blender の最小バージョンをタプルで指定。 アドオンのバージョンと違い3桁で指定しなければなりません。 Blender バージョンが 2.79 なら (2, 79, 0) と指定する今回の Blender バージョン 2.8 の場合、注意が必要で指定は (2, 80, 0) と表記しなければなりません。 |
support | 文字列 | アドオンのサポートレベルを指定します。 指定できる文字列は OFFICIAL COMMUNITY TESTING の、3つです。OFFICIAL は公式サポートのことなので、指定してはいけません。ですので、 COMMUNITY かTESTING のどちらかを指定することになります。デフォルトでは COMMUNITY ですが、今回はTESTING を指定しています。 |
category | 文字列 | アドオンの種類を指定します。 例えば 3D View に対してのアドオンであれば 3D View 、ファイルの読込み、書き出しに対してのアドオンであればImport-Export など Blender に組み込まれているアドオンがすでに設定しているカテゴリー名を指定すれば、そのカテゴリーに追加することも出来ます。もちろん、今回のように自身でカテゴリーを新たに追加することも出来ます。 |
location | 文字列 | アドオンが主に実行される場所を指定します。 これはメインとなる UI の場所を指定するのですが、今回は UI は出てきませんので、空文字です。 |
warning | 文字列 | アドオンの注意文を書きます。(バグの有無や使用上の注意など) |
wiki_url | 文字列(アドレス) | アドオンの説明、使い方などが記載されたサイトの URL を書きます。 |
tracker_url | 文字列(アドレス) | アドオンのバグ報告、改善要求など作成者とコンタクトが取れるサイトの URL を書きます。 |
register()
この関数はインストールされたアドオンが「有効化」されたときに呼び出されます。
関数の名前は変えてはいけません。
この中でbpy.utils.register_class()
を呼んで機能を登録することになります。
unregister()
この関数はregister()
とは逆で、インストールされたアドオンが「無効化」されたときに呼び出されます。
関数の名前は変えてはいけません。
この中でbpy.utils.unregister_class()
を呼んで機能を登録解除することになります。
AddOn Entry
AddOn Entry とは書いてますが、アドオンとは関係ありません。
if __name__ == "__main__": register()
これは Python の話になるのですが、
モジュールが単体実行の場合__name__
は"__main__"
となり、import 呼び出しの場合__name__
はモジュール名となります。
Blender にインストールされたアドオンは import 呼び出しなのでこの if
文に入ることは無く、Blender は直接 register()
unregister()
を呼びます。
if
文に入るのは「Run Script」を押したときです。
このスクリプトを保存して、インストールすればアドオンとして動きます。
ですが、機能が備わっていないので意味は無いのです。
Hello AddOn
ここまでで、
という2つのスクリプトを書いてきました。
あとは、この2つの実装を1つにしてやればアドオンの完成です。
# # bl_info # bl_info = { "name": "HelloAddOn", "description": "This AddOn is first AddOn", "author": "memoteu", "version": (1, 0, 0, 0), "blender": (2, 80, 0), "support": "TESTING", "category": "Tutorial", "location": "", "warning": "", "wiki_url": "", "tracker_url": "" } import bpy # # TUTORIAL_OT_HELLOADDON # class TUTORIAL_OT_HELLOADDON(bpy.types.Operator): bl_idname = "tutorial.helloaddon" bl_label = "HelloAddOn" def execute(self, context): print("Hello AddOn") return {'FINISHED'} # # register # def register(): print("regist addon") bpy.utils.register_class(TUTORIAL_OT_HELLOADDON) # # unregister # def unregister(): print("unregist addon") bpy.utils.unregister_class(TUTORIAL_OT_HELLOADDON) # # AddOn Entry # if __name__ == "__main__": register()
本当に1つにしただけです。機能登録のbpy.utils.register()
と機能登録解除のbpy.utils.unregister()
の位置が移動していますが、ここまで読んでいただけたなら意味は解るはずです。
1点注釈を入れるならば、このスクリプトを「Run Script」で実行した場合、機能の登録はされますが、それはアドオンをインストールしたことにはなりません。
つまり、Blender を再起動すれば登録は解除されています。
アドオンをインストールするには Blender の操作が必要です。
インストール
では、どこでもいいので、拡張子.py
を付けるのを忘れず一旦ファイルを保存してください。
ファイル名は仮に「HelloAddOn.py」とします。
ウィンドウのヘッダーにある「Edit」から「Preferences」を開いてください。
続いて「Add-ons」ページの「Install」を押して、先ほど保存した「HelloAddOn.py」をインストールしてください。
もし、エラー無くインストールされたアドオンが表示されていなければ、サポートフィルターが「Testing」になってるか見てください。
チェックボックス横の三角ボタンを押すと詳細が見えます。
詳細へはbl_info
に設定したいくつかの項目とアドオンをインストールした場所が記載されています。
一番下の「Remove」ボタンを押すとアドオンはアンインストールされます。
では、問題なくインストールされてアドオンが表示されたら、さっそくチェックボックスを押して有効化しましょう。
このとき、コンソールウィンドウへregist addon
と表示されているはずです。
チェックボックスを外すとunregist addon
と表示されるはずです。
このことから、register()
とunregister()
が実行されていることがわかります。
そして、register()
とunregister()
内に書いたbpy.utils.register_class()
とbpy.utils.unregister_class()
も実行されるので、アドオンを有効化して初めて、機能の登録が行われるということも解ります。
ちなみに、bl_info
内のcategory
もカテゴリーフィルターとして登録されています。
では「Save Preferences」を押して Preferences ウィンドウを閉じてください。
登録された、機能が動くか Pythonコンソール、またはサーチメニューから実行して、問題なければ Blender を再起動しましょう。
問題なければ再起動後も登録した機能が動くはずです。
これで、アドオンは完成です。
アップデート
アドオンを開発して使用していると、バグ、改善、機能追加など様々な理由からアップデートをかけることになります。
実はこのアップデートがちょっとしたクセ者なので説明をしたいと思います。
今回の例でいうと、「HelloAddOn.py」が完成し保存しました。
次にインストールをしました。
最後に有効化をしました。
この段階で「HelloAddOn.py」に書いたスクリプトは
- 保存した場所
- インストールした場所
- メモリ上
と3ヶ所に存在していることになります。
インストールした場所はアドオンの詳細の「File」項目に書いてあります。
あるとき、ふと気づきます、「アレ?コンソールウィンドウじゃなくて Blender に「Hello AddOn」て出した方がクールじゃね?」と。
そこでスクリプトを変更するわけです。
# # TUTORIAL_OT_HELLOADDON in HelloAddOn.py # class TUTORIAL_OT_HELLOADDON(bpy.types.Operator): bl_idname = "tutorial.helloaddon" bl_label = "HelloAddOn" def execute(self, context): print("Hello AddOn") self.report({'INFO'}, "Hello AddOn") # added return {'FINISHED'}
そして、保存します。
この段階で、保存した場所のスクリプトはアップデートしたことになります。
次に、インストールした場所のスクリプトですが、アップデートはしていません。それは、アドオンのインストールは Blender が参照しているスクリプトディレクトリへのコピーだからです。
ではどうするか、「Prefereces」からもう一度再インストールしてやる必要があります。
ここで2つに分かれます、
- 「Remove」してからインストールする
- 「Remove」せずに上書きインストールする
「Remove」してからインストールする場合は問題ありません、有効化すればメモリ上もアップデートがかかります。
というか、アンインストール時にunregister()
が呼ばれるので正確にはメモリ上も再インストールされるです。
「Remove」せずに上書きインストールする場合は2つ注意が必要です。
1つ目は元のスクリプトは消さないということです。これはファイルの名前へを変えたときなど、上書きインストールすると元のファイルは残ったままになります。
2つ目はunregister()
が呼ばれず前のスクリプトをメモリ上へ残したまま、インストール先のスクリプトが更新されるため再インストールしたのに動作が更新されないということがおきることです。
これに関しての対処法は2つ
です。
API は bpy.ops.script.reload()
です。サーチメニューでは「Reload Script」で出てきます。
この API はメモリ上のスクリプトを再読込みするものです。スクリプトはインストール先のディレクトリから読み込まれるので、再インストールしていないと意味はありません。
有効/無効のチェックボックスを切り替えた方が手軽に見えますが、その内「Prefereces」を開くのメンドクサくなってきます。
そして、インストール先のファイルを直接イジるようになります。保存したらインストール先のスクリプトのアップデートど同義なので、API で更新してウキウキしながらアドオン開発をします。
そんな折、Blender が更新されて初回起動時のスプライトが更新されます。もちろん、見てみたくなります。
そこで、設定ファイルとかを消して一旦 Blender をフラットにします。開発中のアドオンがあるアドオンのインストール先ディレクトリごと消して。そう、僕です。
これに関しては手間を惜しまないか、気をつける他ありません。