
【Blender】Pythonで自作のパイメニューを作る備忘録 #2 ≪BlnderPython基礎編≫
今回は「そもそもBlenderで操作をした時、裏側では何が行われているのか」をザックリ紐解いて、それをどう自作メニューに応用していくかまでを書きます。
まず初めに、プリファレンスの「開発者用オプション」を有効にしてください。

各操作をするとどうなる…?
まず、Blenderのモードを「Scripting」にします。

すると、このような画面構成になりました。

テキストエディタにコーディングしていき、実行したコードが実際どのような挙動をするのかを、「情報」「ビュー」を見て把握する形です。
では、3Dビュー上でキューブを削除してみましょう。
すると…

このような文字列が表示されました。
その調子でキューブを新規作成してみましょう。

また新たに文字列が表示されました。
更に、3Dビュー上でタブを押し編集モードに切り替えると…

どれも「bpy.ops~」から始まる文字列ですね。
つまり何を言いたいかというと、各操作それぞれには特定のコードが予め設定されており、それを我々はショートカットや画面上で操作することでコードを呼び出しています。そしてこの文字列自体がBlenderPythonの一部なのです。
Blenderは指示されるコード認識して、何をすべきか判断しているのです。
試しに、キューブをビュー上で幾つか複製した後、以下のコードを「Pythonコンソール」に打ち込んで、実行(Enter)してみて下さい。
bpy.ops.object.select_all()
キューブがすべて選択されたかと思います。今のコードはBlenderに「ビューー上のオブジェクトを全て選択」と指示するコードです。
試しにAキーをビュー上で押下してみても、似たコードが情報タブに表示されますね。
文字列の意味
忘却まとめ様を参考にさせていただきました。
詳しく理解したい方は、是非そちらをご覧下さい。(リンク)
bpy.
これからBlenderPythonを使うよ!という宣言みたいなもので、文頭に必須。
bpy.ops.
Blenderが予め用意した機能の中から実行するよという意味。オペレーターと表現するらしいです。
~~~(カッコ)
文末に()が付きますが、この中には値(パラメータ)を記入します。
例えば、編集モードで「頂点」「辺」「面」選択を切り替える時のオペレーターの種類は「bpy.ops.mesh.select_mode()」です。
ですが、「頂点」「辺」「面」のうちどれに切り替えるか?はこれだけの文では判断できません。この時に値を使います。
例えば辺モードにするなら値はEDGE、面ならFACEを値に指定します。
# 辺モードに切り替え
bpy.ops.mesh.select_mode(type='EDGE')
# 面モードに切り替え
bpy.ops.mesh.select_mode(type='FACE')
実際にPythonコンソールで上のコードを実行すると、切り替わります。(※編集モード中に実行してね)
値の書き方はオペレーター毎に決まっています。それを確認するためには、実際に調べたいボタンの上で右クリック > Blender Python APIリファレンスを選びます。
すると、選んだオペレーターの意味や、設定できる値の詳細がブラウザに表示されます。


例えばこの場合、値は四角で囲った通り4種類存在します。
一番上のデフォルト文は、「()にそれぞれの値を含めなかった場合、この通りに値が反映されますよ」と表しています。
例えば値に「use_extend」「use_expand」を含めずコーディングした場合、指定なしと判断され、デフォルト文のFalseが結果に反映されます。
コマンドのコピー
実際に実行させコマンドを情報からコピーする方法の他に、欲しい機能の上で右クリック > Pythonコマンドをコピーで、コマンドをコピーできます。

たまに押下できない時がありますが、実際に実行する or リファレンスページに飛べばコマンドは参照できます。
テキストエディタで実行する時
これまでPythonコンソールに直接コードを実行させていましたが、テキストエディタではどうするか。
試しに、Newで新規作成し以下のコードを1行目に書いてみます。

bpy.ops.object.select_all(action='SELECT')
実行してみると、エラー!

理由は簡単で、ある決まり文句を1行目に書いていないのでBlenderは「?」な状態なのです。
なので、1行目に「import bpy」と入力し、もう一度実行します。すると上手く通りました!

一番最初に必須な決まり文句です。
これからコーディングをテキストエディタで行いますが、これが無いと何をしてもエラーなので注意!
ということで、コードに種類はいろいろ!
bpy.ops.mesh、bpy.ops.wm、 …
と、多種多様ですが、共通して言えるのはどの操作にも何かしら割り当てられているということ。
つまりパイメニューでボタンを選んだ時、欲しいコードが実行されるように設定すれば自作できる!となります。
今回は「欲しいコードをコピー→自作パイメニューに貼り付け」という工程になるので、各オペレーターやコードの意味は深く解説していません。
bpy.data.やbpy.contextを使う
試しにメニュータブを切り替えてみてください。bpy.context.space_data.context = ''という、ops.が付かないパターンが現れました。

このdataやcontextは、「指定した場所は今どんな状況になっているか?」といった設定や状態にアクセスできます。
例えば、オブジェクトを複数選択し、「bpy.context.selected_objects」をPythonコンソールで実行してみて下さい。
すると、その下に「Cube」「Cube.001」…と表示されました。このコードは「3Dビューで選択中のすべてのオブジェクトにアクセスする」というコードで、指示通り青文字で教えてくれました。

このように、何かを実行するコマンドの他に、今どんな状態かを調べるコマンドもあります。
IF文(もし~ならば)を使う
プログラミングの代表格みたいな存在ですよね。PythonではIFを以下のように書きます。
# りんごが「赤い」ならば収穫する
if りんご == 赤い:
収穫する
if not
ifの逆で、もし~でなければとなります。
# りんごが「青い」以外ならば収穫する
if not りんご == 青い:
収穫する
elif
ifの条件に当てはまらなかった場合、elifの内容を実行してくれます。
elif単体では動かず、必ず頭にifが必要です。
else
ifやelifのどれにも当てはまらなかった場合、これを実行してくれます。
bpy.context.とifを応用してみる
これまでの解説を応用して、一つコードを書いてみます。
少し早足になりますが、心配しないでね。
知らぬ間に内側に面を張ってしまうことありますよね。
消そうと思うと「面選択以外のモードにする」→「非多様体選択」→「面の削除」と結構な工程を踏むので面倒です。
これを一発で行い、更にcontextとifを使って非多様体選択ができない面選択モードの時はエラーメッセージを表示させるコードを記述してみます。
処理の手順
以下の通りにしました。この通りに上からコーディングしていけばいいので、整理がつきますね。

まずは、各項目で必要なコードをそれぞれ構築し、最後にIF文でまとめます。
エラーメッセージの構築
まず、どんなエラーメッセージの出し方にするかを予めコーディングし、呼び出せるようにします。
今回はこちらを参考にさせていただきました。
import bpy
def ShowMessage(message = "", title = "", icon = 'INFO'):
def draw(self, context):
self.layout.label(text=message)
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
これでShowMessageを呼び出せば、エラーメッセージが表示されるようになります。
現在の選択モードは何かをアクセスする
こちらの記事を引用させていただきました。
mesh_select_mode = bpy.context.tool_settings.mesh_select_mode[:]
これを単体で実行すると、(頂点, 辺, 面)モードがそれぞれ「有効」「無効」かが結果として表示されます。
この結果を面選択モードか?の判断に用います。
非多様体選択→削除の動作を1発で行うようにする
最初の画像で流れを一通り書きました。
つまり、その動作を縦に並べてコーディングすれば1発で行えるようになります。
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.select_non_manifold()
bpy.ops.mesh.delete(type='FACE')
上から順に、「全選択解除」「非多様体選択」「面の削除」を行っています。
実際に文頭にimport bpyを付けて、面選択モード以外で実行すればこれだけでも非多様体の削除を行ってくれます。
ifを使ってコーディングする
import bpy
# エラーメッセージのコード
def ShowMessage(message = "", title = "", icon = 'INFO'):
def draw(self, context):
self.layout.label(text=message)
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
# 選択モードの状態を参照し"mesh_select_modeに代入する
mesh_select_mode = bpy.context.tool_settings.mesh_select_mode[:]
# もし選択モードが面であれば
if mesh_select_mode == (False, False, True):
ShowMessage(message="面選択モード以外で実行してください", title="Error")
else:
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.mesh.select_non_manifold()
bpy.ops.mesh.delete(type='FACE')
今個々でコーディングしたものを繋ぎ合わせ、ifだけ新たに書き足した状態です。
そうやって砕いて見ればとっつきやすいですね。
ifの解説です。
選択モードが面の時は、(False, False, True)と結果が返ってきます。
なので、(False, False, True)の時はエラーメッセージを表示させる「ShowMessage」を実行させ、それ以外(else)の時は一発削除のオペレーターを走らせています。
これでコードを実行すると…


次回
少し長くなってしまいましたが、前置きは以上となります。
次回からは、自作コードをショートカットで実行させる方法とパイメニューの制作に取り掛かっていきます。