diff --git a/qt_ui/dockwidget_main.py b/qt_ui/dockwidget_main.py
index 014d788..f91df6c 100644
--- a/qt_ui/dockwidget_main.py
+++ b/qt_ui/dockwidget_main.py
@@ -3,6 +3,7 @@
from copy import copy
import inspect
import os
+from specklepy_qt_ui.qt_ui.widget_dashboard import SpeckleDashboard
from specklepy_qt_ui.qt_ui.widget_transforms import MappingSendDialog
from specklepy_qt_ui.qt_ui.LogWidget import LogWidget
@@ -10,33 +11,49 @@ 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,
+ 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.exceptions import SpeckleException, GraphQLException
from specklepy.logging import metrics
from PyQt5 import QtWidgets, uic
from PyQt5.QtGui import QIcon, QPixmap, QCursor
-from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget
+from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget
from PyQt5 import QtCore
-from PyQt5.QtCore import pyqtSignal
+from PyQt5.QtCore import pyqtSignal, Qt
# 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.dirname(__file__), os.path.join("ui", "dockwidget_main.ui") )
+ os.path.join(os.path.dirname(__file__), os.path.join("ui", "dockwidget_main.ui"))
)
-class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
+class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
closingPlugin = pyqtSignal()
streamList: QtWidgets.QComboBox
sendModeButton: QtWidgets.QPushButton
@@ -51,8 +68,10 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
experimental: QCheckBox
msgLog: LogWidget = None
dataStorage: DataStorage = None
- mappingSendDialog = None
- custom_crs_modal = None
+ mappingSendDialog = None
+ custom_crs_modal = None
+ dashboard = None
+ iface = None
signal_1 = pyqtSignal(object)
signal_2 = pyqtSignal(object)
@@ -81,7 +100,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
self.streams_remove_button.setFlat(True)
self.commit_web_view.setFlat(True)
self.reportBtn.setFlat(True)
- #self.saveSurveyPoint.setFlat(True)
+ # self.saveSurveyPoint.setFlat(True)
self.saveLayerSelection.setFlat(True)
self.reloadButton.setFlat(True)
self.closeButton.setFlat(True)
@@ -91,76 +110,118 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
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: rgba{str(COLOR_HIGHLIGHT)};" + f"{COLOR}" + " }")
-
+ self.streams_add_button.setStyleSheet(
+ "QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { "
+ + 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.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.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: rgba{str(COLOR_HIGHLIGHT)};" + f"{COLOR}" + " }") #+ f"{backgr_image_del}"
+ self.streams_remove_button.setStyleSheet(
+ "QPushButton {padding:3px;padding-left:5px;border: none; text-align: left; image-position:right} QPushButton:hover { "
+ + f"background-color: rgba{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.reloadButton.setStyleSheet("QPushButton {text-align: left;} QPushButton:hover { " + f"{COLOR}" + " }")
- self.closeButton.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }")
+ self.saveLayerSelection.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}" + " }"
+ )
-
- self.sendModeButton.setStyleSheet("QPushButton {padding: 10px; border: 0px; " + f"color: rgba{str(SPECKLE_COLOR)};"+ "} QPushButton:hover { " + "}" )
+ self.sendModeButton.setStyleSheet(
+ "QPushButton {padding: 10px; border: 0px; "
+ + f"color: rgba{str(SPECKLE_COLOR)};"
+ + "} QPushButton:hover { "
+ + "}"
+ )
self.sendModeButton.setIcon(QIcon(ICON_SEND_BLUE))
-
+
self.receiveModeButton.setFlat(True)
- self.receiveModeButton.setStyleSheet("QPushButton {padding: 10px; border: 0px;}"+ "QPushButton:hover { " + f"background-color: rgba{str(COLOR_HIGHLIGHT)};" + "}" )
+ self.receiveModeButton.setStyleSheet(
+ "QPushButton {padding: 10px; border: 0px;}"
+ + "QPushButton:hover { "
+ + f"background-color: rgba{str(COLOR_HIGHLIGHT)};"
+ + "}"
+ )
self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLACK))
- self.runButton.setStyleSheet("QPushButton {color: white;border: 0px;border-radius: 17px;padding: 10px;"+ f"{BACKGR_COLOR}" + "} QPushButton:hover { "+ f"{BACKGR_COLOR_LIGHT}" + " }")
- #self.runButton.setGeometry(0, 0, 150, 30)
+ self.runButton.setStyleSheet(
+ "QPushButton {color: white;border: 0px;border-radius: 17px;padding: 10px;"
+ + f"{BACKGR_COLOR}"
+ + "} QPushButton:hover { "
+ + f"{BACKGR_COLOR_LIGHT}"
+ + " }"
+ )
+ # self.runButton.setGeometry(0, 0, 150, 30)
self.runButton.setMaximumWidth(200)
self.runButton.setIcon(QIcon(ICON_SEND))
- # insert checkbox
+ # insert checkbox
l = self.verticalLayout
def runSetup(self, plugin):
-
- #self.addDataStorage(plugin)
+ # self.addDataStorage(plugin)
self.addLabel(plugin)
self.addProps(plugin)
- #self.createMappingDialog()
+ # self.createMappingDialog()
def addProps(self, plugin):
-
- # add widgets that will only show on event trigger
+ # add widgets that will only show on event trigger
logWidget = LogWidget(parent=self)
logWidget.dataStorage = self.dataStorage
self.layout().addWidget(logWidget)
- self.msgLog = logWidget
- self.msgLog.dockwidget = self
-
+ self.msgLog = logWidget
+ self.msgLog.dockwidget = self
+
self.msgLog.active_account = plugin.dataStorage.active_account
self.msgLog.speckle_version = plugin.version
self.setMapping.setFlat(True)
- self.setMapping.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }")
-
+ 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}" + " }")
-
+ self.crsSettings.setStyleSheet(
+ "QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }"
+ )
+
def addDataStorage(self, plugin):
self.dataStorage = plugin.dataStorage
- try:
+ try:
self.dataStorage.project = plugin.project
except:
self.dataStorage.project = plugin.qgis_project
-
- def createMappingDialog(self):
+ def createMappingDialog(self):
if self.mappingSendDialog is None:
self.mappingSendDialog = MappingSendDialog(None)
self.mappingSendDialog.dataStorage = self.dataStorage
@@ -172,51 +233,54 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
self.mappingSendDialog.runSetup()
self.mappingSendDialog.show()
- def addLabel(self, plugin):
+ def addLabel(self, plugin):
try:
exitIcon = QPixmap(ICON_LOGO)
exitActIcon = QIcon(exitIcon)
- # create a label
+ # create a label
text_label = QtWidgets.QPushButton(" for QGIS")
- text_label.setStyleSheet("border: 0px;"
- "color: white;"
- f"{BACKGR_COLOR}"
- "top-margin: 40 px;"
- "padding: 10px;"
- "padding-left: 20px;"
- "font-size: 15px;"
- "height: 30px;"
- "text-align: left;"
- )
+ text_label.setStyleSheet(
+ "border: 0px;"
+ "color: white;"
+ f"{BACKGR_COLOR}"
+ "top-margin: 40 px;"
+ "padding: 10px;"
+ "padding-left: 20px;"
+ "font-size: 15px;"
+ "height: 30px;"
+ "text-align: left;"
+ )
text_label.setIcon(exitActIcon)
text_label.setIconSize(QtCore.QSize(300, 93))
text_label.setMinimumSize(QtCore.QSize(100, 40))
text_label.setMaximumWidth(200)
version = ""
- try:
- if isinstance(plugin.version, str): version = str(plugin.version)
- except: pass
-
+ try:
+ if isinstance(plugin.version, str):
+ version = str(plugin.version)
+ except:
+ pass
version_label = QtWidgets.QPushButton(version)
- version_label.setStyleSheet("border: 0px;"
- "color: white;"
- f"{BACKGR_COLOR}"
- "padding-top: 15px;"
- "padding-left: 0px;"
- "margin-left: 0px;"
- "font-size: 10px;"
- "height: 30px;"
- "text-align: left;"
- )
+ version_label.setStyleSheet(
+ "border: 0px;"
+ "color: white;"
+ f"{BACKGR_COLOR}"
+ "padding-top: 15px;"
+ "padding-left: 0px;"
+ "margin-left: 0px;"
+ "font-size: 10px;"
+ "height: 30px;"
+ "text-align: left;"
+ )
widget = QWidget()
widget.setStyleSheet(f"{BACKGR_COLOR}")
boxLayout = QHBoxLayout(widget)
- boxLayout.addWidget(text_label) #, alignment=Qt.AlignCenter)
- boxLayout.addWidget(version_label)
+ boxLayout.addWidget(text_label) # , alignment=Qt.AlignCenter)
+ boxLayout.addWidget(version_label)
boxLayout.setContentsMargins(0, 0, 0, 0)
self.setWindowTitle("SpeckleQGIS")
self.setTitleBarWidget(widget)
@@ -229,8 +293,13 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
def resizeEvent(self, event):
try:
QtWidgets.QDockWidget.resizeEvent(self, event)
- if self.msgLog.size().height() != 0: # visible
- self.msgLog.setGeometry(0, 0, self.msgLog.parentWidget.frameSize().width(), self.msgLog.parentWidget.frameSize().height()) #.resize(self.frameSize().width(), self.frameSize().height())
+ if self.msgLog.size().height() != 0: # visible
+ self.msgLog.setGeometry(
+ 0,
+ 0,
+ self.msgLog.parentWidget.frameSize().width(),
+ self.msgLog.parentWidget.frameSize().height(),
+ ) # .resize(self.frameSize().width(), self.frameSize().height())
except Exception as e:
return
@@ -239,29 +308,28 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
self.streamBranchDropdown.clear()
self.commitDropdown.clear()
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 reloadDialogUI(self, plugin):
try:
self.clearDropdown()
- self.populateUI(plugin)
+ self.populateUI(plugin)
self.enableElements(plugin)
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 run(self, plugin):
+ def run(self, plugin):
try:
# Setup events on first load only!
self.setupOnFirstLoad(plugin)
# Connect streams section events
self.completeStreamSection(plugin)
# Populate the UI dropdowns
- self.populateUI(plugin)
+ self.populateUI(plugin)
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 closeEvent(self, event):
@@ -269,92 +337,140 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
self.closingPlugin.emit()
event.accept()
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 addMsg(self, obj:dict):
+
+ def addMsg(self, obj: dict):
self.msgLog.addButton(obj)
def setupOnFirstLoad(self, plugin):
try:
- #print("setupOnFirstLoad")
+ # print("setupOnFirstLoad")
self.msgLog.sendMessage.connect(self.addMsg)
self.setMapping.clicked.connect(self.showMappingDialog)
- #print("before")
- #print(self.reportBtn)
- #print(self.msgLog)
+ # print("before")
+ # print(self.reportBtn)
+ # print(self.msgLog)
self.reportBtn.clicked.connect(self.msgLog.showReport)
- #print("after")
+ self.open_dashboard.clicked.connect(self.showDashboard)
- 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.open_dashboard.setFlat(True)
+ self.open_dashboard.setStyleSheet(
+ "QPushButton {border: 0px; color: black; padding: 10px; } QPushButton:hover { "
+ + f"background-color: rgba{str(COLOR_HIGHLIGHT)};"
+ + " };"
+ )
+ # print("after")
+
+ 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.sendModeButton.clicked.connect(lambda: self.setSendMode(plugin))
- self.layerSendModeDropdown.currentIndexChanged.connect( lambda: self.layerSendModeChange(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.commitDropdown.currentIndexChanged.connect( lambda: self.setActiveCommit(plugin) )
- self.commitDropdown.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)
+ )
self.closingPlugin.connect(plugin.onClosePlugin)
- return
+ return
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 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)
-
+
+ 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.dataStorage.active_account, {"name": "Refresh", "connector_version": str(plugin.version)})
+ metrics.track(
+ "Connector Action",
+ plugin.dataStorage.active_account,
+ {"name": "Refresh", "connector_version": str(plugin.version)},
+ )
except Exception as e:
- logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=plugin.dockwidget )
-
+ logToUser(
+ e, level=2, func=inspect.stack()[0][3], plugin=plugin.dockwidget
+ )
+
plugin.reloadUI()
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 closeClicked(self, plugin):
try:
try:
- metrics.track("Connector Action", plugin.dataStorage.active_account, {"name": "Close", "connector_version": str(plugin.version)})
+ metrics.track(
+ "Connector Action",
+ plugin.dataStorage.active_account,
+ {"name": "Close", "connector_version": str(plugin.version)},
+ )
except Exception as e:
- logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=plugin.dockwidget )
-
+ logToUser(
+ e, level=2, func=inspect.stack()[0][3], plugin=plugin.dockwidget
+ )
+
plugin.onClosePlugin()
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 setSendMode(self, plugin):
try:
- plugin.btnAction = 0 # send
+ plugin.btnAction = 0 # send
color = f"color: rgba{str(SPECKLE_COLOR)};"
- self.sendModeButton.setStyleSheet("border: 0px;"
- f"color: rgba{str(SPECKLE_COLOR)};"
- "padding: 10px;")
+ self.sendModeButton.setStyleSheet(
+ "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: rgba{str(COLOR_HIGHLIGHT)};" + " };")
+ self.receiveModeButton.setStyleSheet(
+ "QPushButton {border: 0px; color: black; padding: 10px; } QPushButton:hover { "
+ + f"background-color: rgba{str(COLOR_HIGHLIGHT)};"
+ + " };"
+ )
self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLACK))
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)
+ # enable sections only if in "saved streams" mode
+ if self.layerSendModeDropdown.currentIndex() == 1:
+ self.layersWidget.setEnabled(True)
self.saveLayerSelection.setEnabled(True)
self.commitLabel.setEnabled(False)
self.commitDropdown.setEnabled(False)
@@ -367,18 +483,22 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
self.runBtnStatusChanged(plugin)
return
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 setReceiveMode(self, plugin):
try:
- plugin.btnAction = 1 # receive
+ plugin.btnAction = 1 # receive
color = f"color: rgba{str(SPECKLE_COLOR)};"
- self.receiveModeButton.setStyleSheet("border: 0px;"
- f"color: rgba{str(SPECKLE_COLOR)};"
- "padding: 10px;")
+ self.receiveModeButton.setStyleSheet(
+ "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: rgba{str(COLOR_HIGHLIGHT)};" + " };")
+ self.sendModeButton.setStyleSheet(
+ "QPushButton {border: 0px; color: black; padding: 10px;} QPushButton:hover { "
+ + f"background-color: rgba{str(COLOR_HIGHLIGHT)};"
+ + " };"
+ )
self.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLUE))
self.sendModeButton.setFlat(True)
self.receiveModeButton.setFlat(False)
@@ -386,7 +506,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
self.runButton.setIcon(QIcon(ICON_RECEIVE))
self.commitLabel.setEnabled(True)
self.commitDropdown.setEnabled(True)
-
+
self.layersWidget.setEnabled(False)
self.messageLabel.setEnabled(False)
self.messageInput.setEnabled(False)
@@ -398,7 +518,7 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
self.runBtnStatusChanged(plugin)
return
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 completeStreamSection(self, plugin):
@@ -409,19 +529,22 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
self.populateLayerSendModeDropdown()
self.populateProjectStreams(plugin)
- #self.runBtnStatusChanged(plugin)
- #self.runButton.setEnabled(False)
-
+ # self.runBtnStatusChanged(plugin)
+ # self.runButton.setEnabled(False)
+
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 setActiveCommit(self, plugin):
try:
- #print("__setActiveCommit")
- #print(plugin.active_commit)
+ # 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:
+ 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:
@@ -429,14 +552,15 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
plugin.active_branch = b
break
- if plugin.active_branch is None: return
- #print(plugin.active_branch.name)
+ 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
+ # 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]
@@ -444,115 +568,131 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
for c in plugin.active_branch.commits.items:
if c.id == current_id_text:
plugin.active_commit = c
- return
- # only if not found:
+ 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)
+ print(e)
def runBtnStatusChanged(self, plugin):
try:
commitStr = str(self.commitDropdown.currentText())
branchStr = str(self.streamBranchDropdown.currentText())
-
- if commitStr == "": # populate commits still in progress
+
+ if commitStr == "": # populate commits still in progress
return
- if plugin.btnAction == 1: # on receive
- if commitStr == "":
- self.runButton.setEnabled(False)
- else:
- self.runButton.setEnabled(True)
-
- if plugin.btnAction == 0: # on send
- if branchStr == "":
- self.runButton.setEnabled(False)
- elif self.layerSendModeDropdown.currentIndex() == 1 and len(plugin.dataStorage.current_layers) == 0: # saved layers; but the list is empty
+ if plugin.btnAction == 1: # on receive
+ if commitStr == "":
+ self.runButton.setEnabled(False)
+ else:
+ self.runButton.setEnabled(True)
+
+ if plugin.btnAction == 0: # on send
+ if branchStr == "":
+ self.runButton.setEnabled(False)
+ elif (
+ self.layerSendModeDropdown.currentIndex() == 1
+ and len(plugin.dataStorage.current_layers) == 0
+ ): # saved layers; but the list is empty
self.runButton.setEnabled(False)
else:
self.runButton.setEnabled(True)
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 layerSendModeChange(self, plugin, runMode = None):
+ def layerSendModeChange(self, plugin, runMode=None):
try:
- if self.layerSendModeDropdown.currentIndex() == 0 or runMode == 1: # by manual selection OR receive mode
+ if (
+ self.layerSendModeDropdown.currentIndex() == 0 or runMode == 1
+ ): # by manual selection OR receive mode
self.layersWidget.setEnabled(False)
-
- elif self.layerSendModeDropdown.currentIndex() == 1 and (runMode == 0 or runMode is None): # by saved AND when Send mode
+
+ elif self.layerSendModeDropdown.currentIndex() == 1 and (
+ runMode == 0 or runMode is None
+ ): # by saved AND when Send mode
self.layersWidget.setEnabled(True)
-
+
branchStr = str(self.streamBranchDropdown.currentText())
if self.layerSendModeDropdown.currentIndex() == 0:
- if branchStr == "": self.runButton.setEnabled(False) # by manual selection
- else: self.runButton.setEnabled(True) # by manual selection
- elif self.layerSendModeDropdown.currentIndex() == 1: self.runBtnStatusChanged(plugin) # by saved
+ if branchStr == "":
+ self.runButton.setEnabled(False) # by manual selection
+ else:
+ self.runButton.setEnabled(True) # by manual selection
+ elif self.layerSendModeDropdown.currentIndex() == 1:
+ self.runBtnStatusChanged(plugin) # by saved
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 populateSavedLayerDropdown(self, plugin):
-
try:
- #print(self.dataStorage.saved_layers)
- if not self: return
+ # print(self.dataStorage.saved_layers)
+ if not self:
+ return
self.layersWidget.clear()
- self.dataStorage.current_layers.clear()
+ self.dataStorage.current_layers.clear()
layers = self.dataStorage.saved_layers
- if not layers: return
-
+ if not layers:
+ return
+
for i, layer in enumerate(layers):
- self.dataStorage.current_layers.append(layer)
+ self.dataStorage.current_layers.append(layer)
listItem = self.fillLayerList(layer[0], layer[2])
self.layersWidget.addItem(listItem)
self.layersWidget.setIconSize(QtCore.QSize(20, 20))
self.runBtnStatusChanged(plugin)
- except Exception as e:
- logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
+ except Exception as e:
+ logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def populateSelectedLayerDropdown(self, plugin):
-
try:
- if not self: return
+ if not self:
+ return
self.layersWidget.clear()
for layer_tuple in plugin.dataStorage.current_layers:
- listItem = self.fillLayerList(layer_tuple[0], layer_tuple[2])
+ listItem = self.fillLayerList(layer_tuple[0], layer_tuple[2])
self.layersWidget.addItem(listItem)
self.layersWidget.setIconSize(QtCore.QSize(20, 20))
self.runBtnStatusChanged(plugin)
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 fillLayerList(self, layer, layerType = "generic"):
+ def fillLayerList(self, layer, layerType="generic"):
try:
- icon_xxl = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets", " size-xxl.png")
- listItem = QListWidgetItem(layer.name())
+ icon_xxl = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), "assets", " size-xxl.png"
+ )
+ listItem = QListWidgetItem(layer.name())
- try: # if QGIS
- from qgis.core import QgsVectorLayer, QgsRasterLayer, QgsIconUtils
+ try: # if QGIS
+ from qgis.core import QgsVectorLayer, QgsRasterLayer, QgsIconUtils
- if isinstance(layer, QgsRasterLayer) and layer.width()*layer.height() > 1000000:
- listItem.setIcon(QIcon(icon_xxl))
-
- elif isinstance(layer, QgsVectorLayer) and layer.featureCount() > 20000:
- listItem.setIcon(QIcon(icon_xxl))
+ if (
+ isinstance(layer, QgsRasterLayer)
+ and layer.width() * layer.height() > 1000000
+ ):
+ listItem.setIcon(QIcon(icon_xxl))
+
+ elif isinstance(layer, QgsVectorLayer) and layer.featureCount() > 20000:
+ listItem.setIcon(QIcon(icon_xxl))
else:
- from qgis.core import QgsIconUtils
+ from qgis.core import QgsIconUtils
+
icon = QgsIconUtils().iconForLayer(layer)
listItem.setIcon(icon)
- #print(icon)
+ # print(icon)
except Exception as e:
print(e)
icons = {
@@ -564,13 +704,12 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
}
icon = QIcon(icons[layerType])
listItem.setIcon(icon)
-
- return listItem
-
- except Exception as e:
- logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
- return
+ return listItem
+
+ except Exception as e:
+ logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
+ return
def enableElements(self, plugin):
try:
@@ -580,7 +719,8 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
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)
+ 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)
@@ -588,116 +728,139 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
self.show()
self.setSendMode(plugin)
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 populateProjectStreams(self, plugin):
- return
+ return
def onActiveStreamChanged(self, plugin):
try:
- if not self: return
+ 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)):
+ if (len(plugin.current_streams) == 0 and index == 1) or (
+ len(plugin.current_streams) > 0 and index == len(plugin.current_streams)
+ ):
self.populateProjectStreams(plugin)
plugin.onStreamCreateClicked()
return
- if len(plugin.current_streams) == 0: return
- if index == -1: return
+ if len(plugin.current_streams) == 0:
+ return
+ if index == -1:
+ return
- try: plugin.active_stream = plugin.current_streams[index]
- except: plugin.active_stream = None
+ try:
+ plugin.active_stream = plugin.current_streams[index]
+ except:
+ plugin.active_stream = None
self.populateActiveStreamBranchDropdown(plugin)
self.populateActiveCommitDropdown(plugin)
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 populateLayerSendModeDropdown(self):
- if not self: return
+ if not self:
+ return
try:
self.layerSendModeDropdown.clear()
self.layerSendModeDropdown.addItems(
["Send selected layers", "Send saved layers"]
)
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 populateActiveStreamBranchDropdown(self, plugin):
- if not self: return
+ if not self:
+ return
try:
- #print("___ populateActiveStreamBranchDropdown___")
- #print(plugin.active_branch)
- if plugin.active_stream is None: return
+ # print("___ populateActiveStreamBranchDropdown___")
+ # print(plugin.active_branch)
+ if plugin.active_stream is None:
+ return
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 or active_commit is None: # case of populating from Saved Streams
+ keep_branch = True # case of search by URL
+ if (
+ active_branch is None or active_commit is None
+ ): # case of populating from Saved Streams
keep_branch = False
- #print(active_branch)
+ # print(active_branch)
- #print(1)
- self.streamBranchDropdown.clear() # activates "populate commit"
- #print(2)
- if isinstance(plugin.active_stream[1], SpeckleException):
- logToUser("Some streams cannot be accessed", level = 1, plugin = self)
+ # print(1)
+ self.streamBranchDropdown.clear() # activates "populate commit"
+ # print(2)
+ if isinstance(plugin.active_stream[1], SpeckleException):
+ logToUser("Some streams cannot be accessed", level=1, plugin=self)
return
- elif plugin.active_stream is None or plugin.active_stream[1] is None or plugin.active_stream[1].branches is None:
+ elif (
+ plugin.active_stream is None
+ or plugin.active_stream[1] is None
+ or plugin.active_stream[1].branches is None
+ ):
return
- #print(3)
- #print(plugin.active_branch)
+ # print(3)
+ # print(plugin.active_branch)
- # here the commit dropdown is triggered
+ # here the commit dropdown is triggered
self.streamBranchDropdown.addItems(
[f"{branch.name}" for branch in plugin.active_stream[1].branches.items]
)
- #print(4)
+ # print(4)
self.streamBranchDropdown.addItems(["Create New Branch"])
- #print(5)
+ # print(5)
if keep_branch is True:
plugin.active_branch = active_branch
plugin.active_commit = active_commit
- #print(plugin.active_branch)
+ # 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)
+ # 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)
-
+ # print(plugin.active_branch.name)
+ self.streamBranchDropdown.setCurrentText(
+ plugin.active_branch.name
+ ) # activates "populate commit"
+ # print(6)
+
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 populateActiveCommitDropdown(self, plugin):
- if not self: return
+ if not self:
+ return
try:
- #print("________populateActiveCommitDropdown")
- #print(plugin.active_commit)
- if plugin.active_stream is None:
+ # print("________populateActiveCommitDropdown")
+ # print(plugin.active_commit)
+ 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 Branch":
+ # print(f"CURRENT BRANCH TEXT: {branchName}")
+ if branchName == "":
+ return
+ if branchName == "Create New Branch":
self.streamBranchDropdown.setCurrentText("main")
plugin.onBranchCreateClicked()
return
branch = None
-
- #print("__clear commit dropdwn")
- #print(plugin.active_commit)
+
+ # print("__clear commit dropdwn")
+ # print(plugin.active_commit)
self.commitDropdown.clear()
- if isinstance(plugin.active_stream[1], SpeckleException):
- logToUser("Some streams cannot be accessed", level = 1, plugin = self)
+ if isinstance(plugin.active_stream[1], SpeckleException):
+ 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:
@@ -705,47 +868,78 @@ class SpeckleQGISDialog(QtWidgets.QDockWidget, FORM_CLASS):
branch = b
plugin.active_branch = b
break
-
- if len(branch.commits.items)>0:
+
+ 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 = []
+ # 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}")
+ 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")
+ # 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:
+ 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:
+ # 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:
+ else:
self.commit_web_view.setEnabled(False)
except Exception as e:
- logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
- #print(str(e) + "::" + str(inspect.stack()[0][3]))
+ logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
+ # print(str(e) + "::" + str(inspect.stack()[0][3]))
return
def onStreamRemoveButtonClicked(self, plugin):
- return
+ return
def cancelOperations(self):
- return
+ return
+
+ def showDashboard(self):
+ if self.dashboard is None:
+ self.dashboard = SpeckleDashboard()
+ self.dashboard.dataStorage = self.dataStorage
+ self.iface.addDockWidget(Qt.LeftDockWidgetArea, self.dashboard)
+ self.dashboard.update()
+ self.dashboard.show()
+ else:
+ self.dashboard.update()
+ self.dashboard.show()
diff --git a/qt_ui/ui/dashboard.ui b/qt_ui/ui/dashboard.ui
new file mode 100644
index 0000000..0199765
--- /dev/null
+++ b/qt_ui/ui/dashboard.ui
@@ -0,0 +1,103 @@
+
+
+ SpeckleDashboard
+
+
+
+ 0
+ 0
+ 575
+ 651
+
+
+
+
+ Speckle Dashboard
+
+
+
+
+ -
+
+
-
+
+
+
+ 10
+
+
+ 10
+
+
+ 10
+
+
+ 10
+
+
+
+
-
+
+
+
+
+ -
+
+
+ Model properties
+
+
+
+
+ -
+
+
+
+ -
+
+
+ QAbstractItemView::NoSelection
+
+
+
+ 0
+ 0
+
+
+
+ QAbstractScrollArea::AdjustToContents
+
+
+ QListView::Fixed
+
+
+ QListView::ListMode
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qt_ui/ui/dockwidget_main.ui b/qt_ui/ui/dockwidget_main.ui
index 871efc3..d001808 100644
--- a/qt_ui/ui/dockwidget_main.ui
+++ b/qt_ui/ui/dockwidget_main.ui
@@ -334,6 +334,23 @@
+ -
+
+
+
-
+
+
+ true
+
+
+ Open Dashboard
+
+
+
+
+
+
+
diff --git a/qt_ui/widget_dashboard.py b/qt_ui/widget_dashboard.py
new file mode 100644
index 0000000..8eb8c4d
--- /dev/null
+++ b/qt_ui/widget_dashboard.py
@@ -0,0 +1,231 @@
+import random
+from qgis.PyQt import QtWidgets, uic
+from qgis.PyQt import QtGui
+from qgis.PyQt.QtGui import QIcon, QPixmap
+from qgis.PyQt.QtWidgets import (
+ QCheckBox,
+ QListWidgetItem,
+ QAction,
+ QDockWidget,
+ QVBoxLayout,
+ QHBoxLayout,
+ QWidget,
+ QLabel,
+)
+from qgis.PyQt import QtCore
+from qgis.PyQt.QtCore import pyqtSignal, Qt
+
+# from qgis.PyQt import QtCore, QtWidgets #, QtWebEngineWidgets
+from PyQt5 import *
+from PyQt5.QtCore import QUrl
+import plotly.express as px
+from PyQt5.QtWebKitWidgets import QWebView
+import pandas as pd
+import os
+
+FORM_CLASS, _ = uic.loadUiType(
+ os.path.join(os.path.join(os.path.dirname(__file__), "ui", "dashboard.ui"))
+)
+
+
+class SpeckleDashboard(QtWidgets.QDockWidget, FORM_CLASS):
+ closingPlugin = pyqtSignal()
+ dataStorage = None
+
+ dataNumeric: dict = {}
+ dataText: dict = {}
+
+ current_filter: str = ""
+ current_index: int = -1
+ selectionDropdown: QtWidgets.QComboBox
+ dataWidget: QtWidgets.QListWidget
+ chart: QWidget
+ browser = None
+ existing_web: int = 0
+
+ def __init__(self, parent=None):
+ """Constructor."""
+ super(SpeckleDashboard, self).__init__(parent)
+ # Set up the user interface from Designer through FORM_CLASS.
+ # After self.setupUi() you can access any designer object by doing
+ # self., and you can use autoconnect slots - see
+ # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
+ # #widgets-and-dialogs-with-auto-connect
+
+ self.browser = QWebView(self) # https://github.com/qgis/QGIS/issues/26048
+ self.setupUi(self)
+
+ self.selectionDropdown.clear()
+ self.selectionDropdown.addItems(["area", "property value"])
+ self.selectionDropdown.setCurrentIndex(0)
+
+ self.selectionDropdown.currentIndexChanged.connect(self.populateUI)
+
+ def setup(self):
+ self.dataWidget.clear()
+ for i, (key, val) in enumerate(self.dataNumeric.items()):
+ # property_filter = self.selectionDropdown.currentText()
+ if self.current_filter in key or key in self.current_filter:
+ listItem = QListWidgetItem(f"{key}: {val}")
+ self.dataWidget.addItem(listItem)
+
+ def populateUI(self, force=0):
+ print(self.selectionDropdown.currentIndex())
+ print(self.current_filter)
+ if self.selectionDropdown.currentText() == "":
+ self.current_filter = "area"
+ self.current_index = 0
+ self.setup()
+ self.selectionDropdown.setIndex(0)
+
+ self.createChart()
+ elif self.selectionDropdown.currentText() != self.current_filter:
+ self.current_filter = self.selectionDropdown.currentText()
+ self.current_index = self.selectionDropdown.currentIndex()
+ self.setup()
+ self.createChart()
+ if force == 1:
+ self.setup()
+ self.createChart()
+
+ def update(self):
+ self.dataNumeric = {}
+ self.dataText = {}
+
+ for i in range(10):
+ # keys = ["key1", "key2", "key3", "key4", "key5"]
+ # key = keys[random.randint(0, 4)]
+ value = random.randint(10, 100)
+ self.dataNumeric.update({"index": i, f"area {random.randint(1, 4)}": value})
+ for i in range(10):
+ # keys = ["key1", "key2", "key3", "key4", "key5"]
+ # key = keys[random.randint(0, 4)]
+ value = random.randint(10, 100)
+ self.dataNumeric.update(
+ {
+ "index": i + 10,
+ f"property value {random.randint(1, 4)}": [
+ value,
+ value * 0.5,
+ value * 2,
+ ],
+ }
+ )
+
+ r"""
+ layer = getLayerByName(self.dataStorage.project, "Speckle_dashboard")
+ fields = layer.fields()
+ for i, key in enumerate(fields.names()):
+ if key in ["Branch URL", "commit_id", "updated"]:
+ continue
+
+ value = None
+ variant = fields.at(i).type()
+
+ if "value" in key:
+ value = []
+ if "area" in key:
+ value = 0
+
+ for feat in layer.getFeatures():
+ if isinstance(value, List):
+ if feat[key] is not None:
+ list_vals = (
+ feat[key]
+ .replace("[", "")
+ .replace("]", "")
+ .replace("'", "")
+ .split(",")
+ )
+ value.extend([x for x in list_vals if x != ""])
+ print(value)
+
+ elif isinstance(value, float) or isinstance(value, int):
+ if feat[key] is not None:
+ value += feat[key]
+
+ self.dataNumeric.update({key: value})
+ """
+
+ self.populateUI(force=1)
+
+ def createChart(self):
+ # https://stackoverflow.com/questions/60522103/how-to-have-plotly-graph-as-pyqt5-widget
+ print("PRINT DATAFRAME")
+ df = pd.DataFrame.from_dict(self.dataNumeric, orient="index", columns=["value"])
+ df2 = df.reset_index()
+ print(df2)
+
+ property_filter = str(self.selectionDropdown.currentText())
+ if len(property_filter) <= 1:
+ property_filter = "area"
+ df2 = df2[df2["index"].str.lower().str.contains(property_filter)]
+ print(df2)
+
+ if "area" in property_filter:
+ fig = px.pie(
+ df2,
+ values="value",
+ names="index",
+ title="Land use distribution",
+ hole=0.5,
+ )
+
+ elif "value" in property_filter:
+ all_column_vals = df2["value"].to_list()
+
+ all_column_vals_separated = []
+ [all_column_vals_separated.extend(x) for x in all_column_vals]
+
+ all_vals = [float(x) for x in all_column_vals_separated]
+ df2 = pd.DataFrame([{property_filter: val} for val in all_vals])
+ fig = px.histogram(df2, x=property_filter, title="Property values")
+ else:
+ df2 = df2.loc["area" in df2["index"]]
+ fig = px.pie(
+ df2, values="value", names="index", title="Land use distribution"
+ )
+ print(df2)
+
+ # remove all buttons
+ # try:
+ # for i in reversed(range(self.chart.layout.count())):
+ # self.chart.layout.itemAt(i).widget().setParent(None)
+ # except: pass
+
+ if self.existing_web == 0:
+ self.browser = QWebView(self)
+
+ self.browser.setHtml(fig.to_html(include_plotlyjs="cdn"))
+ # self.browser.setUrl(QUrl("https://speckle.xyz/streams/e2effcfa27/commits/f76cedd9a6"))
+
+ self.chart.layout = QHBoxLayout(self.chart)
+ self.browser.setMaximumHeight(400)
+
+ if self.existing_web == 0:
+ self.chart.layout.addWidget(self.browser)
+ self.existing_web = 1
+
+ return
+ # https://www.pythonguis.com/tutorials/plotting-pyqtgraph/
+
+ graphWidget = pg.PlotWidget()
+
+ hour = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+ temperature = [30, 32, 34, 32, 33, 31, 29, 32, 35, 45]
+
+ # plot data: x, y values
+ graphWidget.plot(hour, temperature)
+
+ # remove all buttons
+ try:
+ for i in reversed(range(self.chart.layout.count())):
+ self.chart.layout.itemAt(i).widget().setParent(None)
+ except:
+ pass
+
+ self.chart.layout = QHBoxLayout(self.chart)
+ self.chart.layout.addWidget(graphWidget)
+
+ # set the QWebEngineView instance as main widget
+ # self.setCentralWidget(plot_widget)