bpy.ops is the interface for calling operators only, you can’t access the operator instance, it’s class variables or attributes or anything this way. You need to use the class name (standard python). Inside class methods, you can either use the explicit name, or self.class to refer to the class instead of the instance (=self).
In the below example, I use both the explicit and the self.class way inside the class methods.
Whether something is a class variable or an attribute (=instance variable) is automatically determined by python and fully depends on how you use it:
class ClassName:
var = 123
var could be a class variable, as well as an attribute. If you access it via the class name (ClassName.var), then it will be a class variable, if you refer to it inside a class method via self.var, then it’s an attribute.
It’s different with class / static methods in classes, they require explicit declaration:
@classmethod
def func(cls):
...
@staticmethod
def func():
...
import bpy
class ModalTimerOperator(bpy.types.Operator):
"""Useful for multiple modal operators all utilizing Timers"""
bl_idname = "wm.modal_timer_operator"
bl_label = "Timer 1"
_timer = None
_count = 0
def modal(self, context, event):
if self.__class__._timer is None:
return {'CANCELLED'}
if event.type == 'TIMER':
ModalTimerOperator._count += 1
redraw_region(context, 'PROPERTIES')
return {'PASS_THROUGH'}
def invoke(self, context, event):
if self.__class__._timer is None:
if ModalTimerOperator2._timer is not None:
bpy.ops.wm.modal_timer_operator_2('INVOKE_DEFAULT')
self.__class__._timer = context.window_manager.event_timer_add(0.008, context.window)
context.window_manager.modal_handler_add(self)
else:
return self.cancel(context)
return {'RUNNING_MODAL'}
def cancel(self, context):
context.window_manager.event_timer_remove(self.__class__._timer)
self.__class__._timer = None
ModalTimerOperator._count = 0
return {'CANCELLED'}
class ModalTimerOperator2(bpy.types.Operator):
"""Useful for multiple modal operators all utilizing Timers"""
bl_idname = "wm.modal_timer_operator_2"
bl_label = "Timer 2"
_timer = None
_count = 0
def modal(self, context, event):
if self.__class__._timer is None:
return {'CANCELLED'}
if event.type == 'TIMER':
ModalTimerOperator2._count += 1
redraw_region(context, 'PROPERTIES')
return {'PASS_THROUGH'}
def invoke(self, context, event):
if self.__class__._timer is None:
if ModalTimerOperator._timer is not None:
bpy.ops.wm.modal_timer_operator('INVOKE_DEFAULT')
self.__class__._timer = context.window_manager.event_timer_add(0.5, context.window)
context.window_manager.modal_handler_add(self)
else:
return self.cancel(context)
return {'RUNNING_MODAL'}
def cancel(self, context):
context.window_manager.event_timer_remove(self.__class__._timer)
self.__class__._timer = None
ModalTimerOperator2._count = 0
return {'CANCELLED'}
def redraw_region(context, area_type, region_type='WINDOW'):
for area in context.screen.areas:
if area.type == area_type:
for region in area.regions:
if region.type == region_type:
region.tag_redraw()
def draw_func(self, context):
layout = self.layout
top1 = ModalTimerOperator
top2 = ModalTimerOperator2
layout.operator(ModalTimerOperator.bl_idname, text=top1.bl_label if top1._timer is None else str(top1._count))
layout.operator(ModalTimerOperator2.bl_idname, text=top2.bl_label if top2._timer is None else str(top2._count))
layout.label("Timer 1: %s" % ModalTimerOperator._timer)
layout.label("Timer 2: %s" % ModalTimerOperator2._timer)
def register():
bpy.utils.register_module(__name__)
bpy.types.RENDER_PT_render.prepend(draw_func)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.RENDER_PT_render.remove(draw_func)
if __name__ == '__main__':
register()