はじめてのBlenderアドオン開発
Last Update: 2023.3.1
Blender 2.8~3.0
はじめてのBlenderアドオン開発
Blender 2.8~3.0
Last Update: 2023.3.1
3-3. タイマのイベントを扱う
3-1節 と 3-2節 では、マウスやキーボードといった、ユーザからの入力イベントを扱う方法を説明しました。 イベントを発生させるほかの方法として、一定時間経過したときにイベントを発生する、タイマを設定する方法もあります。 本節では、タイマから発生するイベントを扱う方法を説明します。
タイマのイベントを扱う方法を理解するため、定期的に発生するイベントを利用した、次の機能を備えるアドオンを作成します。
1-5節 を参考にして次のソースコードを入力し、ファイル名を sample_3-3.py
として保存してください。
import datetime
import bpy
bl_info = {
"name": "サンプル 3-3: 日時を表示するアドオン①",
"author": "ぬっち(Nutti)",
"version": (3, 0),
"blender": (2, 80, 0),
"location": "3Dビューポート > Sidebar > サンプル 3-3",
"description": "現在の日時をテキストオブジェクトとして表示するアドオン",
"warning": "",
"support": "TESTING",
"doc_url": "",
"tracker_url": "",
"category": "Object"
}
# 日時をテキストオブジェクトとして表示するオペレータ
class SAMPLE33_OT_ShowDatetime(bpy.types.Operator):
bl_idname = "object.sample33_show_datetime"
bl_label = "日時を表示"
bl_description = "日時をテキストオブジェクトとして表示します"
# タイマのハンドラ
__timer = None
# テキストオブジェクトの名前
__text_object_name = None
@classmethod
def is_running(cls):
# モーダルモード中はTrue
return True if cls.__timer else False
def __handle_add(self, context):
if not self.is_running():
# タイマを登録
SAMPLE33_OT_ShowDatetime.__timer = \
context.window_manager.event_timer_add(
0.5, window=context.window
)
# モーダルモードへの移行
context.window_manager.modal_handler_add(self)
def __handle_remove(self, context):
if self.is_running():
# タイマの登録を解除
context.window_manager.event_timer_remove(
SAMPLE33_OT_ShowDatetime.__timer)
SAMPLE33_OT_ShowDatetime.__timer = None
def modal(self, context, event):
op_cls = SAMPLE33_OT_ShowDatetime
# エリアを再描画
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'}
if event.type == 'TIMER':
bpy.data.objects[op_cls.__text_object_name].data.body = datetime.datetime.now().strftime("%Y.%m.%d %H:%M:%S")
return {'PASS_THROUGH'}
def invoke(self, context, event):
op_cls = SAMPLE33_OT_ShowDatetime
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 = ""
# モーダルモードを開始
self.__handle_add(context)
print("サンプル 3-3: 日時の表示処理を開始しました。")
return {'RUNNING_MODAL'}
# [終了] ボタンが押された時の処理
else:
# モーダルモードを終了
self.__handle_remove(context)
print("サンプル 3-3: 日時の表示処理を終了しました。")
return {'FINISHED'}
else:
return {'CANCELLED'}
# UI
class SAMPLE33_PT_ShowDatetime(bpy.types.Panel):
bl_label = "日時を表示"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "サンプル 3-3"
bl_context = "objectmode"
def draw(self, context):
op_cls = SAMPLE33_OT_ShowDatetime
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 = [
SAMPLE33_OT_ShowDatetime,
SAMPLE33_PT_ShowDatetime,
]
def register():
for c in classes:
bpy.utils.register_class(c)
print("サンプル 3-3: アドオン『サンプル 3-3』が有効化されました。")
def unregister():
for c in classes:
bpy.utils.unregister_class(c)
print("サンプル 3-3: アドオン『サンプル 3-3』が無効化されました。")
if __name__ == "__main__":
register()
1-5節 を参考にして作成したアドオンを有効化すると、コンソールウィンドウに次の文字列が出力されます。
サンプル 3-3: アドオン『サンプル 3-3』が有効化されました。
Sidebarを表示し、タブ [サンプル 3-3] にパネル [日時を表示] が追加されていることを確認します。
有効化したアドオンの機能を使い、動作を確認します。
1 | [3Dビューポート] スペースのSidebarの [サンプル 3-3] > [日時を表示] に配置されている [開始] ボタンを押します。 |
2 | テキストオブジェクトが作成され、日時がテキストとして表示されるようになります。 |
3 | [終了] ボタンを押すと、テキストオブジェクトが削除されます。 |
1-5節 を参考にして有効化したアドオンを無効化すると、コンソールウィンドウに次の文字列が出力されます。
サンプル 3-3: アドオン『サンプル 3-3』が無効化されました。
本節では、タイマイベントを扱う処理と日時を表示する処理に限定し、サンプルアドオンのソースコードを解説します。 これまでに説明してきた内容については、説明を省いています。 本節のサンプルアドオンのソースコードに関して、ポイントとなる点を次に示します。
本節では、日時を表示するモードをモーダルモードと記載している部分があります。 以降、モーダルモードと書かれていたら、日時を表示するモードとして読みかえても問題ありません。
タイマイベントを発生させるためには、タイマを登録する必要があります。 タイマの登録処理は、次に示す __handle_add
メソッドで行います。
def __handle_add(self, context):
if not self.is_running():
# タイマを登録
SAMPLE33_OT_ShowDatetime.__timer = \
context.window_manager.event_timer_add(
0.5, window=context.window
)
# モーダルモードへの移行
context.window_manager.modal_handler_add(self)
タイマは、context.window_manager.event_timer_add
メソッドを呼び出すことで登録できます。 context.window_manager.event_timer_add
メソッドは次に示す引数を受け取り、戻り値としてタイマのハンドラを返します。
引数 | 型 | 値の意味 |
---|---|---|
第1引数 | float |
タイマイベントを発生させる間隔を秒単位で指定 |
第2引数 | bpy.types.Window |
タイマの登録先ウィンドウ |
本節のサンプルアドオンでは、第1引数に 0.5
を指定することで、タイマによるイベントを0.5秒ごとに発生させます。 [開始] ボタンが存在するウィンドウでタイマイベントを発生させたいため、第2引数には context.window
を指定します。
戻り値として返されたハンドラは、タイマを登録解除するときに使用するため、クラス変数 __timer
に保存します。
__handle_add
メソッドは、最後にモーダルモードへ移行しますが、必ずしも __handle_add
メソッド内で行う必要はありません。 __handle_add
メソッド自体が、invoke
メソッドから呼び出されていることになるため、3-1節 や 3-2節 と同様に、invoke
メソッドの処理内で context.window_manager.modal_handler_add
メソッドを呼び出して、モーダルモードへ移行しても問題ありません。
タイマを登録すると、登録解除するまでタイマイベントが発生します。 このため、タイマが不要になったら登録を解除する必要があります。
タイマの登録解除処理は、次に示す __handle_remove
メソッドで行っています。
def __handle_remove(self, context):
if self.is_running():
# タイマの登録を解除
context.window_manager.event_timer_remove(
SAMPLE33_OT_ShowDatetime.__timer)
SAMPLE33_OT_ShowDatetime.__timer = None
context.window_manager.event_timer_remove
メソッドを呼び出すことで、タイマを登録解除できますが、引数には、 context.window_manager.event_timer_add
メソッドの戻り値として返されたタイマのハンドラを渡す必要があります。 本節のサンプルアドオンでは、タイマのハンドラを保存したクラス変数 __timer
を引数に渡し、タイマを登録解除します。 なお、登録解除済のタイマのハンドラにアクセスすることによる不正な動作を避けるために、クラス変数 __timer
に None
を代入します。
タイマイベントが発生すると、modal
メソッドが呼ばれます。
3-1節 や 3-2節 と同様に、modal
メソッドの最初で [3Dビューポート] スペースを持つエリアの画面更新と、modal
メソッドの終了判定処理を行います。
3-1節 や 3-2節 で説明したように、modal
メソッドはキーボードやマウスのイベントが発生したときにも呼ばれます。 このため、タイマイベントが発生したとき(event.type
が 'TIMER'
のとき)のみ、日時を更新するようにします。
if event.type == 'TIMER':
bpy.data.objects[op_cls.__text_object_name].data.body = datetime.datetime.now().strftime("%Y.%m.%d %H:%M:%S")
[開始] ボタンが押されたとき、日時を表示するためのテキストオブジェクトを作成する必要があります。 テキストオブジェクトは、bpy.ops.object.text_add
関数を使って作成できます。 作成したテキストオブジェクトは、モーダルモード終了時に削除できるように、テキストオブジェクトの名前をクラス変数 __text_object_name
に保存しておきます。 また、テキストオブジェクトのテキストには、空文字列を設定します。
# テキストオブジェクト作成
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 = ""
本節では、タイマのイベントを扱う方法を説明しました。 タイマを使うと、指定した間隔でイベントを発生させることができるため、定期的に処理を実行するような機能を実現できます。
3-1節 から本節まで、3節にわたってイベントを扱う処理を説明しましたが、イベントを扱う場合は、modal
メソッドや invoke
メソッドを実装する必要があることを、理解できたのではないでしょうか。
context.window_manager.event_timer_add
メソッドで行い、不要になったタイマは context.window_manager.event_timer_remove
メソッドで登録を解除するcontext.window_manager.modal_handler_add
の引数に指定したオペレータクラスの modal
メソッドが呼び出され、引数 event
のメンバ変数 event.type
に TIMER
が設定される