diff --git a/nicegui/air.py b/nicegui/air.py index 8f7a5ec30..2b090cd67 100644 --- a/nicegui/air.py +++ b/nicegui/air.py @@ -116,7 +116,7 @@ def _handleerror(data: Dict[str, Any]) -> None: print('Error:', data['message'], flush=True) @self.relay.on('handshake') - def _handle_handshake(data: Dict[str, Any]) -> bool: + async def _handle_handshake(data: Dict[str, Any]) -> bool: client_id = data['client_id'] if client_id not in Client.instances: return False @@ -124,6 +124,12 @@ def _handle_handshake(data: Dict[str, Any]) -> bool: client.environ = data['environ'] client.tab_id = data['tab_id'] client.on_air = True + await client.synchronize_ui( + client_id, + data['last_message_id'], + data['socket_ids'], + data['initial_load'] + ) client.handle_handshake() return True diff --git a/nicegui/client.py b/nicegui/client.py index 598191638..1b8ea4610 100644 --- a/nicegui/client.py +++ b/nicegui/client.py @@ -245,11 +245,11 @@ def on_disconnect(self, handler: Union[Callable[..., Any], Awaitable]) -> None: """Add a callback to be invoked when the client disconnects.""" self.disconnect_handlers.append(handler) - def on_init_javascript(self, handler: Union[Callable[..., Any], Awaitable]) -> None: + def on_init_js_objects(self, handler: Union[Callable[..., Any], Awaitable]) -> None: """Add a callback to be invoked when the""" self.init_js_handlers.append(handler) - def init_javascript(self, socket_id, initial_load): + def init_js_objects(self, socket_id, initial_load): print(f'send_state: {0}') for t in self.init_js_handlers: print(f't: {0}') @@ -261,6 +261,14 @@ def refresh_ui(self, socket_id): } self.outbox.enqueue_message('fullsync', elements, socket_id) + async def synchronize_ui(self, target: str, last_message_id: int, socket_ids: List[str], initial_load: bool): + if not await self.outbox.synchronize(last_message_id, socket_ids): + print(f'sync fail: {0}') + self.refresh_ui(target) + self.init_js_objects(target, initial_load) + elif initial_load: + self.init_js_objects(target, initial_load) + def handle_handshake(self) -> None: """Cancel pending disconnect task and invoke connect handlers.""" if self._disconnect_task: diff --git a/nicegui/elements/scene.js b/nicegui/elements/scene.js index fe811955e..2effe561b 100644 --- a/nicegui/elements/scene.js +++ b/nicegui/elements/scene.js @@ -63,7 +63,7 @@ const cleanMaterial = (material) => { } }; -function delete_object(object) { +function delete_scene_object(object) { if (object.isMesh) { // console.log('dispose geometry!') object.geometry.dispose(); @@ -402,7 +402,7 @@ export default { const object = this.objects.get(object_id); object.removeFromParent(); - delete_object(object); + // delete_scene_object(object); this.objects.delete(object_id); // this.remove(object); // object.dispose(); @@ -473,19 +473,22 @@ export default { } this.camera.updateProjectionMatrix(); }, - clear() {}, + clear() { + for (const [key, object] of this.objects) { + console.log(`ob ${0}`, object, key); + if (!object.isScene) { + object.removeFromParent(); + // delete_scene_object(object); + this.objects.delete(key); + } + } + }, init_objects(data) { + console.log(`init_objects ${0}`); + this.clear(); if (this.initialized) { // this.kkey += 1; // mounted(); - for (const [key, object] of this.objects) { - console.log(`ob ${0}`, object, key); - if (!object.isScene) { - object.removeFromParent(); - delete_object(object); - this.objects.delete(key); - } - } // this.scene.traverse((object) => { // console.log(`ob ${0}`, object); // object.removeFromParent(); diff --git a/nicegui/elements/scene.py b/nicegui/elements/scene.py index 1cef28de6..1d938e668 100644 --- a/nicegui/elements/scene.py +++ b/nicegui/elements/scene.py @@ -116,7 +116,7 @@ def __init__(self, self.on('dragstart', self._handle_drag) self.on('dragend', self._handle_drag) self._props['drag_constraints'] = drag_constraints - self.client.on_init_javascript(self._handle_init_js) + self.client.on_init_js_objects(self._handle_init_js) def on_click(self, callback: Callable[..., Any]) -> Self: """Add a callback to be invoked when a 3D object is clicked.""" @@ -182,12 +182,13 @@ async def initialized(self) -> None: def _handle_init_js(self, socket_id, initial_load): print(f'out: {0}') - if not initial_load: - # self.clear() - pass self.is_initialized = True + if initial_load: + self.move_camera(duration=0) + # self.clear() + # self.run_method('clear') + with self.client.individual_target(socket_id): - # self.move_camera(duration=0) self.run_method('init_objects', [obj.data for obj in self.objects.values()]) def run_method(self, name: str, *args: Any, timeout: float = 1, check_interval: float = 0.01) -> AwaitableResponse: diff --git a/nicegui/nicegui.py b/nicegui/nicegui.py index 9012999cd..6329f793c 100644 --- a/nicegui/nicegui.py +++ b/nicegui/nicegui.py @@ -164,6 +164,8 @@ async def _exception_handler_500(request: Request, exception: Exception) -> Resp @sio.on('handshake') async def _on_handshake(sid: str, data: Dict[str, Any]) -> Dict[str, Any]: + print(f'ooooooooooo: {0}') + client = Client.instances.get(data['client_id']) if not client: return {'success': False, 'reason': 'no_client_id'} @@ -171,10 +173,11 @@ async def _on_handshake(sid: str, data: Dict[str, Any]) -> Dict[str, Any]: client.tab_id = data['tab_id'] await sio.enter_room(sid, client.id) if not await client.outbox.synchronize(data['last_message_id'], data['socket_ids']): + print(f'sync fail: {0}') client.refresh_ui(sid) - client.init_javascript(sid, data['initial_connection']) - elif data['initial_connection']: - client.init_javascript(sid, data['initial_connection']) + client.init_js_objects(sid, data['initial_load']) + elif data['initial_load']: + client.init_js_objects(sid, data['initial_load']) client.handle_handshake() return {'success': True} diff --git a/nicegui/outbox.py b/nicegui/outbox.py index df63931dc..ca4d540e2 100644 --- a/nicegui/outbox.py +++ b/nicegui/outbox.py @@ -77,24 +77,28 @@ def _append_history(self, message_type: MessageType, data: Any, target: ClientId async def synchronize(self, last_message_id: int, socket_ids: List[str]) -> bool: """Synchronize the state of a connecting client by resending missed messages, if possible.""" messages = [] + success = True if self._history is not None: if self._history: next_id = last_message_id + 1 oldest_id = self._history[0][0] if oldest_id > next_id: - return False + success = False + # return False - start = next_id - oldest_id - for i in range(start, len(self._history)): - msg = self._history[i][2] - if msg[2] == self.client.id or msg[2] in socket_ids: - messages.append(msg) + if success: + start = next_id - oldest_id + for i in range(start, len(self._history)): + msg = self._history[i][2] + if msg[2] == self.client.id or msg[2] in socket_ids: + messages.append(msg) elif last_message_id != self._message_count: - return False - + success = False + # return False + # print(f'mmm: {0}') await self._emit('sync', {'target': socket_ids[-1], 'history': messages}, self.client.id) - return True + return success async def loop(self) -> None: """Send updates and messages to all clients in an endless loop.""" diff --git a/nicegui/static/nicegui.js b/nicegui/static/nicegui.js index f1674f24a..c5035d712 100644 --- a/nicegui/static/nicegui.js +++ b/nicegui/static/nicegui.js @@ -292,7 +292,7 @@ function createApp(elements, options) { window.lastMessageId = options.query.starting_message_id; window.syncing = true; window.socketIds = []; - window.initialConnection = true; + window.initialLoad = true; window.socket = io(url, { path: `${options.prefix}/_nicegui_ws/socket.io`, query: options.query, @@ -312,7 +312,7 @@ function createApp(elements, options) { tab_id: tabId, last_message_id: window.lastMessageId, socket_ids: window.socketIds, - initial_connection: window.initialConnection, + initial_load: window.initialLoad, }; window.socket.emit("handshake", args, (res) => { if (!res.success && res.reason === "no_client_id") { @@ -324,7 +324,7 @@ function createApp(elements, options) { } document.getElementById("popup").ariaHidden = true; }); - window.initialConnection = false; + window.initialLoad = false; }, connect_error: (err) => { if (err.message == "timeout") { @@ -364,6 +364,7 @@ function createApp(elements, options) { download: (msg) => download(msg.src, msg.filename, msg.media_type, options.prefix), notify: (msg) => Quasar.Notify.create(msg), sync: (msg) => { + console.log(`window.syncing = false ${0}`, msg.target, window.socket.id); if (msg.target === window.socket.id) { window.syncing = false; for (let i = 0; i < msg.history.length; i++) { @@ -377,6 +378,7 @@ function createApp(elements, options) { } }, fullsync: (msg) => { + console.log(`fullsync ${0}`); replaceUndefinedAttributes(msg, 0); this.elements = msg; }, @@ -386,6 +388,7 @@ function createApp(elements, options) { for (const [event, handler] of Object.entries(messageHandlers)) { window.socket.on(event, async (...args) => { const data = args[0]; + // console.log(`out ${0}`, data); if (data && data.hasOwnProperty("message_id")) { if (window.syncing || data.message_id <= window.lastMessageId) { return;