diff --git a/multi_user/delayable.py b/multi_user/delayable.py index 4aea521..32cf6e4 100644 --- a/multi_user/delayable.py +++ b/multi_user/delayable.py @@ -315,9 +315,9 @@ class SessionUserSync(Timer): new_key.name = user new_key.username = user if user != self.settings.username: - renderer.register(UserFrustumWidget(user)) - renderer.register(UserSelectionWidget(user)) - renderer.register(UserNameWidget(user)) + renderer.add_widget(UserFrustumWidget(user)) + renderer.add_widget(UserSelectionWidget(user)) + renderer.add_widget(UserNameWidget(user)) class MainThreadExecutor(Timer): diff --git a/multi_user/operators.py b/multi_user/operators.py index de7eddb..aa65cd9 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -21,26 +21,25 @@ import logging import os import queue import random +import shutil import string import time from operator import itemgetter from pathlib import Path -import shutil -from pathlib import Path from queue import Queue import bpy import mathutils from bpy.app.handlers import persistent - -from . import bl_types, delayable, environment, presence, ui, utils -from replication.constants import (FETCHED, STATE_ACTIVE, - STATE_INITIAL, - STATE_SYNCING, RP_COMMON, UP) +from replication.constants import (FETCHED, RP_COMMON, STATE_ACTIVE, + STATE_INITIAL, STATE_SYNCING, UP) from replication.data import ReplicatedDataFactory from replication.exception import NonAuthorizedOperationError from replication.interface import session +from . import bl_types, delayable, environment, ui, utils +from .presence import (SessionStatusWidget, refresh_3d_view, renderer, + view3d_find) background_execution_queue = Queue() delayables = [] @@ -108,6 +107,9 @@ def on_connection_end(): bpy.app.handlers.depsgraph_update_post.remove( depsgraph_evaluation) + renderer.clear_widgets() + refresh_3d_view() + # Step 3: remove file handled logger = logging.getLogger() for handler in logger.handlers: @@ -266,6 +268,8 @@ class SessionStartOperator(bpy.types.Operator): bpy.ops.session.apply_armature_operator() + renderer.add_widget(SessionStatusWidget()) + self.report( {'INFO'}, f"connecting to tcp://{settings.ip}:{settings.port}") @@ -464,7 +468,7 @@ class SessionSnapUserOperator(bpy.types.Operator): return {'CANCELLED'} if event.type == 'TIMER': - area, region, rv3d = presence.view3d_find() + area, region, rv3d = view3d_find() if session: target_ref = session.online_users.get(self.target_client) diff --git a/multi_user/preferences.py b/multi_user/preferences.py index fee33b2..1977b9c 100644 --- a/multi_user/preferences.py +++ b/multi_user/preferences.py @@ -492,6 +492,11 @@ class SessionProps(bpy.types.PropertyGroup): description="Show user on different scenes", default=False, ) + presence_show_session_status: bpy.props.BoolProperty( + name="Show session status ", + description="Show session status on the viewport", + default=True, + ) filter_owned: bpy.props.BoolProperty( name="filter_owned", description='Show only owned datablocks', diff --git a/multi_user/presence.py b/multi_user/presence.py index b429b0d..0901686 100644 --- a/multi_user/presence.py +++ b/multi_user/presence.py @@ -31,7 +31,13 @@ from bpy_extras import view3d_utils from gpu_extras.batch import batch_for_shader -from . import utils +from .utils import find_from_attr, get_state_str +from replication.constants import (STATE_ACTIVE, STATE_AUTH, + STATE_CONFIG, STATE_SYNCING, + STATE_INITIAL, STATE_SRV_SYNC, + STATE_WAITING, STATE_QUITTING, + STATE_LOBBY, + STATE_LAUNCHING_SERVICES) from replication.interface import session @@ -193,6 +199,7 @@ class Widget(object): def draw(self): raise NotImplementedError() + class UserFrustumWidget(Widget): # Camera widget indices indices = ((1, 3), (2, 1), (3, 0), @@ -281,7 +288,7 @@ class UserSelectionWidget(Widget): def draw(self): user_selection = self.data.get('selected_objects') for select_ob in user_selection: - ob = utils.find_from_attr("uuid", select_ob, bpy.data.objects) + ob = find_from_attr("uuid", select_ob, bpy.data.objects) if not ob: return @@ -353,7 +360,6 @@ class UserNameWidget(Widget): self.settings.presence_show_user and \ self.settings.enable_presence - def draw(self): view_corners = self.data.get('view_corners') color = self.data.get('color') @@ -366,21 +372,48 @@ class UserNameWidget(Widget): blf.color(0, color[0], color[1], color[2], color[3]) blf.draw(0, self.username) + +class SessionStatusWidget(Widget): + draw_type = 'POST_PIXEL' + + def __init__(self): + self.settings = bpy.context.window_manager.session + + def poll(self): + return self.settings.presence_show_session_status and \ + self.settings.enable_presence + + def draw(self): + color = [1, 1, 0, 1] + state = session.state.get('STATE') + state_str = f"{get_state_str(state)}" + + if state == STATE_ACTIVE: + color = [0, 1, 0, 1] + elif state == STATE_INITIAL: + color = [1, 0, 0, 1] + + blf.position(0, 10, 20, 0) + blf.size(0, 16, 45) + blf.color(0, color[0], color[1], color[2], color[3]) + blf.draw(0, state_str) + + class DrawFactory(object): def __init__(self): self.post_view_handle = None self.post_pixel_handle = None - self.draw_event = None - self.coords = None - self.active_object = None self.widgets = [] - def register(self, widget: Widget): + def add_widget(self, widget: Widget): self.widgets.append(widget) - def unregister(self, widget): + def remove_widget(self, widget): self.widgets.remove(widget) + def clear_widgets(self): + self.widgets.clear() + def register_handlers(self): self.post_view_handle = bpy.types.SpaceView3D.draw_handler_add( self.post_view_callback, @@ -412,7 +445,8 @@ class DrawFactory(object): if widget.draw_type == 'POST_VIEW' and widget.poll(): widget.draw() except Exception as e: - logging.error(f"Post view widget exception: {e} \n {traceback.print_exc()}") + logging.error( + f"Post view widget exception: {e} \n {traceback.print_exc()}") def post_pixel_callback(self): try: @@ -420,7 +454,8 @@ class DrawFactory(object): if widget.draw_type == 'POST_PIXEL' and widget.poll(): widget.draw() except Exception as e: - logging.error(f"Post pixel widget Exception: {e} \n {traceback.print_exc()}") + logging.error( + f"Post pixel widget Exception: {e} \n {traceback.print_exc()}") this = sys.modules[__name__] @@ -428,8 +463,8 @@ this.renderer = DrawFactory() def register(): - renderer.register_handlers() + this.renderer.register_handlers() def unregister(): - renderer.unregister_handlers() + this.renderer.unregister_handlers() diff --git a/multi_user/ui.py b/multi_user/ui.py index bf1e82a..5f83f69 100644 --- a/multi_user/ui.py +++ b/multi_user/ui.py @@ -18,7 +18,7 @@ import bpy -from .utils import get_preferences, get_expanded_icon, get_folder_size +from .utils import get_preferences, get_expanded_icon, get_folder_size, get_state_str from replication.constants import (ADDED, ERROR, FETCHED, MODIFIED, RP_COMMON, UP, STATE_ACTIVE, STATE_AUTH, @@ -60,32 +60,6 @@ def printProgressBar(iteration, total, prefix='', suffix='', decimals=1, length= return f"{prefix} |{bar}| {iteration}/{total}{suffix}" -def get_state_str(state): - state_str = 'UNKNOWN' - if state == STATE_WAITING: - state_str = 'WARMING UP DATA' - elif state == STATE_SYNCING: - state_str = 'FETCHING' - elif state == STATE_AUTH: - state_str = 'AUTHENTIFICATION' - elif state == STATE_CONFIG: - state_str = 'CONFIGURATION' - elif state == STATE_ACTIVE: - state_str = 'ONLINE' - elif state == STATE_SRV_SYNC: - state_str = 'PUSHING' - elif state == STATE_INITIAL: - state_str = 'INIT' - elif state == STATE_QUITTING: - state_str = 'QUITTING' - elif state == STATE_LAUNCHING_SERVICES: - state_str = 'LAUNCHING SERVICES' - elif state == STATE_LOBBY: - state_str = 'LOBBY' - - return state_str - - class SESSION_PT_settings(bpy.types.Panel): """Settings panel""" bl_idname = "MULTIUSER_SETTINGS_PT_panel" @@ -476,6 +450,7 @@ class SESSION_PT_presence(bpy.types.Panel): settings = context.window_manager.session layout.active = settings.enable_presence col = layout.column() + col.prop(settings, "presence_show_session_status") col.prop(settings, "presence_show_selected") col.prop(settings, "presence_show_user") row = layout.column() @@ -642,8 +617,10 @@ class VIEW3D_PT_overlay_session(bpy.types.Panel): settings = context.window_manager.session layout.active = settings.enable_presence col = layout.column() + col.prop(settings, "presence_show_session_status") col.prop(settings, "presence_show_selected") col.prop(settings, "presence_show_user") + row = layout.column() row.active = settings.presence_show_user row.prop(settings, "presence_show_far_user") diff --git a/multi_user/utils.py b/multi_user/utils.py index 2c9c607..9c7c823 100644 --- a/multi_user/utils.py +++ b/multi_user/utils.py @@ -29,7 +29,14 @@ import math import bpy import mathutils -from . import environment, presence +from . import environment + +from replication.constants import (STATE_ACTIVE, STATE_AUTH, + STATE_CONFIG, STATE_SYNCING, + STATE_INITIAL, STATE_SRV_SYNC, + STATE_WAITING, STATE_QUITTING, + STATE_LOBBY, + STATE_LAUNCHING_SERVICES) def find_from_attr(attr_name, attr_value, list): @@ -58,6 +65,32 @@ def get_datablock_users(datablock): return users +def get_state_str(state): + state_str = 'UNKOWN' + if state == STATE_WAITING: + state_str = 'WARMING UP DATA' + elif state == STATE_SYNCING: + state_str = 'FETCHING' + elif state == STATE_AUTH: + state_str = 'AUTHENTIFICATION' + elif state == STATE_CONFIG: + state_str = 'CONFIGURATION' + elif state == STATE_ACTIVE: + state_str = 'ONLINE' + elif state == STATE_SRV_SYNC: + state_str = 'PUSHING' + elif state == STATE_INITIAL: + state_str = 'OFFLINE' + elif state == STATE_QUITTING: + state_str = 'QUITTING' + elif state == STATE_LAUNCHING_SERVICES: + state_str = 'LAUNCHING SERVICES' + elif state == STATE_LOBBY: + state_str = 'LOBBY' + + return state_str + + def clean_scene(): for type_name in dir(bpy.data): try: