はじめてのBlenderアドオン開発

Last Update: 2023.3.1

Blender 2.7

はじめてのBlenderアドオン開発

Blender 2.7

Last Update: 2023.3.1

2-6. ショートカットキーを割り当てる

Blender本体の機能やアドオンの中には、ショートカットキーを割り当てることで頻繁に使う機能を素早く行えるようにできるものがあります。 例えば、[3Dビュー] エリアのメニューである [オブジェクト] > [トランスフォーム] > [移動] で実行される機能には [G] キーが割り当てられてます。 Blenderの機能と同様、個人で作成した機能にもショートカットキーを割り当てることができます。 本節では 2-4節 で紹介したサンプルを改良し、アドオンの機能にショートカットキーを割り当てる方法を紹介します。

作成するアドオンの仕様

アドオンを作成する

1-5節 を参考にして以下のソースコードを入力し、ファイル名を sample_2_6.py として保存してください。

import bpy
from bpy.props import FloatVectorProperty, EnumProperty
from mathutils import Vector


bl_info = {
    "name": "サンプル2-6: オブジェクトを複製するアドオン",
    "author": "Nutti",
    "version": (2, 0),
    "blender": (2, 75, 0),
    "location": "3Dビュー > オブジェクト, Ctrl + Alt + R",
    "description": "選択したオブジェクトを複製するアドオン",
    "warning": "",
    "support": "TESTING",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object"
}

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


# EnumPropertyで表示したい項目リストを作成する関数
def location_list_fn(scene, context):
    items = [
        ('3D_CURSOR', "3Dカーソル", "3Dカーソル上に配置します"),
        ('ORIGIN', "原点", "原点に配置します")]
    items.extend([
        ('OBJ_' + o.name, o.name, "オブジェクトに配置します")
        for o in bpy.data.objects
    ])

    return items


# 選択したオブジェクトを複製するアドオン
class ReplicateObject(bpy.types.Operator):

    bl_idname = "object.replicate_object"
    bl_label = "選択オブジェクトの複製"
    bl_description = "選択中のオブジェクトを複製します"
    bl_options = {'REGISTER', 'UNDO'}

    location = EnumProperty(
        name="配置位置",
        description="複製したオブジェクトの配置位置",
        items=location_list_fn
    )
    scale = FloatVectorProperty(
        name="拡大率",
        description="複製したオブジェクトの拡大率を設定します",
        default=(1.0, 1.0, 1.0),
        subtype='XYZ',
        unit='LENGTH'
    )
    rotation = FloatVectorProperty(
        name="回転角度",
        description="複製したオブジェクトの回転角度を設定します",
        default=(0.0, 0.0, 0.0),
        subtype='AXISANGLE',
        unit='ROTATION'
    )
    offset = FloatVectorProperty(
        name="オフセット",
        description="複製したオブジェクトの配置位置からのオフセットを設定します",
        default=(0.0, 0.0, 0.0),
        subtype='TRANSLATION',
        unit='LENGTH'
    )

    def execute(self, context):
        # bpy.ops.object.duplicate()実行後に複製オブジェクトが選択されるため、選択中のオブジェクトを保存
        src_obj_name = context.active_object.name
        bpy.ops.object.duplicate()
        active_obj = context.active_object

        # 複製したオブジェクトを配置位置に移動
        if self.location == '3D_CURSOR':
            # Shallow copyを避けるため、copy()によるDeep copyを実行
            active_obj.location = context.scene.cursor_location.copy()
        elif self.location == 'ORIGIN':
            active_obj.location = Vector((0.0, 0.0, 0.0))
        elif self.location[0:4] == 'OBJ_':
            objs = bpy.data.objects
            active_obj.location = objs[self.location[4:]].location.copy()

        # 複製したオブジェクトの拡大率を設定
        active_obj.scale.x = active_obj.scale.x * self.scale[0]
        active_obj.scale.y = active_obj.scale.y * self.scale[1]
        active_obj.scale.z = active_obj.scale.z * self.scale[2]

        # 複製したオブジェクトの回転角度を設定
        rot_euler = active_obj.rotation_euler
        active_obj.rotation_euler.x = rot_euler.x + self.rotation[0]
        active_obj.rotation_euler.y = rot_euler.y + self.rotation[1]
        active_obj.rotation_euler.z = rot_euler.z + self.rotation[2]

        # 複製したオブジェクトの最終位置を設定
        active_obj.location = active_obj.location + Vector(self.offset)

        self.report({'INFO'}, "サンプル2-6: 「%s」を複製しました。" % (src_obj_name))
        print("サンプル2-6: オペレーション「%s」が実行されました。" % (self.bl_idname))

        return {'FINISHED'}


