diff --git a/DataStorage.py b/DataStorage.py index 63aeff1..24d8b05 100644 --- a/DataStorage.py +++ b/DataStorage.py @@ -1,5 +1,5 @@ -from typing import List, Tuple, Union, Any +from typing import List, Optional, Tuple, Union, Any class DataStorage: @@ -14,6 +14,13 @@ class DataStorage: currentCRS = None currentUnits = "m" + custom_lat: Optional[float] = None + custom_lon: Optional[float] = None + + crs_offset_x: Optional[float] = None + crs_offset_y: Optional[float] = None + crs_rotation: Optional[float] = None + current_layers: Union[List[Tuple[Any, str, str]], None] = None saved_layers: Union[List, None] = None sending_layers: None diff --git a/dockwidget_main.py b/dockwidget_main.py index 6a8e83b..b85b396 100644 --- a/dockwidget_main.py +++ b/dockwidget_main.py @@ -2,6 +2,7 @@ import inspect import os +from specklepy_qt_ui.widget_custom_crs import CustomCRSDialog from specklepy_qt_ui.widget_transforms import MappingSendDialog from specklepy_qt_ui.LogWidget import LogWidget @@ -49,6 +50,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): msgLog: LogWidget = None dataStorage: DataStorage = None mappingSendDialog = None + custom_crs_modal = None signal_1 = pyqtSignal(object) signal_2 = pyqtSignal(object) @@ -70,7 +72,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): self.streams_add_button.setFlat(True) self.streams_remove_button.setFlat(True) - self.saveSurveyPoint.setFlat(True) + #self.saveSurveyPoint.setFlat(True) self.saveLayerSelection.setFlat(True) self.reloadButton.setFlat(True) self.closeButton.setFlat(True) @@ -88,7 +90,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): self.streams_remove_button.setStyleSheet("QPushButton {padding:3px;padding-left:5px;border: none; text-align: left; image-position:right} QPushButton:hover { " + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + f"{COLOR}" + " }") #+ f"{backgr_image_del}" self.saveLayerSelection.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }") - self.saveSurveyPoint.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }") + #self.saveSurveyPoint.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }") self.reloadButton.setStyleSheet("QPushButton {text-align: left;} QPushButton:hover { " + f"{COLOR}" + " }") self.closeButton.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }") @@ -113,6 +115,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): # l_item = self.verticalLayout.itemAt(i).widget() # add row with "experimental" checkbox + r''' box = QWidget() box.layout = QHBoxLayout(box) btn = QtWidgets.QCheckBox("Send/receive in the background (experimental!)") @@ -122,6 +125,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): self.formLayout.insertRow(10,box) self.experimental = btn self.experimental.setChecked(True) + ''' def runSetup(self, plugin): @@ -142,7 +146,8 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): self.msgLog.active_account = plugin.dataStorage.active_account self.msgLog.speckle_version = plugin.version - # add row with "experimental" checkbox + # add Transforms button + r''' box = QWidget() box.layout = QHBoxLayout(box) btn = QtWidgets.QPushButton("Apply transformations on Send") @@ -152,6 +157,13 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): box.layout.setContentsMargins(65, 0, 0, 0) self.formLayout.insertRow(9,box) self.setMapping = btn + ''' + + self.setMapping.setFlat(True) + self.setMapping.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }") + + self.crsSettings.setFlat(True) + self.crsSettings.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }") def addDataStorage(self, plugin): self.dataStorage = plugin.dataStorage @@ -288,7 +300,9 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): self.streams_add_button.clicked.connect( plugin.onStreamAddButtonClicked ) self.reloadButton.clicked.connect(lambda: self.refreshClicked(plugin)) self.closeButton.clicked.connect(lambda: self.closeClicked(plugin)) - self.saveSurveyPoint.clicked.connect(plugin.set_survey_point) + + self.crsSettings.clicked.connect(self.customCRSDialogCreate) + #self.saveSurveyPoint.clicked.connect(plugin.set_survey_point) self.sendModeButton.clicked.connect(lambda: self.setSendMode(plugin)) self.layerSendModeDropdown.currentIndexChanged.connect( lambda: self.layerSendModeChange(plugin) ) @@ -406,7 +420,6 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): self.populateLayerSendModeDropdown() self.populateProjectStreams(plugin) - self.populateSurveyPoint(plugin) self.runBtnStatusChanged(plugin) self.runButton.setEnabled(False) @@ -535,17 +548,6 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self) return - def populateSurveyPoint(self, plugin): - if not self: - return - try: - self.surveyPointLat.clear() - self.surveyPointLat.setText(str(plugin.lat)) - self.surveyPointLon.clear() - self.surveyPointLon.setText(str(plugin.lon)) - except Exception as e: - logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self) - return def enableElements(self, plugin): try: @@ -562,7 +564,58 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): except Exception as e: logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self) return + + def customCRSDialogCreate(self): + try: + self.custom_crs_modal = CustomCRSDialog(None) + self.custom_crs_modal.dataStorage = self.dataStorage + self.custom_crs_modal.dialog_button_box.button(QtWidgets.QDialogButtonBox.Close).clicked.connect(self.customCRSCreate) + #self.custom_crs_modal.handleBranchCreate.connect(self.handleBranchCreate) + self.custom_crs_modal.show() + #self.custom_crs_modal.populateSurveyPoint() + except Exception as e: + logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self) + return + + + def customCRSApply(self): + index = self.custom_crs_modal.modeDropdown.currentIndex() + if index == 0: #create custom CRS + self.customCRSCreate() + if index == 1: + self.crsOffsetsApply() + def crsOffsetsApply(self): + try: + from speckle.utils.project_vars import set_crs_offsets_rotation + + if float(self.custom_crs_modal.offsetX.text()) is not None and float(self.custom_crs_modal.offsetY.text()) is not None: + self.dataStorage.crs_offset_x = float(self.custom_crs_modal.offsetX.text()) + self.dataStorage.crs_offset_y = float(self.custom_crs_modal.offsetY.text()) + if float(self.custom_crs_modal.rotation.text()) is not None: + self.dataStorage.crs_rotation = float(self.custom_crs_modal.rotation.text()) + set_crs_offsets_rotation(self.dataStorage, self) + self.custom_crs_modal.close() + except: pass + + def customCRSCreate(self): + try: + vals =[ str(self.custom_crs_modal.surveyPointLat.text()), str(self.custom_crs_modal.surveyPointLon.text()) ] + custom_lat, custom_lon = [float(i.replace(" ","")) for i in vals] + if custom_lat>180 or custom_lat<-180 or custom_lon >180 or custom_lon<-180: + logToUser("LAT LON values must be within (-180, 180). You can right-click on the canvas location to copy coordinates in WGS 84", level = 1, plugin=self) + return True + + from speckle.utils.project_vars import set_survey_point, setProjectReferenceSystem + self.dataStorage.custom_lat = custom_lat + self.dataStorage.custom_lon = custom_lon + set_survey_point(self.dataStorage, self) + setProjectReferenceSystem(self.dataStorage, self) + + except Exception as e: + logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self) + return + def populateProjectStreams(self, plugin): try: from speckle.utils.project_vars import set_project_streams diff --git a/ui/custom_crs.ui b/ui/custom_crs.ui new file mode 100644 index 0000000..5eed901 --- /dev/null +++ b/ui/custom_crs.ui @@ -0,0 +1,132 @@ + + + CreateStreamDialog + + + Qt::NonModal + + + + 0 + 0 + + + + Form + + + + + + QLayout::SetNoConstraint + + + + + + + + + + + + + + lat (y) + + + + + + + + ° + + + + + + + + lon (x) + + + + + + + + ° + + + + + + + + + + + + + + + + + + + + + + lat (y) + + + + + + + + lon (x) + + + + + + + + rotation + + + + + + + + ° + + + + + + + + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Close + + + + + + + + + + + diff --git a/ui/dockwidget_main.ui b/ui/dockwidget_main.ui index 5529cee..2f551a4 100644 --- a/ui/dockwidget_main.ui +++ b/ui/dockwidget_main.ui @@ -181,14 +181,72 @@ - + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + + + + + true + + + Apply transformations on Send + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + + + + + true + + + CRS Settings + + + + + + + + + + + + Message - + Sent XXX objects from QGIS @@ -198,7 +256,7 @@ - + @@ -233,62 +291,8 @@ - - - - Lat, Lon - - - - - - - - - 0.0 - - - - - - - - ° - - - - - - - - 0.0 - - - - - - - - ° - - - - - - - - true - - - Set as a project center - - - - - - - + diff --git a/widget_custom_crs.py b/widget_custom_crs.py new file mode 100644 index 0000000..69eb691 --- /dev/null +++ b/widget_custom_crs.py @@ -0,0 +1,135 @@ +import inspect +import os +from typing import List, Tuple, Union +from specklepy_qt_ui.DataStorage import DataStorage +from specklepy_qt_ui.logger import logToUser + +from PyQt5 import QtWidgets, uic, QtCore +from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget +from PyQt5.QtCore import pyqtSignal + +from specklepy.api.client import SpeckleClient + +from specklepy_qt_ui.global_resources import COLOR + +# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer +FORM_CLASS, _ = uic.loadUiType( + os.path.join(os.path.join(os.path.dirname(__file__), "ui", "custom_crs.ui") ) +) + +class CustomCRSDialog(QtWidgets.QWidget, FORM_CLASS): + + name_field: QtWidgets.QLineEdit = None + description_field: QtWidgets.QLineEdit = None + dialog_button_box: QtWidgets.QDialogButtonBox = None + saveSurveyPoint: QtWidgets.QPushButton = None + speckle_client: Union[SpeckleClient, None] = None + dataStorage: DataStorage = None + + #Events + handleCRSCreate = pyqtSignal(str,str) + + def __init__(self, parent=None, speckle_client: SpeckleClient = None): + super(CustomCRSDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint) + self.speckle_client = speckle_client + self.setupUi(self) + self.setWindowTitle("CustomCRS") + + #self.saveSurveyPoint.setFlat(True) + #self.saveSurveyPoint.setStyleSheet("QPushButton {text-align: left;} QPushButton:hover { " + f"{COLOR}" + " }") + #self.saveOffsets.setFlat(True) + #self.saveOffsets.setStyleSheet("QPushButton {text-align: left;} QPushButton:hover { " + f"{COLOR}" + " }") + + #self.label_offsets.setEnabled(False) + self.offsetX.setEnabled(False) + self.offsetY.setEnabled(False) + self.rotation.setEnabled(False) + #self.saveOffsets.setEnabled(False) + + + self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Close).clicked.connect(self.onCancelClicked) + self.modeDropdown.currentIndexChanged.connect(self.onModeChanged) + + self.populateModeDropdown() + self.populateSurveyPoint() + + def onModeChanged(self): + try: + if not self: return + index = self.modeDropdown.currentIndex() + if index == 0: + #self.label_customCRS.setEnabled(True) + self.surveyPointLat.setEnabled(True) + self.surveyPointLon.setEnabled(True) + #self.saveSurveyPoint.setEnabled(True) + + #self.label_offsets.setEnabled(False) + self.offsetX.setEnabled(False) + self.offsetY.setEnabled(False) + self.rotation.setEnabled(False) + #self.saveOffsets.setEnabled(False) + + elif index == 1: + #self.label_customCRS.setEnabled(False) + self.surveyPointLat.setEnabled(False) + self.surveyPointLon.setEnabled(False) + #self.saveSurveyPoint.setEnabled(False) + + #self.label_offsets.setEnabled(True) + self.offsetX.setEnabled(True) + self.offsetY.setEnabled(True) + self.rotation.setEnabled(True) + #self.saveOffsets.setEnabled(True) + + except Exception as e: + logToUser(e, level = 2, func = inspect.stack()[0][3]) + return + + def populateModeDropdown(self): + if not self: return + try: + self.modeDropdown.clear() + self.modeDropdown.addItems( + ["Create custom CRS", "Add offsets / rotation to the current Project CRS"] + ) + except Exception as e: + logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self) + return + + def populateSurveyPoint(self): + if not self: + return + try: + self.surveyPointLat.clear() + self.surveyPointLon.clear() + if self.dataStorage.custom_lat is not None and self.dataStorage.custom_lon is not None: + self.surveyPointLat.setText(str(self.dataStorage.custom_lat)) + self.surveyPointLon.setText(str(self.dataStorage.custom_lon)) + + except Exception as e: + logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self) + return + + def onOkClicked(self): + return + try: + self.close() + except Exception as e: + logToUser(e, level = 2, func = inspect.stack()[0][3]) + return + + def onCancelClicked(self): + try: + self.close() + except Exception as e: + logToUser(e, level = 2, func = inspect.stack()[0][3]) + return + + def onAccountSelected(self, index): + try: + account = self.speckle_accounts[index] + self.speckle_client = SpeckleClient(account.serverInfo.url, account.serverInfo.url.startswith("https")) + self.speckle_client.authenticate_with_token(token=account.token) + except Exception as e: + logToUser(e, level = 2, func = inspect.stack()[0][3]) + return