diff --git a/multi_user/delayable.py b/multi_user/delayable.py index 0bf9f22..a8b79db 100644 --- a/multi_user/delayable.py +++ b/multi_user/delayable.py @@ -35,6 +35,8 @@ from replication.constants import (FETCHED, class Delayable(): """Delayable task interface """ + def __init__(self): + self.is_registered = False def register(self): raise NotImplementedError @@ -53,13 +55,20 @@ class Timer(Delayable): """ def __init__(self, duration=1): + super().__init__() self._timeout = duration self._running = True def register(self): """Register the timer into the blender timer system """ - bpy.app.timers.register(self.main) + + if not self.is_registered: + bpy.app.timers.register(self.main) + self.is_registered = True + logging.debug(f"Register {self.__class__.__name__}") + else: + logging.debug(f"Timer {self.__class__.__name__} already registered") def main(self): self.execute() @@ -205,11 +214,16 @@ class DynamicRightSelectTimer(Timer): class Draw(Delayable): def __init__(self): + super().__init__() self._handler = None def register(self): - self._handler = bpy.types.SpaceView3D.draw_handler_add( - self.execute, (), 'WINDOW', 'POST_VIEW') + if not self.is_registered: + self._handler = bpy.types.SpaceView3D.draw_handler_add( + self.execute, (), 'WINDOW', 'POST_VIEW') + logging.debug(f"Register {self.__class__.__name__}") + else: + logging.debug(f"Drow {self.__class__.__name__} already registered") def execute(self): raise NotImplementedError() @@ -350,3 +364,15 @@ class SessionUserSync(Timer): new_key = ui_users.add() new_key.name = user new_key.username = user + + +class SessionBackgroundExecutor(Timer): + def __init__(self, timout=1, execution_queue=None): + super().__init__(timout) + self.execution_queue = execution_queue + + def execute(self): + while not self.execution_queue.empty(): + function = self.execution_queue.get() + logging.debug(f"Executing {function.__name__}") + function() diff --git a/multi_user/operators.py b/multi_user/operators.py index c445a31..34bf61c 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -25,10 +25,9 @@ import string import time from operator import itemgetter from pathlib import Path -from subprocess import PIPE, Popen, TimeoutExpired -import zmq import shutil from pathlib import Path +from queue import Queue import bpy import mathutils @@ -44,13 +43,70 @@ from replication.interface import Session client = None +background_exec_queue = Queue() delayables = [] stop_modal_executor = False +def on_connection_start(): + """Session connection init hander + """ + settings = utils.get_preferences() + runtime_settings = bpy.context.window_manager.session + + # Step 1: Constrect nodes + for node in client._graph.list_ordered(): + node_ref = client.get(node) + if node_ref.state == FETCHED: + node_ref.resolve() + + # Step 2: Load nodes + for node in client._graph.list_ordered(): + node_ref = client.get(node) + if node_ref.state == FETCHED: + node_ref.apply() + + # Step 3: Launch presence overlay + if runtime_settings.enable_presence: + presence.renderer.run() + + # Step 4: Register blender timers + for d in delayables: + d.register() + + if settings.update_method == 'DEPSGRAPH': + bpy.app.handlers.depsgraph_update_post.append(depsgraph_evaluation) + +def on_connection_end(): + """Session connection finished handler + """ + global delayables, stop_modal_executor + settings = utils.get_preferences() + + # Step 1: Unregister blender timers + for d in delayables: + try: + d.unregister() + except: + continue + + stop_modal_executor = True + + # Step 2: Unregister presence renderer + presence.renderer.stop() + + if settings.update_method == 'DEPSGRAPH': + bpy.app.handlers.depsgraph_update_post.remove( + depsgraph_evaluation) + + # Step 3: remove file handled + logger = logging.getLogger() + for handler in logger.handlers: + if isinstance(handler, logging.FileHandler): + logger.removeHandler(handler) + + # OPERATORS - - class SessionStartOperator(bpy.types.Operator): bl_idname = "session.start" bl_label = "start" @@ -189,60 +245,23 @@ class SessionStartOperator(bpy.types.Operator): session_update = delayable.SessionStatusUpdate() session_user_sync = delayable.SessionUserSync() + session_background_executor = delayable.SessionBackgroundExecutor(execution_queue=background_exec_queue) + session_update.register() session_user_sync.register() + session_background_executor.register() + delayables.append(session_background_executor) delayables.append(session_update) delayables.append(session_user_sync) @client.register('on_connection') def initialize_session(): - settings = utils.get_preferences() - - for node in client._graph.list_ordered(): - node_ref = client.get(node) - if node_ref.state == FETCHED: - node_ref.resolve() - - for node in client._graph.list_ordered(): - node_ref = client.get(node) - if node_ref.state == FETCHED: - node_ref.apply() - - # Launch drawing module - if runtime_settings.enable_presence: - presence.renderer.run() - - # Register blender main thread tools - for d in delayables: - d.register() - - if settings.update_method == 'DEPSGRAPH': - bpy.app.handlers.depsgraph_update_post.append( - depsgraph_evaluation) + background_exec_queue.put(on_connection_start) @client.register('on_exit') def desinitialize_session(): - global delayables, stop_modal_executor - settings = utils.get_preferences() - - for d in delayables: - try: - d.unregister() - except: - continue - - stop_modal_executor = True - presence.renderer.stop() - - if settings.update_method == 'DEPSGRAPH': - bpy.app.handlers.depsgraph_update_post.remove( - depsgraph_evaluation) - - logger = logging.getLogger() - for handler in logger.handlers: - if isinstance(handler, logging.FileHandler): - logger.removeHandler(handler) + background_exec_queue.put(on_connection_end) bpy.ops.session.apply_armature_operator()