
【Blender】Pythonで自作のパイメニューを作る備忘録 #4 ≪記述編2≫
今回から実際に自作パイメニューを制作していきます!
実行させたい処理を準備する
自作パイメニューの各ボタンを選んだ際、処理が実行されるようにするのですが、これまでの記事で匂わせていた通り実行させたい処理そのものは書きません。
処理が書かれたclassのbl_idnameを参照させ、外部の.pyファイル(モジュール)のclassを引用する形を取ります。

「外部のモジュールを引用する」と言っても、そのモジュールは既に起動時にBlender内部へ読み込まれているので、bl_idnameを指定するだけで実行されます。
なのでまず、パイメニューに表示させたいボタンの数だけclassを用意し.pyとして"startup"フォルダ内に保存させます。
パイメニューのコーディング
Blenderはパイメニューのテンプレートも用意してくれているのでそれをベースにコーディングします。
テキストエディタ > テンプレート > Python > UI Pie Menu
この状態で実行するともう既にパイメニューが完成されています。
構文はsimple operatorと大差がないので、なんとなく理解できるのではないでしょうか。

では各項目ごとに解説します。
1:class
これまで同様class名、bl_labelを設定します。 但し以前説明したように命名規則に則る必要があります。
今回のメニューカテゴリはbl_idnameがclass名と同一という規則があるため、bl_idnameは記述する必要がありません。 今回は以下のように記述しました。
class MY_MT_PIE_menu01(Menu):
bl_label = "My_PieMenu01"
2:def draw
必要な文です。そのままでOK
3:pie.operator
pie.operator("bl_idname名","type")が実際にボタンを生成するコードです。
_enumは不必要です。「列挙型」というものらしいですが、しっかり理解ができなかったので今回は理由はパスしました。

おそらく_enumは、複数の値が選択肢にある場合、その値を全て並べる役割ではないかなと思います。
サンプルコードの場合、セレクトモードは「頂点」「辺」「面」3つがありますが、それを1行で済ませています。
値は以下の通りに設定できます。
pie.operator("bl_idname名", text="表示したい文字", icon="表示したいアイコン")
アイコンのIDはこのアドオンから調べます。


IDをそのまま値の「icon=""」に打ち込めば、参照されます。
4:register ,unregister
simple operatorと同様、classの登録に関する処理を行います。
同様、パイメニューを登録させてください。
実際にコーディングして実行してみる
事前に準備したbl_idnameを参照するだけですので難しいことはありません。
以下の通りにコーディングしてみました。

実行すると、

pie.operatorの数だけボタンが現れます。
ボタンを実行するとm._mesh_subdivに割り当てられた処理が実行されます。
ホットキーへの登録
前回同様、bl_idnameをホットキーへ登録します。
この時、上の欄には「wm.call_menu_pie」を入力し、下の欄にclass名を入力します。


サブメニューを表示する
GIFのように別枠にサブメニューを表示したり、更に深い階層にメニューを表示させます。

こちらも同様、実行させたい処理のclassを事前に作成します。
別窓用のコードを追加する
以下のコードをこれまで記述してきたパイメニューコードの真下に追加します。
pie.separator()
other = pie.column()
gap = other.column()
gap.separator()
gap.scale_y = 1
other_menu = other.box().column()
other_menu.scale_y=1.5
other_menu.operator("bl_idname", text="サンプル")

各コードの意味は以下の通りです。実際にコーディングして実行してみるのが一番理解できるかと思います。
# 別窓をどの位置に配置するか。コピペして数を増やすたびに位置が変わる
pie.separator()
pie.separator()
pie.separator()
# 別窓に必要なコード
other = pie.column()
gap = other.column()
gap.separator()
# 別窓を既定の位置からどれだけオフセットするか y = value
gap.scale_y = 1
other_menu = other.box().column()
# 各ボタンのYサイズ y = value
other_menu.scale_y = 1.5
# ボタンの項目の生成。欲しい数だけ増やす
other_menu.operator("bl_idname", text="サンプル")
例えばpie.separator()を4つ配列し、other.menu.scale_y = 15とするとこうなります。

また、上記のコードを丸々複製して配列すれば、別窓を複数作ることも可能です。

さらに深い階層のメニューを表示させる
サブメニューのサブメニューを作ります。こちらのサイトを参考にさせていただきました。

サブメニューのサブメニューはClassを別でコーディングします。以下の通り構文します。
class **_MT_**_****(Menu):
bl_label = 'Sample'
def draw(self, context):
layout = self.layout
# ウィンドウ上部に表示するテキスト
layout.label(text="Display as")
layout.separator()
# ボタン
layout.operator("bl_idanme", text="テクスチャ")
layout.operator("bl_idname", text="ソリッド")
layout.operator("bl_idname", text="ワイヤ")
layout.operator("bl_idname", text="バウンス")
メニューカテゴリなので、命名規則に則ってclass名を作成し、それ以外はコメント通りです。
今作ったclassのregister() , unregister()の追加を忘れずに!
classが構築出来たら、さっき作った自作パイメニューに入れ込みます。
other_menu.menu('さっき作ったClassのbl_idname', text="ビューポートの表示方法", icon="TRIA_RIGHT")
パイメニューのボタンの並びにこのコードを追加するだけでボタンが生成されます。

全体のコードとしてはこうなりました。
この記事冒頭に貼ったGIFのパイメニューコードがこれになります。

今思ったのが、_enumを使えば省略できそうな部分はあるなって思ったことです。これは近々挑戦したい…。
ビューポートモードによってパイメニューを切り替える
例えば「オブジェクトモード」と「編集モード」、またはそれ以外のモードで欲しい項目って変わりますよね。
今回の場合だと、頂点等の選択モードはオブジェクトモードでは要らないし、何なら選べばエラーになります。
なので、IF文を使ってモード別に呼び出すパイメニューを切り替えます。
まずは現在のモードの参照を行うコードです。
import bpy
select_mode = bpy.context.mode
print(str(select_mode))
実行すると、システムコンソールに現在のモードがプリントされます。
これで、IFの右辺に代入する文字列を確認します。
今回はSimple OperatorのClassをベースにコーディングします。
実際のIF文は以下の通りです。
class VIEW_OT_PIE_my01main(bpy.types.Operator):
bl_idname = "view.my01main"
bl_label = "select_PieMenu_Main"
def execute(self, context):
select_mode = bpy.context.mode
if select_mode == "OBJECT":
bpy.ops.wm.call_menu_pie(name="VIEW_MT_PIE_my01")
else:
bpy.ops.wm.call_menu_pie(name="VIEW_MT_PIE_my02")
return {'FINISHED'}
モードの参照でOBJECTだった時、_my01を実行させます。
用いたいパイメニューを全て構築した後に、ここは設定する形ですね。
今回はカテゴリがオペレーターなので、ホットキーへの登録はbl_idnameを用います。
上手くIF文が走れば、同じホットキーでも別々のパイメニューを表示してくれます。

以上です
お疲れさまでした。これでBlenderの惜しい作業効率面が向上するかと思います。
そして今回自作するにあたってベースの知識を与えてくださった参考元様、ありがとうございました!