I wrote the code as below.
import bpy
bl_info = {
"name": "TestModal",
"author": "Taichi",
"version": (1, 0),
"blender": (2, 80, 0),
"location": "3DView",
"description": "",
"warning": "",
"support": "COMMUNITY",
"wiki_url": "",
"tracker_url": "",
"category": "Animation"
}
class TestModal_PT_Panel(bpy.types.Panel):
blidname = "TestModal_PT_Panel"
bl_label = "TestModal Panel"
bl_category = "TestModal"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
def draw(self,context):
layout = self.layout
row = layout.row()
row.operator('wm.modal_testmodal_operator',text ='Start')
row = layout.row()
row.operator('wm.modal_testmodal_operator',text ='Stop')
class TestModal_OT_Operator(bpy.types.Operator):
bl_idname = "wm.modal_testmodal_operator"
bl_label = "Modal TestModal Operator"
_timer = None
def modal(self, context, event):
if event.type in {'ESC'}:
self.cancel(context)
return{'CANCELLED'}
if event.type=='TIMER':
try:
print("TestModal")
except:
pass
return{'PASS_THROUGH'}
def execute(self, context):
wm = context.window_manager
if TestModal_OT_Operator._timer is None:
TestModal_OT_Operator._timer = wm.event_timer_add(0.1, window=context.window)
wm.modal_handler_add(self)
return {'RUNNING_MODAL'}
def cancel(self, context):
wm = context.window_manager
self.realtimeAnimFlag = 0
wm.event_timer_remove(self._timer)
TestModal_OT_Operator._timer = None
classes = (TestModal_PT_Panel,TestModal_OT_Operator)
register,unregister = bpy.utils.register_classes_factory(classes)
if name == "main":
register()
This code just prints "Test Modal" 10 times a second when you press the "Start" button.
I want to cancel ModalOperator when I press the Stop button.
I haven't written the code to set multiple buttons and I'm not sure how to access functions etc. from buttons. Also, I don't know how to access ModalOperator Cancel.
Even now, I can press the Esc key on the keyboard to cancel, but how can I cancel it with the button?
I am using Blender 2.81.
Addendum: I tried the following code according to the answer but got an error.
import bpy
bl_info = {
"name": "TestModal",
"author": "Taichi",
"version": (1, 0),
"blender": (2, 80, 0),
"location": "3DView",
"description": "",
"warning": "",
"support": "COMMUNITY",
"wiki_url": "",
"tracker_url": "",
"category": "Animation"
}
class TestModal_PT_Panel(bpy.types.Panel):
blidname = "TestModal_PT_Panel"
bl_label = "TestModal Panel"
bl_category = "TestModal"
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
def draw(self,context):
layout = self.layout
lmadd = context.scene.lm_addon
vis_box = layout.box()
if not lmadd.vis_run:
vis_box.operator(TestModal_OT_Operator.bl_idname, text='Visualize')
else:
vis_box.prop(lmadd, 'vis_run', text="Stop", toggle=True)
class TestModal_OT_Operator(bpy.types.Operator):
bl_idname = "wm.modal_testmodal_operator"
bl_label = "Modal TestModal Operator"
_timer = None
def modal(self, context, event):
if event.type in {'ESC'}:
self.cancel(context)
return{'CANCELLED'}
if event.type=='TIMER':
lm_addon.vis_run = context.scene.lm_addon.vis_run
if not lm_addon.vis_run:
lm_addon.vis_run = False
return {"FINISHED"}
else:
print("testModal")
return{'PASS_THROUGH'}
def execute(self, context):
context.scene.lm_addon.vis_run = True
wm = context.window_manager
if TestModal_OT_Operator._timer is None:
TestModal_OT_Operator._timer = wm.event_timer_add(0.1, window=context.window)
wm.modal_handler_add(self)
return {'RUNNING_MODAL'}
def cancel(self, context):
wm = context.window_manager
self.realtimeAnimFlag = 0
wm.event_timer_remove(self._timer)
TestModal_OT_Operator._timer = None
classes = (TestModal_PT_Panel,TestModal_OT_Operator)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
bpy.types.Scene.lm_addon.vis_run = bpy.props.BoolProperty(
name="modal running",
description="determined whether running landmark visualizer",
default=False,
)
def unregister():
from bpy.utils import unregister_class
for cls in classes:
unregister_class(cls)
del bpy.types.Scene.lm_addon.vis_run
if name == "main":
register()
Error message is below:
AttributeError:type object 'Scene' has no attribute 'lm_addon'

lm_addon. Thebpy.context.sceneis needed, that's the scene in Blender, you can register any attribute to your scene. In your addon, you will need a Boolean Property to store your operator running state. It will be access by both operator and UI panel – HikariTW Sep 02 '20 at 01:41type.Scene.foo_bar = BoolProp(...)Suggest this could be re-closed as a duplicate. – batFINGER Sep 02 '20 at 06:30