blender-python-addon について
`bpy` API を使った Blender アドオン開発のスキルです。構成、UI、安全なシーン操作、登録、テスト、パッケージ化を扱います。
主な機能
- 01アドオンのパッケージとマニフェスト構成
- 02オペレーター、パネル、プロパティ
- 03安全なシーンとメッシュの操作
- 04登録とクリーンアップの手順
- 05Blender バージョン間のテスト
- 06拡張機能とアドオンのパッケージ化
活用シーン
- 01新しい Blender アドオンのひな形作成
- 02Blender UI への操作と設定の追加
- 03登録と解除が適切かの確認
- 04インストール用アドオンパッケージの準備
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mindrally/ skills --skill blender-python-addonインストール後、Claude Code で/blender-python-addonを入力して読み込まれたか確認します。
詳細
Blender Python アドオン開発
このスキルでは、アドオンの構成、オペレーター、パネル、プロパティ、安全なシーン操作、Blender のバージョンをまたいだテストなど、bpy API を使った Blender アドオンの開発方法を説明します。
Blender アドオンの開発ワークフロー
- アドオンのひな形を作成する — エントリーポイントとして
__init__.pyを含むパッケージを作成し、名前、バージョン、対応する Blender のバージョンを記述したblender_manifest.toml(Blender 4.2 以降の拡張機能)またはbl_info辞書(従来のアドオン)を用意します。 - データを定義する — 関連する設定をまとめる
PropertyGroupクラスを作成し、適切な ID 型に登録します(例:bpy.types.Scene.my_addon = PointerProperty(type=MyAddonSettings))。 - オペレーターを実装する — 各ユーザー操作について
bpy.types.Operatorをサブクラス化します。利用可能かどうかを判定するpoll()、対話型のセットアップを行うinvoke()、実際の処理を行うexecute()を実装します。 - UI パネルを作成する —
bpy.types.Panelをサブクラス化し、適切なエディター(3D ビューポートのサイドバー、プロパティエディターなど)にオペレーターとプロパティを表示します。 - すべてを登録する — すべてのクラスを
classesタプルに列挙し、対応するregister()/unregister()関数で登録・解除します。プロパティポインターも、クラスとあわせて登録します。 - クリーンなプロファイルでテストする — Blender を
--factory-startupで起動し、アドオンをインストールして有効化し、各オペレーターを試します。その後、無効化し、後に何も残っていないことを確認します。 - パッケージ化して配布する — アドオンフォルダーを ZIP 化する(または
.zip拡張機能パッケージをビルドする)とともに、Blender の Preferences > Add-ons(または Extensions)パネルから問題なくインストールできることを確認します。
アドオンの構成
- アドオンのエントリーポイントは
__init__.pyに置き、互いに対応する明確なregister()関数とunregister()関数を用意します(登録したものはすべて、逆順に登録解除します)。 - アドオンが簡単なものより大きくなったら、オペレーター、パネル、プロパティ、プリファレンス、ユーティリティコードを別々のモジュールに分け、
__init__.pyからインポートして登録します。 - 対象の Blender バージョンとパッケージ形式に合わせて、
bl_info(4.2 より前の従来のアドオン)またはblender_manifest.toml(4.2 以降の拡張機能)を使用します。規約を混在させないでください。 - UI のラベルは簡潔にします。プロジェクトが対応している場合は、
bpy.app.translationsまたは"Category"/ラベルの規約を使用して、ユーザー向けのテキストをローカライズできるようにします。
API の使用
- 操作には
bpy.types.Operator、UI レイアウトにはbpy.types.Panel、関連する設定のグループ化にはbpy.types.PropertyGroupを使用します。 - すべてのオペレーターに
bl_idname(小文字のcategory.action形式。例:object.apply_custom_modifier)、bl_label、bl_options(例:{'REGISTER', 'UNDO'})を明示的に定義します。 - オペレーターを実行可能にする前に
poll()でコンテキストを検証します。アクティブなオブジェクト、適切なモード、有効な選択があるかを確認し、エラーを発生させる代わりにオペレーターボタンをグレー表示にします。 - 対話型のセットアップ(ダイアログの表示、マウス位置の読み取り)には
invoke()を、実際の操作にはexecute()を使用します。追加の対話が不要な場合は、invoke()からself.execute(context)を呼び出します。 - 成功時は一貫して
{'FINISHED'}を、失敗時またはユーザーによる中断時は{'CANCELLED'}を返します。何も返さないNoneや、認識されない文字列は返さないでください。 - モディファイアー、シェイプキー、その他の依存関係グラフに基づく結果を含む最終的なシーン状態を読み取る場合は、
bpy.context.evaluated_depsgraph_get()とobject.evaluated_get(depsgraph)を使用します。
例:オペレーター、パネル、登録
bl_info = {
"name": "Random Vertex Color",
"author": "Example",
"version": (1, 0, 0),
"blender": (4, 0, 0),
"category": "Mesh",
}
import bpy
import random
class MESH_OT_random_vertex_color(bpy.types.Operator):
"""Assign a random color to the active vertex color layer"""
bl_idname = "mesh.random_vertex_color"
bl_label = "Randomize Vertex Colors"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
obj = context.active_object
return (
obj is not None
and obj.type == "MESH"
and obj.mode == "OBJECT"
and obj.data.color_attributes.active_color is not None
)
def execute(self, context):
obj = context.active_object
color_layer = obj.data.color_attributes.active_color
for data in color_layer.data:
data.color = (random.random(), random.random(), random.random(), 1.0)
obj.data.update()
self.report({"INFO"}, f"Randomized colors on '{obj.name}'")
return {"FINISHED"}
class VIEW3D_PT_random_vertex_color(bpy.types.Panel):
bl_label = "Vertex Color Tools"
bl_idname = "VIEW3D_PT_random_vertex_color"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Tool"
def draw(self, context):
layout = self.layout
layout.operator(MESH_OT_random_vertex_color.bl_idname, icon="COLOR")
classes = (
MESH_OT_random_vertex_color,
VIEW3D_PT_random_vertex_color,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
if __name__ == "__main__":
register()
データとプロパティ
- モジュールレベルの変数に場当たり的なグローバル状態を詰め込むのではなく、
PropertyGroupクラスを通じてカスタムプロパティを登録してください。こうした変数はファイルの再読み込み後に保持されず、アンドゥにも対応していません。 - アドオンの設定(API キー、既定のパス、ファイルをまたいで保持される UI の切り替え設定)は、
bl_idnameがアドオンのモジュール名と一致するAddonPreferencesサブクラスに保存してください。 PointerProperty、CollectionPropertyと、型を明示したプロパティ(StringProperty、FloatProperty、BoolPropertyなど)を使用し、ツールチップと Python API が自己説明的になるよう、name=とdescription=を明示してください。unregister()の際に、カスタムプロパティ、bpy.app.handlersのエントリ、タイマー、キーマップをクリーンアップしてください。bpy.types.*クラスやハンドラーリストに追加したものは、すべて明示的に削除する必要があります。
安全性とパフォーマンス
- オブジェクトの削除やファイルの上書きなど、破壊的なシーン操作は、ユーザーが明示的に操作しない限り実行しないでください。破壊的な操作をファイルの読み込み時に無断で自動実行してはいけません。
- モーダルオペレーターで UI スレッドをブロックしないでください。時間のかかる処理では、タイトなループの代わりに
context.window_manager.event_timer_add()とモーダル状態マシンを使用してください。 - メッシュの編集はまとめて行い、メッシュデータをプログラムで編集する場合は、構造的な変更のために
mesh.verticesを一つずつループするのではなく、bmeshモジュール(bmesh.from_edit_mesh、bmesh.new())を使用してください。 - 大規模なシーンを繰り返し走査したり、
draw()メソッド内でコストの高いデータを再計算したりしないでください。draw()は UI が再描画されるたびに実行されるため、結果をキャッシュし、基になるデータが変わったときにのみキャッシュを無効化してください。 - ファイルパスは設定可能にし、OS 固有のパスをハードコードするのではなく、Blender のパスユーティリティ(
bpy.path.abspath、bpy.utils.resource_path)を使って解決してください。
テストとデバッグ
- 他のアドオンへの隠れた依存を見つけるため、クリーンな Blender プロファイル(
blender --factory-startup)と、パフォーマンスやデータ形状の問題を見つけるための代表的な本番シーンの両方でテストしてください。 - アドオンモジュールをインポートし、
register()を呼び出し、各コアオペレーターをbpy.ops経由で実行した後、エラーや状態の残留なしにunregister()を呼び出すスモークテストを追加してください。 - ユーザー向けのオペレーターのフィードバックには、コンソールを見ないユーザーがほとんどであるため、コンソールへの出力ではなく、
self.report({'ERROR'}, "message")または{'WARNING'}/{'INFO'}を使用してください。 - Blender 3.x と 4.x の間の API 変更など、バージョン固有の API の違いは、小さなヘルパー関数の中に分離してください。そうすれば、アドオンの他の部分にバージョンチェックを散在させずに済みます。
よくある間違い
unregister()でクラス、ハンドラー、タイマー、キーマップの登録解除を忘れ、アドオンを無効にした後に Blender の状態を壊してしまう。Panel.draw()メソッド内で Blender のデータ(オブジェクトの追加、メッシュデータの変更)を変更する。draw()ではデータの読み取りと UI のレイアウトのみを行う必要があります。contextを先に確認せず、アクティブオブジェクト、空でない選択状態、または特定のモード(Object/Edit/Sculpt)が存在すると仮定する。- 開発者のマシンにしか存在しないアセットの絶対パスをハードコードし、相対パスや
bpy.utils.resource_pathを使用しない。
コメント
Markdown 対応