From d2a27999ba180c1a85709af5dd0948fc5210b22d Mon Sep 17 00:00:00 2001 From: Ziad-I <68874104+Ziad-I@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:51:51 +0300 Subject: [PATCH] add handling for web operator selection where if user click on item in the table it gets selected in blender --- .../blenderbim/bim/data/webui/sioserver.py | 16 +++------ .../bim/data/webui/static/js/index.js | 23 ++++++++---- src/blenderbim/blenderbim/tool/web.py | 36 +++++++++++++++++-- 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/data/webui/sioserver.py b/src/blenderbim/blenderbim/bim/data/webui/sioserver.py index b5d186abf9..83f43a723b 100644 --- a/src/blenderbim/blenderbim/bim/data/webui/sioserver.py +++ b/src/blenderbim/blenderbim/bim/data/webui/sioserver.py @@ -35,11 +35,10 @@ class WebNamespace(socketio.AsyncNamespace): async def on_disconnect(self, sid): print(f"Web client disconnected: {sid}") - async def on_operator(self, sid, data): + async def on_web_operator(self, sid, data): print(f"Web client {sid} operator: {data}") - json_data = json.loads(data) # TODO: need to add event listeners in blenderbim side for event web_operator - # await sio.emit("web_operator", json_data, namespace="/blender", room=json_data["blenderId"]) + await sio.emit("web_operator", data["operator"], namespace="/blender", room=data["blenderId"]) # Blender namespace @@ -50,6 +49,7 @@ class BlenderNamespace(socketio.AsyncNamespace): async def on_connect(self, sid, environ): print(f"Blender client connected: {sid}") # Notify web client about new connection + blender_messages[sid] = {} await sio.emit("blender_connect", sid, namespace="/web") async def on_disconnect(self, sid): @@ -59,21 +59,15 @@ class BlenderNamespace(socketio.AsyncNamespace): del blender_messages[sid] await sio.emit("blender_disconnect", sid, namespace="/web") - async def on_data(self, sid, data): - print(f"Data from Blender client {sid}: {data}") - # Store the message and forward it to the web client - blender_messages[sid] = data - await sio.emit("blender_data", {"blenderId": sid, "data": data}, namespace="/web") - async def on_default_data(self, sid, data): print(f"Default data from Blender client {sid}: {data}") - # blender_messages[sid] = data + # blender_messages[sid]["default_data"] = data # await sio.emit("default_data", {"blenderId": sid, "data": data}, namespace="/web") async def on_csv_data(self, sid, data): print(f"CSV data from Blender client {sid}:") # Store the message and forward it to the web client - blender_messages[sid] = data + blender_messages[sid]["csv_data"] = data await sio.emit("csv_data", {"blenderId": sid, "data": data}, namespace="/web") diff --git a/src/blenderbim/blenderbim/bim/data/webui/static/js/index.js b/src/blenderbim/blenderbim/bim/data/webui/static/js/index.js index 86c70f13bd..6c585a230e 100644 --- a/src/blenderbim/blenderbim/bim/data/webui/static/js/index.js +++ b/src/blenderbim/blenderbim/bim/data/webui/static/js/index.js @@ -4,8 +4,11 @@ $(document).ready(function () { const socket = io(url); console.log("socket: ", socket); + // keeps track of blenders connected in form of + // {shown:bool, filename:name} const connectedClients = {}; + // add a new talbe with data and filename function addTableElement(blenderId, csvData, filename) { const tableContainer = $("
") .addClass("table-container") @@ -36,21 +39,29 @@ $(document).ready(function () { table.on("rowSelected", function (row) { var index = row.getIndex(); // the guid of the object + // table id is usually "table-BlenderId" so blender id is always + // after the first 6 characters var tableId = row.getTable().element.id.substr(6); - const msg = JSON.stringify({ - operator_type: "selection", + const msg = { blenderId: tableId, - guid: index, - }); - socket.emit("operator", msg); + operator: { + type: "selection", + GlobalId: index, + }, + }; + socket.emit("web_operator", msg); }); } + // update table and filename function updateTableElement(blenderId, csvData, filename) { + // TODO: see if we can only replace updated rows only. + // TODO: and handle case where new columns are added + // for now just replace the data in the table const table = Tabulator.findTable("#table-" + blenderId)[0]; if (table) { - table.replaceData(csvData, { importFormat: "csv" }); + table.setData(csvData); $("#title-" + blenderId).text(filename); // Update the title } } diff --git a/src/blenderbim/blenderbim/tool/web.py b/src/blenderbim/blenderbim/tool/web.py index 2a8a4a11d0..01aedbf108 100644 --- a/src/blenderbim/blenderbim/tool/web.py +++ b/src/blenderbim/blenderbim/tool/web.py @@ -19,6 +19,7 @@ import bpy from blenderbim.bim.module.web.data import WebData import blenderbim.core.tool +import blenderbim.tool as tool import socket import os import subprocess @@ -26,11 +27,13 @@ import webbrowser import asyncio import socketio import threading +import queue from time import sleep sio = None ws_process = None ws_thread = None +web_operator_queue = queue.Queue() class Web(blenderbim.core.tool.Web): @@ -81,6 +84,7 @@ class Web(blenderbim.core.tool.Web): ws_url = f"ws://localhost:{port}/blender" ws_thread.run_coro(cls.sio_connect(ws_url)) cls.set_is_connected(True) + bpy.app.timers.register(cls.check_operator_queue) cls.send_default_webui_data() @classmethod @@ -130,6 +134,27 @@ class Web(blenderbim.core.tool.Web): if ws_thread is not None and bpy.context.scene.WebProperties.is_connected: ws_thread.run_coro(cls.sio_send(data, event, namespace)) + @classmethod + def check_operator_queue(cls): + if not bpy.context.scene.WebProperties.is_connected: + with web_operator_queue.mutex: + web_operator_queue.queue.clear() + return None # unregister timer if not connected + + while not web_operator_queue.empty(): + operator_data = web_operator_queue.get_nowait() + if not operator_data: + continue + + if operator_data["type"] == "selection": + bpy.ops.object.select_all(action="DESELECT") + guid = operator_data["GlobalId"] + ele = tool.Ifc.get().by_guid(guid) + obj = tool.Ifc.get_object(ele) + obj.select_set(True) + + return 1.0 + @classmethod def open_web_browser(cls, port): webbrowser.open(f"http://127.0.0.1:{port}/") @@ -139,9 +164,7 @@ class Web(blenderbim.core.tool.Web): await sio.connect(url, transports=["websocket"], namespaces="/blender") # TODO: register event handlers to handle incoming messages - # sio.on("data", test.message_handler, namespace="/blender") - - # await sio.emit("data", {"from": "client"}, namespace="/blender") + sio.on("web_operator", cls.sio_handle_web_operator, namespace="/blender") @classmethod async def sio_disconnect(cls): @@ -151,6 +174,13 @@ class Web(blenderbim.core.tool.Web): async def sio_send(cls, data, event="data", namespace="/blender"): await sio.emit(event, data, namespace=namespace) + @classmethod + async def sio_handle_web_operator(cls, data): + try: + web_operator_queue.put_nowait(data) + except queue.Full: + pass + @classmethod def set_is_running(cls, is_running): bpy.context.scene.WebProperties.is_running = is_running