はじめてのBlenderアドオン開発
Last Update: 2023.3.1
Blender 2.8~3.0
はじめてのBlenderアドオン開発
Blender 2.8~3.0
Last Update: 2023.3.1
3-2. キーボードのイベントを扱う
3-1節 では、マウスのイベントを扱う方法を説明しました。 本節はこの流れで、キーボードのイベントを扱う方法を説明します。
キーボードのイベントを扱う方法を理解するため、次のような機能を備えるサンプルアドオンを紹介します。
1-5節 を参考にして次のソースコードを入力し、ファイル名を sample_3-2.py
として保存してください。
import bpy
bl_info = {
"name": "サンプル 3-2: 入力したキーを表示するアドオン",
"author": "ぬっち(Nutti)",
"version": (3, 0),
"blender": (2, 80, 0),
"location": "3Dビューポート > Sidebar > サンプル 3-2",
"description": "入力したキーのテキストオブジェクトを表示するアドオン",
"warning": "",
"support": "TESTING",
"doc_url": "",
"tracker_url": "",
"category": "Object"
}
# 大文字のアルファベットリスト
ALPHABET_LIST = [chr(i) for i in range(65, 65 + 26)]
# 入力したキーのテキストオブジェクトを表示するオペレータ
class SAMPLE32_OT_ShowInputKey(bpy.types.Operator):
bl_idname = "object.sample32_show_input_key"
bl_label = "入力キーを表示"
bl_description = "入力したキーをテキストオブジェクトとして表示します"
# Trueの場合は、キーを入力したときに入力したキーに対する
# テキストオブジェクトが表示される(Trueの場合は、モーダルモード中である)
__running = False
# テキストオブジェクトの名前
__text_object_name = None
# モーダルモード中はTrueを返す
@classmethod
def is_running(cls):
return cls.__running
def modal(self, context, event):
op_cls = SAMPLE32_OT_ShowInputKey
# エリアを再描画
if context.area:
context.area.tag_redraw()
# パネル [入力キーのテキストオブジェクト表示] のボタン [終了] を
# 押したときに、モーダルモードを終了
if not self.is_running():
# テキストオブジェクトを削除
if op_cls.__text_object_name in bpy.data.objects:
bpy.data.objects.remove(bpy.data.objects[op_cls.__text_object_name])
op_cls.__text_object_name = None
return {'FINISHED'}
input_key = event.type
# 大文字のアルファベット以外はすべてイベントを無視する
if input_key not in ALPHABET_LIST:
return {'PASS_THROUGH'}
if op_cls.__text_object_name in bpy.data.objects:
bpy.data.objects[op_cls.__text_object_name].data.body = input_key
return {'RUNNING_MODAL'}
def invoke(self, context, event):
op_cls = SAMPLE32_OT_ShowInputKey
if context.area.type == 'VIEW_3D':
# [開始] ボタンが押された時の処理
if not op_cls.is_running():
# テキストオブジェクト作成
bpy.ops.object.text_add(location=(0.0, 0.0, 0.0), radius=2.0)
op_cls.__text_object_name = context.active_object.name
bpy.data.objects[op_cls.__text_object_name].data.body = ""
# モーダルモードを開始
context.window_manager.modal_handler_add(self)
op_cls.__running = True
print("サンプル 3-2: 入力キーの表示処理を開始しました。")
return {'RUNNING_MODAL'}
# [終了] ボタンが押された時の処理
else:
op_cls.__running = False
print("サンプル 3-2: 入力キーの表示処理を終了しました。")
return {'FINISHED'}
else:
return {'CANCELLED'}
# UI
class SAMPLE32_PT_ShowInputKey(bpy.types.Panel):
bl_label = "入力キーを表示"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "サンプル 3-2"
bl_context = "objectmode"
def draw(self, context):
op_cls = SAMPLE32_OT_ShowInputKey
layout = self.layout
# [開始] / [停止] ボタンを追加
if not op_cls.is_running():
layout.operator(op_cls.bl_idname, text="開始", icon="PLAY")
else:
layout.operator(op_cls.bl_idname, text="終了", icon="PAUSE")
classes = [
SAMPLE32_OT_ShowInputKey,
SAMPLE32_PT_ShowInputKey,
]
def register():
for c in classes:
bpy.utils.register_class(c)
print("サンプル 3-2: アドオン『サンプル 3-2』が有効化されました。")
def unregister():
for c in classes:
bpy.utils.unregister_class(c)
print("サンプル 3-2: アドオン『サンプル 3-2』が無効化されました。")
if __name__ == "__main__":
register()
1-5節 を参考に作成したアドオンを有効化すると、コンソールウィンドウに次の文字列が出力されます。
サンプル 3-2: アドオン『サンプル 3-2』が有効化されました。
Sidebarを表示し、タブ [サンプル 3-2] が追加されていることを確認します。
有効化したアドオンの機能を使い、動作を確認します。
1 | [3Dビューポート] スペースのSidebarの > [サンプル 3-2] > [入力キーを表示] に配置されている [開始] ボタンをクリックすると、入力キーを表示するモードに移行します。このとき、コンソールウィンドウに次のメッセージが出力されます。サンプル 3-2: 入力キーの表示処理を開始しました。 |
2 | 入力キーを表示するモードでは、入力したキーボードのキーをテキストオブジェクトとして表示します。 |
3 | [3Dビューポート] スペースのSidebarの [サンプル 3-2] > [入力キーを表示] に配置されている [終了] ボタンをクリックすると、入力キーを表示するモードが終了します。また、コンソールウィンドウに次のメッセージが表示されます。サンプル 3-2: 入力キーの表示処理を終了しました。 |
1-5節 を参考にして有効化したアドオンを無効化すると、コンソールウィンドウに次のような文字列が出力されます。
サンプル 3-2: アドオン『サンプル 3-2』が無効化されました。
ソースコードを見ると、変数名などの細かい部分やオペレータクラスの invoke
メソッドと modal
メソッドの処理を除いて 3-1節 で説明した内容とほとんど同じであることがわかります。 このため本節では、オペレータクラス SAMPLE32_OT_ShowInputKey
の invoke
メソッドと modal
メソッドについてのみ説明します。
3-1節 では、オブジェクトが回転中であることとモーダルモード中であることが対応していましたが、本節のサンプルアドオンでは、入力キーの表示中であることがモーダルモード中に対応します。 以降本節では、モーダルモードと書かれていたら、入力キーの表示中であるとして読み進めて問題ありません。
オペレータクラス SAMPLE32_OT_ShowInputKey
の invoke
メソッドでは、モーダルモード中ではない場合(クラスメソッド is_running
が False
を返した場合)に、bpy.ops.object.text_add
関数を呼び出して、キーを表示するためのテキストオブジェクトを作成します。 このとき、テキストオブジェクトで表示するテキストは空文字列とします。 また、modal
メソッドからテキストオブジェクトにアクセスするために、クラス変数 __text_object_name
に作成したテキストオブジェクトのオブジェクト名を保存しておきます。
modal
メソッドの最初の処理である [3Dビューポート] スペースの更新処理は、3-1節 と同様です。 ここでは、それ以降の処理について説明します。
クラスメソッド is_running
が False
を返すとき、モーダルモードを終了させます。 このとき、モーダルモード開始時に作成したテキストオブジェクトを削除する必要があります。 クラス変数 __text_object_name
に保存しておいた、テキストオブジェクトのオブジェクト名を利用し、削除対象のオブジェクトを bpy.data.objects
から削除します。 bpy.data.objects.remove
メソッドの引数に、削除対象のオブジェクトのオブジェクト名を指定することで、オブジェクトを削除できます。 オブジェクト削除後、modal
メソッドは {'FINISHED'}
を返して、モーダルモードを終了します。
# パネル [入力キーのテキストオブジェクト表示] のボタン [終了] を
# 押したときに、モーダルモードを終了
if not self.is_running():
# テキストオブジェクトを削除
if op_cls.__text_object_name in bpy.data.objects:
bpy.data.objects.remove(bpy.data.objects[op_cls.__text_object_name])
op_cls.__text_object_name = None
return {'FINISHED'}
入力されたキーに従って、テキストオブジェクトのテキストを更新します。 テキストオブジェクトのテキストを更新するためには、オブジェクトのメンバ変数 data.body
に、表示するテキストを設定する必要があります。
入力されたキーは、イベント情報である引数 event
のメンバ変数 type
に識別子として保存されている(3-1節 参照)ため、これを利用します。 ALPHABET_LIST
は、大文字のアルファベット 'A'
から 'Z'
までが保存されたリストとなっていて、event.type
がリスト内のいずれかの要素に一致する場合に、テキストオブジェクトのテキストを更新しています。 もし、リスト中のいずれの要素にも一致しない場合、modal
メソッドは {'PASS_THROUGH'}
を返してしまうため、テキストオブジェクトを更新しません。
input_key = event.type
# 大文字のアルファベット以外はすべてイベントを無視する
if input_key not in ALPHABET_LIST:
return {'PASS_THROUGH'}
if op_cls.__text_object_name in bpy.data.objects:
bpy.data.objects[op_cls.__text_object_name].data.body = input_key
なお、大文字のアルファベットのリストは、次のコードで作成できます。
# 大文字のアルファベットリスト
ALPHABET_LIST = [chr(i) for i in range(65, 65 + 26)]
本節では、キーボードのキーイベントを扱う方法を紹介しました。 3-1節 のサンプルアドオンと比較すると、modal
メソッドまたは invoke
メソッドの引数に渡されてくるイベント情報を用いる点で、マウスのイベントとキーボードのイベントの扱い方が、ほとんど同じであることが理解できたかと思います。
ユーザからの入力イベントを扱い、インタラクティブ性の高い機能を提供することは、これまでに紹介してきた execute
メソッドを単に実行するだけの処理と比べて、処理が複雑になりがちでバグも発生しやすくなります。 しかし、キーボードやマウスのイベントを適切に扱うことができるようになると、アドオンで実現できることが広がるため、より高度なアドオンを作ることができるようになるでしょう。
modal
メソッドや invoke
メソッドの引数に渡されてくるイベント情報を用いることにより、キーボードのキーイベントを扱うことができる