Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(qchat): send and receive a bbox message #220

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions qtribu/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,4 @@ def local_path(self, base_path: Path = Path().home() / ".geotribu/cdn/") -> Path
QCHAT_MESSAGE_TYPE_LIKE = "like"
QCHAT_MESSAGE_TYPE_GEOJSON = "geojson"
QCHAT_MESSAGE_TYPE_CRS = "crs"
QCHAT_MESSAGE_TYPE_BBOX = "bbox"
39 changes: 35 additions & 4 deletions qtribu/gui/dck_qchat.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
CHEATCODE_IAMAROBOT,
CHEATCODE_QGIS_PRO_LICENSE,
CHEATCODES,
QCHAT_MESSAGE_TYPE_BBOX,
QCHAT_MESSAGE_TYPE_CRS,
QCHAT_MESSAGE_TYPE_GEOJSON,
QCHAT_MESSAGE_TYPE_IMAGE,
Expand All @@ -42,13 +43,15 @@
from qtribu.gui.qchat_tree_widget_items import (
MESSAGE_COLUMN,
QChatAdminTreeWidgetItem,
QChatBboxTreeWidgetItem,
QChatCrsTreeWidgetItem,
QChatGeojsonTreeWidgetItem,
QChatImageTreeWidgetItem,
QChatTextTreeWidgetItem,
)
from qtribu.logic.qchat_api_client import QChatApiClient
from qtribu.logic.qchat_messages import (
QChatBboxMessage,
QChatCrsMessage,
QChatExiterMessage,
QChatGeojsonMessage,
Expand Down Expand Up @@ -169,6 +172,7 @@ def __init__(
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)
self.qchat_ws.bbox_message_received.connect(self.on_bbox_message_received)

# send message signal listener
self.lne_message.returnPressed.connect(self.on_send_button_clicked)
Expand All @@ -190,7 +194,7 @@ def __init__(
)

# send extent message signal listener
self.btn_send_extent.pressed.connect(self.on_send_extent_button_clicked)
self.btn_send_extent.pressed.connect(self.on_send_bbox_button_clicked)
self.btn_send_extent.setIcon(
QIcon(QgsApplication.iconPath("mActionViewExtentInCanvas.svg"))
)
Expand Down Expand Up @@ -576,6 +580,13 @@ def on_crs_message_received(self, message: QChatCrsMessage) -> None:
item = QChatCrsTreeWidgetItem(self.twg_chat, message)
self.add_tree_widget_item(item)

def on_bbox_message_received(self, message: QChatBboxMessage) -> None:
"""
Launched when a BBOX message is received from the websocket
"""
item = QChatBboxTreeWidgetItem(self.twg_chat, message, self.iface.mapCanvas())
self.add_tree_widget_item(item)

# endregion

def on_message_clicked(self, item: QTreeWidgetItem, column: int) -> None:
Expand Down Expand Up @@ -637,6 +648,15 @@ def on_custom_context_menu_requested(self, point: QPoint) -> None:
set_crs_action.triggered.connect(partial(item.on_click, MESSAGE_COLUMN))
menu.addAction(set_crs_action)

# if this is a bbox message
if type(item) is QChatBboxTreeWidgetItem:
set_bbox_action = QAction(
QgsApplication.getThemeIcon("mActionViewExtentInCanvas.svg"),
self.tr("Set current extent"),
)
set_bbox_action.triggered.connect(partial(item.on_click, MESSAGE_COLUMN))
menu.addAction(set_bbox_action)

# like message action if possible
if item.can_be_liked:
like_action = QAction(
Expand Down Expand Up @@ -799,13 +819,24 @@ def on_send_screenshot_button_clicked(self) -> None:
)
self.qchat_ws.send_message(message)

def on_send_extent_button_clicked(self) -> None:
def on_send_bbox_button_clicked(self) -> None:
"""
Action called when the Send extent button is clicked
"""
QMessageBox.critical(
self, self.tr("Send extent"), self.tr("Not implemented yet")
crs = QgsProject.instance().crs()
rect = self.iface.mapCanvas().extent()
message = QChatBboxMessage(
type=QCHAT_MESSAGE_TYPE_BBOX,
author=self.settings.author_nickname,
avatar=self.settings.author_avatar,
crs_wkt=crs.toWkt(),
crs_authid=crs.authid(),
xmin=rect.xMinimum(),
xmax=rect.xMaximum(),
ymin=rect.yMinimum(),
ymax=rect.yMaximum(),
)
self.qchat_ws.send_message(message)

def on_send_crs_button_clicked(self) -> None:
"""
Expand Down
6 changes: 6 additions & 0 deletions qtribu/gui/dck_qchat.ui
Original file line number Diff line number Diff line change
Expand Up @@ -325,13 +325,19 @@
</item>
<item>
<widget class="QPushButton" name="btn_send_extent">
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="text">
<string>Send Extent</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_send_crs">
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="text">
<string>Send CRS</string>
</property>
Expand Down
52 changes: 51 additions & 1 deletion qtribu/gui/qchat_tree_widget_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
from qgis.core import (
QgsApplication,
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
QgsPointXY,
QgsProject,
QgsRectangle,
QgsVectorLayer,
)
from qgis.gui import QgsMapCanvas
from qgis.PyQt.QtCore import QTime
from qgis.PyQt.QtGui import QBrush, QColor, QIcon, QPixmap
from qgis.PyQt.QtWidgets import (
Expand All @@ -22,6 +26,7 @@

from qtribu.constants import ADMIN_MESSAGES_AVATAR, ADMIN_MESSAGES_NICKNAME
from qtribu.logic.qchat_messages import (
QChatBboxMessage,
QChatCrsMessage,
QChatGeojsonMessage,
QChatImageMessage,
Expand Down Expand Up @@ -268,4 +273,49 @@ 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))
QgsApplication.instance().clipboard().setText(self.message.crs_wkt)


class QChatBboxTreeWidgetItem(QChatTreeWidgetItem):
def __init__(
self, parent: QTreeWidget, message: QChatBboxMessage, canvas: QgsMapCanvas
):
super().__init__(parent, QTime.currentTime(), message.author, message.avatar)
self.message = message
self.canvas = canvas
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 canvas extent to the received one
project = QgsProject.instance()
tr = QgsCoordinateTransform(
QgsCoordinateReferenceSystem(self.message.crs_wkt),
project.crs(),
project,
)
rect = QgsRectangle(
tr.transform(QgsPointXY(self.message.xmin, self.message.ymin)),
tr.transform(QgsPointXY(self.message.xmax, self.message.ymax)),
)
self.canvas.setExtent(rect)
self.canvas.refresh()

@property
def liked_message(self) -> str:
msg = f"[{self.message.xmin} {self.message.ymin}, {self.message.xmax} {self.message.ymax}]"
return f"<BBOX {self.message.crs_authid}: {msg}>"

@property
def can_be_copied_to_clipboard(self) -> bool:
return True

def copy_to_clipboard(self) -> None:
msg = f"[{self.message.xmin} {self.message.ymin}, {self.message.xmax} {self.message.ymax}]"
QgsApplication.instance().clipboard().setText(msg)
12 changes: 12 additions & 0 deletions qtribu/logic/qchat_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,15 @@ class QChatCrsMessage(QChatMessage):
avatar: Optional[str]
crs_wkt: str
crs_authid: str


@dataclass(init=True, frozen=True)
class QChatBboxMessage(QChatMessage):
author: str
avatar: Optional[str]
crs_wkt: str
crs_authid: str
xmin: float
xmax: float
ymin: float
ymax: float
5 changes: 5 additions & 0 deletions qtribu/logic/qchat_websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from qgis.PyQt.QtCore import QObject, QUrl, pyqtSignal

from qtribu.constants import (
QCHAT_MESSAGE_TYPE_BBOX,
QCHAT_MESSAGE_TYPE_CRS,
QCHAT_MESSAGE_TYPE_EXITER,
QCHAT_MESSAGE_TYPE_GEOJSON,
Expand All @@ -18,6 +19,7 @@
QCHAT_MESSAGE_TYPE_UNCOMPLIANT,
)
from qtribu.logic.qchat_messages import (
QChatBboxMessage,
QChatCrsMessage,
QChatExiterMessage,
QChatGeojsonMessage,
Expand Down Expand Up @@ -72,6 +74,7 @@ def __init__(self):
like_message_received = pyqtSignal(QChatLikeMessage)
geojson_message_received = pyqtSignal(QChatGeojsonMessage)
crs_message_received = pyqtSignal(QChatCrsMessage)
bbox_message_received = pyqtSignal(QChatBboxMessage)

def open(self, qchat_instance_uri: str, room: str) -> None:
"""
Expand Down Expand Up @@ -136,3 +139,5 @@ def on_message_received(self, text: str) -> None:
self.geojson_message_received.emit(QChatGeojsonMessage(**message))
elif msg_type == QCHAT_MESSAGE_TYPE_CRS:
self.crs_message_received.emit(QChatCrsMessage(**message))
elif msg_type == QCHAT_MESSAGE_TYPE_BBOX:
self.bbox_message_received.emit(QChatBboxMessage(**message))