0
$\begingroup$

As the title suggests, I would like to check if a modal operator is active, this using a threading.Thread function. This cola is not really easy. I have tried various techniques, but all of them lead to possible new problems. I haven't found an acceptable method, as in the threading.Thread function I absolutely don't want to access the bpy module, otherwise Blender will crash.

Example:

import bpy, threading


def thread_function():
    while True:
        # How to Check if the modal operator "ModalTimerOperator" is running?
        print("Modal Operator is........")
        
        

class ModalTimerOperator(bpy.types.Operator):
    """Operator which runs itself 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 = context.preferences.themes[0].view_3d.space.gradients.high_gradient
            color.s = 1.0
            color.h += 0.01

        return {'PASS_THROUGH'}

    def execute(self, context):
        thread = threading.Thread(target=thread_function)
        thread.name = "Test"
        thread.start()
        
        wm = context.window_manager
        self._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
        wm.event_timer_remove(self._timer)

The main goal is to check if the Modal still running. Since blender could Crash, and the Thread function would go on indefinitely even if blender is no longer running (And in that case quit the thread function).

It could also happen that by running another process in Blender (such as rendering or some heavy operation) the Modal is Freezed for a few seconds, so I have to take this into consideration too.

$\endgroup$
4
  • $\begingroup$ Why not just put some kind of print statement in the modal itself? Right before any of the if event.type statements. Then you can see in the terminal if its running, because it will print every even tick. $\endgroup$
    – Jakemoyo
    Commented Jul 9, 2022 at 17:03
  • $\begingroup$ This is one of the last things I've tried. But it has a problem. Precisely, when you use blender (For example any operator, which performs a heavy operation) it will also block the 'RUNNING_MODAL' cycle so the method you suggested, as soon as this situation is encountered, the modal will be not working, as it is Blocked Until the other operator ends $\endgroup$
    – Noob Cat
    Commented Jul 9, 2022 at 17:20
  • $\begingroup$ I was starting to look at the ctypes module, I was thinking of checking if the allocated memory address of the modal operator print(self) is really in use. But I'm not sure about this $\endgroup$
    – Noob Cat
    Commented Jul 9, 2022 at 17:25
  • $\begingroup$ Have you seen this? $\endgroup$
    – Jakemoyo
    Commented Jul 9, 2022 at 18:10

1 Answer 1

2
$\begingroup$
import bpy, threading

thread = None

def thread_function():
    while True:

        if ModalTimerOperator.is_running():
            pass
            # print("modal is running...")
        else:
            print("modal is done")
            break


    global thread
    thread = None
    print("thread END")

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

    _timer = None
    _handler = None

    @classmethod
    def is_running(cls):
        return cls._handler is not None

    def invoke(self, context, event):
        cls = self.__class__
        if cls._handler is not None:    return {'CANCELLED'}

        wm = context.window_manager
    
        if cls._timer is None:
            cls._timer = wm.event_timer_add(1.0, window=context.window)
            wm.modal_handler_add(self)
            cls._handler = self

        global thread
        if thread is None:
            thread = threading.Thread(target=thread_function)
            thread.name = "Test"
            thread.start()

        return {'RUNNING_MODAL'}

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

        if event.type == 'TIMER':
            print("do something")

        return {'PASS_THROUGH'}

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

        cls._timer = None
        cls._handler = None
        print(" -- modal END --")

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

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

if __name__ == "__main__":
    register()
$\endgroup$
1
  • $\begingroup$ This solution works like a Swiss watch 😎 I hadn't thought about it, the is_running() function is a great idea it will always return what I was looking for. Excuse my enthusiasm, I really want to thank you for this trick $\endgroup$
    – Noob Cat
    Commented Jul 10, 2022 at 11:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .