diff --git a/qt_ui/ConnectorBindings.py b/qt_ui/ConnectorBindings.py new file mode 100644 index 0000000..43b2bf0 --- /dev/null +++ b/qt_ui/ConnectorBindings.py @@ -0,0 +1,126 @@ + +from typing import Dict, List + +from numpy import double +from import UpdateSavedStreams +from import UpdateSelectedStream + +using static DesktopUI2.ViewModels.MappingViewModel; + +namespace DesktopUI2; + +public delegate void UpdateSavedStreams(List streams); +public delegate void UpdateSelectedStream(); + +class ConnectorBindings(): + + def __init__(self): + self.ConnectorVersion: str == Assembly.GetAssembly(GetType()).GetNameAndVersion().Version + self.ConnectorName: str == Assembly.GetAssembly(GetType()).GetNameAndVersion().Name + #SavedStreamStates: List == new List() + + self.CanReceive: bool = True + '''Indicates if the connector can Receive and if that function has been implemented''' + self.CanPreviewSend: bool == False + '''Indicates if previewing send has been implemented''' + self.CanPreviewReceive: bool == False + '''Indicates if previewing receive has been implemented''' + self.CanOpen3DView: bool == False + '''Returns true if the method is overwritten and implemented.''' + + def UpdateSavedStreams(self, streams: List[StreamState]): + UpdateSavedStreams(streams) + + def UpdateSelectedStream(self): + UpdateSelectedStream() + + def Open3DView(self, viewCoordinates: List[double], viewName: str = ""): + '''Opens a 3D view in the host application + viewCoordinates: First three values are the camera position, second three the target. + viewName: Id or Name of the view''' + return + + def GetHostAppNameVersion(self)-> str: + '''Gets the current host application name with version.''' + return + + def GetHostAppName(self) -> str: + '''Gets the current host application name.''' + return + + def GetFileName(self) -> str: + '''Gets the current opened/focused file's name. + Make sure to check regarding unsaved/temporary files.''' + return + + def GetDocumentId(self) -> str: + '''Gets the current opened/focused file's id. + Generate one in here if the host app does not provide one.''' + return + + def GetDocumentLocation(self) -> str: + '''Gets the current opened/focused file's locations. + Make sure to check regarding unsaved/temporary files.''' + return + + def ResetDocument(self): + '''Clears the document state of selections and previews''' + return + + def GetActiveViewName(self) -> str: + '''Gets the current opened/focused file's view, if applicable.''' + return + + def GetStreamsInFile(self) -> List[StreamState]: + '''Returns the serialised clients present in the current open host file.''' + return + + def WriteStreamsToFile(self, streams: List[StreamState]): + '''Writes serialised clients to the current open host file.''' + return + + def AddNewStream(self, state: StreamState): + '''Adds a new client and persists the info to the host file''' + return + + def PersistAndUpdateStreamInFile(self, state: StreamState): + '''Persists the stream info to the host file; if maintaining a local in memory copy, make sure to update it too.''' + return + + def SendStream(self, state: StreamState, progress: ProgressViewModel) -> str: + '''Pushes a client's stream''' + return + + def PreviewSend(self, state: StreamState, progress: ProgressViewModel): + '''Previews a send operation''' + + def ReceiveStream(self, state: StreamState, progress: ProgressViewModel) -> StreamState: + '''Receives stream data from the server''' + + def PreviewReceive(self, state: StreamState, progress: ProgressViewModel) -> StreamState: + '''Previews a receive operation''' + + def GetSelectedObjects(self) -> List[str]: + '''Adds the current selection to the provided client.''' + + def GetObjectsInView(self) -> List[str]: + '''Gets a list of objects in the currently active view''' + + def SelectClientObjects(self, objs: List[str], deselect: bool = False): + '''clients should be able to select/preview/hover one way or another their associated objects''' + + def GetSelectionFilters(self) -> List[ISelectionFilter]: + '''Should return a list of filters that the application supports.''' + + def GetReceiveModes(self) -> List[ReceiveMode]: + '''Should return a list of receive modes that the application supports.''' + + def GetCustomStreamMenuItems(self) -> List[MenuItem]: + '''Return a list of custom menu items for stream cards.''' + + def GetSettings(self) -> List[ISetting]: + return + + def ImportFamilyCommand(self, Mapping: Dict[str, List[MappingValue]] ) -> Dict[str, List[MappingValue]] : + '''Imports family symbols in Revit''' + return diff --git a/qt_ui/DataStorage.py b/qt_ui/DataStorage.py index 7cb4ae0..773adef 100644 --- a/qt_ui/DataStorage.py +++ b/qt_ui/DataStorage.py @@ -3,7 +3,11 @@ import inspect from typing import List, Optional, Tuple, Union, Any import webbrowser -from speckle.utils.panel_logging import logToUser +try: + from specklepy_qt_ui.qt_ui.utils.logger import logToUser +except ModuleNotFoundError: + from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser + from specklepy.core.api.credentials import get_local_accounts @@ -18,6 +22,7 @@ class DataStorage: currentCRS = None currentUnits = "m" currentOriginalUnits = "" + workspace = "" custom_lat: Optional[float] = None custom_lon: Optional[float] = None @@ -49,6 +54,9 @@ class DataStorage: latestActionLayers: Optional[list] = None latestActionUnits: str = "" + flat_report_receive: dict = {} + flat_report_latest: dict = {} + def __init__(self): # print("hello") # self.streamsToFollow.append(("https://speckle.xyz/streams/17b0b76d13/branches/random_tests", "", "09a0f3e41a")) diff --git a/qt_ui/LogWidget.py b/qt_ui/LogWidget.py index 5dac87a..83f51a2 100644 --- a/qt_ui/LogWidget.py +++ b/qt_ui/LogWidget.py @@ -9,19 +9,38 @@ import webbrowser from specklepy.logging import metrics from specklepy.core.api.credentials import Account -from specklepy_qt_ui.qt_ui.global_resources import ( - BACKGR_COLOR, - BACKGR_COLOR_LIGHT, - BACKGR_COLOR_GREY, - BACKGR_COLOR_TRANSPARENT, - BACKGR_COLOR_HIGHLIGHT, - NEW_GREY, - NEW_GREY_HIGHLIGHT, - BACKGR_ERROR_COLOR, - BACKGR_ERROR_COLOR_LIGHT, -) -from specklepy_qt_ui.qt_ui.widget_dependencies_upgrade import DependenciesUpgradeDialog -from specklepy_qt_ui.qt_ui.widget_report import ReportDialog +try: + from specklepy_qt_ui.qt_ui.utils.global_resources import ( + BACKGR_COLOR, + BACKGR_COLOR_LIGHT, + BACKGR_COLOR_GREY, + BACKGR_COLOR_TRANSPARENT, + BACKGR_COLOR_HIGHLIGHT, + NEW_GREY, + NEW_GREY_HIGHLIGHT, + BACKGR_ERROR_COLOR, + BACKGR_ERROR_COLOR_LIGHT, + ) + from specklepy_qt_ui.qt_ui.widget_dependencies_upgrade import ( + DependenciesUpgradeDialog, + ) + from specklepy_qt_ui.qt_ui.widget_report import ReportDialog +except ModuleNotFoundError: + from speckle.specklepy_qt_ui.qt_ui.utils.global_resources import ( + BACKGR_COLOR, + BACKGR_COLOR_LIGHT, + BACKGR_COLOR_GREY, + BACKGR_COLOR_TRANSPARENT, + BACKGR_COLOR_HIGHLIGHT, + NEW_GREY, + NEW_GREY_HIGHLIGHT, + BACKGR_ERROR_COLOR, + BACKGR_ERROR_COLOR_LIGHT, + ) + from speckle.specklepy_qt_ui.qt_ui.widget_dependencies_upgrade import ( + DependenciesUpgradeDialog, + ) + from speckle.specklepy_qt_ui.qt_ui.widget_report import ReportDialog class LogWidget(QWidget): @@ -268,20 +287,22 @@ class LogWidget(QWidget): def getNextBtn(self): index = len(self.used_btns) # get the next "free" button - # print(index) - # print(self.btns) - if index >= len(self.btns): # remove first button print(self.layout.itemAt(0).widget()) self.layout.itemAt(0).widget().setParent(None) - - # self.used_btns.clear() self.createBtns() index = 0 - btn = self.btns[index] - # print(btn) + + return btn, index + + def getLastBtn(self): + index = len(self.used_btns) - 1 # get the next "free" button + btn = None + if index > 0: + btn = self.btns[index] + return btn, index def getBtnByKeyword(self, keyword: str): diff --git a/qt_ui/dockwidget_main.py b/qt_ui/dockwidget_main.py index 3bc6dfa..9571064 100644 --- a/qt_ui/dockwidget_main.py +++ b/qt_ui/dockwidget_main.py @@ -4,37 +4,71 @@ from copy import copy import inspect import os -from specklepy_qt_ui.qt_ui.widget_transforms import MappingSendDialog -from specklepy_qt_ui.qt_ui.LogWidget import LogWidget -from specklepy_qt_ui.qt_ui.logger import logToUser -from specklepy_qt_ui.qt_ui.utils import constructCommitURL -from specklepy_qt_ui.qt_ui.DataStorage import DataStorage -from specklepy_qt_ui.qt_ui.global_resources import ( - COLOR_HIGHLIGHT, - SPECKLE_COLOR, - SPECKLE_COLOR_LIGHT, - ICON_OPEN_WEB, - ICON_REPORT, - ICON_LOGO, - ICON_SEARCH, - ICON_DELETE, - ICON_DELETE_BLUE, - ICON_SEND, - ICON_RECEIVE, - ICON_SEND_BLACK, - ICON_RECEIVE_BLACK, - ICON_SEND_BLUE, - ICON_RECEIVE_BLUE, - COLOR, - BACKGR_COLOR, - BACKGR_COLOR_LIGHT, - ICON_XXL, - ICON_RASTER, - ICON_POLYGON, - ICON_LINE, - ICON_POINT, - ICON_GENERIC, -) +try: + from specklepy_qt_ui.qt_ui.widget_transforms import MappingSendDialog + from specklepy_qt_ui.qt_ui.LogWidget import LogWidget + from specklepy_qt_ui.qt_ui.utils.logger import logToUser + from specklepy_qt_ui.qt_ui.utils.utils import constructCommitURL + from specklepy_qt_ui.qt_ui.DataStorage import DataStorage + from specklepy_qt_ui.qt_ui.utils.global_resources import ( + COLOR_HIGHLIGHT, + SPECKLE_COLOR, + SPECKLE_COLOR_LIGHT, + ICON_OPEN_WEB, + ICON_REPORT, + ICON_LOGO, + ICON_SEARCH, + ICON_DELETE, + ICON_DELETE_BLUE, + ICON_SEND, + ICON_RECEIVE, + ICON_SEND_BLACK, + ICON_RECEIVE_BLACK, + ICON_SEND_BLUE, + ICON_RECEIVE_BLUE, + COLOR, + BACKGR_COLOR, + BACKGR_COLOR_LIGHT, + ICON_XXL, + ICON_RASTER, + ICON_POLYGON, + ICON_LINE, + ICON_POINT, + ICON_GENERIC, + ) +except ModuleNotFoundError: + from speckle.specklepy_qt_ui.qt_ui.widget_transforms import MappingSendDialog + from speckle.specklepy_qt_ui.qt_ui.LogWidget import LogWidget + from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser + from speckle.specklepy_qt_ui.qt_ui.utils.utils import constructCommitURL + from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage + from speckle.specklepy_qt_ui.qt_ui.utils.global_resources import ( + COLOR_HIGHLIGHT, + SPECKLE_COLOR, + SPECKLE_COLOR_LIGHT, + ICON_OPEN_WEB, + ICON_REPORT, + ICON_LOGO, + ICON_SEARCH, + ICON_DELETE, + ICON_DELETE_BLUE, + ICON_SEND, + ICON_RECEIVE, + ICON_SEND_BLACK, + ICON_RECEIVE_BLACK, + ICON_SEND_BLUE, + ICON_RECEIVE_BLUE, + COLOR, + BACKGR_COLOR, + BACKGR_COLOR_LIGHT, + ICON_XXL, + ICON_RASTER, + ICON_POLYGON, + ICON_LINE, + ICON_POINT, + ICON_GENERIC, + ) + from specklepy.logging.exceptions import SpeckleException, GraphQLException from specklepy.logging import metrics @@ -775,7 +809,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): self.streamBranchDropdown.clear() # activates "populate commit" # print(2) if isinstance(plugin.active_stream[1], SpeckleException): - logToUser("Some Projects cannot be accessed", level=1, plugin=self) + logToUser("Some streams cannot be accessed", level=1, plugin=self) return elif ( plugin.active_stream is None @@ -791,7 +825,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): [f"{branch.name}" for branch in plugin.active_stream[1].branches.items] ) # print(4) - self.streamBranchDropdown.addItems(["Create New Model"]) + self.streamBranchDropdown.addItems(["Create New Branch"]) # print(5) if keep_branch is True: plugin.active_branch = active_branch @@ -830,13 +864,13 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): # print("________populateActiveCommitDropdown") # print(plugin.active_commit) if plugin.active_stream is None: - print("Active project is None") + print("Active stream is None") return branchName = self.streamBranchDropdown.currentText() # print(f"CURRENT BRANCH TEXT: {branchName}") if branchName == "": return - if branchName == "Create New Model": + if branchName == "Create New Branch": self.streamBranchDropdown.setCurrentText("main") plugin.onBranchCreateClicked() return @@ -846,7 +880,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): # print(plugin.active_commit) self.commitDropdown.clear() if isinstance(plugin.active_stream[1], SpeckleException): - logToUser("Some Projects cannot be accessed", level=1, plugin=self) + logToUser("Some streams cannot be accessed", level=1, plugin=self) return elif plugin.active_stream[1]: for b in plugin.active_stream[1].branches.items: @@ -898,7 +932,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS): else: plugin.active_commit = None - self.commitDropdown.setItemText(0, "Latest version of this model") + self.commitDropdown.setItemText(0, "Latest commit from this branch") # enable or disable web view button # print("_________ENABLE OR DISABLE") # print(plugin.active_commit) diff --git a/qt_ui/mainWindow.py b/qt_ui/mainWindow.py index 2473702..72c6f8b 100644 --- a/qt_ui/mainWindow.py +++ b/qt_ui/mainWindow.py @@ -1,15 +1,16 @@ +from copy import copy import os import sys from typing import List -# from speckle.converter.layers import getLayers +# from speckle.speckle.converter.layers import getLayers # import ui.speckle_qgis_dialog from specklepy.logging.exceptions import SpeckleException, GraphQLException from PyQt5 import QtWidgets, uic from PyQt5 import QtGui -from PyQt5.QtGui import QIcon, QPixmap +from PyQt5.QtGui import QIcon, QPixmap, QCursor from PyQt5.QtWidgets import ( QMainWindow, QApplication, @@ -38,38 +39,26 @@ import arcpy import inspect -try: - # from speckle.speckle_arcgis_new import Speckle - from speckle.converter.layers import getLayers, getAllProjLayers - from speckle.ui.logger import logToUser - from speckle.ui.LogWidget import LogWidget -except: - # from speckle_toolbox.esri.toolboxes.speckle.speckle_arcgis_new import Speckle - from speckle_toolbox.esri.toolboxes.speckle.converter.layers import ( - getLayers, - getAllProjLayers, - ) - from speckle_toolbox.esri.toolboxes.speckle.ui.logger import logToUser - from speckle_toolbox.esri.toolboxes.speckle.ui.LogWidget import LogWidget - -# from ui.validation import tryGetStream - -# Create module-like object -# pytPath = os.path.dirname(os.path.abspath(__file__)).replace("/speckle/ui","/Speckle.pyt") -# print(pytPath) -# pytModule = importlib.machinery.SourceFileLoader("specklePyt", pytPath ) -# specklePyt = pytModule.load_module("specklePyt") - +# from speckle.speckle_arcgis_new import Speckle +from speckle.speckle.converter.layers import getLayersWithStructure, getAllProjLayers +from speckle.speckle.utils.panel_logging import logToUser +from speckle.specklepy_qt_ui.qt_ui.LogWidget import LogWidget +from speckle.specklepy_qt_ui.qt_ui.utils.utils import constructCommitURL # This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer -from specklepy_qt_ui.qt_ui.global_resources import ( + +from speckle.specklepy_qt_ui.qt_ui.utils.global_resources import ( COLOR_HIGHLIGHT, SPECKLE_COLOR, SPECKLE_COLOR_LIGHT, + ICON_OPEN_WEB, + ICON_REPORT, ICON_LOGO, ICON_SEARCH, ICON_DELETE, ICON_DELETE_BLUE, + ICON_PIN_ACTIVE, + ICON_PIN_DISABLED, ICON_SEND, ICON_RECEIVE, ICON_SEND_BLACK, @@ -79,14 +68,26 @@ from specklepy_qt_ui.qt_ui.global_resources import ( COLOR, BACKGR_COLOR, BACKGR_COLOR_LIGHT, + ICON_XXL, + ICON_RASTER, + ICON_POLYGON, + ICON_LINE, + ICON_POINT, + ICON_GENERIC, ) -ui_class = uic.loadUiType( - os.path.join(os.path.dirname(__file__), os.path.join("ui", "mainWindow.ui")) +ui_file = uic.loadUiType( + os.path.join(os.path.dirname(__file__), os.path.join("ui", "mainWindow_main.ui")) +) +ui_file_path = os.path.join( + os.path.dirname(__file__), os.path.join("ui", "mainWindow_main.ui") ) class SpeckleGISDialog(QMainWindow): + + on_top: bool = False + pin_label: QtWidgets.QPushButton closingPlugin = pyqtSignal() streamList: QtWidgets.QComboBox sendModeButton: QtWidgets.QPushButton @@ -98,46 +99,78 @@ class SpeckleGISDialog(QMainWindow): saveLayerSelection: QtWidgets.QPushButton runButton: QtWidgets.QPushButton msgLog: LogWidget = None + custom_crs_modal = None + + signal_1 = pyqtSignal(object) + signal_2 = pyqtSignal(object) + signal_3 = pyqtSignal(object) + signal_4 = pyqtSignal(object) + signal_5 = pyqtSignal(object) + signal_6 = pyqtSignal(object) + signal_remove_btn_url = pyqtSignal(str) + signal_cancel_operation = pyqtSignal() gridLayoutTitleBar = QtWidgets.QGridLayout - def __init__(self): + def __init__(self, parent=None): """Constructor.""" print("START MAIN WINDOW") super(SpeckleGISDialog, self).__init__( - None + parent ) # , QtCore.Qt.WindowStaysOnTopHint) - uic.loadUi(ui_class, self) # Load the .ui file - # self.installEventFilter(self) - self.show() - # self.instances.append(1) + uic.loadUi(ui_file_path, self) # Load the .ui file + # self.show() + self.runAllSetup() + + def runAllSetup(self): try: self.streamBranchDropdown.setMaxCount(100) self.commitDropdown.setMaxCount(100) self.streams_add_button.setFlat(True) self.streams_remove_button.setFlat(True) - self.saveSurveyPoint.setFlat(True) + self.commit_web_view.setFlat(True) + self.reportBtn.setFlat(True) + # self.saveSurveyPoint.setFlat(True) self.saveLayerSelection.setFlat(True) self.reloadButton.setFlat(True) self.closeButton.setFlat(True) + self.commit_web_view.setEnabled(False) - # backgr_color = f"background-color: rgb{str(SPECKLE_COLOR)};" - # backgr_color_light = f"background-color: rgb{str(SPECKLE_COLOR_LIGHT)};" + # https://stackoverflow.com/questions/67585501/pyqt-how-to-use-hover-in-button-stylesheet backgr_image_del = f"border-image: url({ICON_DELETE_BLUE});" self.streams_add_button.setIcon(QIcon(ICON_SEARCH)) self.streams_add_button.setMaximumWidth(25) self.streams_add_button.setStyleSheet( "QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { " - + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + + f"background-color: rgba{str(COLOR_HIGHLIGHT)};" + f"{COLOR}" + " }" ) + + self.commit_web_view.setIcon(QIcon(ICON_OPEN_WEB)) + self.commit_web_view.setMaximumWidth(25) + self.commit_web_view.setStyleSheet( + "QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { " + + f"background-color: rgba{str(COLOR_HIGHLIGHT)};" + + f"{COLOR}" + + " }" + ) + + self.reportBtn.setIcon(QIcon(ICON_REPORT)) + self.reportBtn.setMaximumWidth(25) + self.reportBtn.setStyleSheet( + "QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { " + + f"background-color: rgba{str(COLOR_HIGHLIGHT)};" + + f"{COLOR}" + + " }" + ) + self.streams_remove_button.setIcon(QIcon(ICON_DELETE)) self.streams_remove_button.setMaximumWidth(25) 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"background-color: rgba{str(COLOR_HIGHLIGHT)};" + f"{COLOR}" + " }" ) # + f"{backgr_image_del}" @@ -147,11 +180,7 @@ class SpeckleGISDialog(QMainWindow): + 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}" @@ -165,7 +194,7 @@ class SpeckleGISDialog(QMainWindow): self.sendModeButton.setStyleSheet( "QPushButton {padding: 10px; border: 0px; " - + f"color: rgb{str(SPECKLE_COLOR)};" + + f"color: rgba{str(SPECKLE_COLOR)};" + "} QPushButton:hover { " + "}" ) @@ -175,7 +204,7 @@ class SpeckleGISDialog(QMainWindow): self.receiveModeButton.setStyleSheet( "QPushButton {padding: 10px; border: 0px;}" + "QPushButton:hover { " - + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + + f"background-color: rgba{str(COLOR_HIGHLIGHT)};" + "}" ) self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLACK)) @@ -187,20 +216,45 @@ class SpeckleGISDialog(QMainWindow): + f"{BACKGR_COLOR_LIGHT}" + " }" ) + # self.runButton.setGeometry(0, 0, 150, 30) self.runButton.setMaximumWidth(200) self.runButton.setIcon(QIcon(ICON_SEND)) - # add widgets that will only show on event trigger - logWidget = LogWidget(parent=self) - self.layout().addWidget(logWidget) - self.msgLog = logWidget + # insert checkbox + # l = self.verticalLayout except Exception as e: logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) + def runSetup(self, plugin): + # self.addDataStorage(plugin) + print("run setup") + self.addLabel(plugin) + self.addProps(plugin) + # self.createMappingDialog() + def addProps(self, plugin): - self.msgLog.active_account = plugin.active_account - self.msgLog.speckle_version = plugin.version + try: + # add widgets that will only show on event trigger + logWidget = LogWidget(parent=self) + logWidget.layout.setContentsMargins(10, 60, 10, 40) + logWidget.dataStorage = self.dataStorage + + self.layout().addWidget(logWidget) + self.msgLog = logWidget + self.msgLog.dockwidget = self + + self.msgLog.active_account = plugin.dataStorage.active_account + self.msgLog.speckle_version = plugin.version + + self.crsSettings.setFlat(True) + self.crsSettings.setStyleSheet( + "QPushButton {text-align: right;} QPushButton:hover { " + + f"{COLOR}" + + " }" + ) + except Exception as e: + logToUser(e, level=2, plugin=self) def addLabel(self, plugin): try: @@ -221,9 +275,9 @@ class SpeckleGISDialog(QMainWindow): "text-align: left;" ) text_label.setIcon(exitActIcon) - text_label.setIconSize(QSize(300, 93)) - text_label.setMinimumSize(QSize(100, 40)) - text_label.setMaximumWidth(220) + text_label.setIconSize(QtCore.QSize(300, 93)) + text_label.setMinimumSize(QtCore.QSize(100, 40)) + text_label.setMaximumWidth(250) version = "" try: @@ -232,7 +286,7 @@ class SpeckleGISDialog(QMainWindow): except: pass - version_label = QtWidgets.QPushButton(f"{version}") + version_label = QtWidgets.QPushButton(version) version_label.setStyleSheet( "border: 0px;" "color: white;" @@ -247,15 +301,49 @@ class SpeckleGISDialog(QMainWindow): widget = QWidget() widget.setStyleSheet(f"{BACKGR_COLOR}") - connect_box = QHBoxLayout(widget) - connect_box.addWidget(text_label) # , alignment=Qt.AlignCenter) - connect_box.addWidget(version_label) - connect_box.setContentsMargins(0, 0, 0, 0) + boxLayout = QHBoxLayout(widget) + boxLayout.addWidget(text_label) # , alignment=Qt.AlignCenter) + boxLayout.addWidget(version_label) + boxLayout.setContentsMargins(0, 0, 30, 0) + self.setWindowTitle("SpeckleArcGIS") self.gridLayoutTitleBar.addWidget(widget) # fro QMainWindow - # self.setTitleBarWidget(widget) # for QDockWidget + # self.setTitleBarWidget(widget) # for dockwidget + self.labelWidget = text_label + self.labelWidget.setCursor(QCursor(QtCore.Qt.PointingHandCursor)) + self.labelWidget.clicked.connect(self.onClickLogo) + + pin_label = QtWidgets.QPushButton("") + pin_label.setIcon(QIcon(ICON_PIN_DISABLED)) + pin_label.setMaximumWidth(25) + # pin_label.setFlat(True) + pin_label.setStyleSheet("QPushButton {border: none;}") + boxLayout.addWidget(pin_label) + pin_label.setCursor(QCursor(QtCore.Qt.PointingHandCursor)) + self.pin_label = pin_label + self.pin_label.clicked.connect(self.pinWindow) + except Exception as e: logToUser(e) + def pinWindow(self): + if self.on_top == True: + self.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, False) + self.pin_label.setIcon(QIcon(ICON_PIN_DISABLED)) + self.on_top = False + self.show() + else: + self.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, True) + self.pin_label.setIcon(QIcon(ICON_PIN_ACTIVE)) + self.on_top = True + self.show() + + def addDataStorage(self, plugin): + self.dataStorage = plugin.dataStorage + try: + self.dataStorage.project = plugin.project + except: + self.dataStorage.project = plugin.qproject + def resizeEvent(self, event): try: # print("resize") @@ -271,6 +359,36 @@ class SpeckleGISDialog(QMainWindow): # logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self) return + def clearDropdown(self): + try: + self.streamBranchDropdown.clear() + self.commitDropdown.clear() + except Exception as e: + logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self) + return + + def reloadDialogUI(self, plugin): + try: + self.clearDropdown() + self.populateUI(plugin) + self.enableElements(plugin) + except Exception as e: + logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self) + return + + def run(self, plugin): + try: + print("dockwidget run") + # Setup events on first load only! + self.setupOnFirstLoad(plugin) + # Connect streams section events + self.completeStreamSection(plugin) + # Populate the UI dropdowns + self.populateUI(plugin) + print("dockwidget run end") + except Exception as e: + logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) + def closeEvent(self, event): try: # import threading @@ -286,55 +404,40 @@ class SpeckleGISDialog(QMainWindow): except Exception as e: logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) - def clearDropdown(self): + def addMsg(self, obj: dict): try: - # self.streamIdField.clear() - self.streamBranchDropdown.clear() - self.commitDropdown.clear() - # self.layerSendModeDropdown.clear() - except Exception as e: - logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) - - def reloadDialogUI(self, plugin): - try: - self.clearDropdown() - self.populateUI(plugin) - self.enableElements(plugin) - except Exception as e: - logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) - - def run(self, plugin): - try: - print("dockwidget run") - # Setup events on first load only! - self.setupOnFirstLoad(plugin) - # Connect streams section events - self.completeStreamSection(plugin) - # Populate the UI dropdowns - self.populateUI(plugin) - print("dockwidget run end") + self.msgLog.addButton(obj) + # last_btn, index = self.msgLog.getLastBtn() except Exception as e: logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) def setupOnFirstLoad(self, plugin): try: - self.runButton.clicked.connect(plugin.onRunButtonClicked) + self.msgLog.sendMessage.connect(self.addMsg) + self.reportBtn.clicked.connect(self.msgLog.showReport) self.streams_add_button.clicked.connect(plugin.onStreamAddButtonClicked) + self.commit_web_view.clicked.connect( + lambda: plugin.openUrl( + constructCommitURL( + plugin.active_stream, + plugin.active_branch.id, + plugin.active_commit.id, + ) + ) + ) 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.saveLayerSelection.clicked.connect( - lambda: self.populateLayerDropdown(plugin) - ) + self.sendModeButton.clicked.connect(lambda: self.setSendMode(plugin)) self.layerSendModeDropdown.currentIndexChanged.connect( lambda: self.layerSendModeChange(plugin) ) self.receiveModeButton.clicked.connect(lambda: self.setReceiveMode(plugin)) - self.streamBranchDropdown.currentIndexChanged.connect( - lambda: self.runBtnStatusChanged(plugin) + # self.streamBranchDropdown.currentIndexChanged.connect( lambda: self.runBtnStatusChanged(plugin) ) + self.commitDropdown.currentIndexChanged.connect( + lambda: self.setActiveCommit(plugin) ) self.commitDropdown.currentIndexChanged.connect( lambda: self.runBtnStatusChanged(plugin) @@ -346,12 +449,30 @@ class SpeckleGISDialog(QMainWindow): except Exception as e: logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) + def onClickLogo(self): + import webbrowser + + url = "https://speckle.systems/" + webbrowser.open(url, new=0, autoraise=True) + + try: + metrics.track( + "Connector Action", + self.dataStorage.active_account, + { + "name": "Logo Click", + "connector_version": str(self.dataStorage.plugin_version), + }, + ) + except Exception as e: + print(e) + def refreshClicked(self, plugin): try: try: metrics.track( "Connector Action", - plugin.active_account, + plugin.dataStorage.active_account, {"name": "Refresh", "connector_version": str(plugin.version)}, ) except Exception as e: @@ -369,7 +490,7 @@ class SpeckleGISDialog(QMainWindow): try: metrics.track( "Connector Action", - plugin.active_account, + plugin.dataStorage.active_account, {"name": "Close", "connector_version": str(plugin.version)}, ) except Exception as e: @@ -385,31 +506,32 @@ class SpeckleGISDialog(QMainWindow): def setSendMode(self, plugin): try: plugin.btnAction = 0 # send - color = f"color: rgb{str(SPECKLE_COLOR)};" + color = f"color: rgba{str(SPECKLE_COLOR)};" self.sendModeButton.setStyleSheet( - "border: 0px;" f"color: rgb{str(SPECKLE_COLOR)};" "padding: 10px;" + "border: 0px;" f"color: rgba{str(SPECKLE_COLOR)};" "padding: 10px;" ) self.sendModeButton.setIcon(QIcon(ICON_SEND_BLUE)) self.sendModeButton.setFlat(False) self.receiveModeButton.setFlat(True) self.receiveModeButton.setStyleSheet( "QPushButton {border: 0px; color: black; padding: 10px; } QPushButton:hover { " - + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + + f"background-color: rgba{str(COLOR_HIGHLIGHT)};" + " };" ) self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLACK)) - # self.receiveModeButton.setFlat(True) self.runButton.setProperty("text", " SEND") self.runButton.setIcon(QIcon(ICON_SEND)) # enable sections only if in "saved streams" mode if self.layerSendModeDropdown.currentIndex() == 1: self.layersWidget.setEnabled(True) - if self.layerSendModeDropdown.currentIndex() == 1: - self.saveLayerSelection.setEnabled(True) + self.saveLayerSelection.setEnabled(True) + self.commitLabel.setEnabled(False) self.commitDropdown.setEnabled(False) + self.messageLabel.setEnabled(True) self.messageInput.setEnabled(True) self.layerSendModeDropdown.setEnabled(True) + self.commit_web_view.setEnabled(False) self.runBtnStatusChanged(plugin) return @@ -419,28 +541,30 @@ class SpeckleGISDialog(QMainWindow): def setReceiveMode(self, plugin): try: plugin.btnAction = 1 # receive - color = f"color: rgb{str(SPECKLE_COLOR)};" + color = f"color: rgba{str(SPECKLE_COLOR)};" self.receiveModeButton.setStyleSheet( - "border: 0px;" f"color: rgb{str(SPECKLE_COLOR)};" "padding: 10px;" + "border: 0px;" f"color: rgba{str(SPECKLE_COLOR)};" "padding: 10px;" ) self.sendModeButton.setIcon(QIcon(ICON_SEND_BLACK)) self.sendModeButton.setStyleSheet( "QPushButton {border: 0px; color: black; padding: 10px;} QPushButton:hover { " - + f"background-color: rgb{str(COLOR_HIGHLIGHT)};" + + f"background-color: rgba{str(COLOR_HIGHLIGHT)};" + " };" ) self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLUE)) self.sendModeButton.setFlat(True) self.receiveModeButton.setFlat(False) - # self.sendModeButton.setFlat(True) self.runButton.setProperty("text", " RECEIVE") self.runButton.setIcon(QIcon(ICON_RECEIVE)) - # self.layerSendModeChange(plugin, 1) + self.commitLabel.setEnabled(True) self.commitDropdown.setEnabled(True) + self.layersWidget.setEnabled(False) + self.messageLabel.setEnabled(False) self.messageInput.setEnabled(False) self.saveLayerSelection.setEnabled(False) self.layerSendModeDropdown.setEnabled(False) + self.commit_web_view.setEnabled(True) self.runBtnStatusChanged(plugin) return @@ -448,35 +572,67 @@ class SpeckleGISDialog(QMainWindow): logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) def completeStreamSection(self, plugin): - self.streams_remove_button.clicked.connect( - lambda: self.onStreamRemoveButtonClicked(plugin) - ) - self.streamList.currentIndexChanged.connect( - lambda: self.onActiveStreamChanged(plugin) - ) - self.streamBranchDropdown.currentIndexChanged.connect( - lambda: self.populateActiveCommitDropdown(plugin) - ) return def populateUI(self, plugin): try: self.populateLayerSendModeDropdown() - self.populateLayerDropdown(plugin, False) - # items = [self.layersWidget.item(x).text() for x in range(self.layersWidget.count())] self.populateProjectStreams(plugin) - self.populateSurveyPoint(plugin) - self.runBtnStatusChanged(plugin) - self.runButton.setEnabled(False) + # self.runBtnStatusChanged(plugin) + # self.runButton.setEnabled(False) except Exception as e: logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) + def setActiveCommit(self, plugin): + try: + # print("__setActiveCommit") + # print(plugin.active_commit) + if plugin.active_branch is None: + if ( + plugin.active_stream is not None + and plugin.active_stream[1] is not None + ): + branchName = self.streamBranchDropdown.currentText() + for b in plugin.active_stream[1].branches.items: + if b.name == branchName: + branch = b + plugin.active_branch = b + break + + if plugin.active_branch is None: + return + # print(plugin.active_branch.name) + + current_id_text = str(self.commitDropdown.currentText()).split(" ")[0] + # print(current_id_text) + if current_id_text == "": # populate commits still in progress + return + + if len(plugin.active_branch.commits.items) > 0: + if "Latest" in current_id_text: + plugin.active_commit = plugin.active_branch.commits.items[0] + return + for c in plugin.active_branch.commits.items: + if c.id == current_id_text: + plugin.active_commit = c + return + # only if not found: + plugin.active_commit = plugin.active_branch.commits.items[0] + else: + plugin.active_commit = None + except Exception as e: + plugin.active_commit = None + print(e) + def runBtnStatusChanged(self, plugin): try: commitStr = str(self.commitDropdown.currentText()) branchStr = str(self.streamBranchDropdown.currentText()) + if commitStr == "": # populate commits still in progress + return + if plugin.btnAction == 1: # on receive if commitStr == "": self.runButton.setEnabled(False) @@ -487,15 +643,15 @@ class SpeckleGISDialog(QMainWindow): if branchStr == "": self.runButton.setEnabled(False) elif ( - branchStr != "" - and self.layerSendModeDropdown.currentIndex() == 1 - and len(plugin.current_layers) == 0 + self.layerSendModeDropdown.currentIndex() == 1 + and len(plugin.dataStorage.saved_layers) == 0 ): # saved layers; but the list is empty self.runButton.setEnabled(False) else: self.runButton.setEnabled(True) except Exception as e: - logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) + logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self) + return def layerSendModeChange(self, plugin, runMode=None): try: @@ -504,15 +660,12 @@ class SpeckleGISDialog(QMainWindow): if ( self.layerSendModeDropdown.currentIndex() == 0 or runMode == 1 ): # by manual selection OR receive mode - self.current_layers = [] self.layersWidget.setEnabled(False) - self.saveLayerSelection.setEnabled(False) elif self.layerSendModeDropdown.currentIndex() == 1 and ( runMode == 0 or runMode is None ): # by saved AND when Send mode self.layersWidget.setEnabled(True) - self.saveLayerSelection.setEnabled(True) branchStr = str(self.streamBranchDropdown.currentText()) if self.layerSendModeDropdown.currentIndex() == 0: @@ -522,32 +675,34 @@ class SpeckleGISDialog(QMainWindow): self.runButton.setEnabled(True) # by manual selection elif self.layerSendModeDropdown.currentIndex() == 1: self.runBtnStatusChanged(plugin) # by saved + except Exception as e: logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) - def populateLayerDropdown(self, plugin, bySelection: bool = True): + def populateSavedLayerDropdown(self, plugin, bySelection: bool = True): print("populate layer dropdown / clicked save selection") if not self: return try: - from speckle.ui.project_vars import set_project_layer_selection + from speckle.speckle.utils.project_vars import set_project_layer_selection except: - from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import ( + from speckle_toolbox.esri.toolboxes.speckle.speckle.utils.project_vars import ( set_project_layer_selection, ) try: self.layersWidget.clear() nameDisplay = [] - project = plugin.gis_project + project = plugin.dataStorage.project if bySelection is False: # read from project data print("populate layers from saved data") - # print(project) - # print(project.activeMap) + all_layers = getAllProjLayers(plugin) + if all_layers is None: + return - all_layers_ids = [l.dataSource for l in getAllProjLayers(project)] - for layer_tuple in plugin.current_layers: + all_layers_ids = [l.dataSource for l in all_layers] + for layer_tuple in plugin.dataStorage.saved_layers: if layer_tuple[1].dataSource in all_layers_ids: listItem = self.fillLayerList(layer_tuple[1]) self.layersWidget.addItem(listItem) @@ -556,16 +711,19 @@ class SpeckleGISDialog(QMainWindow): # Fetch selected layers print("populate layers from selection") - plugin.current_layers = [] - layers = getLayers(plugin, bySelection) # List[QgsLayerTreeNode] - print(layers) - for i, layer in enumerate(layers): - plugin.current_layers.append((layer.name, layer)) - listItem = self.fillLayerList(layer) - self.layersWidget.addItem(listItem) - print("populate layers from selection 2") - set_project_layer_selection(plugin) - print("populate layers from selection 3") + plugin.dataStorage.current_layers = [] + layers, structure = getLayersWithStructure( + plugin, bySelection=True + ) # List[QgsLayerTreeNode] + # print(layers) + if layers is not None: + for i, layer in enumerate(layers): + plugin.dataStorage.current_layers.append((layer.name, layer)) + listItem = self.fillLayerList(layer) + self.layersWidget.addItem(listItem) + # print("populate layers from selection 2") + set_project_layer_selection(plugin) + # print("populate layers from selection 3") self.layersWidget.setIconSize(QSize(20, 20)) self.runBtnStatusChanged(plugin) @@ -575,21 +733,9 @@ class SpeckleGISDialog(QMainWindow): logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) def fillLayerList(self, layer): - print("Fill layer list") + # print("Fill layer list") try: - ICON_XXL = os.path.dirname(os.path.abspath(__file__)) + "/size-xxl.png" - ICON_RASTER = ( - os.path.dirname(os.path.abspath(__file__)) + "/legend_raster.png" - ) - ICON_POLYGON = ( - os.path.dirname(os.path.abspath(__file__)) + "/legend_polygon.png" - ) - ICON_LINE = os.path.dirname(os.path.abspath(__file__)) + "/legend_line.png" - ICON_POINT = ( - os.path.dirname(os.path.abspath(__file__)) + "/legend_point.png" - ) - listItem = QListWidgetItem(layer.name) # print(listItem) @@ -614,72 +760,35 @@ class SpeckleGISDialog(QMainWindow): except Exception as e: logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) - 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(str(e), level=2, func=inspect.stack()[0][3], plugin=self) - def enableElements(self, plugin): try: self.sendModeButton.setEnabled(plugin.is_setup) self.receiveModeButton.setEnabled(plugin.is_setup) self.runButton.setEnabled(plugin.is_setup) self.streams_add_button.setEnabled(plugin.is_setup) + self.commit_web_view.setEnabled(plugin.active_commit is not None) + self.reportBtn.setEnabled(False) if plugin.is_setup is False: self.streams_remove_button.setEnabled(plugin.is_setup) self.streamBranchDropdown.setEnabled(plugin.is_setup) self.layerSendModeDropdown.setEnabled(plugin.is_setup) + self.commitLabel.setEnabled(False) self.commitDropdown.setEnabled(False) self.show() + self.setSendMode(plugin) except Exception as e: logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) def populateProjectStreams(self, plugin): - try: - from speckle.ui.project_vars import set_project_streams - except: - from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import ( - set_project_streams, - ) - - try: - if not self: - return - self.streamList.clear() - for stream in plugin.current_streams: - self.streamList.addItems( - [ - f"Stream not accessible - {stream[0].stream_id}" - if stream[1] is None or isinstance(stream[1], SpeckleException) - else f"{stream[1].name}, {stream[1].id} | {stream[0].stream_url.split('/streams')[0]}" - ] - ) - if len(plugin.current_streams) == 0: - self.streamList.addItems([""]) - self.streamList.addItems(["Create New Project"]) - set_project_streams(plugin) - index = self.streamList.currentIndex() - if index == -1: - self.streams_remove_button.setEnabled(False) - else: - self.streams_remove_button.setEnabled(True) - - if len(plugin.current_streams) > 0: - plugin.active_stream = plugin.current_streams[0] - except Exception as e: - logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) + return def onActiveStreamChanged(self, plugin): + if not self: return try: + if not self: + return index = self.streamList.currentIndex() if (len(plugin.current_streams) == 0 and index == 1) or ( len(plugin.current_streams) > 0 and index == len(plugin.current_streams) @@ -719,9 +828,18 @@ class SpeckleGISDialog(QMainWindow): if plugin.active_stream is None: return try: - self.streamBranchDropdown.clear() + active_branch = copy(plugin.active_branch) + active_commit = copy(plugin.active_commit) + keep_branch = True # case of search by URL + if active_branch is None: # case of populating from Saved Streams + keep_branch = False + # print(active_branch) + + # print(1) + self.streamBranchDropdown.clear() # activates "populate commit" + # print(2) if isinstance(plugin.active_stream[1], SpeckleException): - # logger.logToUser("Some streams cannot be accessed", Qgis.Warning) + logToUser("Some streams cannot be accessed", level=1, plugin=self) return elif ( plugin.active_stream is None @@ -729,10 +847,41 @@ class SpeckleGISDialog(QMainWindow): or plugin.active_stream[1].branches is None ): return + # print(3) + # print(plugin.active_branch) + + # here the commit dropdown is triggered self.streamBranchDropdown.addItems( [f"{branch.name}" for branch in plugin.active_stream[1].branches.items] ) - self.streamBranchDropdown.addItems(["Create New Model"]) + # print(4) + self.streamBranchDropdown.addItems(["Create New Branch"]) + # print(5) + if keep_branch is True: + plugin.active_branch = active_branch + if active_commit is not None: + plugin.active_commit = active_commit + elif len(plugin.active_branch.commits.items) > 0: + plugin.active_commit = plugin.active_branch.commits.items[0] + # else: + # plugin.active_commit = plugin.active_branch.commits.items[0] + # print(plugin.active_branch) + + # set index to current (if added from URL) + if ( + plugin.active_branch is not None + and plugin.active_branch in plugin.active_stream[1].branches.items + ): + # print("__________SET BRANCH TEXT") + # print(plugin.active_branch.name) + if keep_branch is True: + plugin.active_branch = active_branch + plugin.active_commit = active_commit + # print(plugin.active_branch.name) + self.streamBranchDropdown.setCurrentText( + plugin.active_branch.name + ) # activates "populate commit" + # print(6) except Exception as e: logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) @@ -740,51 +889,89 @@ class SpeckleGISDialog(QMainWindow): if not self: return try: - self.commitDropdown.clear() if plugin.active_stream is None: + print("Active stream is None") return branchName = self.streamBranchDropdown.currentText() + # print(f"CURRENT BRANCH TEXT: {branchName}") if branchName == "": return - if branchName == "Create New Model": + if branchName == "Create New Branch": self.streamBranchDropdown.setCurrentText("main") plugin.onBranchCreateClicked() return branch = None + + # print("__clear commit dropdwn") + # print(plugin.active_commit) + self.commitDropdown.clear() if isinstance(plugin.active_stream[1], SpeckleException): - # logger.logToUser("Some streams cannot be accessed", Qgis.Warning) + logToUser("Some streams cannot be accessed", level=1, plugin=self) return elif plugin.active_stream[1]: for b in plugin.active_stream[1].branches.items: if b.name == branchName: branch = b + plugin.active_branch = b break - try: - self.commitDropdown.addItems( - [ - f"{commit.id}" + " | " + f"{commit.message}" - for commit in branch.commits.items - ] - ) - except: - pass + + if len(branch.commits.items) > 0: + commits = [] + commits.append("") + # commits.append("Latest commit from this branch") + # self.commitDropdown.addItem("Latest commit from this branch") + + # commits = [] + for commit in branch.commits.items: + sourceApp = ( + str(commit.sourceApplication) + .replace(" ", "") + .split(".")[0] + .split("-")[0] + ) + commits.append( + f"{commit.id}" + + " | " + + f"{sourceApp}" + + " | " + + f"{commit.message}" + ) + self.commitDropdown.addItems(commits) + + # set index to current (if added from URL) + if ( + plugin.active_commit is not None + and plugin.active_commit in branch.commits.items + ): + # print("set index to current (if added from URL) ") + # print(plugin.active_commit) + self.commitDropdown.setCurrentText( + f"{plugin.active_commit.id}" + + " | " + + f"{plugin.active_commit.sourceApplication}" + + " | " + + f"{plugin.active_commit.message}" + ) + else: # overwrite active commit if plugin.active_commit is None: + # print("set index to 1st") + plugin.active_commit = branch.commits.items[0] + else: + plugin.active_commit = None + + self.commitDropdown.setItemText(0, "Latest commit from this branch") + # enable or disable web view button + # print("_________ENABLE OR DISABLE") + # print(plugin.active_commit) + # print(f"CURRENT TEXT2: {self.streamBranchDropdown.currentText()}") + if plugin.active_commit is not None and plugin.btnAction == 1: + self.commit_web_view.setEnabled(True) + else: + self.commit_web_view.setEnabled(False) except Exception as e: logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) def onStreamRemoveButtonClicked(self, plugin): - try: - # from ui.project_vars import set_project_streams - if not self: - return - index = self.streamList.currentIndex() - if len(plugin.current_streams) > 0: - plugin.current_streams.pop(index) - plugin.active_stream = None - self.streamBranchDropdown.clear() - self.commitDropdown.clear() - # self.streamIdField.setText("") + return - # set_project_streams(plugin) - self.populateProjectStreams(plugin) - except Exception as e: - logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self) + def cancelOperations(self): + return diff --git a/qt_ui/ui/add_stream_modal.ui b/qt_ui/ui/add_stream_modal.ui index 3159275..b50dc45 100644 --- a/qt_ui/ui/add_stream_modal.ui +++ b/qt_ui/ui/add_stream_modal.ui @@ -25,7 +25,7 @@ - Search Project by name or URL + Search Stream by name or URL diff --git a/qt_ui/ui/create_branch.ui b/qt_ui/ui/create_branch.ui index 5e5c0e9..992234f 100644 --- a/qt_ui/ui/create_branch.ui +++ b/qt_ui/ui/create_branch.ui @@ -26,7 +26,7 @@ - Model Name + Branch Name diff --git a/qt_ui/ui/create_stream.ui b/qt_ui/ui/create_stream.ui index 98c194c..b0ae678 100644 --- a/qt_ui/ui/create_stream.ui +++ b/qt_ui/ui/create_stream.ui @@ -34,7 +34,7 @@ - Project Name + Stream Name diff --git a/qt_ui/ui/dockwidget_main.ui b/qt_ui/ui/dockwidget_main.ui index ffc76df..871efc3 100644 --- a/qt_ui/ui/dockwidget_main.ui +++ b/qt_ui/ui/dockwidget_main.ui @@ -39,7 +39,7 @@ - Project + Stream @@ -77,7 +77,7 @@ - Model + Branch @@ -87,7 +87,7 @@ - Version + Commit diff --git a/qt_ui/ui/mainWindow_main.ui b/qt_ui/ui/mainWindow_main.ui index 6a75e36..d4b38b2 100644 --- a/qt_ui/ui/mainWindow_main.ui +++ b/qt_ui/ui/mainWindow_main.ui @@ -1,7 +1,7 @@ - SpeckleQArcGISDialog - + SpeckleGISDialog + 0 @@ -43,7 +43,7 @@ - Project + Stream @@ -83,7 +83,7 @@ - Model + Branch @@ -93,14 +93,26 @@ - Version + Commit - - + + + + + + + + + + + + + + @@ -126,6 +138,15 @@ + + + + + true + + + + @@ -178,21 +199,49 @@ true - Set visible layers as selection + Save visible layers as selection - + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + + + + + true + + + Set project center on Send/Receive + + + + + + + + Message - + Sent XXX objects from ArcGIS @@ -202,7 +251,7 @@ - + @@ -236,46 +285,8 @@ - - - - - Lat, Lon - - - - - - - - - 0.0 - - - - - - - 0.0 - - - - - - - - true - - - Set as a project center - - - - - - - + diff --git a/qt_ui/assets/__init__.py b/qt_ui/utils/__init__.py similarity index 100% rename from qt_ui/assets/__init__.py rename to qt_ui/utils/__init__.py diff --git a/qt_ui/utils/assets/__init__.py b/qt_ui/utils/assets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/qt_ui/assets/chart-line.png b/qt_ui/utils/assets/chart-line.png similarity index 100% rename from qt_ui/assets/chart-line.png rename to qt_ui/utils/assets/chart-line.png diff --git a/qt_ui/assets/cube-receive-black.png b/qt_ui/utils/assets/cube-receive-black.png similarity index 100% rename from qt_ui/assets/cube-receive-black.png rename to qt_ui/utils/assets/cube-receive-black.png diff --git a/qt_ui/assets/cube-receive-blue.png b/qt_ui/utils/assets/cube-receive-blue.png similarity index 100% rename from qt_ui/assets/cube-receive-blue.png rename to qt_ui/utils/assets/cube-receive-blue.png diff --git a/qt_ui/assets/cube-receive.png b/qt_ui/utils/assets/cube-receive.png similarity index 100% rename from qt_ui/assets/cube-receive.png rename to qt_ui/utils/assets/cube-receive.png diff --git a/qt_ui/assets/cube-send-black.png b/qt_ui/utils/assets/cube-send-black.png similarity index 100% rename from qt_ui/assets/cube-send-black.png rename to qt_ui/utils/assets/cube-send-black.png diff --git a/qt_ui/assets/cube-send-blue.png b/qt_ui/utils/assets/cube-send-blue.png similarity index 100% rename from qt_ui/assets/cube-send-blue.png rename to qt_ui/utils/assets/cube-send-blue.png diff --git a/qt_ui/assets/cube-send.png b/qt_ui/utils/assets/cube-send.png similarity index 100% rename from qt_ui/assets/cube-send.png rename to qt_ui/utils/assets/cube-send.png diff --git a/qt_ui/assets/delete-blue.png b/qt_ui/utils/assets/delete-blue.png similarity index 100% rename from qt_ui/assets/delete-blue.png rename to qt_ui/utils/assets/delete-blue.png diff --git a/qt_ui/assets/delete.png b/qt_ui/utils/assets/delete.png similarity index 100% rename from qt_ui/assets/delete.png rename to qt_ui/utils/assets/delete.png diff --git a/qt_ui/assets/legend_generic.png b/qt_ui/utils/assets/legend_generic.png similarity index 100% rename from qt_ui/assets/legend_generic.png rename to qt_ui/utils/assets/legend_generic.png diff --git a/qt_ui/assets/legend_line.png b/qt_ui/utils/assets/legend_line.png similarity index 100% rename from qt_ui/assets/legend_line.png rename to qt_ui/utils/assets/legend_line.png diff --git a/qt_ui/assets/legend_point.png b/qt_ui/utils/assets/legend_point.png similarity index 100% rename from qt_ui/assets/legend_point.png rename to qt_ui/utils/assets/legend_point.png diff --git a/qt_ui/assets/legend_polygon.png b/qt_ui/utils/assets/legend_polygon.png similarity index 100% rename from qt_ui/assets/legend_polygon.png rename to qt_ui/utils/assets/legend_polygon.png diff --git a/qt_ui/assets/legend_raster.png b/qt_ui/utils/assets/legend_raster.png similarity index 100% rename from qt_ui/assets/legend_raster.png rename to qt_ui/utils/assets/legend_raster.png diff --git a/qt_ui/assets/logo-slab-white@0.5x.png b/qt_ui/utils/assets/logo-slab-white@0.5x.png similarity index 100% rename from qt_ui/assets/logo-slab-white@0.5x.png rename to qt_ui/utils/assets/logo-slab-white@0.5x.png diff --git a/qt_ui/assets/magnify.png b/qt_ui/utils/assets/magnify.png similarity index 100% rename from qt_ui/assets/magnify.png rename to qt_ui/utils/assets/magnify.png diff --git a/qt_ui/assets/open-in-new.png b/qt_ui/utils/assets/open-in-new.png similarity index 100% rename from qt_ui/assets/open-in-new.png rename to qt_ui/utils/assets/open-in-new.png diff --git a/qt_ui/utils/assets/pin-outline.png b/qt_ui/utils/assets/pin-outline.png new file mode 100644 index 0000000..682d64a Binary files /dev/null and b/qt_ui/utils/assets/pin-outline.png differ diff --git a/qt_ui/utils/assets/pin.png b/qt_ui/utils/assets/pin.png new file mode 100644 index 0000000..94395f0 Binary files /dev/null and b/qt_ui/utils/assets/pin.png differ diff --git a/qt_ui/assets/size-xxl.png b/qt_ui/utils/assets/size-xxl.png similarity index 100% rename from qt_ui/assets/size-xxl.png rename to qt_ui/utils/assets/size-xxl.png diff --git a/qt_ui/global_resources.py b/qt_ui/utils/global_resources.py similarity index 92% rename from qt_ui/global_resources.py rename to qt_ui/utils/global_resources.py index 52d60a9..54511f4 100644 --- a/qt_ui/global_resources.py +++ b/qt_ui/utils/global_resources.py @@ -81,3 +81,10 @@ ICON_POINT = os.path.join( ICON_GENERIC = os.path.join( os.path.dirname(os.path.abspath(__file__)), "assets", "legend_generic.png" ) +ICON_PIN_ACTIVE = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "assets", "pin.png" +) +ICON_PIN_DISABLED = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "assets", "pin-outline.png" +) + diff --git a/qt_ui/logger.py b/qt_ui/utils/logger.py similarity index 93% rename from qt_ui/logger.py rename to qt_ui/utils/logger.py index 95154f2..dd74e1c 100644 --- a/qt_ui/logger.py +++ b/qt_ui/utils/logger.py @@ -1,4 +1,7 @@ -from specklepy_qt_ui.qt_ui.utils import splitTextIntoLines +try: + from specklepy_qt_ui.qt_ui.utils.utils import splitTextIntoLines +except ModuleNotFoundError: + from speckle.specklepy_qt_ui.qt_ui.utils.utils import splitTextIntoLines def logToUser( diff --git a/qt_ui/utils.py b/qt_ui/utils/utils.py similarity index 99% rename from qt_ui/utils.py rename to qt_ui/utils/utils.py index 8cc73b6..41be9b1 100644 --- a/qt_ui/utils.py +++ b/qt_ui/utils/utils.py @@ -2,6 +2,8 @@ from textwrap import wrap from typing import Union import requests +SYMBOL = "_x_x_" + def splitTextIntoLines(text: str = "", number: int = 40) -> str: msg = "" diff --git a/qt_ui/widget_add_stream.py b/qt_ui/widget_add_stream.py index 26be9b2..8f5425d 100644 --- a/qt_ui/widget_add_stream.py +++ b/qt_ui/widget_add_stream.py @@ -2,9 +2,14 @@ import inspect import os from typing import List, Union import urllib.parse - -from specklepy_qt_ui.qt_ui.DataStorage import DataStorage -from specklepy_qt_ui.qt_ui.logger import logToUser +try: + from specklepy_qt_ui.qt_ui.DataStorage import DataStorage + from specklepy_qt_ui.qt_ui.utils.logger import logToUser + from specklepy_qt_ui.qt_ui.utils.utils import constructCommitURLfromServerCommit +except ModuleNotFoundError: + from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage + from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser + from speckle.specklepy_qt_ui.qt_ui.utils.utils import constructCommitURLfromServerCommit from PyQt5 import QtWidgets, uic, QtCore from PyQt5.QtCore import pyqtSignal @@ -16,7 +21,6 @@ from specklepy.core.api.credentials import get_local_accounts # , StreamWrapper from specklepy.core.api.wrapper import StreamWrapper from specklepy.logging import metrics -from specklepy_qt_ui.qt_ui.utils import constructCommitURLfromServerCommit # This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer FORM_CLASS, _ = uic.loadUiType( @@ -46,7 +50,7 @@ class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS): ) self.speckle_client = speckle_client self.setupUi(self) - self.setWindowTitle("Add Speckle Project") + self.setWindowTitle("Add Speckle stream") self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False) @@ -89,7 +93,7 @@ class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS): "Connector Action", self.dataStorage.active_account, { - "name": "Project Search By Name", + "name": "Stream Search By Name", "connector_version": str(self.dataStorage.plugin_version), }, ) @@ -149,7 +153,7 @@ class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS): "Connector Action", self.dataStorage.active_account, { - "name": "Project Search By URL", + "name": "Stream Search By URL", "connector_version": str(self.dataStorage.plugin_version), }, ) @@ -163,7 +167,7 @@ class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS): "Connector Action", self.dataStorage.active_account, { - "name": "Project Search By Name", + "name": "Stream Search By Name", "connector_version": str(self.dataStorage.plugin_version), }, ) @@ -191,7 +195,7 @@ class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS): self.search_results_list.clear() if isinstance(self.stream_results, SpeckleException): logToUser( - "Some Projects cannot be accessed", + "Some streams cannot be accessed", level=1, func=inspect.stack()[0][3], ) @@ -205,7 +209,7 @@ class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS): if isinstance(stream, SpeckleException): logToUser( - "Some Projects cannot be accessed", + "Some streams cannot be accessed", level=1, func=inspect.stack()[0][3], ) @@ -223,7 +227,7 @@ class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS): try: if isinstance(self.stream_results, SpeckleException): logToUser( - "Selected Project cannot be accessed: " + "Selected stream cannot be accessed: " + str(self.stream_results.message), level=1, func=inspect.stack()[0][3], @@ -261,7 +265,7 @@ class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS): self.close() except Exception as e: logToUser( - "Some Projects cannot be accessed: " + str(e), + "Some streams cannot be accessed: " + str(e), level=1, func=inspect.stack()[0][3], ) diff --git a/qt_ui/widget_create_branch.py b/qt_ui/widget_create_branch.py index a816dc3..687387a 100644 --- a/qt_ui/widget_create_branch.py +++ b/qt_ui/widget_create_branch.py @@ -1,7 +1,11 @@ import inspect import os from typing import List, Tuple, Union -from specklepy_qt_ui.qt_ui.logger import logToUser + +try: + from specklepy_qt_ui.qt_ui.utils.logger import logToUser +except ModuleNotFoundError: + from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser from PyQt5 import QtWidgets, uic, QtCore from PyQt5.QtCore import pyqtSignal @@ -11,50 +15,39 @@ from specklepy.core.api.client import SpeckleClient # 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", "create_branch.ui")) + os.path.join(os.path.join(os.path.dirname(__file__), "ui", "create_branch.ui") ) ) - class CreateBranchModalDialog(QtWidgets.QWidget, FORM_CLASS): + name_field: QtWidgets.QLineEdit = None description_field: QtWidgets.QLineEdit = None dialog_button_box: QtWidgets.QDialogButtonBox = None speckle_client: Union[SpeckleClient, None] = None - # Events - handleBranchCreate = pyqtSignal(str, str) + #Events + handleBranchCreate = pyqtSignal(str,str) def __init__(self, parent=None, speckle_client: SpeckleClient = None): - super(CreateBranchModalDialog, self).__init__( - parent, QtCore.Qt.WindowStaysOnTopHint - ) + super(CreateBranchModalDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint) self.speckle_client = speckle_client self.setupUi(self) - self.setWindowTitle("Create New Model") - self.setMinimumWidth(300) + self.setWindowTitle("Create New Branch") self.name_field.textChanged.connect(self.nameCheck) - self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False) - self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect( - self.onOkClicked - ) - self.dialog_button_box.button( - QtWidgets.QDialogButtonBox.Cancel - ).clicked.connect(self.onCancelClicked) + self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False) + self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.onOkClicked) + self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.onCancelClicked) def nameCheck(self): try: if len(self.name_field.text()) >= 3: - self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled( - True - ) - else: - self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled( - False - ) + self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True) + else: + self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False) return except Exception as e: - logToUser(e, level=2, func=inspect.stack()[0][3]) + logToUser(e, level = 2, func = inspect.stack()[0][3]) return def onOkClicked(self): @@ -64,17 +57,16 @@ class CreateBranchModalDialog(QtWidgets.QWidget, FORM_CLASS): self.handleBranchCreate.emit(name, description) self.close() except Exception as e: - logToUser(e, level=2, func=inspect.stack()[0][3]) + 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]) + logToUser(e, level = 2, func = inspect.stack()[0][3]) return - - r""" + r''' def onAccountSelected(self, index): try: account = self.speckle_accounts[index] @@ -83,4 +75,4 @@ class CreateBranchModalDialog(QtWidgets.QWidget, FORM_CLASS): except Exception as e: logToUser(e, level = 2, func = inspect.stack()[0][3]) return - """ + ''' \ No newline at end of file diff --git a/qt_ui/widget_create_stream.py b/qt_ui/widget_create_stream.py index 0810e15..66bc48b 100644 --- a/qt_ui/widget_create_stream.py +++ b/qt_ui/widget_create_stream.py @@ -1,7 +1,11 @@ import inspect import os from typing import List, Tuple, Union -from specklepy_qt_ui.qt_ui.logger import logToUser + +try: + from specklepy_qt_ui.qt_ui.utils.logger import logToUser +except ModuleNotFoundError: + from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser from PyQt5 import QtWidgets, uic, QtCore from PyQt5.QtCore import pyqtSignal @@ -37,7 +41,7 @@ class CreateStreamModalDialog(QtWidgets.QWidget, FORM_CLASS): ) self.speckle_client = speckle_client self.setupUi(self) - self.setWindowTitle("Create New Project") + self.setWindowTitle("Create New Stream") self.name_field.textChanged.connect(self.nameCheck) self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True) diff --git a/qt_ui/widget_custom_crs.py b/qt_ui/widget_custom_crs.py index 85aa728..99013ba 100644 --- a/qt_ui/widget_custom_crs.py +++ b/qt_ui/widget_custom_crs.py @@ -1,80 +1,91 @@ import inspect import os from typing import List, Tuple, Union -from specklepy_qt_ui.qt_ui.DataStorage import DataStorage -from specklepy_qt_ui.qt_ui.logger import logToUser + +try: + from specklepy_qt_ui.qt_ui.DataStorage import DataStorage + from specklepy_qt_ui.qt_ui.utils.logger import logToUser + from specklepy_qt_ui.qt_ui.utils.global_resources import COLOR +except ModuleNotFoundError: + from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage + from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser + from speckle.specklepy_qt_ui.qt_ui.utils.global_resources import COLOR from PyQt5 import QtWidgets, uic, QtCore -from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget +from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget from PyQt5.QtCore import pyqtSignal from specklepy.core.api.client import SpeckleClient -from specklepy_qt_ui.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") ) + os.path.join(os.path.join(os.path.dirname(__file__), "ui", "custom_crs.ui")) ) -class CustomCRSDialog(QtWidgets.QWidget, FORM_CLASS): +class CustomCRSDialog(QtWidgets.QWidget, FORM_CLASS): name_field: QtWidgets.QLineEdit = None - description_field: QtWidgets.QLineEdit = None + description: QtWidgets.QLineEdit = None dialog_button_box: QtWidgets.QDialogButtonBox = None saveSurveyPoint: QtWidgets.QPushButton = None speckle_client: Union[SpeckleClient, None] = None - dataStorage: DataStorage = None + dataStorage: DataStorage = None - #Events - #handleCRSCreate = pyqtSignal(str,str) + # Events + # handleCRSCreate = pyqtSignal(str,str) def __init__(self, parent=None): - - super(CustomCRSDialog,self).__init__(parent, QtCore.Qt.WindowStaysOnTopHint) + super(CustomCRSDialog, self).__init__(parent, QtCore.Qt.WindowStaysOnTopHint) self.setupUi(self) - self.setWindowTitle("Set project center on Send/Receive") - - #self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Close).clicked.connect(self.onCancelClicked) - self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).setText("More Info") + self.setWindowTitle("Set project center on Send/Receive") + + # self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Close).clicked.connect(self.onCancelClicked) + self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).setText( + "More Info" + ) self.modeDropdown.currentIndexChanged.connect(self.onModeChanged) - + def onModeChanged(self): try: - if not self: return + if not self: + return index = self.modeDropdown.currentIndex() - if index == 1: # custom crs + if index == 1: # custom crs self.surveyPointLat.show() self.surveyPointLon.show() self.degreeSignX.show() self.degreeSignY.show() self.label_survey.show() - + self.offsetX.hide() self.offsetY.hide() self.label_offsets.hide() self.offsetXDegreeSign.hide() self.offsetYDegreeSign.hide() - self.description.setText("Use this option when you don't have to use a specific CRS.\ + self.description.setText( + "Use this option when you don't have to use a specific CRS.\ \n\nThis will change your Project CRS to a new custom CRS.\ - \n\nHint: right-click on the canvas -> Copy Coordinate -> EPSG:4326. ") + \n\nHint: right-click on the canvas -> Copy Coordinate -> EPSG:4326. " + ) - elif index == 0: # offsets + elif index == 0: # offsets self.surveyPointLat.hide() self.surveyPointLon.hide() self.degreeSignX.hide() self.degreeSignY.hide() self.label_survey.hide() - + self.offsetX.show() self.offsetY.show() self.label_offsets.show() - #if self.dataStorage.currentOriginalUnits == 'degrees': + # if self.dataStorage.currentOriginalUnits == 'degrees': self.offsetXDegreeSign.show() self.offsetYDegreeSign.show() - + units = self.dataStorage.currentOriginalUnits - if units == 'degrees': + print(units) + if units == "degrees": self.offsetXDegreeSign.setText("°") self.offsetYDegreeSign.setText("°") elif units is not None: @@ -83,49 +94,64 @@ class CustomCRSDialog(QtWidgets.QWidget, FORM_CLASS): else: self.offsetXDegreeSign.hide() self.offsetYDegreeSign.hide() - + + try: + authid = self.dataStorage.currentCRS.authid() + except: + try: + authid = self.dataStorage.currentCRS.name + except: + authid = str(self.dataStorage.currentCRS) + text = f"Use this option when your project requires a use of a specific CRS. \ \n\nThis will only affect Speckle data properties, not your Project CRS.\ - \n\nHint: your current project CRS is '{self.dataStorage.currentCRS.authid()}' and using units '{self.dataStorage.currentOriginalUnits}'." + \n\nHint: your current project CRS is '{authid}' and using units '{self.dataStorage.currentOriginalUnits}'." - if units == 'degrees': + if units == "degrees": text += "\nThis CRS is not recommended if data was sent or needs to be \ \nreceived in a non-GIS application." - + self.description.setText(text) - + self.populateSurveyPoint() self.populateOffsets() self.populateRotation() except Exception as e: - logToUser(e, level = 2, func = inspect.stack()[0][3]) + logToUser(e, level=2, func=inspect.stack()[0][3]) return - + def populateModeDropdown(self): - if not self: return + if not self: + return try: self.modeDropdown.clear() self.modeDropdown.addItems( - ["Add offsets / rotation to the current Project CRS", "Create custom CRS"] + [ + "Add offsets / rotation to the current Project CRS", + "Create custom CRS", + ] ) except Exception as e: - logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self) + 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: + 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) + logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self) return - + def populateRotation(self): if not self: return @@ -134,19 +160,22 @@ class CustomCRSDialog(QtWidgets.QWidget, FORM_CLASS): if self.dataStorage.crs_rotation is not None: self.rotation.setText(str(self.dataStorage.crs_rotation)) except Exception as e: - logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self) + logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self) return def populateOffsets(self): try: self.offsetX.clear() self.offsetY.clear() - if self.dataStorage.crs_offset_x is not None and self.dataStorage.crs_offset_y is not None: + if ( + self.dataStorage.crs_offset_x is not None + and self.dataStorage.crs_offset_y is not None + ): self.offsetX.setText(str(self.dataStorage.crs_offset_x)) self.offsetY.setText(str(self.dataStorage.crs_offset_y)) - + except Exception as e: - logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self) + logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self) return def onOkClicked(self): @@ -154,5 +183,5 @@ class CustomCRSDialog(QtWidgets.QWidget, FORM_CLASS): try: self.close() except Exception as e: - logToUser(e, level = 2, func = inspect.stack()[0][3]) + logToUser(e, level=2, func=inspect.stack()[0][3]) return diff --git a/qt_ui/widget_dependencies_upgrade.py b/qt_ui/widget_dependencies_upgrade.py index 2b7d895..11411e2 100644 --- a/qt_ui/widget_dependencies_upgrade.py +++ b/qt_ui/widget_dependencies_upgrade.py @@ -3,7 +3,11 @@ import urllib3 import requests import requests_toolbelt from specklepy.logging import metrics -from specklepy_qt_ui.qt_ui.DataStorage import DataStorage + +try: + from specklepy_qt_ui.qt_ui.DataStorage import DataStorage +except ModuleNotFoundError: + from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage from PyQt5 import QtWidgets, uic, QtCore @@ -87,7 +91,11 @@ To do it manually, you can run 2 following commands from QGIS Plugins panel->Pyt def runSubprocess(self): import subprocess - from speckle.utils.utils import get_qgis_python_path as path + + try: + from speckle.utils.utils import get_qgis_python_path as path + except ModuleNotFoundError: + from speckle.speckle.utils.utils import get_qgis_python_path as path result1 = subprocess.run( [path(), "-m", "pip", "install", "requests==2.31.0"], diff --git a/qt_ui/widget_report.py b/qt_ui/widget_report.py index e18901a..4a5337f 100644 --- a/qt_ui/widget_report.py +++ b/qt_ui/widget_report.py @@ -1,70 +1,83 @@ import inspect import os from typing import List, Tuple, Union -from specklepy_qt_ui.qt_ui.DataStorage import DataStorage -#from specklepy_qt_ui.qt_ui.logger import logToUser + +try: + from specklepy_qt_ui.qt_ui.DataStorage import DataStorage + from specklepy_qt_ui.qt_ui.utils.global_resources import COLOR + from specklepy_qt_ui.qt_ui.utils.utils import SYMBOL +except ModuleNotFoundError: + from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage + from speckle.specklepy_qt_ui.qt_ui.utils.global_resources import COLOR + from speckle.specklepy_qt_ui.qt_ui.utils.utils import SYMBOL + +# from specklepy_qt_ui.qt_ui.utils.logger import logToUser from PyQt5 import QtWidgets, uic, QtCore -from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget +from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget from PyQt5.QtCore import pyqtSignal from specklepy.core.api.client import SpeckleClient -from specklepy_qt_ui.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", "report.ui") ) + os.path.join(os.path.join(os.path.dirname(__file__), "ui", "report.ui")) ) -class ReportDialog(QtWidgets.QWidget, FORM_CLASS): +class ReportDialog(QtWidgets.QWidget, FORM_CLASS): report_label: QtWidgets.QLabel = None report_text: QtWidgets.QTextEdit = None dataStorage: DataStorage = None def __init__(self, parent=None): - - super(ReportDialog,self).__init__(parent, QtCore.Qt.WindowStaysOnTopHint) + super(ReportDialog, self).__init__(parent, QtCore.Qt.WindowStaysOnTopHint) self.setupUi(self) self.runAllSetup() - + def runAllSetup(self): - self.setWindowTitle("Report (Speckle)") + self.setWindowTitle("Report (Speckle)") self.setMinimumWidth(500) self.setMinimumHeight(600) self.report_label.setWordWrap(True) - self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.onOkClicked) + self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect( + self.onOkClicked + ) return - #self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).setText("More Info") - + # self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).setText("More Info") + def assembleReport(self): try: - if self.dataStorage is None: return - reportList = self.dataStorage.latestActionReport - if reportList is None: return + if self.dataStorage is None: + return + reportList: List[dict] = self.dataStorage.latestActionReport + if reportList is None: + return operation = "" total_layers = 0 total_objects = 0 text = "" sending = True - - # details + + # details last_report = "" last_report += "Details:" + "\n" for item in reportList: line = "✅ " - try: # if sending - line += f'{item["feature_id"]}: {item["obj_type"]}' + try: # if sending + some_id = item["feature_id"].replace(SYMBOL, "\\") + line += f'{some_id}: {item["obj_type"]}' operation = f"Sent at {self.dataStorage.latestActionTime}" - except: # if receiving + except: # if receiving sending = False - line += f'{item["speckle_id"]}: {item["obj_type"]}' + some_id = item[list(item.keys())[0]].replace(SYMBOL, "\\") + line += f'{some_id}: {item["obj_type"]}' # f'{item["speckle_id"]}: {item["obj_type"]}' operation = f"Received at {self.dataStorage.latestActionTime}" - - # edit based on the type + + # edit based on the type if "Layer" in item["obj_type"]: total_layers += 1 if item["errors"] != "": @@ -75,61 +88,84 @@ class ReportDialog(QtWidgets.QWidget, FORM_CLASS): if item["errors"] != "": line += f', errors: {item["errors"]}' line = "❌ " + line[1:] - else: total_objects += 1 + else: + total_objects += 1 line = "__ " + line last_report += line + "\n" text += f"Operation: {operation}\n" text += f"Total: {total_layers} layer{'' if str(total_layers).endswith('1') else 's'}, {total_objects} feature{'' if str(total_objects).endswith('1') else 's'}\n\n" - if sending is False: + if sending is False: try: text += f"Host application: {self.dataStorage.latestHostApp}\n\n" - except: pass + except: + pass # layers and transformations (if applicable) text += "Layers and transformations (if applicable):" + "\n" for i, layer in enumerate(self.dataStorage.latestActionLayers): - #print(self.dataStorage.latestActionLayers) - name = layer #if isinstance(layer, str) else layer.name() - try: + # print(self.dataStorage.latestActionLayers) + name = layer # if isinstance(layer, str) else layer.name() + try: transformExists = 0 for item in self.dataStorage.savedTransforms: - layer_name = item.split(" -> ")[0].split(" (\'")[0] + layer_name = item.split(" -> ")[0].split(" ('")[0] transform_name = item.split(" -> ")[1] if layer_name == name: - text += f"{i+1}. {layer_name} -> '{transform_name}'" + "\n" + text += ( + f"{i+1}. {layer_name} -> '{transform_name}'" + "\n" + ) transformExists += 1 - break - if transformExists==0: + break + if transformExists == 0: text += f"{i+1}. {name} \n" - except Exception as e: print(e) - text += "\n" + except Exception as e: + print(e) + text += "\n" # add info about the offsets - text += "Project CRS: " + self.dataStorage.project.crs().authid() + "\n" + try: + crs = self.dataStorage.project.crs() + text += "Project CRS: " + crs.authid() + "\n" + crs_keyword = "CRS" + except AttributeError: + crs = self.dataStorage.project.activeMap.spatialReference + crs_keyword = "Spatial Reference" + text += f"Project {crs_keyword}: " + crs.name + "\n" units = self.dataStorage.latestActionUnits - text += "Project CRS units: " + units + f"{' (not supported, treated as Meters)' if 'degrees' in units else ''}" + "\n" - text += "Project CRS WKT: \n" + self.dataStorage.project.crs().toWkt() + "\n\n" - text += f"CRS offsets: x={self.dataStorage.crs_offset_x}, y={self.dataStorage.crs_offset_y}" + "\n" - text += f"CRS rotation: {self.dataStorage.crs_rotation}°" + "\n\n" + text += ( + f"Project {crs_keyword} units: " + + units + + f"{' (not supported, treated as Meters)' if 'degrees' in units else ''}" + + "\n" + ) + try: + text += f"Project {crs_keyword} WKT: \n" + crs.toWkt() + "\n\n" + except: + text += f"Project {crs_keyword} WKT: \n" + crs.exportToString() + "\n\n" + + text += ( + f"{crs_keyword} offsets: x={self.dataStorage.crs_offset_x}, y={self.dataStorage.crs_offset_y}" + + "\n" + ) + text += f"{crs_keyword} rotation: {self.dataStorage.crs_rotation}°" + "\n\n" text += last_report - return operation, total_layers, total_objects, text + return operation, total_layers, total_objects, text except Exception as e: print(e) - return + return - def applyReport(self): + def applyReport(self): result = self.assembleReport() if result is None: print("no report generated") - return + return operation, total_layers, total_objects, report = result - #self.report_label.setText(f"Operation: {operation}\nTotal: {total_layers} layer{'' if str(total_layers).endswith('1') else 's'}, {total_objects} feature{'' if str(total_objects).endswith('1') else 's'}") + # self.report_label.setText(f"Operation: {operation}\nTotal: {total_layers} layer{'' if str(total_layers).endswith('1') else 's'}, {total_objects} feature{'' if str(total_objects).endswith('1') else 's'}") self.report_text.setText(report) - + def onOkClicked(self): self.close() - \ No newline at end of file diff --git a/qt_ui/widget_transforms.py b/qt_ui/widget_transforms.py index d9a0fb9..2c20e7c 100644 --- a/qt_ui/widget_transforms.py +++ b/qt_ui/widget_transforms.py @@ -1,6 +1,10 @@ import os -from specklepy_qt_ui.qt_ui.DataStorage import DataStorage + +try: + from specklepy_qt_ui.qt_ui.DataStorage import DataStorage +except ModuleNotFoundError: + from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage from PyQt5 import QtWidgets, uic, QtCore