def menu_fn(self, context):
    self.layout.separator()
    self.layout.operator(ReplicateObject.bl_idname)


def register_shortcut():
    wm = bpy.context.window_manager
    kc = wm.keyconfigs.addon
    if kc:
        # 3Dビューのショートカットキーとして登録
        km = kc.keymaps.new(name="3D View", space_type="VIEW_3D")
        # ショートカットキーの登録
        kmi = km.keymap_items.new(
            idname=ReplicateObject.bl_idname,
            type="R",
            value="PRESS",
            shift=False,
            ctrl=True,
            alt=True
        )
        # ショートカットキー一覧に登録
        addon_keymaps.append((km, kmi))


def unregister_shortcut():
    for km, kmi in addon_keymaps:
        # ショートカットキーの登録解除
        km.keymap_items.remove(kmi)
    # ショートカットキー一覧をクリア
    addon_keymaps.clear()


def register():
    bpy.utils.register_module(__name__)
    bpy.types.VIEW3D_MT_object.append(menu_fn)
    register_shortcut()
    print("サンプル2-6: アドオン「サンプル2-6」が有効化されました。")


def unregister():
    unregister_shortcut()
    bpy.types.VIEW3D_MT_object.remove(menu_fn)
    bpy.utils.unregister_module(__name__)
    print("サンプル2-6: アドオン「サンプル2-6」が無効化されました。")


if __name__ == "__main__":
    register()

アドオンを使用する

アドオンを有効化する

1-5節 を参考に作成したアドオンを有効化すると、コンソールウィンドウに以下の文字列が出力されます。

サンプル2-6: アドオン「サンプル2-6」が有効化されました。

アドオンの機能を使用する

[3Dビュー] エリア上でオブジェクトを選択し、[ctrl] + [alt] + [R] キーを押すと、[3Dビュー] エリアのメニューである [オブジェクト] > [選択オブジェクトの複製] を実行した時と同じく、選択されたオブジェクトが複製されます。

オブジェクト [Cube] を選択して [ctrl] + [alt] + [R] キーを押した時の様子を示しています。

アドオンを無効化する

1-5節 を参考にアドオンを無効化すると、コンソールウィンドウに以下の文字列が出力されます。

サンプル2-6: アドオン「サンプル2-6」が無効化されました。

ソースコードの解説

ショートカットキーの割り当て

ショートカットキーの割り当ては、register_shortcut 関数で行います。

def register_shortcut():
    wm = bpy.context.window_manager
    kc = wm.keyconfigs.addon
    if kc:
        # 3Dビューのショートカットキーとして登録
        km = kc.keymaps.new(name="3D View", space_type="VIEW_3D")
        # ショートカットキーの登録
        kmi = km.keymap_items.new(
            idname=ReplicateObject.bl_idname,
            type="R",
            value="PRESS",
            shift=False,
            ctrl=True,
            alt=True
        )
        # ショートカットキー一覧に登録
        addon_keymaps.append((km, kmi))

bpy.context.window_manager.keyconfigs.addon.keymaps はアドオンに割り当てられているキーマップです。

keymaps.new 関数を実行することで、新たにキーマップを割り当てることができます。 本節のサンプルは keymaps.new 関数に、以下に示す引数を指定してキーマップを割り当てます。

引数 値の意味
name キーマップ名
space_type キーマップを割り当てるエリア名

km.keymap_items.new 関数を使い、新しく割り当てたキーマップにショートカットキーを登録します。 km.keymap_items.new 関数に指定する引数は以下の通りです。

引数 値の意味
idname イベント発生時に実行する処理を記述した、オペレータクラスの bl_idname
type イベントを発生させるキーボードのキー
value イベント発生の条件値
shift イベント発生のためにshiftキーが押されている必要がある時は True
ctrl イベント発生のためにctrlキーが押されている必要がある時は True
alt イベント発生のためにaltキーが押されている必要がある時は True

本節のサンプルは、[shift] + [ctrl] + [R] キーが押された時にイベントを発生させ、オブジェクトの複製の処理をイベント発生時に実行するように引数を指定しています。 イベント発生時に引数 idname に指定した処理を実行しますが、引数 value には例えば以下のようなイベントを発生する時の条件値を指定できます。

イベント値 値の意味
PRESS ボタンを押した時にイベントを発生させる
RELEASE ボタンを離した時にイベントを発生させる
ANY ボタンの状態に何かしら変更があった時にイベントを発生させる
NOTHING イベントを発生させない

最後に、割り当てたキーマップとショートカットキーのペアをグローバル変数 addon_keymaps に保存します。 この変数はアドオン無効化時に、割り当ててたショートカットキーを削除するために必要になります。

ショートカットキーの割り当て解除

アドオン内で登録したショートカットキーは、アドオン無効化時に割り当てを解除する必要があります。

ショートカットキーの割り当て解除は、unregister_shortcut 関数で行います。

def unregister_shortcut():
    for km, kmi in addon_keymaps:
        # ショートカットキーの登録解除
        km.keymap_items.remove(kmi)
    # ショートカットキー一覧をクリア
    addon_keymaps.clear()

アドオン有効化時にグローバル変数 addon_keymaps に保存したキーマップを、keymap_items.remove 関数の引数に指定して実行することで、ショートカットキーのペアを削除します。

そして最後に、キーマップとショートカットキーのペアを保存したグローバル変数 addon_keymaps をクリアします。

割り当てるショートカットキー

Blenderでは既に多くの機能にショートカットキーが割当てられているため、単一のキーの中からショートカットキーとして何も割当たっていないキーを探すのは意外と大変です。 このため、ctrlキーやshiftキー、altキーとの組み合わせたショートカットキーを割り当てる ことも検討しましょう。 これらのキーと組み合わせることで、既に割り当てられているキーと被る可能性を低くすることができ、より簡単に空いているキーを見つけることができると思います。 本節のサンプルでも、ctrlキーやaltキーを組み合わせたショートカットキーを登録しています。

割り当てるショートカットキーは、ショートカットキーを割り当てる機能を簡単に推測できる ようなものにしましょう。 ショートカットキーを割り当てる機能であるオブジェクトの複製は英訳するとReplicate Objectになるため、本節のサンプルでは英訳の頭文字を取って [R] キーを割り当てています。

まとめ

2-4節 を改造し、[3Dビュー] エリアのメニューである [オブジェクト] > [選択オブジェクトの複製] にショートカットキーを割り当て、ショートカットキーからアドオンの機能を実行できるようにしました。 ショートカットキーを機能に割り当てることで、ユーザが機能を素早く利用できるようになります。

頻繁に使う機能に対してショートカットキーを割り当てることは、アドオンの利便性の改善に繋がる可能性があります。 一方、ショートカットキーに設定できる組み合わせには限りがあるため、ショートカットキーを乱用してユーザが利用可能なショートカットキーを減らしてしまうことは良いとは言えません。

アドオンを様々な人に使ってもらうことを考えている場合は、ショートカットキーを設定することでユーザの利便性が本当に向上するのかを検討してから設定すべき です。 個人的に利用するアドオンではない限り、試しにショートカットキーを割り当てたとか、とりあえず割り当てとけば良い程度の判断でショートカットキーを設定すべきではありません。

ポイント