[Blender] スクリプト内部で自作の機能にショートカットキーを割り当てる

Blenderの機能やアドオンにはショートカットキーを割り当てて、頻繁に使う機能を素早く実行できるようにしているものがあります。
個人で作成するアドオンでもBlenderの機能と同様、機能にショートカットキーを割り当てることができます。
例えば、キーボードやマウスで押したキーを画面に表示するアドオン「Screencast Key」はその一例ですね。

この記事では、割り当てたショートキーを押した時に選択されたオブジェクトの拡大・縮小を行うサンプルをもとに、自作の機能にショートカットキーを割り当てる方法を紹介します。

※ ちなみに、 ```bpy.types.Operator``` の ```bl_idname``` さえ分かってしまえば、自作の機能に限らず既存の機能にもショートカットキーを割り当てることが出来ます。

# サンプル

```py3:shortcut_key.py

import bpy


bl_info = {
    "name": "Tutorial: Shortcut key",
    "author": "Nutti",
    "version": (1, 0),
    "blender": (2, 74, 0),
    "location": "Object > Tutorial: Shortcut key",
    "description": "Tutorial: Shortcut key.",
    "warning": "",
    "support": "COMMUNITY",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object"
}

addon_keymaps = []    # 登録したショートカットキーの一覧


class ObjectScaleUp(bpy.types.Operator):
    """選択されたオブジェクトを拡大"""
    
    bl_idname = "object_transform.object_scale_up"
    bl_label = "Scale Up Object"
    bl_description = "Scale up selected object"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        active_obj = context.active_object
        active_obj.scale = active_obj.scale * 2.0    # 選択されたオブジェクトを2倍に拡大
        return {'FINISHED'}


class ObjectScaleDown(bpy.types.Operator):
    """選択されたオブジェクトを縮小"""
    
    bl_idname = "object_transform.object_scale_down"
    bl_label = "Scale Down Object"
    bl_description = "Scale down selected object"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        active_obj = context.active_object
        active_obj.scale = active_obj.scale / 2.0    # 選択されたオブジェクトを1/2に縮小
        return {'FINISHED'}


def register():
    bpy.utils.register_module(__name__)
    wm = bpy.context.window_manager
    kc = wm.keyconfigs.addon
    # 登録するショートカットキーのリストを作成
    # (キーが押されたときに実行する bpy.types.Operator のbl_idname, キー, イベント, Ctrlキー, Altキー, Shiftキー)
    key_assign_list = [
        (ObjectScaleUp.bl_idname, "U", "PRESS", True, True, False),
        (ObjectScaleDown.bl_idname, "D", "PRESS", True, True, False),
        ]
    if kc:
        km = kc.keymaps.new(name="3D View", space_type="VIEW_3D")    # 「View3D」のショートカットキーとして登録
        for (idname, key, event, ctrl, alt, shift) in key_assign_list:
            kmi = km.keymap_items.new(
                idname, key, event, ctrl=ctrl, alt=alt, shift=shift)    # ショートカットキーの登録
            addon_keymaps.append((km, kmi))


def unregister():
    bpy.utils.unregister_module(__name__)
    for km, kmi in addon_keymaps:
        km.keymap_items.remove(kmi)    # ショートカットキーの登録解除
    addon_keymaps.clear()


if __name__ == "__main__":
    register()

```

# 使い方

1. [Blender Wikiページ](http://wiki.blender.org/index.php/Doc:JA/2.6/Manual/Extensions/Python/Add-Ons)を参考にスクリプトをインストールします。
2. 拡大・縮小するオブジェクトを選択します。
3. **Ctrl + Alt + U** を同時に押して、選択されたオブジェクトが拡大されたことを確認します。
4. **Ctrl + Alt + D** を同時に押して、選択されたオブジェクトが縮小されたことを確認します。


# サンプルの解説

Blenderスクリプトの基本的な解説は以下の記事で紹介しているので、ここでは新しく追加された要素の解説に絞ります。
[[Blender] Blenderプラグインの作り方](http://qiita.com/nutti/items/a836391723bd28cd3e4c)

## ショートカットキーの登録

スクリプトのインストール時にショートカットキーを登録します。
ショートカットキーを登録する前に、 ```bpy.context.window_manager.eyconfigs.addon.keymaps.new```  関数でショートカットキーを割り当てる領域のキーマップを取得し、 ```km``` に代入します。
今回は「View3D」領域に対してキーマップを割り当てるので、引数 ```space_type``` に```View_3D``` を設定しています。

```km.keymap_items.new``` に渡す引数は、 ```key_assign_list``` に対して事前に登録しています。
```for (idname, key, event, ctrl, alt, shift) in key_assign_list:``` により ```key_assign_list```を展開してから、```km.keymap_items.new``` 関数を用いて、ショートカットキーを登録します。
、for文の各変数の意味を以下に示します。

|変数|意味|
|---|---|
|```bl_idname```|ショートカットキーが押されたときに実行するイベント（```bpy.types.Operator``` の ```bl_idname```）|
|```key```|登録するキーボードのキー|
|```event```|イベントを実行する契機 <br> (ex. ```"PRESS"```: キーが押された瞬間)|
|```ctrl```|イベントを発生させる際に、Ctrlキーの同時押しが必要な場合は```True```|
|```alt```|イベントを発生させる際に、Altキーの同時押しが必要な場合は```True```|
|```shift```|イベントを発生させる際に、Shiftキーの同時押しが必要な場合は```True```|

他にも引数を指定できるようですが、基本的に上記だけで覚えておけば問題ないでしょう。
具体的な引数を知りたい方は、以下のURLから確認してください。
http://www.blender.org/api/blender_python_api_2_63_14/bpy.types.KeyMapItems.html
最後にショートカットキーを登録解除する場合に備え、グローバル変数である ```addon_keymaps``` に登録情報を保存します。

ちなみに、Blenderでは既にたくさんの機能にショートカットキーが割当たっているので、単一のキーの中から何も割当たっていないキーを探すのが意外と大変です。
そのような時は、 **CtrlやShift、Altとの組み合わせ** にも目を向けてみましょう。
組み合わせのキーは単一のキーに比べてすでに割り当てられている場合が少ないので、割と簡単に空いているキーを見つけることができると思います。

```py3
    wm = bpy.context.window_manager
    kc = wm.keyconfigs.addon
    # 登録するショートカットキーのリストを作成
    # (キーが押されたときに実行する bpy.types.Operator のbl_idname, キー, イベント, Ctrlキー, Altキー, Shiftキー)
    key_assign_list = [
        (ObjectScaleUp.bl_idname, "U", "PRESS", True, True, False),
        (ObjectScaleDown.bl_idname, "D", "PRESS", True, True, False),
        ]
    if kc:
        km = kc.keymaps.new(name="3D View", space_type="VIEW_3D")    # 「View3D」のショートカットキーとして登録
        for (idname, key, event, ctrl, alt, shift) in key_assign_list:
            kmi = km.keymap_items.new(
                idname, key, event, ctrl=ctrl, alt=alt, shift=shift)    # ショートカットキーの登録
            addon_keymaps.append((km, kmi))
```

## ショートカットキーの登録解除

スクリプトのアンインストール時にはショートカットキーの登録を解除する必要があります。
ショートカットキーの登録を解除するため、 ```addon_keymaps``` に保存しておいた情報を用いて、 ```keymap_items.remove()``` を呼びます。

```py3
    for km, kmi in addon_keymaps:
        km.keymap_items.remove(kmi)    # ショートカットキーの登録解除
    addon_keymaps.clear()
```