[Blender] プラグインでサブメニューを作成する方法

前回の[投稿](http://qiita.com/nutti/items/a836391723bd28cd3e4c)では、Blenderのプラグインを作る方法を簡単なサンプルコードを取り上げて説明しました。
今回はサブ（ツリー上の）メニューの作り方を説明します。
サブメニューを作成するための情報がほとんどなく、試行錯誤をした結果であるため間違っている部分があるかもしれません。

# 通常のメニュー

前回の[投稿](http://qiita.com/nutti/items/a836391723bd28cd3e4c)から、メニューを作成する部分だけを抜粋しています。

```py3:menu_1.py
# メニュー
class CHoge(bpy.types.Operator):
    
    bl_idname = "uv.hoge"               # ID名
    bl_label = "Hoge Menu"              # メニューに表示される文字列
    bl_description = "Hoge Piyo"        # メニューに表示される説明文
    bl_options = {'REGISTER', 'UNDO'}

    # 実際にプラグインが処理を実施する処理
    def execute(self, context):
        return {'FINISHED'}             # 成功した場合はFINISHEDを返す

# メニューを登録する関数
def menu_func(self, context):
    self.layout.operator("uv.hoge")     # 登録したいクラスの「bl_idname」を指定

# プラグインをインストールしたときの処理
def register():
    bpy.utils.register_module(__name__)
    bpy.types.VIEW3D_MT_uv_map.append(menu_func)
```

# サブ（ツリー上の）メニュー

サブメニューを作るための簡単なサンプルコードを示します。
表示されるメニューの構成は、「Hoge Menu」-「Hoge Sub Menu」としています。

```py3:submenu_1.py
# サブメニュー
class SubMenu(bpy.types.Operator):
    bl_idname = "uv.hoge_sub"
    bl_label = "Sub Hoge Menu"
    out_text = bpy.props.StringProperty()

    def execute(self, context):
        self.report({'INFO'}, self.out_text)
        return {'FINISHED'}

# メインメニュー
class Menu(bpy.types.Menu):
    bl_idname = "uv.hoge"
    bl_label = "Hoge Menu"
    bl_description = "Hoge Piyo"

    def draw(self, context):
        layout = self.layout
        # サブメニューの登録＋出力文字列の登録
        layout.operator(SubMenu.bl_idname).out_text = "Sub Hoge Menu"

# Editモードにて「U」を押したときに表示されるメニューに登録（bpy.types.VIEW3D_MT_uv_map.append(menu_func)から呼ばれる）
def menu_func(self, context):
    self.layout.separator()            # メニューにセパレータを登録
    self.layout.menu(Menu.bl_idname)

# 登録
def register():
    bpy.utils.register_module(__name__)
    bpy.types.VIEW3D_MT_uv_map.append(menu_func)
```

# 解説
## サブメニュー用クラス

サブメニューのクラスは基本的に通常のメニューを作る時のクラスと変わらず、bpy.types.Operatorクラスを継承します。
ここでは確認として、サブメニューにout_textと呼ばれる変数を追加し、メニューが実行されたときにout_textが出力されるようにしてみます。

```py3:submenu_2.py
# サブメニュー
class SubMenu(bpy.types.Operator):
    bl_idname = "uv.hoge_sub"
    bl_label = "Sub Hoge Menu"
    out_text = bpy.props.StringProperty()

    def execute(self, context):
        self.report({'INFO'}, self.out_text)
        return {'FINISHED'}
```

## メインメニュー用クラス

メインメニューではツリー上のメニューを作成する必要がありますので、bpy.types.Menuクラスを継承しています。
さらにメインメニューは選択された時の動作を考える必要がなく、executeメソッドは不要です。
その代わりにメニューを表示するためのdrawメソッドを追加する必要があります。
drawメソッド内部ではlayout.operatorの引数に、bpy.types.Operatorのサブクラスのbl_idnameを代入して実行することでサブメニューを登録し、かつ登録されたサブメニューのメンバ変数out_textに文字列を代入しています。

```py3:submenu_3.py
# メインメニュー
class Menu(bpy.types.Menu):
    bl_idname = "uv.hoge"
    bl_label = "Hoge Menu"
    bl_description = "Hoge Piyo"

    def draw(self, context):
        layout = self.layout
        # サブメニューの登録＋出力文字列の登録
        layout.operator(SubMenu.bl_idname).out_text = "Sub Hoge Menu"
```

## 登録

登録の部分は通常の部分とほとんど変わりませんが、bpy.types.VIEW3D_MT_uv_map.appendの引数に指定した関数から呼ばれるmenu_funcに修正を入れています。
サブメニューの作成とは関係ありませんが、self.layout.separator関数を実行することで、メニューにセパレートを入れることが出来ます。
これにより他のメニューと明確に区別でき、プラグインをインストールしたことにより追加されたメニューを分かりやすくします。
self.layout.menu関数の引数にbpy.types.Menuの派生クラスのbl_idnameを渡すことで、ツリー上のメニューを登録することが出来ます。

```py3:submenu_4.py
# Editモードにて「U」を押したときに表示されるメニューに登録（bpy.types.VIEW3D_MT_uv_map.append(menu_func)から呼ばれる）
def menu_func(self, context):
    self.layout.separator()            # メニューにセパレータを登録
    self.layout.menu(Menu.bl_idname)

# 登録
def register():
    bpy.utils.register_module(__name__)
    bpy.types.VIEW3D_MT_uv_map.append(menu_func)
```

# サンプルプラグイン

以上の内容をまとめた、実際に動作するサンプルプラグインのコードを示します。

```py3:submenu.py
# Blender内部のデータ構造にアクセスするために必要
import bpy

# プラグインに関する情報
bl_info = {
    "name" : "Hoge Plugin",             # プラグイン名
    "author" : "Piyo",                  # 作者
    "version" : (0,1),                  # プラグインのバージョン
    "blender" : (2, 6, 5),              # プラグインが動作するBlenderのバージョン
    "location" : "UV Mapping > Hoge",   # Blender内部でのプラグインの位置づけ
    "description" : "Hoge Fuga Piyo",   # プラグインの説明
    "warning" : "",
    "wiki_url" : "",                    # プラグインの説明が存在するWikiページのURL
    "tracker_url" : "",                 # Blender Developer OrgのスレッドURL
    "category" : "UV"                   # プラグインのカテゴリ名
}

# サブメニュー
class SubMenu(bpy.types.Operator):
    bl_idname = "uv.hoge_sub"
    bl_label = "Sub Hoge Menu"
    out_text = bpy.props.StringProperty()

    def execute(self, context):
        self.report({'INFO'}, self.out_text)
        return {'FINISHED'}

# メインメニュー
class Menu(bpy.types.Menu):
    bl_idname = "uv.hoge"
    bl_label = "Hoge Menu"
    bl_description = "Hoge Piyo"

    def draw(self, context):
        layout = self.layout
        # サブメニューの登録＋出力文字列の登録
        layout.operator(SubMenu.bl_idname).out_text = "Sub Hoge Menu"

# Editモードにて「U」を押したときに表示されるメニューに登録（bpy.types.VIEW3D_MT_uv_map.append(menu_func)から呼ばれる）
def menu_func(self, context):
    self.layout.separator()            # メニューにセパレータを登録
    self.layout.menu(Menu.bl_idname)

# 登録
def register():
    bpy.utils.register_module(__name__)
    bpy.types.VIEW3D_MT_uv_map.append(menu_func)

# プラグインをアンインストールしたときの処理
def unregister():
    bpy.utils.unregister_module(__name__)
    bpy.types.VIEW3D_MT_uv_map.remove(menu_func)

# メイン関数
if __name__ == "__main__":
    register()
```

# サンプルプラグインの実行方法

上記のサンプルプラグインを実際に動作させてみましょう。プラグインのインストールの手順は以下のとおりです。なお、インストール手順についてはBlenderのWikiページにも記載されています。
http://wiki.blender.org/index.php/Doc:JA/2.6/Manual/Extensions/Python/Add-Ons

1. 「File」-「User Preferences」を選択します
2. 「Addons」タブを選択します
3. 「Install from File...」ボタンを押します
4. インストールしたいプラグインを選択します
5. インストールされたプラグインの横のチェックボックスをオンにします
6. 「Edit Mode」で「U」を押します
7. 「Hoge Menu」がツリー状のメニューとなっていることを確認します
8. 「Hoge Menu」-「Sub Hoge Menu」をクリックすると処理が実行されます（ログに「Sub Hoge Menu」と表示されます）

# 実行結果

## ツリー状のメニュー表示
![20141118230248.jpg](https://qiita-image-store.s3.amazonaws.com/0/38888/02d9225a-73d0-28f0-77d6-8789127add5f.jpeg)
## 「Sub Hoge Menu」の実行結果
![20141118230358.jpg](https://qiita-image-store.s3.amazonaws.com/0/38888/8551e442-a4ac-259b-776b-7de6d3917608.jpeg)

# 参考文献

* [Blenderプラグインの作り方（Qiita）](http://qiita.com/nutti/items/a836391723bd28cd3e4c)