main UI window

This commit is contained in:
KatKatKateryna
2023-04-30 01:37:44 +02:00
parent 94487ce8ea
commit 7ae5bbf680
16 changed files with 2110 additions and 0 deletions
+125
View File
@@ -0,0 +1,125 @@
./idea/
/.vscode/
/speckle_qgis_dialog_base.ui.autosave
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Jetbrains stuff:
.idea
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# other
scratch.py
settings.json
**/.DS_Store
zip_build
.qt_for_python
speckle-sharp-ci-tools*
zip-build*
/typings/*
*.csv
*.json
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

+710
View File
@@ -0,0 +1,710 @@
# -*- coding: utf-8 -*-
"""
/***************************************************************************
SpeckleQGISDialog
A QGIS plugin
SpeckleQGIS Description
Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
-------------------
begin : 2021-08-04
git sha : $Format:%H$
copyright : (C) 2021 by Speckle Systems
email : alan@speckle.systems
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
"""
import inspect
import os
import threading
from plugin_utils.helpers import splitTextIntoLines
from speckle.automation.mapping_send import MappingSendDialog
from speckle.converter.layers import getAllLayers, getLayers
from speckle.DataStorage import DataStorage
from ui.LogWidget import LogWidget
from ui.logger import logToUser
#from speckle_qgis import SpeckleQGIS
import ui.speckle_qgis_dialog
from qgis.core import Qgis, QgsProject,QgsVectorLayer, QgsRasterLayer, QgsIconUtils
from specklepy.logging.exceptions import (SpeckleException, GraphQLException)
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 speckle.logging import logger
from specklepy.api.credentials import get_local_accounts
from specklepy.api.wrapper import StreamWrapper
from specklepy.api.client import SpeckleClient
from specklepy.logging import metrics
from ui.validation import tryGetStream
# 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__), "main_dialog_qDockwidget.ui")
)
COLOR_HIGHLIGHT = (210,210,210)
SPECKLE_COLOR = (59,130,246)
SPECKLE_COLOR_LIGHT = (69,140,255)
ICON_LOGO = os.path.dirname(os.path.abspath(__file__)) + "/icons/logo-slab-white@0.5x.png"
ICON_SEARCH = os.path.dirname(os.path.abspath(__file__)) + "/icons/magnify.png"
ICON_DELETE = os.path.dirname(os.path.abspath(__file__)) + "/icons/delete.png"
ICON_DELETE_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/icons/delete-blue.png"
ICON_SEND = os.path.dirname(os.path.abspath(__file__)) + "/icons/cube-send.png"
ICON_RECEIVE = os.path.dirname(os.path.abspath(__file__)) + "/icons/cube-receive.png"
ICON_SEND_BLACK = os.path.dirname(os.path.abspath(__file__)) + "/icons/cube-send-black.png"
ICON_RECEIVE_BLACK = os.path.dirname(os.path.abspath(__file__)) + "/icons/cube-receive-black.png"
ICON_SEND_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/icons/cube-send-blue.png"
ICON_RECEIVE_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/icons/cube-receive-blue.png"
COLOR = f"color: rgb{str(SPECKLE_COLOR)};"
BACKGR_COLOR = f"background-color: rgb{str(SPECKLE_COLOR)};"
BACKGR_COLOR_LIGHT = f"background-color: rgb{str(SPECKLE_COLOR_LIGHT)};"
class MainDialog(QtWidgets.QDockWidget, FORM_CLASS):
closingPlugin = pyqtSignal()
streamList: QtWidgets.QComboBox
sendModeButton: QtWidgets.QPushButton
receiveModeButton: QtWidgets.QPushButton
streamBranchDropdown: QtWidgets.QComboBox
layerSendModeDropdown: QtWidgets.QComboBox
commitDropdown: QtWidgets.QComboBox
layersWidget: QtWidgets.QListWidget
saveLayerSelection: QtWidgets.QPushButton
runButton: QtWidgets.QPushButton
setMapping: QtWidgets.QPushButton
experimental: QCheckBox
msgLog: LogWidget = None
dataStorage: DataStorage = None
mappingSendDialog = None
def __init__(self, parent=None):
"""Constructor."""
super(MainDialog, 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.<objectname>, 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.setupUi(self)
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.saveLayerSelection.setFlat(True)
self.reloadButton.setFlat(True)
self.closeButton.setFlat(True)
# https://stackoverflow.com/questions/67585501/pyqt-how-to-use-hover-in-button-stylesheet
#color = f"color: rgb{str(SPECKLE_COLOR)};"
#backgr_color = f"background-color: rgb{str(SPECKLE_COLOR)};"
#backgr_color_light = f"background-color: rgb{str(SPECKLE_COLOR_LIGHT)};"
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"{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"{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.sendModeButton.setStyleSheet("QPushButton {padding: 10px; border: 0px; " + f"color: rgb{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: rgb{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.setMaximumWidth(200)
self.runButton.setIcon(QIcon(ICON_SEND))
# insert checkbox
l = self.verticalLayout
#l_item = None
#for i in reversed(range(self.verticalLayout.count())):
# l_item = self.verticalLayout.itemAt(i).widget()
# add row with "experimental" checkbox
box = QWidget()
box.layout = QHBoxLayout(box)
btn = QtWidgets.QCheckBox("Send/receive in the background (experimental!)")
btn.setStyleSheet("QPushButton {color: black; border: 0px;padding: 0px;height: 40px;text-align: left;}")
box.layout.addWidget(btn)
box.layout.setContentsMargins(65, 0, 0, 0)
self.formLayout.insertRow(10,box)
self.experimental = btn
def runSetup(self, plugin):
self.addDataStorage(plugin)
self.addLabel(plugin)
self.addProps(plugin)
self.createMappingDialog()
def addProps(self, plugin):
# add widgets that will only show on event trigger
logWidget = LogWidget(parent=self)
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
# add row with "experimental" checkbox
box = QWidget()
box.layout = QHBoxLayout(box)
btn = QtWidgets.QPushButton("Apply transformations on Send")
btn.setFlat(True)
btn.setStyleSheet("QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }")
box.layout.addWidget(btn)
box.layout.setContentsMargins(65, 0, 0, 0)
self.formLayout.insertRow(9,box)
self.setMapping = btn
def addDataStorage(self, plugin):
self.dataStorage = plugin.dataStorage
self.dataStorage.project = plugin.qgis_project
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
def createMappingDialog(self):
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
if self.mappingSendDialog is None:
self.mappingSendDialog = MappingSendDialog(None)
self.mappingSendDialog.dataStorage = self.dataStorage
self.mappingSendDialog.runSetup()
def showMappingDialog(self):
# updata DataStorage
self.mappingSendDialog.show()
def addLabel(self, plugin):
try:
exitIcon = QPixmap(ICON_LOGO)
#scaledExitIcon = exitIcon.scaled(QtCore.QSize(100, 31))
exitActIcon = QIcon(exitIcon)
# 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.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
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;"
)
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)
self.setTitleBarWidget(widget) # for dockwidget
#self.gridLayoutTitleBar.addWidget(widget) # fro QMainWindow
except Exception as e:
logToUser(e)
def resizeEvent(self, event):
try:
#print("resize")
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())
except Exception as e:
#logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def clearDropdown(self):
try:
#self.streamIdField.clear()
self.streamBranchDropdown.clear()
self.commitDropdown.clear()
#self.layerSendModeDropdown.clear()
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def reloadDialogUI(self, plugin):
try:
#logToUser("long errror something something msg1", level=2, plugin= plugin)
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:
# Setup events on first load only!
self.setupOnFirstLoad(plugin)
# Connect streams section events
self.completeStreamSection(plugin)
# Populate the UI dropdowns
self.populateUI(plugin)
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def closeEvent(self, event):
try:
self.closingPlugin.emit()
event.accept()
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def addMsg(self, text:str, level:int, url:str, blue:bool):
#t_name = threading.current_thread().getName()
#print(t_name)
self.msgLog.addButton(text, level, url, blue)
def setupOnFirstLoad(self, plugin):
try:
self.runButton.clicked.connect(plugin.onRunButtonClicked)
self.msgLog.sendMessage.connect(self.addMsg)
self.setMapping.clicked.connect(self.showMappingDialog)
self.streams_add_button.clicked.connect( plugin.onStreamAddButtonClicked )
self.reloadButton.clicked.connect(lambda: self.refreshClicked(plugin))
self.closeButton.clicked.connect(lambda: self.closeClicked(plugin))
self.saveSurveyPoint.clicked.connect(plugin.set_survey_point)
self.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.commitDropdown.currentIndexChanged.connect( lambda: self.runBtnStatusChanged(plugin) )
self.closingPlugin.connect(plugin.onClosePlugin)
return
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def refreshClicked(self, plugin):
try:
try:
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 )
plugin.reloadUI()
except Exception as e:
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)})
except Exception as e:
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)
return
def setSendMode(self, plugin):
try:
plugin.btnAction = 0 # send
color = f"color: rgb{str(SPECKLE_COLOR)};"
self.sendModeButton.setStyleSheet("border: 0px;"
f"color: rgb{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)};" + " };")
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.commitDropdown.setEnabled(False)
self.messageInput.setEnabled(True)
self.layerSendModeDropdown.setEnabled(True)
self.setMapping.setEnabled(True)
self.runBtnStatusChanged(plugin)
return
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def setReceiveMode(self, plugin):
try:
plugin.btnAction = 1 # receive
color = f"color: rgb{str(SPECKLE_COLOR)};"
self.receiveModeButton.setStyleSheet("border: 0px;"
f"color: rgb{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)};" + " };")
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.commitDropdown.setEnabled(True)
self.layersWidget.setEnabled(False)
self.messageInput.setEnabled(False)
self.saveLayerSelection.setEnabled(False)
self.layerSendModeDropdown.setEnabled(False)
self.setMapping.setEnabled(False)
self.runBtnStatusChanged(plugin)
return
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def completeStreamSection(self, plugin):
try:
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
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
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)
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def runBtnStatusChanged(self, plugin):
try:
commitStr = str(self.commitDropdown.currentText())
branchStr = str(self.streamBranchDropdown.currentText())
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)
return
def layerSendModeChange(self, plugin, runMode = None):
try:
if self.layerSendModeDropdown.currentIndex() == 0 or runMode == 1: # by manual selection OR receive mode
#self.current_layers = []
#self.dataStorage.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:
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)
return
def populateLayerDropdown(self, plugin, bySelection: bool = True):
try:
if not self: return
from ui.project_vars import set_project_layer_selection
self.layersWidget.clear()
nameDisplay = []
project = plugin.qgis_project
if bySelection is False: # read from project data
all_layers_ids = [l.id() for l in project.mapLayers().values()]
for layer_tuple in plugin.dataStorage.current_layers:
if layer_tuple[1].id() in all_layers_ids:
listItem = self.fillLayerList(layer_tuple[1])
self.layersWidget.addItem(listItem)
else: # read selected layers
# Fetch selected layers
#plugin.current_layers = []
self.dataStorage.current_layers.clear()
layers = getLayers(plugin, bySelection) # List[QgsLayerTreeNode]
for i, layer in enumerate(layers):
#plugin.current_layers.append((layer.name(), layer))
self.dataStorage.current_layers.append((layer.name(), layer))
listItem = self.fillLayerList(layer)
self.layersWidget.addItem(listItem)
set_project_layer_selection(plugin)
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)
return
def fillLayerList(self, layer):
try:
icon_xxl = os.path.dirname(os.path.abspath(__file__)) + "/size-xxl.png"
listItem = QListWidgetItem(layer.name())
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:
icon = QgsIconUtils().iconForLayer(layer)
listItem.setIcon(icon)
return listItem
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def populateSurveyPoint(self, plugin):
if not self:
return
try:
self.surveyPointLat.clear()
self.surveyPointLat.setText(str(plugin.lat))
self.surveyPointLon.clear()
self.surveyPointLon.setText(str(plugin.lon))
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def enableElements(self, plugin):
try:
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)
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.commitDropdown.setEnabled(False)
self.show()
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def populateProjectStreams(self, plugin):
try:
from ui.project_vars import set_project_streams
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 Stream"])
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(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def onActiveStreamChanged(self, plugin):
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)):
self.populateProjectStreams(plugin)
plugin.onStreamCreateClicked()
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
self.populateActiveStreamBranchDropdown(plugin)
self.populateActiveCommitDropdown(plugin)
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def populateLayerSendModeDropdown(self):
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)
return
def populateActiveStreamBranchDropdown(self, plugin):
if not self: return
try:
if plugin.active_stream is None: return
self.streamBranchDropdown.clear()
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:
return
self.streamBranchDropdown.addItems(
[f"{branch.name}" for branch in plugin.active_stream[1].branches.items]
)
self.streamBranchDropdown.addItems(["Create New Branch"])
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def populateActiveCommitDropdown(self, plugin):
if not self: return
try:
self.commitDropdown.clear()
if plugin.active_stream is None: return
branchName = self.streamBranchDropdown.currentText()
if branchName == "": return
if branchName == "Create New Branch":
self.streamBranchDropdown.setCurrentText("main")
plugin.onBranchCreateClicked()
return
branch = None
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:
if b.name == branchName:
branch = b
break
print(branch)
self.commitDropdown.addItems(
[f"{commit.id}"+ " | " + f"{commit.message}" for commit in branch.commits.items]
)
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
print(str(e) + "::" + str(inspect.stack()[0][3]))
return
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("")
set_project_streams(plugin)
self.populateProjectStreams(plugin)
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
+647
View File
@@ -0,0 +1,647 @@
import os
import sys
from typing import List
#from 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.QtWidgets import (QMainWindow, QApplication, QWidget,
QListWidgetItem, QAction, QDockWidget, QVBoxLayout,
QHBoxLayout, QWidget, QLabel)
from PyQt5 import QtCore
from PyQt5.QtCore import pyqtSignal, Qt, QSize, QEvent
from PyQt5 import QtGui, uic
from specklepy.api.credentials import get_local_accounts
import importlib
from specklepy.api.wrapper import StreamWrapper
from specklepy.api.client import SpeckleClient
from specklepy.logging import metrics
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")
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
COLOR_HIGHLIGHT = (210,210,210)
SPECKLE_COLOR = (59,130,246)
SPECKLE_COLOR_LIGHT = (69,140,255)
ICON_LOGO = os.path.dirname(os.path.abspath(__file__)) + "/logo-slab-white@0.5x.png"
ICON_SEARCH = os.path.dirname(os.path.abspath(__file__)) + "/magnify.png"
ICON_DELETE = os.path.dirname(os.path.abspath(__file__)) + "/delete.png"
ICON_DELETE_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/delete-blue.png"
ICON_SEND = os.path.dirname(os.path.abspath(__file__)) + "/cube-send.png"
ICON_RECEIVE = os.path.dirname(os.path.abspath(__file__)) + "/cube-receive.png"
ICON_SEND_BLACK = os.path.dirname(os.path.abspath(__file__)) + "/cube-send-black.png"
ICON_RECEIVE_BLACK = os.path.dirname(os.path.abspath(__file__)) + "/cube-receive-black.png"
ICON_SEND_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/cube-send-blue.png"
ICON_RECEIVE_BLUE = os.path.dirname(os.path.abspath(__file__)) + "/cube-receive-blue.png"
COLOR = f"color: rgb{str(SPECKLE_COLOR)};"
BACKGR_COLOR = f"background-color: rgb{str(SPECKLE_COLOR)};"
BACKGR_COLOR_LIGHT = f"background-color: rgb{str(SPECKLE_COLOR_LIGHT)};"
ui_class = os.path.dirname(os.path.abspath(__file__)) + "/speckle_qgis_dialog_base.ui"
print(os.path.dirname(__file__))
class SpeckleGISDialog(QMainWindow):
closingPlugin = pyqtSignal()
streamList: QtWidgets.QComboBox
sendModeButton: QtWidgets.QPushButton
receiveModeButton: QtWidgets.QPushButton
streamBranchDropdown: QtWidgets.QComboBox
layerSendModeDropdown: QtWidgets.QComboBox
commitDropdown: QtWidgets.QComboBox
layersWidget: QtWidgets.QListWidget
saveLayerSelection: QtWidgets.QPushButton
runButton: QtWidgets.QPushButton
msgLog: LogWidget = None
gridLayoutTitleBar = QtWidgets.QGridLayout
def __init__(self):
"""Constructor."""
print("START MAIN WINDOW")
super(SpeckleGISDialog, self).__init__(None)#, QtCore.Qt.WindowStaysOnTopHint)
uic.loadUi(ui_class, self) # Load the .ui file
#self.installEventFilter(self)
self.show()
#self.instances.append(1)
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.saveLayerSelection.setFlat(True)
self.reloadButton.setFlat(True)
self.closeButton.setFlat(True)
#backgr_color = f"background-color: rgb{str(SPECKLE_COLOR)};"
#backgr_color_light = f"background-color: rgb{str(SPECKLE_COLOR_LIGHT)};"
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"{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"{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.sendModeButton.setStyleSheet("QPushButton {padding: 10px; border: 0px; " + f"color: rgb{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: rgb{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.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
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def addProps(self, plugin):
self.msgLog.active_account = plugin.active_account
self.msgLog.speckle_version = plugin.version
def addLabel(self, plugin):
try:
exitIcon = QPixmap(ICON_LOGO)
exitActIcon = QIcon(exitIcon)
# create a label
text_label = QtWidgets.QPushButton(" for ArcGIS")
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(QSize(300, 93))
text_label.setMinimumSize(QSize(100, 40))
text_label.setMaximumWidth(220)
version = ""
try:
if isinstance(plugin.version, str): version = str(plugin.version)
except: pass
version_label = QtWidgets.QPushButton(f"{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;"
)
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)
self.gridLayoutTitleBar.addWidget(widget) # fro QMainWindow
#self.setTitleBarWidget(widget) # for QDockWidget
except Exception as e:
logToUser(e)
def resizeEvent(self, event):
try:
#print("resize")
QtWidgets.QMainWindow.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())
except Exception as e:
#logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def closeEvent(self, event):
try:
#import threading
print("Close event")
#threads = threading.enumerate()
#print(f"Threads total: {str(len(threads))}: {str(threads)}")
#print(self.instances)
self.closingPlugin.emit()
event.accept()
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def clearDropdown(self):
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")
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.streams_add_button.clicked.connect( plugin.onStreamAddButtonClicked )
self.reloadButton.clicked.connect(lambda: self.refreshClicked(plugin))
self.closeButton.clicked.connect(lambda: self.closeClicked(plugin))
self.saveSurveyPoint.clicked.connect(plugin.set_survey_point)
self.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.commitDropdown.currentIndexChanged.connect( lambda: self.runBtnStatusChanged(plugin) )
self.closingPlugin.connect(plugin.onClosePlugin)
return
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def refreshClicked(self, plugin):
try:
try:
metrics.track("Connector Action", plugin.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 )
plugin.reloadUI()
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def closeClicked(self, plugin):
try:
try:
metrics.track("Connector Action", plugin.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 )
plugin.onClosePlugin()
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def setSendMode(self, plugin):
try:
plugin.btnAction = 0 # send
color = f"color: rgb{str(SPECKLE_COLOR)};"
self.sendModeButton.setStyleSheet("border: 0px;"
f"color: rgb{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)};" + " };")
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.commitDropdown.setEnabled(False)
self.messageInput.setEnabled(True)
self.layerSendModeDropdown.setEnabled(True)
self.runBtnStatusChanged(plugin)
return
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def setReceiveMode(self, plugin):
try:
plugin.btnAction = 1 # receive
color = f"color: rgb{str(SPECKLE_COLOR)};"
self.receiveModeButton.setStyleSheet("border: 0px;"
f"color: rgb{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)};" + " };")
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.commitDropdown.setEnabled(True)
self.layersWidget.setEnabled(False)
self.messageInput.setEnabled(False)
self.saveLayerSelection.setEnabled(False)
self.layerSendModeDropdown.setEnabled(False)
self.runBtnStatusChanged(plugin)
return
except Exception as e:
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)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def runBtnStatusChanged(self, plugin):
try:
commitStr = str(self.commitDropdown.currentText())
branchStr = str(self.streamBranchDropdown.currentText())
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 branchStr != "" and self.layerSendModeDropdown.currentIndex() == 1 and len(plugin.current_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)
def layerSendModeChange(self, plugin, runMode = None):
try:
print("Send mode changed")
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:
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(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def populateLayerDropdown(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
except:
from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import set_project_layer_selection
try:
self.layersWidget.clear()
nameDisplay = []
project = plugin.gis_project
if bySelection is False: # read from project data
print("populate layers from saved data")
#print(project)
#print(project.activeMap)
all_layers_ids = [l.dataSource for l in getAllProjLayers(project)]
for layer_tuple in plugin.current_layers:
if layer_tuple[1].dataSource in all_layers_ids:
listItem = self.fillLayerList(layer_tuple[1])
self.layersWidget.addItem(listItem)
else: # read selected layers
# 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")
self.layersWidget.setIconSize(QSize(20, 20))
self.runBtnStatusChanged(plugin)
return
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def fillLayerList(self, layer):
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)
if layer.isRasterLayer: # and layer.width()*layer.height() > 1000000:
listItem.setIcon(QIcon(ICON_RASTER))
elif layer.isFeatureLayer: # and layer.featureCount() > 20000:
geomType = arcpy.Describe(layer.dataSource).shapeType
if geomType == "Polygon": listItem.setIcon(QIcon(ICON_POLYGON))
elif geomType == "Polyline": listItem.setIcon(QIcon(ICON_LINE))
elif geomType == "Point" or geomType == "Multipoint": listItem.setIcon(QIcon(ICON_POINT))
else:
listItem.setIcon(QIcon(ICON_XXL))
#else:
# icon = QgsIconUtils().iconForLayer(layer)
# listItem.setIcon(icon)
return listItem
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)
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.commitDropdown.setEnabled(False)
self.show()
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 Stream"])
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)
def onActiveStreamChanged(self, plugin):
if not self: return
try:
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)):
self.populateProjectStreams(plugin)
plugin.onStreamCreateClicked()
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
self.populateActiveStreamBranchDropdown(plugin)
self.populateActiveCommitDropdown(plugin)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def populateLayerSendModeDropdown(self):
if not self: return
try:
self.layerSendModeDropdown.clear()
self.layerSendModeDropdown.addItems(
["Send visible layers", "Send saved layers"]
)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def populateActiveStreamBranchDropdown(self, plugin):
if not self: return
if plugin.active_stream is None: return
try:
self.streamBranchDropdown.clear()
if isinstance(plugin.active_stream[1], SpeckleException):
#logger.logToUser("Some streams cannot be accessed", Qgis.Warning)
return
elif plugin.active_stream is None or plugin.active_stream[1] is None or plugin.active_stream[1].branches is None:
return
self.streamBranchDropdown.addItems(
[f"{branch.name}" for branch in plugin.active_stream[1].branches.items]
)
self.streamBranchDropdown.addItems(["Create New Branch"])
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
def populateActiveCommitDropdown(self, plugin):
if not self: return
try:
self.commitDropdown.clear()
if plugin.active_stream is None: return
branchName = self.streamBranchDropdown.currentText()
if branchName == "": return
if branchName == "Create New Branch":
self.streamBranchDropdown.setCurrentText("main")
plugin.onBranchCreateClicked()
return
branch = None
if isinstance(plugin.active_stream[1], SpeckleException):
#logger.logToUser("Some streams cannot be accessed", Qgis.Warning)
return
elif plugin.active_stream[1]:
for b in plugin.active_stream[1].branches.items:
if b.name == branchName:
branch = b
break
try:
self.commitDropdown.addItems(
[f"{commit.id}"+ " | " + f"{commit.message}" for commit in branch.commits.items]
)
except: pass
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("")
#set_project_streams(plugin)
self.populateProjectStreams(plugin)
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin=self)
+312
View File
@@ -0,0 +1,312 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SpeckleQGISDialogBase</class>
<widget class="QDockWidget" name="SpeckleQGISDialogBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>575</width>
<height>651</height>
</rect>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item row="0" column="1">
<layout class="QHBoxLayout" name="streamListButtons">
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="streamListLabel">
<property name="text">
<string>Stream</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="streamListButtons" stretch="20,1">
<item>
<widget class="QComboBox" name="streamList"/>
</item>
<item>
<widget class="QPushButton" name="streams_add_button">
<property name="text">
<string> </string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="streams_remove_button">
<property name="text">
<string> </string>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>10</width>
<height>10</height>
</rect>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="streamBranchLabel">
<property name="text">
<string>Branch</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="streamBranchDropdown"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="commitLabel">
<property name="text">
<string>Commit</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="commitDropdown"/>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
</layout>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="sendModeButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="receiveModeButton">
<property name="text">
<string>Receive</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="layerSendModeDropdown"/>
</item>
<item row="7" column="1">
<widget class="QListWidget" name="layersWidget">
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="resizeMode">
<enum>QListView::Fixed</enum>
</property>
<property name="viewMode">
<enum>QListView::ListMode</enum>
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="saveLayerSelection">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Save current layer selection</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="9" column="0">
<widget class="QLabel" name="messageLabel">
<property name="text">
<string>Message</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLineEdit" name="messageInput">
<property name="placeholderText">
<string>Sent XXX objects from QGIS</string>
</property>
</widget>
</item>
<item row="10" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="runButton">
<property name="text">
<string> SEND</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
</layout>
</item>
<item row="11" column="0">
<widget class="QLabel" name="surveyPointLabel">
<property name="text">
<string>Lat, Lon</string>
</property>
</widget>
</item>
<item row="11" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="surveyPointLat">
<property name="placeholderText">
<string>0.0</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="surveyPointLon">
<property name="placeholderText">
<string>0.0</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="saveSurveyPoint">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Set as a project center</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="12" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="reloadButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
+316
View File
@@ -0,0 +1,316 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SpeckleQArcGISDialog</class>
<widget class="QMainWindow" name="SpeckleQArcGISDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>600</height>
</rect>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayoutTitleBar">
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="leftMargin">
<number>20</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>30</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item row="0" column="1">
<layout class="QHBoxLayout" name="streamListButtons">
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="streamListLabel">
<property name="text">
<string>Stream</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="streamListButtons" stretch="20,1">
<item>
<widget class="QComboBox" name="streamList"/>
</item>
<item>
<widget class="QPushButton" name="streams_add_button">
<property name="text">
<string> </string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="streams_remove_button">
<property name="text">
<string> </string>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>10</width>
<height>10</height>
</rect>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="streamBranchLabel">
<property name="text">
<string>Branch</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="streamBranchDropdown"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="commitLabel">
<property name="text">
<string>Commit</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="commitDropdown"/>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
</layout>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="sendModeButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Send</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="receiveModeButton">
<property name="text">
<string>Receive</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="layerSendModeDropdown"/>
</item>
<item row="7" column="1">
<widget class="QListWidget" name="layersWidget">
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="resizeMode">
<enum>QListView::Fixed</enum>
</property>
<property name="viewMode">
<enum>QListView::ListMode</enum>
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="saveLayerSelection">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Set visible layers as selection</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="9" column="0">
<widget class="QLabel" name="messageLabel">
<property name="text">
<string>Message</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLineEdit" name="messageInput">
<property name="placeholderText">
<string>Sent XXX objects from ArcGIS</string>
</property>
</widget>
</item>
<item row="10" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="runButton">
<property name="text">
<string> SEND</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
</layout>
</item>
<item row="11" column="0">
<widget class="QLabel" name="surveyPointLabel">
<property name="text">
<string>Lat, Lon</string>
</property>
</widget>
</item>
<item row="11" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="surveyPointLat">
<property name="placeholderText">
<string>0.0</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="surveyPointLon">
<property name="placeholderText">
<string>0.0</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="saveSurveyPoint">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Set as a project center</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="12" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="reloadButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>