From cd6b0896ebd46aeadef360f3e7de59aa05e11b50 Mon Sep 17 00:00:00 2001 From: gounux Date: Thu, 14 Nov 2024 07:57:21 +0100 Subject: [PATCH] feature(qchat): send and receive a crs message --- qtribu/constants.py | 1 + qtribu/gui/dck_qchat.py | 36 ++++++++++++++++++++++++--- qtribu/gui/qchat_tree_widget_items.py | 31 +++++++++++++++++++++++ qtribu/logic/qchat_messages.py | 8 ++++++ qtribu/logic/qchat_websocket.py | 5 ++++ 5 files changed, 77 insertions(+), 4 deletions(-) diff --git a/qtribu/constants.py b/qtribu/constants.py index 2b5a9b0d..13c23ffa 100644 --- a/qtribu/constants.py +++ b/qtribu/constants.py @@ -240,3 +240,4 @@ def local_path(self, base_path: Path = Path().home() / ".geotribu/cdn/") -> Path QCHAT_MESSAGE_TYPE_EXITER = "exiter" QCHAT_MESSAGE_TYPE_LIKE = "like" QCHAT_MESSAGE_TYPE_GEOJSON = "geojson" +QCHAT_MESSAGE_TYPE_CRS = "crs" diff --git a/qtribu/gui/dck_qchat.py b/qtribu/gui/dck_qchat.py index e815bad9..b246e165 100644 --- a/qtribu/gui/dck_qchat.py +++ b/qtribu/gui/dck_qchat.py @@ -9,7 +9,7 @@ # PyQGIS from PyQt5 import QtWebSockets # noqa QGS103 -from qgis.core import Qgis, QgsApplication, QgsJsonExporter, QgsMapLayer +from qgis.core import Qgis, QgsApplication, QgsJsonExporter, QgsMapLayer, QgsProject from qgis.gui import QgisInterface, QgsDockWidget from qgis.PyQt import uic from qgis.PyQt.QtCore import QPoint, Qt @@ -31,6 +31,7 @@ CHEATCODE_IAMAROBOT, CHEATCODE_QGIS_PRO_LICENSE, CHEATCODES, + QCHAT_MESSAGE_TYPE_CRS, QCHAT_MESSAGE_TYPE_GEOJSON, QCHAT_MESSAGE_TYPE_IMAGE, QCHAT_MESSAGE_TYPE_LIKE, @@ -41,12 +42,14 @@ from qtribu.gui.qchat_tree_widget_items import ( MESSAGE_COLUMN, QChatAdminTreeWidgetItem, + QChatCrsTreeWidgetItem, QChatGeojsonTreeWidgetItem, QChatImageTreeWidgetItem, QChatTextTreeWidgetItem, ) from qtribu.logic.qchat_api_client import QChatApiClient from qtribu.logic.qchat_messages import ( + QChatCrsMessage, QChatExiterMessage, QChatGeojsonMessage, QChatImageMessage, @@ -165,6 +168,7 @@ def __init__( self.qchat_ws.exiter_message_received.connect(self.on_exiter_message_received) self.qchat_ws.like_message_received.connect(self.on_like_message_received) self.qchat_ws.geojson_message_received.connect(self.on_geojson_message_received) + self.qchat_ws.crs_message_received.connect(self.on_crs_message_received) # send message signal listener self.lne_message.returnPressed.connect(self.on_send_button_clicked) @@ -565,6 +569,13 @@ def on_geojson_message_received(self, message: QChatGeojsonMessage) -> None: item = QChatGeojsonTreeWidgetItem(self.twg_chat, message) self.add_tree_widget_item(item) + def on_crs_message_received(self, message: QChatCrsMessage) -> None: + """ + Launched when a CRS message is received from the websocket + """ + item = QChatCrsTreeWidgetItem(self.twg_chat, message) + self.add_tree_widget_item(item) + # endregion def on_message_clicked(self, item: QTreeWidgetItem, column: int) -> None: @@ -606,17 +617,26 @@ def on_custom_context_menu_requested(self, point: QPoint) -> None: menu = QMenu(self.tr("QChat Menu"), self) - # if this is a geojson + # if this is a geojson message if type(item) is QChatGeojsonTreeWidgetItem: load_geojson_action = QAction( QgsApplication.getThemeIcon("mActionAddLayer.svg"), - self.tr("Load geojson in QGIS"), + self.tr("Load layer in QGIS"), ) load_geojson_action.triggered.connect( partial(item.on_click, MESSAGE_COLUMN) ) menu.addAction(load_geojson_action) + # if this is a crs message + if type(item) is QChatCrsTreeWidgetItem: + set_crs_action = QAction( + QgsApplication.getThemeIcon("mActionSetProjection.svg"), + self.tr("Set current project CRS"), + ) + set_crs_action.triggered.connect(partial(item.on_click, MESSAGE_COLUMN)) + menu.addAction(set_crs_action) + # like message action if possible if item.can_be_liked: like_action = QAction( @@ -791,7 +811,15 @@ def on_send_crs_button_clicked(self) -> None: """ Action called when the Send CRS button is clicked """ - QMessageBox.critical(self, self.tr("Send CRS"), self.tr("Not implemented yet")) + crs = QgsProject.instance().crs() + message = QChatCrsMessage( + type=QCHAT_MESSAGE_TYPE_CRS, + author=self.settings.author_nickname, + avatar=self.settings.author_avatar, + crs_wkt=crs.toWkt(), + crs_authid=crs.authid(), + ) + self.qchat_ws.send_message(message) def add_admin_message(self, text: str) -> None: """ diff --git a/qtribu/gui/qchat_tree_widget_items.py b/qtribu/gui/qchat_tree_widget_items.py index 701899e3..57f2a0c4 100644 --- a/qtribu/gui/qchat_tree_widget_items.py +++ b/qtribu/gui/qchat_tree_widget_items.py @@ -22,6 +22,7 @@ from qtribu.constants import ADMIN_MESSAGES_AVATAR, ADMIN_MESSAGES_NICKNAME from qtribu.logic.qchat_messages import ( + QChatCrsMessage, QChatGeojsonMessage, QChatImageMessage, QChatTextMessage, @@ -238,3 +239,33 @@ def can_be_copied_to_clipboard(self) -> bool: def copy_to_clipboard(self) -> None: QgsApplication.instance().clipboard().setText(json.dumps(self.message.geojson)) + + +class QChatCrsTreeWidgetItem(QChatTreeWidgetItem): + def __init__(self, parent: QTreeWidget, message: QChatCrsMessage): + super().__init__(parent, QTime.currentTime(), message.author, message.avatar) + self.message = message + self.init_time_and_author() + self.setText(MESSAGE_COLUMN, self.liked_message) + self.setToolTip(MESSAGE_COLUMN, self.liked_message) + + # set foreground color if sent by user + if message.author == self.settings.author_nickname: + self.set_foreground_color(self.settings.qchat_color_self) + + def on_click(self, column: int) -> None: + if column == MESSAGE_COLUMN: + # set current QGIS project CRS to the message one + crs = QgsCoordinateReferenceSystem.fromWkt(self.message.crs_wkt) + QgsProject.instance().setCrs(crs) + + @property + def liked_message(self) -> str: + return f"" + + @property + def can_be_copied_to_clipboard(self) -> bool: + return True + + def copy_to_clipboard(self) -> None: + QgsApplication.instance().clipboard().setText(json.dumps(self.message.crs_wkt)) diff --git a/qtribu/logic/qchat_messages.py b/qtribu/logic/qchat_messages.py index 14642be2..979dbafb 100644 --- a/qtribu/logic/qchat_messages.py +++ b/qtribu/logic/qchat_messages.py @@ -56,3 +56,11 @@ class QChatGeojsonMessage(QChatMessage): crs_wkt: str crs_authid: str geojson: dict + + +@dataclass(init=True, frozen=True) +class QChatCrsMessage(QChatMessage): + author: str + avatar: Optional[str] + crs_wkt: str + crs_authid: str diff --git a/qtribu/logic/qchat_websocket.py b/qtribu/logic/qchat_websocket.py index d5613e07..eb38cc28 100644 --- a/qtribu/logic/qchat_websocket.py +++ b/qtribu/logic/qchat_websocket.py @@ -7,6 +7,7 @@ from qgis.PyQt.QtCore import QObject, QUrl, pyqtSignal from qtribu.constants import ( + QCHAT_MESSAGE_TYPE_CRS, QCHAT_MESSAGE_TYPE_EXITER, QCHAT_MESSAGE_TYPE_GEOJSON, QCHAT_MESSAGE_TYPE_IMAGE, @@ -17,6 +18,7 @@ QCHAT_MESSAGE_TYPE_UNCOMPLIANT, ) from qtribu.logic.qchat_messages import ( + QChatCrsMessage, QChatExiterMessage, QChatGeojsonMessage, QChatImageMessage, @@ -69,6 +71,7 @@ def __init__(self): exiter_message_received = pyqtSignal(QChatExiterMessage) like_message_received = pyqtSignal(QChatLikeMessage) geojson_message_received = pyqtSignal(QChatGeojsonMessage) + crs_message_received = pyqtSignal(QChatCrsMessage) def open(self, qchat_instance_uri: str, room: str) -> None: """ @@ -131,3 +134,5 @@ def on_message_received(self, text: str) -> None: self.like_message_received.emit(QChatLikeMessage(**message)) elif msg_type == QCHAT_MESSAGE_TYPE_GEOJSON: self.geojson_message_received.emit(QChatGeojsonMessage(**message)) + elif msg_type == QCHAT_MESSAGE_TYPE_CRS: + self.crs_message_received.emit(QChatCrsMessage(**message))