0

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.

enter image description here

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'
taichi
  • 398
  • 4
  • 16
  • I'm not sure I understood. Could you explain the context in which you need to use this? – mugnozzo Aug 31 '20 at 12:34
  • If you continue to loop ModalOperator after pressing Start button, the process becomes heavy, so you have to stop it. You can stop it by pressing the Esc key, but add-on users may not notice the Esc key's presence. So I want to be able to stop the ModalOperator with the Stop button. – taichi Aug 31 '20 at 13:11
  • 1
    You don't need to follow the same name lm_addon. The bpy.context.scene is 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:41
  • 1
    Basic python syntax error. Cannot assign "foo.bar" as a property in one hit. As with variable names they cannot contain a period "." Either make a property group "foo" with member "bar" or use something like type.Scene.foo_bar = BoolProp(...) Suggest this could be re-closed as a duplicate. – batFINGER Sep 02 '20 at 06:30
  • @DuarteFarrajotaRamos ♦ believe this can be re-closed as duplicate. – batFINGER Sep 03 '20 at 15:33
  • @batfinger thanks, done – Duarte Farrajota Ramos Sep 03 '20 at 21:27

1 Answers1

1

A hacky method is to have a Boolean property outside your operator and let the modal constantly check that properties. And let the panel link to the properties which user can see it and toggle the state of the Boolean.

A sample snippet As below:

# There was a group property. Only for example.
bpy.types.Scene.vis_run = BoolProperty(
    name="running visualize",
    description="determined whether running landmark visualizer",
    default=False,
)

class mesh_OT_visualize(bpy.types.Operator): bl_idname = "mesh.lm_vis" bl_label = "Draw Landmarks" bl_options = {'REGISTER'}

def invoke(self, context, event):
    # Mark the modal running tag
    context.scene.vis_run = True
    return {"RUNNING_MODAL"}

def modal(self, context, event):
    # Get whether user change the state of run tag
    vis_run= context.scene.vis_run
    if not vis_run:
        vis_run = False
        return {"FINISHED"}
    return {"PASS_THROUGH"}


class LandmarkPanel(bpy.types.Panel): bl_idname = "VIEW3D_PT_landmark" bl_label = "Landmark Panel" bl_space_type = "VIEW_3D" bl_region_type = "UI"

def draw(self, context):
    layout = self.layout
    vis_run = context.scene.vis_run

    vis_box = layout.box()
    if not vis_run:
        vis_box.operator(mesh_OT_visualize.bl_idname, text='Visualize')
    else:
        vis_box.prop(context.scene, 'vis_run', text="Stop", toggle=True)

HikariTW
  • 7,821
  • 2
  • 18
  • 37