# Processing when ModalOperator has already been executed

I want to make an add-on using the Timer of ModalOperator.

I want to prevent the same ModalOperator from running if the ModalOperator was already running.

What should I do now?

https://github.com/dfelinto/blender/blob/master/release/scripts/templates_py/operator_modal_timer.py

import bpy

class ModalTimerOperator(bpy.types.Operator):
"""Operator which runs its self from a timer"""
bl_idname = "wm.modal_timer_operator"
bl_label = "Modal Timer Operator"

_timer = None

def modal(self, context, event):
if event.type in {'RIGHTMOUSE', 'ESC'}:
self.cancel(context)
return {'CANCELLED'}

if event.type == 'TIMER':
# change theme color, silly!
color.s = 1.0
color.h += 0.01

return {'PASS_THROUGH'}

def execute(self, context):
wm = context.window_manager
return {'RUNNING_MODAL'}

def cancel(self, context):
wm = context.window_manager
wm.event_timer_remove(self._timer)

def register():
bpy.utils.register_class(ModalTimerOperator)

def unregister():
bpy.utils.unregister_class(ModalTimerOperator)

if __name__ == "__main__":
register()

# test call
bpy.ops.wm.modal_timer_operator()


This can be solved by checking whether the timer has been set before. Unfortunately the example has a little confusing mistake. self._timer = wm.event_timer_add(0.1, window=context.window) does not assign a value to the class attribute _timer as declared at the beginning of the class. Instead it creates an instance attribute named _timer. You can see this by adding the following two lines after the assignment:

print(f"self._timer = {self._timer}")
print(f"ModalTimerOperator._timer = {ModalTimerOperator._timer}")


The first print() will show the the content of the instance attribute, the second print() the content of the class attribute. If you try this you will see that the class attribute will always be None, it's never assigned the value. Therefore this needs to be fixed before you add the check to see if a timer has been previously assigned.

One way of solving this, is making all reads and writes happen to the class attribute by replacing self with ModalTimerOperator and thereby actually using the class attribute declared at the beginning. Then you can check in execute whether the timer is None and only then add a new timer when it is. Set timer to None when the modal operator is canceled.

Update: In order to follow the naming convention I've renamed ModalTimerOperator to WM_OT_modal_timer_operator.

import bpy

class WM_OT_modal_timer_operator(bpy.types.Operator):
"""Operator which runs its self from a timer"""
bl_idname = "wm.modal_timer_operator"
bl_label = "Modal Timer Operator"

timer = None

def modal(self, context, event):
if event.type in {'RIGHTMOUSE', 'ESC'}:
self.cancel(context)
return {'CANCELLED'}

if event.type == 'TIMER':
# change theme color, silly!
color.s = 1.0
color.h += 0.01

return {'PASS_THROUGH'}

def execute(self, context):
wm = context.window_manager
if WM_OT_modal_timer_operator.timer is None:
return {'RUNNING_MODAL'}

def cancel(self, context):
wm = context.window_manager
wm.event_timer_remove(self.timer)
WM_OT_modal_timer_operator.timer = None

def register():
bpy.utils.register_class(WM_OT_modal_timer_operator)

def unregister():
bpy.utils.unregister_class(WM_OT_modal_timer_operator)

if __name__ == "__main__":
register()

# test call
bpy.ops.wm.modal_timer_operator()


If you register the operator and then run it multiple times through F3 or Edit > Operator Search, you will see that a new timer is only added when there hasn't been a previous one or it has been canceled.

• You could also use type(self) instead of ModalTimerOperator, which makes refactoring a bit quicker in case you decide to rename the class. Not sure what's considered more pythonic. – Robert Gützkow Oct 13 '19 at 19:34