110 Commits

Author SHA1 Message Date
KatKatKateryna 9cd480fb6e terminology 2024-02-29 17:46:13 +00:00
KatKatKateryna 3b1d62c322 typo 2024-02-29 13:22:22 +00:00
KatKatKateryna 4ee673d9cd Change terminology to FE2 2024-02-29 13:19:24 +00:00
KatKatKateryna ed66a0fb2e Merge branch 'main' into 2.19 2024-02-23 14:24:19 +00:00
KatKatKateryna 18435bf4af add reports placeholders for regression testing 2024-02-20 00:09:12 +00:00
KatKatKateryna 61af1d7676 typo 2024-02-12 20:46:53 +00:00
KatKatKateryna bb081a9b52 Merge pull request #10 from specklesystems/2.18-fixes-for-arcgis
2.18 fixes for arcgis
2024-02-13 03:26:34 +08:00
KatKatKateryna 563b54aa60 add workspace property 2024-02-12 13:28:26 +00:00
KatKatKateryna 0b099810e5 don't pin window on launch 2024-02-12 00:03:43 +00:00
KatKatKateryna 30c3514bde get layers with structure 2024-02-11 18:15:45 +00:00
KatKatKateryna 97adaca60a fix arcgis report on receive 2024-02-10 23:17:35 +00:00
KatKatKateryna 66f45e8147 arcgis support: crs format; pin window 2024-02-10 19:00:28 +00:00
KatKatKateryna 8d505db4fd btn active 2024-02-07 18:07:42 +00:00
KatKatKateryna 5a85a97785 support other CRS data structures; saved layers in MainWindow 2024-02-07 17:37:23 +00:00
KatKatKateryna 04e4f43972 resize 2024-02-07 00:56:26 +00:00
KatKatKateryna ef07bedc26 fixes_main window 2024-02-07 00:26:07 +00:00
KatKatKateryna 97f2afedfb update main_window 2024-02-06 23:53:38 +00:00
KatKatKateryna ae83a21179 Merge pull request #9 from specklesystems/2.18
2.18
2024-02-06 03:20:48 +08:00
KatKatKateryna 240efef89a Merge branch 'main' into 2.18 2024-02-05 18:39:20 +00:00
KatKatKateryna 6db462df3c Revert "Change terminology to FE2"
This reverts commit 876068a39a.
2024-01-31 15:09:25 +00:00
KatKatKateryna 4600fa5167 Change terminology to FE2 2024-01-31 15:08:43 +00:00
KatKatKateryna 876068a39a Change terminology to FE2 2024-01-30 20:08:56 +00:00
KatKatKateryna d0c464d736 pack pyqt5 separately 2024-01-18 22:20:23 +00:00
KatKatKateryna ea76011ed2 fix icon's path 2024-01-13 17:16:47 +00:00
KatKatKateryna 51b1de4587 place Assets into Utils 2023-12-07 19:04:41 +08:00
KatKatKateryna 2a577de45c remove imports names overlap 2023-12-07 02:16:39 +08:00
KatKatKateryna 937ff8a878 backup import from 'speckle' submodule 2023-12-07 02:09:55 +08:00
KatKatKateryna d2451a41d8 create utils, import logger from UI repo 2023-12-07 01:56:21 +08:00
KatKatKateryna 912ba2d734 get branch from URL 2023-12-05 05:51:10 +08:00
KatKatKateryna 99a1c99422 get stream via client; fix branch name from url formatting; remove shell keyword from subprocess 2023-12-05 00:20:29 +08:00
KatKatKateryna 62733b13d8 Merge pull request #7 from specklesystems/2.17-hotfixes
2.17 hotfixes
2023-11-13 23:54:37 +00:00
KatKatKateryna 3ebe5c478d remove prints 2023-11-13 23:54:08 +00:00
KatKatKateryna fd340a2026 handle cancel operations through "emit"; formatting 2023-11-13 21:17:43 +00:00
KatKatKateryna b73753d15c adjust to fe2 stream wrapper 2023-11-10 12:54:54 +00:00
KatKatKateryna 90285387f9 add time checks to datastorage 2023-11-04 00:13:17 +00:00
KatKatKateryna f773b1b7c6 formatting 2023-11-03 19:00:10 +00:00
KatKatKateryna 91ee94a965 add DataStorage to dependencies window 2023-10-31 10:21:19 +00:00
KatKatKateryna 3fc8428e5a metric for resolving dependencies 2023-10-31 09:07:00 +00:00
KatKatKateryna 2d42f13f49 delete extra license 2023-10-30 16:13:42 +00:00
KatKatKateryna 2e1afa7f9c add error symbol to messages, bigger window with dependencies error 2023-10-30 13:12:04 +00:00
KatKatKateryna 65a1f4730c show full error logs 2023-10-30 12:34:11 +00:00
KatKatKateryna 1c4dc0c07e widget for upgrading dependencies; line breaks in notifications; formatting 2023-10-28 01:04:26 +01:00
KatKatKateryna 11c6129e81 Merge pull request #6 from specklesystems/2.16-fixes_rc
proper operation cancel; cancel and logo metrics; error-proof report btn
2023-09-30 04:49:45 +08:00
KatKatKateryna ca6b63fea8 cancel and logo metrics; error-proof report btn 2023-09-29 19:50:24 +01:00
KatKatKateryna eb7f90dacb Merge pull request #5 from specklesystems/2.16
2.16
2023-09-08 19:57:22 +01:00
KatKatKateryna 483ffcf3c2 Merge branch 'main' into 2.16 2023-09-08 19:55:08 +01:00
KatKatKateryna ff5c3f3ad4 change Report btn appearance if there are errors 2023-09-08 18:35:37 +01:00
KatKatKateryna 239ca78102 remove prints 2023-09-07 05:23:15 +01:00
KatKatKateryna 1cfad1ab47 btn status changed fix 2023-09-06 23:33:55 +01:00
KatKatKateryna 1db92e6a21 add host app to receiving report 2023-08-22 12:09:11 +01:00
KatKatKateryna 1b784e6699 _signal for Cancel Operation 2023-08-22 11:57:14 +01:00
KatKatKateryna 21ca787be9 _remove prints 2023-08-21 12:54:59 +01:00
KatKatKateryna 54c81046cf _fix 2023-08-19 16:14:14 +01:00
KatKatKateryna 31d66e2a50 add signal for Excel data 2023-08-17 12:34:50 +01:00
KatKatKateryna b0956c215a _added signal 2023-08-16 19:37:05 +01:00
KatKatKateryna 26ebf81727 manage commit id set 2023-08-16 18:54:48 +01:00
KatKatKateryna dec8d1c64c catch report errors 2023-08-16 13:11:23 +01:00
KatKatKateryna 213db29bc0 _fix 2023-08-15 19:16:51 +01:00
KatKatKateryna db271e879d _fix 2023-08-15 15:54:16 +01:00
KatKatKateryna 7c69b4fbc0 _standard naming for report layers 2023-08-15 15:48:27 +01:00
KatKatKateryna 8bd86ca029 report btn 2023-08-15 14:51:40 +01:00
KatKatKateryna 2b6f82e13f set active commit correctly 2023-08-15 13:33:31 +01:00
KatKatKateryna e30ba5d5ce display Report dialog 2023-08-15 02:16:42 +01:00
KatKatKateryna 5a9e69ea2f report class 2023-08-04 18:56:57 +01:00
KatKatKateryna 4075fe3dde add Report btn (no style yet) 2023-08-04 17:08:56 +01:00
KatKatKateryna fa6855cd7c _remove .name if branch is None 2023-08-03 13:52:31 +01:00
KatKatKateryna 12e17283fb keep active_branch and commit objects throughout populating dropdowns (if not None) 2023-08-03 09:55:57 +01:00
KatKatKateryna 9abe5538c9 _active elements only when commits available 2023-08-02 12:21:47 +01:00
KatKatKateryna 9bda01eea6 commit link with logo 2023-08-01 18:27:18 +01:00
KatKatKateryna 74da8af536 add DataStorage to LogWidget 2023-07-28 13:56:58 +01:00
KatKatKateryna f2a263b399 exit if stream is Exception 2023-07-28 11:10:00 +01:00
KatKatKateryna 7052a5cd81 commit from url search 2023-07-27 23:24:02 +01:00
KatKatKateryna c3c0f6a202 url set branch 2023-07-27 22:45:20 +01:00
KatKatKateryna dd429e2996 set as modules 2023-07-26 21:13:15 +01:00
KatKatKateryna bbcc946a2a remove prints 2023-07-26 17:34:46 +01:00
KatKatKateryna 17a0d311d5 assign dataStorage to LogWidget 2023-07-25 19:04:52 +01:00
KatKatKateryna 2dd3b5e179 remove btn url when expired 2023-07-25 18:53:39 +01:00
KatKatKateryna 8c07bf8f41 Merge branch 'main' into 2.16 2023-07-19 20:23:08 +01:00
KatKatKateryna 01f7c0c8cb metrics to Core 2023-07-19 20:22:48 +01:00
KatKatKateryna 15f6fa50a6 separate button functions: openURL vs cancelOperation 2023-07-19 20:21:41 +01:00
KatKatKateryna a4694c36f9 move ReloadUI 2023-07-19 19:01:30 +01:00
KatKatKateryna 5974a94759 metrics to Core 2023-07-11 16:24:07 +01:00
KatKatKateryna 6cfee19bed shorten app alias 2023-07-07 19:54:23 +01:00
KatKatKateryna 0e8ca45a0c track account selection before stream search 2023-07-07 13:58:35 +01:00
KatKatKateryna 42a25eb085 Merge pull request #4 from specklesystems/initial_version
add commit hostApp to the dropdown
2023-07-05 19:07:57 +01:00
KatKatKateryna 2ed4e2d0ab add commit hostApp to the dropdown 2023-07-05 19:07:41 +01:00
KatKatKateryna beb04e0cc5 Merge pull request #3 from specklesystems/initial_version
layer_offsets_added to DataStorage
2023-07-05 09:06:25 +08:00
KatKatKateryna 52ae8b96ba layer_offsets_added to DataStorage 2023-07-04 22:55:30 +01:00
KatKatKateryna fb36569263 Merge pull request #2 from specklesystems/initial_version
Fixes for 2.15
2023-07-04 20:50:41 +08:00
KatKatKateryna 098cceac0a _typo 2023-07-04 13:50:05 +01:00
KatKatKateryna c57b502e03 Merge branch 'main' into initial_version 2023-07-04 12:08:56 +01:00
KatKatKateryna de2c90e9dc FE2 urls; display account streams immediately; link to the Speckle website from logo 2023-07-04 12:08:45 +01:00
KatKatKateryna e33bec3e89 Create LICENSE 2023-06-26 18:19:28 +08:00
KatKatKateryna fc893148b8 Merge pull request #1 from specklesystems/initial_version
Initial version
2023-06-26 17:47:15 +08:00
KatKatKateryna e4f9e0bf11 removing references 2023-06-26 10:45:46 +01:00
KatKatKateryna 2295e145c7 separating qgis-specific functions 2023-06-23 22:16:42 +01:00
KatKatKateryna 7b475233c7 cleaning offsets settings 2023-06-22 15:15:20 +01:00
KatKatKateryna 4b17aef8d3 custom CRS hints 2023-06-22 02:37:16 +01:00
KatKatKateryna 720ed755e9 unfreeze panel on reload 2023-06-22 01:46:56 +01:00
KatKatKateryna d124a3d6f7 offsets with menu 2023-06-22 01:24:28 +01:00
KatKatKateryna 1abafd2ecf offsets and rotation 2023-06-21 18:19:01 +01:00
KatKatKateryna 1146a55614 assets 2023-06-21 12:40:03 +01:00
KatKatKateryna 8da2270781 generalize signals 2023-06-15 14:31:24 +01:00
KatKatKateryna a5d85b1b64 print log messages 2023-06-15 11:05:55 +01:00
KatKatKateryna c796136b5b separating dropdowns 2023-06-15 00:27:19 +01:00
KatKatKateryna f12f468dd9 remove copyright from code 2023-06-14 22:04:09 +01:00
KatKatKateryna 1425f2874b PyQt5 references 2023-06-13 18:38:46 +01:00
KatKatKateryna dfb20fa4ad groupping resources 2023-06-13 18:26:56 +01:00
KatKatKateryna df86bab6ce support for floating main window 2023-06-13 18:01:38 +01:00
KatKatKateryna 4f2b1a5545 start 2023-06-13 17:55:56 +01:00
53 changed files with 5288 additions and 1 deletions
+1
View File
@@ -0,0 +1 @@
*.pyc
-1
View File
@@ -1 +0,0 @@
# PyQt-UI
View File
+106
View File
@@ -0,0 +1,106 @@
from datetime import datetime
import inspect
from typing import List, Optional, Tuple, Union, Any
import webbrowser
try:
from specklepy_qt_ui.qt_ui.utils.logger import logToUser
except ModuleNotFoundError:
from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser
from specklepy.core.api.credentials import get_local_accounts
class DataStorage:
plugin_version = "0.0.99"
project = None
accounts = None
active_account = None
default_account = None
currentCRS = None
currentUnits = "m"
currentOriginalUnits = ""
workspace = ""
custom_lat: Optional[float] = None
custom_lon: Optional[float] = None
crs_offset_x: Optional[float] = 0
crs_offset_y: Optional[float] = 0
crs_rotation: Optional[float] = 0
current_layer_crs_offset_x: Optional[float] = None
current_layer_crs_offset_y: Optional[float] = None
current_layer_crs_rotation: Optional[float] = None
current_layers: Union[List[Tuple[Any, str, str]], None] = None
saved_layers: Union[List, None] = None
all_layers: Union[List, None] = None
elevationLayer: None
savedTransforms: Union[List, None] = None
transformsCatalog: Union[List, None] = None
matrix = None # if receiving instance with transform
latestHostApp: str = ""
latestActionReport: Optional[list] = None
latestActionFeaturesReport: Optional[list] = None
latestActionTime: str = ""
latestTransferTime: datetime = None
latestConversionTime: datetime = None
latestActionLayers: Optional[list] = None
latestActionUnits: str = ""
flat_report_receive: dict = {}
flat_report_latest: dict = {}
def __init__(self):
# print("hello")
# self.streamsToFollow.append(("https://speckle.xyz/streams/17b0b76d13/branches/random_tests", "", "09a0f3e41a"))
self.transformsCatalog = [
"Convert Raster Elevation to a 3d Mesh",
"Set Raster as a Texture for the Elevation Layer",
"Extrude polygons by selected attribute (randomly populate NULL values)",
"Extrude polygons by selected attribute (ignore NULL values)",
"Extrude polygons by selected attribute (randomly populate NULL values) and project on 3d elevation",
"Extrude polygons by selected attribute (ignore NULL values) and project on 3d elevation",
]
self.savedTransforms = []
self.all_layers = []
self.current_layers = []
self.saved_layers = []
self.accounts = []
self.elevationLayer = None
self.latestActionReport = []
self.latestActionFeaturesReport = []
self.latestActionLayers = []
def check_for_accounts(self):
try:
def go_to_manager():
webbrowser.open("https://speckle-releases.netlify.app/")
accounts = get_local_accounts()
self.accounts = accounts
if len(accounts) == 0:
logToUser(
"No accounts were found. Please remember to install the Speckle Manager and setup at least one account",
level=1,
url="https://speckle-releases.netlify.app/",
func=inspect.stack()[0][3],
plugin=self.dockwidget,
) # , action_text="Download Manager", callback=go_to_manager)
return False
for acc in accounts:
if acc.isDefault:
self.default_account = acc
self.active_account = acc
break
return True
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
+350
View File
@@ -0,0 +1,350 @@
import inspect
from typing import Any, List
from PyQt5 import QtCore
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QVBoxLayout, QWidget, QPushButton, QHBoxLayout
import webbrowser
from specklepy.logging import metrics
from specklepy.core.api.credentials import Account
try:
from specklepy_qt_ui.qt_ui.utils.global_resources import (
BACKGR_COLOR,
BACKGR_COLOR_LIGHT,
BACKGR_COLOR_GREY,
BACKGR_COLOR_TRANSPARENT,
BACKGR_COLOR_HIGHLIGHT,
NEW_GREY,
NEW_GREY_HIGHLIGHT,
BACKGR_ERROR_COLOR,
BACKGR_ERROR_COLOR_LIGHT,
)
from specklepy_qt_ui.qt_ui.widget_dependencies_upgrade import (
DependenciesUpgradeDialog,
)
from specklepy_qt_ui.qt_ui.widget_report import ReportDialog
except ModuleNotFoundError:
from speckle.specklepy_qt_ui.qt_ui.utils.global_resources import (
BACKGR_COLOR,
BACKGR_COLOR_LIGHT,
BACKGR_COLOR_GREY,
BACKGR_COLOR_TRANSPARENT,
BACKGR_COLOR_HIGHLIGHT,
NEW_GREY,
NEW_GREY_HIGHLIGHT,
BACKGR_ERROR_COLOR,
BACKGR_ERROR_COLOR_LIGHT,
)
from speckle.specklepy_qt_ui.qt_ui.widget_dependencies_upgrade import (
DependenciesUpgradeDialog,
)
from speckle.specklepy_qt_ui.qt_ui.widget_report import ReportDialog
class LogWidget(QWidget):
dataStorage = None
msgs: List[str] = []
used_btns: List[int] = []
btns: List[QPushButton]
max_msg: int
sendMessage = pyqtSignal(object)
reportBtn = None
active_account: Account
speckle_version: str
dockwidget: Any = None
reportDialog: Any = None
# constructor
def __init__(self, parent=None):
super(LogWidget, self).__init__(parent)
self.parentWidget = parent
# print(self.parentWidget)
self.max_msg = 8
# create a temporary floating button
width = 0 # parent.frameSize().width()
height = 0 # parent.frameSize().height()
self.setAttribute(QtCore.Qt.WA_StyledBackground, True)
self.setStyleSheet("background-color: rgba(250,250,250,80);")
self.layout = QVBoxLayout(self)
self.layout.setContentsMargins(0, 60, 10, 20)
self.layout.setAlignment(Qt.AlignBottom)
self.setGeometry(0, 0, width, height)
self.createBtns()
self.hide()
def createBtns(self):
# generate 100 buttons to use later
self.btns = []
for i in range(self.max_msg):
button = QPushButton(f"👌 Error") # to '{streamName}' Sent , v
# button.setStyleSheet("QPushButton {color: black; border: 0px;border-radius: 17px;padding: 20px;height: 40px;text-align: left;"+ f"{BACKGR_COLOR_GREY}" + "}")
button.clicked.connect(lambda: self.btnClicked())
self.btns.append(button)
# overriding the mouseReleaseEvent method
def mouseReleaseEvent(self, event):
# print("Mouse Release Event")
self.hide()
# self.parentWidget.hideError()
def hide(self):
# print("___HIDE LOG WIDGET")
self.setGeometry(0, 0, 0, 0)
# remove all buttons
for i in reversed(range(self.layout.count())):
# print(self.layout.itemAt(i))
# print(self.layout.itemAt(i).widget())
self.layout.itemAt(i).widget().setParent(None)
self.createBtns()
# remove list of used btns
self.used_btns.clear()
self.msgs.clear()
def addButton(self, obj: dict):
# print("Add button")
text: str = obj["text"]
level: int = obj["level"]
url: str = obj["url"]
blue: bool = obj["blue"]
report: bool = obj["report"]
self.setGeometry(
0,
0,
self.parentWidget.frameSize().width(),
self.parentWidget.frameSize().height(),
)
# find index of the first unused button
btn, index = self.getNextBtn()
# print(btn)
btn.setAccessibleName(url)
# print(btn)
btn.setText(text)
self.resizeToText(btn)
widget = QWidget()
boxLayout = QHBoxLayout(widget)
spacer = QPushButton("")
spacer.setStyleSheet(
"QPushButton {padding:0px;" + f"{BACKGR_COLOR_TRANSPARENT}" + "}"
)
spacer.setMaximumWidth(10)
# add btns to widget layout
boxLayout.addWidget(btn) # , alignment=Qt.AlignCenter)
# add report
reportBtn = QPushButton(f"☑️ Report") # 📈 to '{streamName}' Sent , v
reportBtn.clicked.connect(lambda: self.showReport())
reportBtn.setMaximumWidth(150)
reportBtn.setStyleSheet(
"QPushButton {color: white; border-radius: 17px;padding:0px;padding-left: 10px;padding-right: 10px;text-align: center;"
+ f"{NEW_GREY}"
+ "} QPushButton:hover { "
+ f"{NEW_GREY_HIGHLIGHT}"
+ " }"
)
if report is True:
# color report btn
reportList = self.dataStorage.latestActionReport
if reportList is not None:
for item in reportList:
if item["errors"] != "":
reportBtn.setText("⚠️ Report")
# reportBtn.setStyleSheet("QPushButton {color: white; border-radius: 17px;padding:0px;padding-left: 10px;padding-right: 10px;text-align: center;"+ f"{BACKGR_ERROR_COLOR}" + "} QPushButton:hover { "+ f"{BACKGR_ERROR_COLOR_LIGHT}" + " }")
break
boxLayout.addWidget(reportBtn)
boxLayout.addWidget(spacer)
if url != "":
widget.setStyleSheet(
"QWidget {border-radius: 17px;padding: 20px;height: 40px;text-align: left;"
+ f"{BACKGR_COLOR}"
+ "} QWidget:hover { "
+ f"{BACKGR_COLOR_LIGHT}"
+ " }"
)
btn.setStyleSheet(
"QPushButton {color: white;border: 0px; padding:0px; padding-left: 10px;text-align: left;"
+ f"{BACKGR_COLOR_TRANSPARENT}"
+ "}"
)
else: # without url
if blue is False:
widget.setStyleSheet(
"QWidget {border-radius: 17px;padding: 20px;height: 40px;text-align: left;"
+ f"{BACKGR_COLOR_GREY}"
+ "}"
)
btn.setStyleSheet(
"QPushButton {color: black; border: 0px; padding:0px; padding-left: 10px;text-align: left;"
+ f"{BACKGR_COLOR_TRANSPARENT}"
+ "}"
)
else: # blue, no URL (after receive)
widget.setStyleSheet(
"QWidget {border-radius: 17px;padding: 20px;height: 40px;text-align: left;"
+ f"{BACKGR_COLOR}"
+ "}"
)
btn.setStyleSheet(
"QPushButton {color: white;border: 0px; padding:0px; padding-left: 10px;text-align: left;"
+ f"{BACKGR_COLOR_TRANSPARENT}"
+ "}"
)
self.reportBtn = reportBtn
self.layout.addWidget(widget) # , alignment=Qt.AlignCenter)
self.msgs.append(text)
self.used_btns.append(1)
def showReport(self):
self.reportDialog = ReportDialog()
self.reportDialog.dataStorage = self.dataStorage
self.reportDialog.applyReport()
self.reportDialog.show()
return
def openURL(self, url: str = ""):
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{"name": "Open In Web", "connector_version": str(self.speckle_version)},
)
except Exception as e:
print(e)
print(inspect.stack()[0][3])
if url is not None and url != "":
webbrowser.open(url, new=0, autoraise=True)
def btnClicked(self, url=""):
try:
btn = self.sender()
url = btn.accessibleName()
if url == "" or not isinstance(url, str):
return
elif isinstance(url, str):
if url.startswith("http"):
self.openURL(url)
elif url.startswith("dependencies"):
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Details on resolving dependencies",
"connector_version": str(
self.dataStorage.plugin_version
),
},
)
except Exception as e:
print(e)
self.dependenciesDialog = DependenciesUpgradeDialog()
self.dependenciesDialog.dataStorage = self.dataStorage
self.dependenciesDialog.show()
elif url.startswith("cancel"):
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Cancel Operation",
"connector_version": str(
self.dataStorage.plugin_version
),
},
)
except Exception as e:
print(e)
self.hide()
self.parentWidget.cancelOperations()
except Exception as e:
print(e)
pass # logger.logToUser(str(e), level=2, func = inspect.stack()[0][3])
# self.hide()
def getNextBtn(self):
index = len(self.used_btns) # get the next "free" button
if index >= len(self.btns):
# remove first button
print(self.layout.itemAt(0).widget())
self.layout.itemAt(0).widget().setParent(None)
self.createBtns()
index = 0
btn = self.btns[index]
return btn, index
def getLastBtn(self):
index = len(self.used_btns) - 1 # get the next "free" button
btn = None
if index > 0:
btn = self.btns[index]
return btn, index
def getBtnByKeyword(self, keyword: str):
try:
new_btn = None
for btn in self.btns:
url = btn.accessibleName()
if keyword in url:
new_btn = btn
break
return new_btn
except Exception as e:
print(e)
def removeBtnUrl(self, keyword: str = "cancel"):
try:
btn = self.getBtnByKeyword(keyword)
if btn is not None:
btn.setAccessibleName("")
btn.setStyleSheet(
"QPushButton {color: black; border: 0px; padding-left: 10px;text-align: left;"
+ f"{BACKGR_COLOR_TRANSPARENT}"
+ "}"
)
# widget.setStyleSheet("QWidget {border-radius: 17px;padding: 20px;height: 40px;text-align: left;"+ f"{BACKGR_COLOR_GREY}" + "}")
# btn.setStyleSheet("QPushButton {color: black}")
btn.parent().setStyleSheet(
"QWidget {border-radius: 17px;padding-left: 10px;height: 40px;text-align: left;"
+ f"{BACKGR_COLOR_GREY}"
+ "}"
)
except Exception as e:
print(e)
def resizeToText(self, btn):
try:
text = btn.text()
# if len(text.split("\n"))>2:
height = len(text.split("\n")) * 25 + 20
btn.setMinimumHeight(height)
return btn
except Exception as e:
print(e)
return btn
+4
View File
@@ -0,0 +1,4 @@
# specklepy_qt_ui
Python-based Qt UI for Speckle connectors
Based on PyQt5
View File
+954
View File
@@ -0,0 +1,954 @@
# -*- coding: utf-8 -*-
from copy import copy
import inspect
import os
try:
from specklepy_qt_ui.qt_ui.widget_transforms import MappingSendDialog
from specklepy_qt_ui.qt_ui.LogWidget import LogWidget
from specklepy_qt_ui.qt_ui.utils.logger import logToUser
from specklepy_qt_ui.qt_ui.utils.utils import constructCommitURL
from specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from specklepy_qt_ui.qt_ui.utils.global_resources import (
COLOR_HIGHLIGHT,
SPECKLE_COLOR,
SPECKLE_COLOR_LIGHT,
ICON_OPEN_WEB,
ICON_REPORT,
ICON_LOGO,
ICON_SEARCH,
ICON_DELETE,
ICON_DELETE_BLUE,
ICON_SEND,
ICON_RECEIVE,
ICON_SEND_BLACK,
ICON_RECEIVE_BLACK,
ICON_SEND_BLUE,
ICON_RECEIVE_BLUE,
COLOR,
BACKGR_COLOR,
BACKGR_COLOR_LIGHT,
ICON_XXL,
ICON_RASTER,
ICON_POLYGON,
ICON_LINE,
ICON_POINT,
ICON_GENERIC,
)
except ModuleNotFoundError:
from speckle.specklepy_qt_ui.qt_ui.widget_transforms import MappingSendDialog
from speckle.specklepy_qt_ui.qt_ui.LogWidget import LogWidget
from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser
from speckle.specklepy_qt_ui.qt_ui.utils.utils import constructCommitURL
from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from speckle.specklepy_qt_ui.qt_ui.utils.global_resources import (
COLOR_HIGHLIGHT,
SPECKLE_COLOR,
SPECKLE_COLOR_LIGHT,
ICON_OPEN_WEB,
ICON_REPORT,
ICON_LOGO,
ICON_SEARCH,
ICON_DELETE,
ICON_DELETE_BLUE,
ICON_SEND,
ICON_RECEIVE,
ICON_SEND_BLACK,
ICON_RECEIVE_BLACK,
ICON_SEND_BLUE,
ICON_RECEIVE_BLUE,
COLOR,
BACKGR_COLOR,
BACKGR_COLOR_LIGHT,
ICON_XXL,
ICON_RASTER,
ICON_POLYGON,
ICON_LINE,
ICON_POINT,
ICON_GENERIC,
)
from specklepy.logging.exceptions import SpeckleException, GraphQLException
from specklepy.logging import metrics
from PyQt5 import QtWidgets, uic
from PyQt5.QtGui import QIcon, QPixmap, QCursor
from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget
from PyQt5 import QtCore
from PyQt5.QtCore import pyqtSignal
# 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"))
)
class SpeckleQGISDialog(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
custom_crs_modal = None
signal_1 = pyqtSignal(object)
signal_2 = pyqtSignal(object)
signal_3 = pyqtSignal(object)
signal_4 = pyqtSignal(object)
signal_5 = pyqtSignal(object)
signal_6 = pyqtSignal(object)
signal_remove_btn_url = pyqtSignal(str)
signal_cancel_operation = pyqtSignal()
def __init__(self, parent=None):
"""Constructor."""
super(SpeckleQGISDialog, 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.runAllSetup()
def runAllSetup(self):
self.streamBranchDropdown.setMaxCount(100)
self.commitDropdown.setMaxCount(100)
self.streams_add_button.setFlat(True)
self.streams_remove_button.setFlat(True)
self.commit_web_view.setFlat(True)
self.reportBtn.setFlat(True)
# self.saveSurveyPoint.setFlat(True)
self.saveLayerSelection.setFlat(True)
self.reloadButton.setFlat(True)
self.closeButton.setFlat(True)
self.commit_web_view.setEnabled(False)
# https://stackoverflow.com/questions/67585501/pyqt-how-to-use-hover-in-button-stylesheet
backgr_image_del = f"border-image: url({ICON_DELETE_BLUE});"
self.streams_add_button.setIcon(QIcon(ICON_SEARCH))
self.streams_add_button.setMaximumWidth(25)
self.streams_add_button.setStyleSheet(
"QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { "
+ f"background-color: rgba{str(COLOR_HIGHLIGHT)};"
+ f"{COLOR}"
+ " }"
)
self.commit_web_view.setIcon(QIcon(ICON_OPEN_WEB))
self.commit_web_view.setMaximumWidth(25)
self.commit_web_view.setStyleSheet(
"QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { "
+ f"background-color: rgba{str(COLOR_HIGHLIGHT)};"
+ f"{COLOR}"
+ " }"
)
self.reportBtn.setIcon(QIcon(ICON_REPORT))
self.reportBtn.setMaximumWidth(25)
self.reportBtn.setStyleSheet(
"QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { "
+ f"background-color: rgba{str(COLOR_HIGHLIGHT)};"
+ f"{COLOR}"
+ " }"
)
self.streams_remove_button.setIcon(QIcon(ICON_DELETE))
self.streams_remove_button.setMaximumWidth(25)
self.streams_remove_button.setStyleSheet(
"QPushButton {padding:3px;padding-left:5px;border: none; text-align: left; image-position:right} QPushButton:hover { "
+ f"background-color: 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.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.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
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)
logWidget.dataStorage = self.dataStorage
self.layout().addWidget(logWidget)
self.msgLog = logWidget
self.msgLog.dockwidget = self
self.msgLog.active_account = plugin.dataStorage.active_account
self.msgLog.speckle_version = plugin.version
self.setMapping.setFlat(True)
self.setMapping.setStyleSheet(
"QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }"
)
self.crsSettings.setFlat(True)
self.crsSettings.setStyleSheet(
"QPushButton {text-align: right;} QPushButton:hover { " + f"{COLOR}" + " }"
)
def addDataStorage(self, plugin):
self.dataStorage = plugin.dataStorage
try:
self.dataStorage.project = plugin.project
except:
self.dataStorage.project = plugin.qgis_project
def createMappingDialog(self):
if self.mappingSendDialog is None:
self.mappingSendDialog = MappingSendDialog(None)
self.mappingSendDialog.dataStorage = self.dataStorage
self.mappingSendDialog.runSetup()
def showMappingDialog(self):
# updata DataStorage
self.mappingSendDialog.runSetup()
self.mappingSendDialog.show()
def addLabel(self, plugin):
try:
exitIcon = QPixmap(ICON_LOGO)
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}")
boxLayout = QHBoxLayout(widget)
boxLayout.addWidget(text_label) # , alignment=Qt.AlignCenter)
boxLayout.addWidget(version_label)
boxLayout.setContentsMargins(0, 0, 0, 0)
self.setWindowTitle("SpeckleQGIS")
self.setTitleBarWidget(widget)
self.labelWidget = text_label
self.labelWidget.setCursor(QCursor(QtCore.Qt.PointingHandCursor))
self.labelWidget.clicked.connect(self.onClickLogo)
except Exception as e:
logToUser(e)
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())
except Exception as e:
return
def clearDropdown(self):
try:
self.streamBranchDropdown.clear()
self.commitDropdown.clear()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def reloadDialogUI(self, plugin):
try:
self.clearDropdown()
self.populateUI(plugin)
self.enableElements(plugin)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def run(self, plugin):
try:
# 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, obj: dict):
self.msgLog.addButton(obj)
def setupOnFirstLoad(self, plugin):
try:
# print("setupOnFirstLoad")
self.msgLog.sendMessage.connect(self.addMsg)
self.setMapping.clicked.connect(self.showMappingDialog)
self.reportBtn.clicked.connect(self.msgLog.showReport)
self.streams_add_button.clicked.connect(plugin.onStreamAddButtonClicked)
self.commit_web_view.clicked.connect(
lambda: plugin.openUrl(
constructCommitURL(
plugin.active_stream,
plugin.active_branch.id,
plugin.active_commit.id,
)
)
)
self.reloadButton.clicked.connect(lambda: self.refreshClicked(plugin))
self.closeButton.clicked.connect(lambda: self.closeClicked(plugin))
self.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.setActiveCommit(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 onClickLogo(self):
import webbrowser
url = "https://speckle.systems/"
webbrowser.open(url, new=0, autoraise=True)
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Logo Click",
"connector_version": str(self.dataStorage.plugin_version),
},
)
except Exception as e:
print(e)
def refreshClicked(self, plugin):
try:
try:
metrics.track(
"Connector Action",
plugin.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: rgba{str(SPECKLE_COLOR)};"
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.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)
self.saveLayerSelection.setEnabled(True)
self.commitLabel.setEnabled(False)
self.commitDropdown.setEnabled(False)
self.messageLabel.setEnabled(True)
self.messageInput.setEnabled(True)
self.layerSendModeDropdown.setEnabled(True)
self.setMapping.setEnabled(True)
self.commit_web_view.setEnabled(False)
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: rgba{str(SPECKLE_COLOR)};"
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.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLUE))
self.sendModeButton.setFlat(True)
self.receiveModeButton.setFlat(False)
self.runButton.setProperty("text", " RECEIVE")
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)
self.saveLayerSelection.setEnabled(False)
self.layerSendModeDropdown.setEnabled(False)
self.setMapping.setEnabled(False)
self.commit_web_view.setEnabled(True)
self.runBtnStatusChanged(plugin)
return
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def completeStreamSection(self, plugin):
return
def populateUI(self, plugin):
try:
self.populateLayerSendModeDropdown()
self.populateProjectStreams(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 setActiveCommit(self, plugin):
try:
# print("__setActiveCommit")
# print(plugin.active_commit)
if plugin.active_branch is None:
if (
plugin.active_stream is not None
and plugin.active_stream[1] is not None
):
branchName = self.streamBranchDropdown.currentText()
for b in plugin.active_stream[1].branches.items:
if b.name == branchName:
branch = b
plugin.active_branch = b
break
if plugin.active_branch is None:
return
# print(plugin.active_branch.name)
current_id_text = str(self.commitDropdown.currentText()).split(" ")[0]
# print(current_id_text)
if current_id_text == "": # populate commits still in progress
return
if len(plugin.active_branch.commits.items) > 0:
if "Latest" in current_id_text:
plugin.active_commit = plugin.active_branch.commits.items[0]
return
for c in plugin.active_branch.commits.items:
if c.id == current_id_text:
plugin.active_commit = c
return
# only if not found:
plugin.active_commit = plugin.active_branch.commits.items[0]
else:
plugin.active_commit = None
except Exception as e:
plugin.active_commit = None
print(e)
def runBtnStatusChanged(self, plugin):
try:
commitStr = str(self.commitDropdown.currentText())
branchStr = str(self.streamBranchDropdown.currentText())
if commitStr == "": # populate commits still in progress
return
if plugin.btnAction == 1: # on receive
if commitStr == "":
self.runButton.setEnabled(False)
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.layersWidget.setEnabled(False)
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
except Exception as e:
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
self.layersWidget.clear()
self.dataStorage.current_layers.clear()
layers = self.dataStorage.saved_layers
if not layers:
return
for i, layer in enumerate(layers):
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)
return
def populateSelectedLayerDropdown(self, plugin):
try:
if not self:
return
self.layersWidget.clear()
for layer_tuple in plugin.dataStorage.current_layers:
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)
return
def fillLayerList(self, layer, layerType="generic"):
try:
listItem = QListWidgetItem(layer.name())
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))
else:
from qgis.core import QgsIconUtils
icon = QgsIconUtils().iconForLayer(layer)
listItem.setIcon(icon)
# print(icon)
except Exception as e:
print(e)
icons = {
"generic": ICON_GENERIC,
"polygon": ICON_POLYGON,
"point": ICON_POINT,
"line": ICON_LINE,
"raster": ICON_RASTER,
}
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
def enableElements(self, plugin):
try:
self.sendModeButton.setEnabled(plugin.is_setup)
self.receiveModeButton.setEnabled(plugin.is_setup)
self.runButton.setEnabled(plugin.is_setup)
self.streams_add_button.setEnabled(plugin.is_setup)
self.commit_web_view.setEnabled(plugin.active_commit is not None)
self.reportBtn.setEnabled(False)
if plugin.is_setup is False:
self.streams_remove_button.setEnabled(plugin.is_setup)
self.streamBranchDropdown.setEnabled(plugin.is_setup)
self.layerSendModeDropdown.setEnabled(plugin.is_setup)
self.commitLabel.setEnabled(False)
self.commitDropdown.setEnabled(False)
self.show()
self.setSendMode(plugin)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def populateProjectStreams(self, plugin):
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:
# 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: # case of populating from Saved Streams
keep_branch = False
# print(active_branch)
# print(1)
self.streamBranchDropdown.clear() # activates "populate commit"
# print(2)
if isinstance(plugin.active_stream[1], SpeckleException):
logToUser("Some Projects 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
# print(3)
# print(plugin.active_branch)
# here the commit dropdown is triggered
self.streamBranchDropdown.addItems(
[f"{branch.name}" for branch in plugin.active_stream[1].branches.items]
)
# print(4)
self.streamBranchDropdown.addItems(["Create New Model"])
# print(5)
if keep_branch is True:
plugin.active_branch = active_branch
if active_commit is not None:
plugin.active_commit = active_commit
elif len(plugin.active_branch.commits.items) > 0:
plugin.active_commit = plugin.active_branch.commits.items[0]
# else:
# plugin.active_commit = plugin.active_branch.commits.items[0]
# print(plugin.active_branch)
# set index to current (if added from URL)
if (
plugin.active_branch is not None
and plugin.active_branch in plugin.active_stream[1].branches.items
):
# print("__________SET BRANCH TEXT")
# print(plugin.active_branch.name)
if keep_branch is True:
plugin.active_branch = active_branch
plugin.active_commit = active_commit
# print(plugin.active_branch.name)
self.streamBranchDropdown.setCurrentText(
plugin.active_branch.name
) # activates "populate commit"
# print(6)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def populateActiveCommitDropdown(self, plugin):
if not self:
return
try:
# print("________populateActiveCommitDropdown")
# print(plugin.active_commit)
if plugin.active_stream is None:
print("Active project is None")
return
branchName = self.streamBranchDropdown.currentText()
# print(f"CURRENT BRANCH TEXT: {branchName}")
if branchName == "":
return
if branchName == "Create New Model":
self.streamBranchDropdown.setCurrentText("main")
plugin.onBranchCreateClicked()
return
branch = None
# print("__clear commit dropdwn")
# print(plugin.active_commit)
self.commitDropdown.clear()
if isinstance(plugin.active_stream[1], SpeckleException):
logToUser("Some Projects cannot be accessed", level=1, plugin=self)
return
elif plugin.active_stream[1]:
for b in plugin.active_stream[1].branches.items:
if b.name == branchName:
branch = b
plugin.active_branch = b
break
if len(branch.commits.items) > 0:
commits = []
commits.append("")
# commits.append("Latest commit from this branch")
# self.commitDropdown.addItem("Latest commit from this branch")
# commits = []
for commit in branch.commits.items:
sourceApp = (
str(commit.sourceApplication)
.replace(" ", "")
.split(".")[0]
.split("-")[0]
)
commits.append(
f"{commit.id}"
+ " | "
+ f"{sourceApp}"
+ " | "
+ f"{commit.message}"
)
self.commitDropdown.addItems(commits)
# set index to current (if added from URL)
if (
plugin.active_commit is not None
and plugin.active_commit in branch.commits.items
):
# print("set index to current (if added from URL) ")
# print(plugin.active_commit)
self.commitDropdown.setCurrentText(
f"{plugin.active_commit.id}"
+ " | "
+ f"{plugin.active_commit.sourceApplication}"
+ " | "
+ f"{plugin.active_commit.message}"
)
else: # overwrite active commit if plugin.active_commit is None:
# print("set index to 1st")
plugin.active_commit = branch.commits.items[0]
else:
plugin.active_commit = None
self.commitDropdown.setItemText(0, "Latest version of this model")
# enable or disable web view button
# print("_________ENABLE OR DISABLE")
# print(plugin.active_commit)
# print(f"CURRENT TEXT2: {self.streamBranchDropdown.currentText()}")
if plugin.active_commit is not None and plugin.btnAction == 1:
self.commit_web_view.setEnabled(True)
else:
self.commit_web_view.setEnabled(False)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
# print(str(e) + "::" + str(inspect.stack()[0][3]))
return
def onStreamRemoveButtonClicked(self, plugin):
return
def cancelOperations(self):
return
+976
View File
@@ -0,0 +1,976 @@
from copy import copy
import os
import sys
from typing import List
# from speckle.speckle.converter.layers import getLayers
# import ui.speckle_qgis_dialog
from specklepy.logging.exceptions import SpeckleException, GraphQLException
from PyQt5 import QtWidgets, uic
from PyQt5 import QtGui
from PyQt5.QtGui import QIcon, QPixmap, QCursor
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
# from speckle.speckle_arcgis_new import Speckle
from speckle.speckle.converter.layers import getLayersWithStructure, getAllProjLayers
from speckle.speckle.utils.panel_logging import logToUser
from speckle.specklepy_qt_ui.qt_ui.LogWidget import LogWidget
from speckle.specklepy_qt_ui.qt_ui.utils.utils import constructCommitURL
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
from speckle.specklepy_qt_ui.qt_ui.utils.global_resources import (
COLOR_HIGHLIGHT,
SPECKLE_COLOR,
SPECKLE_COLOR_LIGHT,
ICON_OPEN_WEB,
ICON_REPORT,
ICON_LOGO,
ICON_SEARCH,
ICON_DELETE,
ICON_DELETE_BLUE,
ICON_PIN_ACTIVE,
ICON_PIN_DISABLED,
ICON_SEND,
ICON_RECEIVE,
ICON_SEND_BLACK,
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,
)
ui_file = uic.loadUiType(
os.path.join(os.path.dirname(__file__), os.path.join("ui", "mainWindow_main.ui"))
)
ui_file_path = os.path.join(
os.path.dirname(__file__), os.path.join("ui", "mainWindow_main.ui")
)
class SpeckleGISDialog(QMainWindow):
on_top: bool = False
pin_label: QtWidgets.QPushButton
closingPlugin = pyqtSignal()
streamList: QtWidgets.QComboBox
sendModeButton: QtWidgets.QPushButton
receiveModeButton: QtWidgets.QPushButton
streamBranchDropdown: QtWidgets.QComboBox
layerSendModeDropdown: QtWidgets.QComboBox
commitDropdown: QtWidgets.QComboBox
layersWidget: QtWidgets.QListWidget
saveLayerSelection: QtWidgets.QPushButton
runButton: QtWidgets.QPushButton
msgLog: LogWidget = None
custom_crs_modal = None
signal_1 = pyqtSignal(object)
signal_2 = pyqtSignal(object)
signal_3 = pyqtSignal(object)
signal_4 = pyqtSignal(object)
signal_5 = pyqtSignal(object)
signal_6 = pyqtSignal(object)
signal_remove_btn_url = pyqtSignal(str)
signal_cancel_operation = pyqtSignal()
gridLayoutTitleBar = QtWidgets.QGridLayout
def __init__(self, parent=None):
"""Constructor."""
print("START MAIN WINDOW")
super(SpeckleGISDialog, self).__init__(
parent
) # , QtCore.Qt.WindowStaysOnTopHint)
uic.loadUi(ui_file_path, self) # Load the .ui file
# self.show()
self.runAllSetup()
def runAllSetup(self):
try:
self.streamBranchDropdown.setMaxCount(100)
self.commitDropdown.setMaxCount(100)
self.streams_add_button.setFlat(True)
self.streams_remove_button.setFlat(True)
self.commit_web_view.setFlat(True)
self.reportBtn.setFlat(True)
# self.saveSurveyPoint.setFlat(True)
self.saveLayerSelection.setFlat(True)
self.reloadButton.setFlat(True)
self.closeButton.setFlat(True)
self.commit_web_view.setEnabled(False)
# https://stackoverflow.com/questions/67585501/pyqt-how-to-use-hover-in-button-stylesheet
backgr_image_del = f"border-image: url({ICON_DELETE_BLUE});"
self.streams_add_button.setIcon(QIcon(ICON_SEARCH))
self.streams_add_button.setMaximumWidth(25)
self.streams_add_button.setStyleSheet(
"QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { "
+ f"background-color: rgba{str(COLOR_HIGHLIGHT)};"
+ f"{COLOR}"
+ " }"
)
self.commit_web_view.setIcon(QIcon(ICON_OPEN_WEB))
self.commit_web_view.setMaximumWidth(25)
self.commit_web_view.setStyleSheet(
"QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { "
+ f"background-color: rgba{str(COLOR_HIGHLIGHT)};"
+ f"{COLOR}"
+ " }"
)
self.reportBtn.setIcon(QIcon(ICON_REPORT))
self.reportBtn.setMaximumWidth(25)
self.reportBtn.setStyleSheet(
"QPushButton {padding:3px;padding-left:5px;border: none; text-align: left;} QPushButton:hover { "
+ f"background-color: rgba{str(COLOR_HIGHLIGHT)};"
+ f"{COLOR}"
+ " }"
)
self.streams_remove_button.setIcon(QIcon(ICON_DELETE))
self.streams_remove_button.setMaximumWidth(25)
self.streams_remove_button.setStyleSheet(
"QPushButton {padding:3px;padding-left:5px;border: none; text-align: left; image-position:right} QPushButton:hover { "
+ f"background-color: 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.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.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
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self)
def runSetup(self, plugin):
# self.addDataStorage(plugin)
print("run setup")
self.addLabel(plugin)
self.addProps(plugin)
# self.createMappingDialog()
def addProps(self, plugin):
try:
# add widgets that will only show on event trigger
logWidget = LogWidget(parent=self)
logWidget.layout.setContentsMargins(10, 60, 10, 40)
logWidget.dataStorage = self.dataStorage
self.layout().addWidget(logWidget)
self.msgLog = logWidget
self.msgLog.dockwidget = self
self.msgLog.active_account = plugin.dataStorage.active_account
self.msgLog.speckle_version = plugin.version
self.crsSettings.setFlat(True)
self.crsSettings.setStyleSheet(
"QPushButton {text-align: right;} QPushButton:hover { "
+ f"{COLOR}"
+ " }"
)
except Exception as e:
logToUser(e, level=2, plugin=self)
def addLabel(self, plugin):
try:
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(QtCore.QSize(300, 93))
text_label.setMinimumSize(QtCore.QSize(100, 40))
text_label.setMaximumWidth(250)
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}")
boxLayout = QHBoxLayout(widget)
boxLayout.addWidget(text_label) # , alignment=Qt.AlignCenter)
boxLayout.addWidget(version_label)
boxLayout.setContentsMargins(0, 0, 30, 0)
self.setWindowTitle("SpeckleArcGIS")
self.gridLayoutTitleBar.addWidget(widget) # fro QMainWindow
# self.setTitleBarWidget(widget) # for dockwidget
self.labelWidget = text_label
self.labelWidget.setCursor(QCursor(QtCore.Qt.PointingHandCursor))
self.labelWidget.clicked.connect(self.onClickLogo)
pin_label = QtWidgets.QPushButton("")
pin_label.setIcon(QIcon(ICON_PIN_DISABLED))
pin_label.setMaximumWidth(25)
# pin_label.setFlat(True)
pin_label.setStyleSheet("QPushButton {border: none;}")
boxLayout.addWidget(pin_label)
pin_label.setCursor(QCursor(QtCore.Qt.PointingHandCursor))
self.pin_label = pin_label
self.pin_label.clicked.connect(self.pinWindow)
except Exception as e:
logToUser(e)
def pinWindow(self):
if self.on_top == True:
self.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, False)
self.pin_label.setIcon(QIcon(ICON_PIN_DISABLED))
self.on_top = False
self.show()
else:
self.setWindowFlag(Qt.WindowType.WindowStaysOnTopHint, True)
self.pin_label.setIcon(QIcon(ICON_PIN_ACTIVE))
self.on_top = True
self.show()
def addDataStorage(self, plugin):
self.dataStorage = plugin.dataStorage
try:
self.dataStorage.project = plugin.project
except:
self.dataStorage.project = plugin.qproject
def resizeEvent(self, event):
try:
# print("resize")
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 clearDropdown(self):
try:
self.streamBranchDropdown.clear()
self.commitDropdown.clear()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def reloadDialogUI(self, plugin):
try:
self.clearDropdown()
self.populateUI(plugin)
self.enableElements(plugin)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def run(self, plugin):
try:
print("dockwidget run")
# Setup events on first load only!
self.setupOnFirstLoad(plugin)
# Connect streams section events
self.completeStreamSection(plugin)
# Populate the UI dropdowns
self.populateUI(plugin)
print("dockwidget run end")
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self)
def closeEvent(self, event):
try:
# import threading
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 addMsg(self, obj: dict):
try:
self.msgLog.addButton(obj)
# last_btn, index = self.msgLog.getLastBtn()
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self)
def setupOnFirstLoad(self, plugin):
try:
self.msgLog.sendMessage.connect(self.addMsg)
self.reportBtn.clicked.connect(self.msgLog.showReport)
self.streams_add_button.clicked.connect(plugin.onStreamAddButtonClicked)
self.commit_web_view.clicked.connect(
lambda: plugin.openUrl(
constructCommitURL(
plugin.active_stream,
plugin.active_branch.id,
plugin.active_commit.id,
)
)
)
self.reloadButton.clicked.connect(lambda: self.refreshClicked(plugin))
self.closeButton.clicked.connect(lambda: self.closeClicked(plugin))
self.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.setActiveCommit(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 onClickLogo(self):
import webbrowser
url = "https://speckle.systems/"
webbrowser.open(url, new=0, autoraise=True)
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Logo Click",
"connector_version": str(self.dataStorage.plugin_version),
},
)
except Exception as e:
print(e)
def refreshClicked(self, plugin):
try:
try:
metrics.track(
"Connector Action",
plugin.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: rgba{str(SPECKLE_COLOR)};"
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.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)
self.saveLayerSelection.setEnabled(True)
self.commitLabel.setEnabled(False)
self.commitDropdown.setEnabled(False)
self.messageLabel.setEnabled(True)
self.messageInput.setEnabled(True)
self.layerSendModeDropdown.setEnabled(True)
self.commit_web_view.setEnabled(False)
self.runBtnStatusChanged(plugin)
return
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: rgba{str(SPECKLE_COLOR)};"
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.receiveModeButton.setIcon(QIcon(ICON_RECEIVE_BLUE))
self.sendModeButton.setFlat(True)
self.receiveModeButton.setFlat(False)
self.runButton.setProperty("text", " RECEIVE")
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)
self.saveLayerSelection.setEnabled(False)
self.layerSendModeDropdown.setEnabled(False)
self.commit_web_view.setEnabled(True)
self.runBtnStatusChanged(plugin)
return
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self)
def completeStreamSection(self, plugin):
return
def populateUI(self, plugin):
try:
self.populateLayerSendModeDropdown()
self.populateProjectStreams(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 setActiveCommit(self, plugin):
try:
# print("__setActiveCommit")
# print(plugin.active_commit)
if plugin.active_branch is None:
if (
plugin.active_stream is not None
and plugin.active_stream[1] is not None
):
branchName = self.streamBranchDropdown.currentText()
for b in plugin.active_stream[1].branches.items:
if b.name == branchName:
branch = b
plugin.active_branch = b
break
if plugin.active_branch is None:
return
# print(plugin.active_branch.name)
current_id_text = str(self.commitDropdown.currentText()).split(" ")[0]
# print(current_id_text)
if current_id_text == "": # populate commits still in progress
return
if len(plugin.active_branch.commits.items) > 0:
if "Latest" in current_id_text:
plugin.active_commit = plugin.active_branch.commits.items[0]
return
for c in plugin.active_branch.commits.items:
if c.id == current_id_text:
plugin.active_commit = c
return
# only if not found:
plugin.active_commit = plugin.active_branch.commits.items[0]
else:
plugin.active_commit = None
except Exception as e:
plugin.active_commit = None
print(e)
def runBtnStatusChanged(self, plugin):
try:
commitStr = str(self.commitDropdown.currentText())
branchStr = str(self.streamBranchDropdown.currentText())
if commitStr == "": # populate commits still in progress
return
if plugin.btnAction == 1: # on receive
if commitStr == "":
self.runButton.setEnabled(False)
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.saved_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:
print("Send mode changed")
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
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
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self)
def populateSavedLayerDropdown(self, plugin, bySelection: bool = True):
print("populate layer dropdown / clicked save selection")
if not self:
return
try:
from speckle.speckle.utils.project_vars import set_project_layer_selection
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.utils.project_vars import (
set_project_layer_selection,
)
try:
self.layersWidget.clear()
nameDisplay = []
project = plugin.dataStorage.project
if bySelection is False: # read from project data
print("populate layers from saved data")
all_layers = getAllProjLayers(plugin)
if all_layers is None:
return
all_layers_ids = [l.dataSource for l in all_layers]
for layer_tuple in plugin.dataStorage.saved_layers:
if layer_tuple[1].dataSource in all_layers_ids:
listItem = self.fillLayerList(layer_tuple[1])
self.layersWidget.addItem(listItem)
else: # read selected layers
# Fetch selected layers
print("populate layers from selection")
plugin.dataStorage.current_layers = []
layers, structure = getLayersWithStructure(
plugin, bySelection=True
) # List[QgsLayerTreeNode]
# print(layers)
if layers is not None:
for i, layer in enumerate(layers):
plugin.dataStorage.current_layers.append((layer.name, layer))
listItem = self.fillLayerList(layer)
self.layersWidget.addItem(listItem)
# print("populate layers from selection 2")
set_project_layer_selection(plugin)
# print("populate layers from selection 3")
self.layersWidget.setIconSize(QSize(20, 20))
self.runBtnStatusChanged(plugin)
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:
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 enableElements(self, plugin):
try:
self.sendModeButton.setEnabled(plugin.is_setup)
self.receiveModeButton.setEnabled(plugin.is_setup)
self.runButton.setEnabled(plugin.is_setup)
self.streams_add_button.setEnabled(plugin.is_setup)
self.commit_web_view.setEnabled(plugin.active_commit is not None)
self.reportBtn.setEnabled(False)
if plugin.is_setup is False:
self.streams_remove_button.setEnabled(plugin.is_setup)
self.streamBranchDropdown.setEnabled(plugin.is_setup)
self.layerSendModeDropdown.setEnabled(plugin.is_setup)
self.commitLabel.setEnabled(False)
self.commitDropdown.setEnabled(False)
self.show()
self.setSendMode(plugin)
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self)
def populateProjectStreams(self, plugin):
return
def onActiveStreamChanged(self, plugin):
if not self:
return
try:
if not self:
return
index = self.streamList.currentIndex()
if (len(plugin.current_streams) == 0 and index == 1) or (
len(plugin.current_streams) > 0 and index == len(plugin.current_streams)
):
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:
active_branch = copy(plugin.active_branch)
active_commit = copy(plugin.active_commit)
keep_branch = True # case of search by URL
if active_branch is None: # case of populating from Saved Streams
keep_branch = False
# print(active_branch)
# print(1)
self.streamBranchDropdown.clear() # activates "populate commit"
# print(2)
if isinstance(plugin.active_stream[1], SpeckleException):
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
# print(3)
# print(plugin.active_branch)
# here the commit dropdown is triggered
self.streamBranchDropdown.addItems(
[f"{branch.name}" for branch in plugin.active_stream[1].branches.items]
)
# print(4)
self.streamBranchDropdown.addItems(["Create New Model"])
# print(5)
if keep_branch is True:
plugin.active_branch = active_branch
if active_commit is not None:
plugin.active_commit = active_commit
elif len(plugin.active_branch.commits.items) > 0:
plugin.active_commit = plugin.active_branch.commits.items[0]
# else:
# plugin.active_commit = plugin.active_branch.commits.items[0]
# print(plugin.active_branch)
# set index to current (if added from URL)
if (
plugin.active_branch is not None
and plugin.active_branch in plugin.active_stream[1].branches.items
):
# print("__________SET BRANCH TEXT")
# print(plugin.active_branch.name)
if keep_branch is True:
plugin.active_branch = active_branch
plugin.active_commit = active_commit
# print(plugin.active_branch.name)
self.streamBranchDropdown.setCurrentText(
plugin.active_branch.name
) # activates "populate commit"
# print(6)
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self)
def populateActiveCommitDropdown(self, plugin):
if not self:
return
try:
if plugin.active_stream is None:
print("Active stream is None")
return
branchName = self.streamBranchDropdown.currentText()
# print(f"CURRENT BRANCH TEXT: {branchName}")
if branchName == "":
return
if branchName == "Create New Model":
self.streamBranchDropdown.setCurrentText("main")
plugin.onBranchCreateClicked()
return
branch = None
# print("__clear commit dropdwn")
# print(plugin.active_commit)
self.commitDropdown.clear()
if isinstance(plugin.active_stream[1], SpeckleException):
logToUser("Some streams cannot be accessed", level=1, plugin=self)
return
elif plugin.active_stream[1]:
for b in plugin.active_stream[1].branches.items:
if b.name == branchName:
branch = b
plugin.active_branch = b
break
if len(branch.commits.items) > 0:
commits = []
commits.append("")
# commits.append("Latest commit from this branch")
# self.commitDropdown.addItem("Latest commit from this branch")
# commits = []
for commit in branch.commits.items:
sourceApp = (
str(commit.sourceApplication)
.replace(" ", "")
.split(".")[0]
.split("-")[0]
)
commits.append(
f"{commit.id}"
+ " | "
+ f"{sourceApp}"
+ " | "
+ f"{commit.message}"
)
self.commitDropdown.addItems(commits)
# set index to current (if added from URL)
if (
plugin.active_commit is not None
and plugin.active_commit in branch.commits.items
):
# print("set index to current (if added from URL) ")
# print(plugin.active_commit)
self.commitDropdown.setCurrentText(
f"{plugin.active_commit.id}"
+ " | "
+ f"{plugin.active_commit.sourceApplication}"
+ " | "
+ f"{plugin.active_commit.message}"
)
else: # overwrite active commit if plugin.active_commit is None:
# print("set index to 1st")
plugin.active_commit = branch.commits.items[0]
else:
plugin.active_commit = None
self.commitDropdown.setItemText(0, "Latest commit from this branch")
# enable or disable web view button
# print("_________ENABLE OR DISABLE")
# print(plugin.active_commit)
# print(f"CURRENT TEXT2: {self.streamBranchDropdown.currentText()}")
if plugin.active_commit is not None and plugin.btnAction == 1:
self.commit_web_view.setEnabled(True)
else:
self.commit_web_view.setEnabled(False)
except Exception as e:
logToUser(str(e), level=2, func=inspect.stack()[0][3], plugin=self)
def onStreamRemoveButtonClicked(self, plugin):
return
def cancelOperations(self):
return
View File
+87
View File
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AddStreamDialog</class>
<widget class="QWidget" name="AddStreamDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item>
<layout class="QFormLayout" name="search_form">
<item row="1" column="0">
<widget class="QLabel" name="search_label">
<property name="text">
<string>Search Project by name or URL</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="search_text_field"/>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="search_button">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="accounts_dropdown"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="accounts_label">
<property name="text">
<string>Account</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="search_results_label">
<property name="text">
<string>Search Results</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="search_results_list">
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="dialog_button_box">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+64
View File
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CreateStreamDialog</class>
<widget class="QWidget" name="AddBranchDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item>
<layout class="QFormLayout" name="search_form">
<item row="0" column="0">
<widget class="QLabel" name="name_label">
<property name="text">
<string>Model Name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="name_label">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="name_field"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="description_field"/>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="dialog_button_box">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+85
View File
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CreateStreamDialog</class>
<widget class="QWidget" name="AddStreamDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item>
<layout class="QFormLayout" name="search_form">
<item row="0" column="0">
<widget class="QLabel" name="accounts_label">
<property name="text">
<string>Account</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="name_label">
<property name="text">
<string>Project Name</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="name_label">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="name_label">
<property name="text">
<string>Public</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="accounts_dropdown"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="name_field"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="description_field"/>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="public_toggle"/>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="dialog_button_box">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+230
View File
@@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CreateStreamDialog</class>
<widget class="QWidget" name="CustomCRSDialog">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="search_form">
<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="QVBoxLayout" name="verticalLayout">
<item row="2" >
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Option: </string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="modeDropdown">
</widget>
</item>
</layout>
</item>
<item row="3" >
<widget class="QLabel" name="description">
<property name="text">
<string> </string>
</property>
</widget>
</item>
<item row="4" >
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item row="8">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item row="10" >
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_survey">
<property name="text">
<string>Lat, Lon: </string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="surveyPointLat">
<property name="placeholderText">
<string>lat (y)</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="degreeSignY">
<property name="text">
<string>°</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="surveyPointLon">
<property name="placeholderText">
<string>lon (x)</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="degreeSignX">
<property name="text">
<string>°</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="12" >
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_offsets">
<property name="text">
<string>Lat, Lon: </string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="offsetY">
<property name="placeholderText">
<string>lat (y)</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="offsetYDegreeSign">
<property name="text">
<string>°</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="offsetX">
<property name="placeholderText">
<string>lon (x)</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="offsetXDegreeSign">
<property name="text">
<string>°</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="14" >
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_angle">
<property name="text">
<string>Angle: </string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="rotation">
<property name="placeholderText">
<string>angle (degrees)</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rotationDegreeSign">
<property name="text">
<string>°</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QDialogButtonBox" name="dialog_button_box">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Apply</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+99
View File
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DependenciesUpgradeDialog</class>
<widget class="QWidget" name="ReportDialog">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="search_form">
<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="QVBoxLayout" name="verticalLayout">
<item row="2" >
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="report_label">
<property name="text">
<string> </string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="report_widget">
<property name="text">
<string>Option: </string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QWidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btn_upgrade">
<property name="text">
<string>Upgrade dependencies automatically</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+346
View File
@@ -0,0 +1,346 @@
<?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>Project</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>Model</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>Version</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="commitListButtons" stretch="20,1">
<item>
<widget class="QComboBox" name="commitDropdown"/>
</item>
<item>
<widget class="QPushButton" name="commit_web_view">
<property name="text">
<string> </string>
</property>
</widget>
</item>
</layout>
</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>
<item>
<widget class="QPushButton" name="reportBtn">
<property name="enabled">
<bool>true</bool>
</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="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="setMapping">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Apply transformations on Send</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" 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="crsSettings">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Set project center on Send/Receive</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="11" column="0">
<widget class="QLabel" name="messageLabel">
<property name="text">
<string>Message</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QLineEdit" name="messageInput">
<property name="placeholderText">
<string>Sent XXX objects from QGIS</string>
</property>
</widget>
</item>
<item row="12" 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="13" 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>
+327
View File
@@ -0,0 +1,327 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SpeckleGISDialog</class>
<widget class="QMainWindow" name="SpeckleGISDialog">
<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>Project</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>Model</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>Version</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="commitListButtons" stretch="20,1">
<item>
<widget class="QComboBox" name="commitDropdown"/>
</item>
<item>
<widget class="QPushButton" name="commit_web_view">
<property name="text">
<string> </string>
</property>
</widget>
</item>
</layout>
</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>
<item>
<widget class="QPushButton" name="reportBtn">
<property name="enabled">
<bool>true</bool>
</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 visible layers as selection</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" 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="crsSettings">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Set project center on Send/Receive</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="11" column="0">
<widget class="QLabel" name="messageLabel">
<property name="text">
<string>Message</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QLineEdit" name="messageInput">
<property name="placeholderText">
<string>Sent XXX objects from ArcGIS</string>
</property>
</widget>
</item>
<item row="12" 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="13" 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>
+84
View File
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ReportDialog</class>
<widget class="QWidget" name="ReportDialog">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="search_form">
<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="QVBoxLayout" name="verticalLayout">
<item row="2" >
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="report_label">
<property name="text">
<string> </string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="report_text">
<property name="text">
<string>Option: </string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QDialogButtonBox" name="dialog_button_box">
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StreamListDialog</class>
<widget class="QWidget" name="StreamListDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>70</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pushButton_3">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+156
View File
@@ -0,0 +1,156 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MappingSendDialog</class>
<widget class="QWidget" name="AddStreamDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Add custom transformations</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item>
<layout class="QFormLayout" name="form">
<item row="0" column="0">
<widget class="QLabel" name="transform_label">
<property name="text">
<string>Transformation</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="transformDropdown"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="layers_label">
<property name="text">
<string>Layer</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="layerDropdown"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="attr_label">
<property name="text">
<string>Attribute</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="attrDropdown"/>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item >
<widget class="QPushButton" name="addTransform">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item >
<widget class="QPushButton" name="removeTransform">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<widget class="QListWidget" name="transformationsList"/>
<property name="viewMode">
<enum>QListView::ListMode</enum>
</property>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
</layout>
</item>
<item row="6" column="0">
<widget class="QLabel" name="elevation_label">
<property name="text">
<string>Elevation layer</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="elevationLayerDropdown"/>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="3,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="more_info">
<property name="text">
<string>More info</string>
</property>
</widget>
</item >
<item >
<widget class="QPushButton" name="dialog_button">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
View File
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

+90
View File
@@ -0,0 +1,90 @@
import os
# colors
COLOR_HIGHLIGHT = (210, 210, 210, 1)
SPECKLE_COLOR = (59, 130, 246, 1)
SPECKLE_COLOR_LIGHT = (69, 140, 255, 1)
ICON_LOGO = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "logo-slab-white@0.5x.png"
)
ERROR_COLOR = (255, 150, 150, 1)
ERROR_COLOR_LIGHT = (255, 210, 210, 1)
COLOR = f"color: rgba{str(SPECKLE_COLOR)};"
BACKGR_COLOR_TRANSPARENT = f"background-color: rgba(0,0,0,0);"
BACKGR_COLOR_HIGHLIGHT = f"background-color: rgba{str(COLOR_HIGHLIGHT)};"
BACKGR_COLOR = f"background-color: rgba{str(SPECKLE_COLOR)};"
BACKGR_COLOR_LIGHT = f"background-color: rgba{str(SPECKLE_COLOR_LIGHT)};"
BACKGR_COLOR_GREY = f"background-color: rgba(220,220,220,1);"
BACKGR_ERROR_COLOR = f"background-color: rgba{str(ERROR_COLOR)};"
BACKGR_ERROR_COLOR_LIGHT = f"background-color: rgba{str(ERROR_COLOR_LIGHT)};"
NEW_GREY = BACKGR_COLOR_GREY.replace("1);", "0.2);")
NEW_GREY_HIGHLIGHT = BACKGR_COLOR_HIGHLIGHT.replace("1);", "0.3);")
# images
ICON_SEARCH = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "magnify.png"
)
ICON_OPEN_WEB = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "open-in-new.png"
)
ICON_REPORT = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "chart-line.png"
)
ICON_DELETE = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "delete.png"
)
ICON_DELETE_BLUE = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "delete-blue.png"
)
ICON_SEND = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "cube-send.png"
)
ICON_RECEIVE = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "cube-receive.png"
)
ICON_SEND_BLACK = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "cube-send-black.png"
)
ICON_RECEIVE_BLACK = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "cube-receive-black.png"
)
ICON_SEND_BLUE = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "cube-send-blue.png"
)
ICON_RECEIVE_BLUE = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "cube-receive-blue.png"
)
ICON_XXL = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "size-xxl.png"
)
ICON_RASTER = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "legend_raster.png"
)
ICON_POLYGON = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "legend_polygon.png"
)
ICON_LINE = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "legend_line.png"
)
ICON_POINT = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "legend_point.png"
)
ICON_GENERIC = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "legend_generic.png"
)
ICON_PIN_ACTIVE = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "pin.png"
)
ICON_PIN_DISABLED = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets", "pin-outline.png"
)
+93
View File
@@ -0,0 +1,93 @@
try:
from specklepy_qt_ui.qt_ui.utils.utils import splitTextIntoLines
except ModuleNotFoundError:
from speckle.specklepy_qt_ui.qt_ui.utils.utils import splitTextIntoLines
def logToUser(
msg: str, func=None, level: int = 2, plugin=None, url="", blue=False, report=False
):
msg = str(msg)
dockwidget = plugin
try:
if (
url == "" and blue is False
) or level == 2: # only for info messages or anything with error
msg = addLevelSymbol(msg, level)
if func is not None:
msg += "::" + str(func)
if dockwidget is None:
return
new_msg = splitTextIntoLines(msg)
dockwidget.msgLog.sendMessage.emit(
{
"text": new_msg,
"level": level,
"url": url,
"blue": blue,
"report": report,
}
)
except Exception as e:
print(e)
return
def addLevelSymbol(msg: str, level: int):
if level == 0:
msg = "🛈 " + msg
if level == 1:
msg = "⚠️ " + msg
if level == 2:
msg = "" + msg
return msg
def displayUserMsg(msg: str, func=None, level: int = 2):
try:
window = createWindow(msg, func, level)
window.exec_()
except Exception as e:
print(e)
def createWindow(msg_old: str, func=None, level: int = 2):
# print("Create window")
from PyQt5.QtWidgets import QMessageBox
from PyQt5 import QtCore
window = None
try:
# https://www.techwithtim.net/tutorials/pyqt5-tutorial/messageboxes/
window = QMessageBox()
msg = ""
if len(msg_old) > 80:
for line in msg_old.split("\n"):
line = splitTextIntoLines(line)
msg += line + "\n"
else:
msg = msg_old
window.setWindowFlag(QtCore.Qt.WindowStaysOnTopHint)
if level == 0:
window.setWindowTitle("Info (Speckle)")
window.setIcon(QMessageBox.Icon.Information)
if level == 1:
window.setWindowTitle("Warning (Speckle)")
window.setIcon(QMessageBox.Icon.Warning)
elif level == 2:
window.setWindowTitle("Error (Speckle)")
window.setIcon(QMessageBox.Icon.Critical)
window.setFixedWidth(200)
# window.setTextFormat(QtCore.Qt.RichText)
if func is not None:
window.setText(str(msg + "\n" + str(func)))
else:
window.setText(str(msg))
# print(window)
except Exception as e:
print(e)
return window
+76
View File
@@ -0,0 +1,76 @@
from textwrap import wrap
from typing import Union
import requests
SYMBOL = "_x_x_"
def splitTextIntoLines(text: str = "", number: int = 40) -> str:
msg = ""
try:
if len(text) > number:
try:
for i, text_part in enumerate(text.split("\n")):
lines = wrap(text_part, number)
for k, x in enumerate(lines):
msg += x
if k != len(lines) - 1:
msg += "\n"
if i != len(text.split("\n")) - 1:
msg += "\n"
except Exception as e:
print(e)
else:
msg = text
except Exception as e:
print(e)
print(text)
return msg
def constructCommitURL(
streamWrapper,
branch_id: Union[str, None] = None,
commit_id: Union[str, None] = None,
) -> Union[str, None]:
import requests
try:
if isinstance(streamWrapper, tuple) or isinstance(streamWrapper, list):
streamWrapper = streamWrapper[0]
streamUrl = streamWrapper.stream_url.split("?")[0].split("&")[0].split("@")[0]
r = requests.get(streamUrl)
url = streamUrl
# check for frontend2
try:
header = r.headers["x-speckle-frontend-2"] # will throw Exception in FE1
url = (
streamUrl.replace("streams", "projects")
+ "/models/"
+ branch_id
+ "@"
+ commit_id
)
except:
url = streamUrl.replace("projects", "streams") + "/commits/" + commit_id
return url
except:
return None
def constructCommitURLfromServerCommit(serverURL: str, stream_id: str) -> str:
r = requests.get(serverURL)
# check for frontend2
# only check the url string
try:
header = r.headers["x-speckle-frontend-2"]
url = (
serverURL
+ "/projects/"
+ stream_id # + "/models/" + branch_id + "@" + commit_id
)
except KeyError:
url = serverURL + "/streams/" + stream_id
return url
+329
View File
@@ -0,0 +1,329 @@
import inspect
import os
from typing import List, Union
import urllib.parse
try:
from specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from specklepy_qt_ui.qt_ui.utils.logger import logToUser
from specklepy_qt_ui.qt_ui.utils.utils import constructCommitURLfromServerCommit
except ModuleNotFoundError:
from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser
from speckle.specklepy_qt_ui.qt_ui.utils.utils import constructCommitURLfromServerCommit
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtCore import pyqtSignal
from specklepy.core.api.models import Stream, Branch, Commit
from specklepy.core.api.client import SpeckleClient
from specklepy.logging.exceptions import SpeckleException
from specklepy.core.api.credentials import get_local_accounts # , StreamWrapper
from specklepy.core.api.wrapper import StreamWrapper
from specklepy.logging import metrics
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
os.path.join(os.path.join(os.path.dirname(__file__), "ui", "add_stream_modal.ui"))
)
class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):
search_button: QtWidgets.QPushButton = None
search_text_field: QtWidgets.QLineEdit = None
search_results_list: QtWidgets.QListWidget = None
dialog_button_box: QtWidgets.QDialogButtonBox = None
accounts_dropdown: QtWidgets.QComboBox
stream_results: List[Stream] = []
branch_result: Branch = None
commit_result: Commit = None
speckle_client: Union[SpeckleClient, None] = None
dataStorage: DataStorage = None
# Events
handleStreamAdd = pyqtSignal(object)
def __init__(self, parent=None, speckle_client: SpeckleClient = None):
super(AddStreamModalDialog, self).__init__(
parent, QtCore.Qt.WindowStaysOnTopHint
)
self.speckle_client = speckle_client
self.setupUi(self)
self.setWindowTitle("Add Speckle Project")
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
def connect(self):
self.search_button.clicked.connect(self.onSearchClicked)
self.search_results_list.currentItemChanged.connect(self.searchResultChanged)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(
self.onOkClicked
)
self.dialog_button_box.button(
QtWidgets.QDialogButtonBox.Cancel
).clicked.connect(self.onCancelClicked)
self.accounts_dropdown.currentIndexChanged.connect(self.onAccountSelected)
self.populate_accounts_dropdown()
def searchResultChanged(self):
try:
index = self.search_results_list.currentIndex().row()
if index == -1:
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(
False
)
else:
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(
True
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def getAllStreams(self):
try:
query = ""
sw = None
results = []
if self.speckle_client is not None:
results = self.speckle_client.stream.search(query)
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Project Search By Name",
"connector_version": str(self.dataStorage.plugin_version),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
elif self.speckle_client is None:
logToUser(
f"Account cannot be authenticated: {self.accounts_dropdown.currentText()}",
level=1,
func=inspect.stack()[0][3],
)
self.stream_results = results
self.populateResultsList()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onSearchClicked(self):
try:
query = self.search_text_field.text()
sw = None
streams = []
branch = None
commit = None
if "http" in query and len(query.split("/")) >= 3: # URL
sw = StreamWrapper(
query,
)
stream = sw.get_client().stream.get(
id=sw.stream_id, branch_limit=100, commit_limit=100
)
if isinstance(stream, Stream):
streams = [stream]
if sw.type == "branch":
branch_name = sw.branch_name
for br in stream.branches.items:
name = urllib.parse.quote(br.name)
if br.name == branch_name:
branch = br
break
if sw.type == "commit":
commit_id = sw.commit_id
for br in stream.branches.items:
for com in br.commits.items:
if com.id == commit_id:
branch = br
commit = com
break
elif isinstance(stream, Exception):
print(stream)
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Project Search By URL",
"connector_version": str(self.dataStorage.plugin_version),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
elif self.speckle_client is not None:
streams = self.speckle_client.stream.search(query)
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Project Search By Name",
"connector_version": str(self.dataStorage.plugin_version),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
elif self.speckle_client is None:
logToUser(
f"Account cannot be authenticated: {self.accounts_dropdown.currentText()}",
level=1,
func=inspect.stack()[0][3],
)
self.stream_results = streams
self.branch_result = branch
self.commit_result = commit
self.populateResultsList(sw)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateResultsList(self, sw=None):
try:
self.search_results_list.clear()
if isinstance(self.stream_results, SpeckleException):
logToUser(
"Some Projects cannot be accessed",
level=1,
func=inspect.stack()[0][3],
)
return
for stream in self.stream_results:
host = ""
if sw is not None:
host = sw.get_account().serverInfo.url
else:
host = self.speckle_client.account.serverInfo.url
if isinstance(stream, SpeckleException):
logToUser(
"Some Projects cannot be accessed",
level=1,
func=inspect.stack()[0][3],
)
else:
self.search_results_list.addItems(
[
f"{stream.name}, {stream.id} | {host}" # for stream in self.stream_results
]
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onOkClicked(self):
try:
if isinstance(self.stream_results, SpeckleException):
logToUser(
"Selected Project cannot be accessed: "
+ str(self.stream_results.message),
level=1,
func=inspect.stack()[0][3],
)
return
else:
try:
index = self.search_results_list.currentIndex().row()
# stream = self.stream_results[index]
item = self.search_results_list.item(index)
url = constructCommitURLfromServerCommit(
item.text().split(" | ")[1],
item.text().split(", ")[1].split(" | ")[0],
)
sw = StreamWrapper(url)
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Stream Add From Search",
"connector_version": str(
self.dataStorage.plugin_version
),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
# acc = sw.get_account() #get_local_accounts()[self.accounts_dropdown.currentIndex()]
self.handleStreamAdd.emit(
(sw, self.branch_result, self.commit_result)
) # StreamWrapper(f"{acc.serverInfo.url}/streams/{stream.id}?u={acc.userInfo.id}"))
self.close()
except Exception as e:
logToUser(
"Some Projects cannot be accessed: " + str(e),
level=1,
func=inspect.stack()[0][3],
)
print(e)
return
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
print(e)
return
def onCancelClicked(self):
try:
self.close()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onAccountSelected(self, index=0):
try:
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Account Select",
"connector_version": str(self.dataStorage.plugin_version),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
account = self.speckle_accounts[index]
self.speckle_client = SpeckleClient(
account.serverInfo.url, account.serverInfo.url.startswith("https")
)
try:
self.speckle_client.authenticate_with_token(token=account.token)
except SpeckleException as ex:
raise Exception(f"Dependencies versioning error: {ex.message}")
self.getAllStreams()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populate_accounts_dropdown(self):
try:
# Populate the accounts comboBox
self.speckle_accounts = get_local_accounts()
self.accounts_dropdown.clear()
self.accounts_dropdown.addItems(
[
f"{acc.userInfo.name}, {acc.userInfo.email} | {acc.serverInfo.url}"
for acc in self.speckle_accounts
]
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
+90
View File
@@ -0,0 +1,90 @@
import inspect
import os
from typing import List, Tuple, Union
try:
from specklepy_qt_ui.qt_ui.utils.logger import logToUser
except ModuleNotFoundError:
from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtCore import pyqtSignal
from specklepy.core.api.client import SpeckleClient
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
os.path.join(os.path.join(os.path.dirname(__file__), "ui", "create_branch.ui"))
)
class CreateBranchModalDialog(QtWidgets.QWidget, FORM_CLASS):
name_field: QtWidgets.QLineEdit = None
description_field: QtWidgets.QLineEdit = None
dialog_button_box: QtWidgets.QDialogButtonBox = None
speckle_client: Union[SpeckleClient, None] = None
# Events
handleBranchCreate = pyqtSignal(str, str)
def __init__(self, parent=None, speckle_client: SpeckleClient = None):
super(CreateBranchModalDialog, self).__init__(
parent, QtCore.Qt.WindowStaysOnTopHint
)
self.speckle_client = speckle_client
self.setupUi(self)
self.setWindowTitle("Create New Model")
self.setMinimumWidth(300)
self.name_field.textChanged.connect(self.nameCheck)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(
self.onOkClicked
)
self.dialog_button_box.button(
QtWidgets.QDialogButtonBox.Cancel
).clicked.connect(self.onCancelClicked)
def nameCheck(self):
try:
if len(self.name_field.text()) >= 3:
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(
True
)
else:
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(
False
)
return
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onOkClicked(self):
try:
name = self.name_field.text()
description = self.description_field.text()
self.handleBranchCreate.emit(name, description)
self.close()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onCancelClicked(self):
try:
self.close()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
r"""
def onAccountSelected(self, index):
try:
account = self.speckle_accounts[index]
self.speckle_client = SpeckleClient(account.serverInfo.url, account.serverInfo.url.startswith("https"))
self.speckle_client.authenticate_with_token(token=account.token)
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3])
return
"""
+121
View File
@@ -0,0 +1,121 @@
import inspect
import os
from typing import List, Tuple, Union
try:
from specklepy_qt_ui.qt_ui.utils.logger import logToUser
except ModuleNotFoundError:
from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtCore import pyqtSignal
from specklepy.core.api.client import SpeckleClient
from specklepy.core.api.credentials import (
Account,
get_local_accounts,
) # , StreamWrapper
from specklepy.logging.exceptions import SpeckleException
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
os.path.join(os.path.join(os.path.dirname(__file__), "ui", "create_stream.ui"))
)
class CreateStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):
name_field: QtWidgets.QLineEdit = None
description_field: QtWidgets.QLineEdit = None
dialog_button_box: QtWidgets.QDialogButtonBox = None
accounts_dropdown: QtWidgets.QComboBox
public_toggle: QtWidgets.QCheckBox
speckle_client: Union[SpeckleClient, None] = None
# Events
handleStreamCreate = pyqtSignal(Account, str, str, bool)
def __init__(self, parent=None, speckle_client: SpeckleClient = None):
super(CreateStreamModalDialog, self).__init__(
parent, QtCore.Qt.WindowStaysOnTopHint
)
self.speckle_client = speckle_client
self.setupUi(self)
self.setWindowTitle("Create New Project")
self.name_field.textChanged.connect(self.nameCheck)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(True)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(
self.onOkClicked
)
self.dialog_button_box.button(
QtWidgets.QDialogButtonBox.Cancel
).clicked.connect(self.onCancelClicked)
self.accounts_dropdown.currentIndexChanged.connect(self.onAccountSelected)
self.populate_accounts_dropdown()
def nameCheck(self):
try:
if len(self.name_field.text()) == 0 or len(self.name_field.text()) >= 3:
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(
True
)
else:
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(
False
)
return
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onOkClicked(self):
try:
acc = get_local_accounts()[self.accounts_dropdown.currentIndex()]
name = self.name_field.text()
description = self.description_field.text()
public = self.public_toggle.isChecked()
self.handleStreamCreate.emit(acc, name, description, public)
self.close()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onCancelClicked(self):
try:
# self.handleCancelStreamCreate.emit()
self.close()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onAccountSelected(self, index):
try:
account = self.speckle_accounts[index]
self.speckle_client = SpeckleClient(
account.serverInfo.url, account.serverInfo.url.startswith("https")
)
try:
self.speckle_client.authenticate_with_token(token=account.token)
except SpeckleException as ex:
raise Exception(f"Dependencies versioning error: {ex.message}")
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populate_accounts_dropdown(self):
try:
# Populate the accounts comboBox
self.speckle_accounts = get_local_accounts()
self.accounts_dropdown.clear()
self.accounts_dropdown.addItems(
[
f"{acc.userInfo.name}, {acc.userInfo.email} | {acc.serverInfo.url}"
for acc in self.speckle_accounts
]
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
+187
View File
@@ -0,0 +1,187 @@
import inspect
import os
from typing import List, Tuple, Union
try:
from specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from specklepy_qt_ui.qt_ui.utils.logger import logToUser
from specklepy_qt_ui.qt_ui.utils.global_resources import COLOR
except ModuleNotFoundError:
from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from speckle.specklepy_qt_ui.qt_ui.utils.logger import logToUser
from speckle.specklepy_qt_ui.qt_ui.utils.global_resources import COLOR
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget
from PyQt5.QtCore import pyqtSignal
from specklepy.core.api.client import SpeckleClient
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
os.path.join(os.path.join(os.path.dirname(__file__), "ui", "custom_crs.ui"))
)
class CustomCRSDialog(QtWidgets.QWidget, FORM_CLASS):
name_field: QtWidgets.QLineEdit = None
description: QtWidgets.QLineEdit = None
dialog_button_box: QtWidgets.QDialogButtonBox = None
saveSurveyPoint: QtWidgets.QPushButton = None
speckle_client: Union[SpeckleClient, None] = None
dataStorage: DataStorage = None
# Events
# handleCRSCreate = pyqtSignal(str,str)
def __init__(self, parent=None):
super(CustomCRSDialog, self).__init__(parent, QtCore.Qt.WindowStaysOnTopHint)
self.setupUi(self)
self.setWindowTitle("Set project center on Send/Receive")
# self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Close).clicked.connect(self.onCancelClicked)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).setText(
"More Info"
)
self.modeDropdown.currentIndexChanged.connect(self.onModeChanged)
def onModeChanged(self):
try:
if not self:
return
index = self.modeDropdown.currentIndex()
if index == 1: # custom crs
self.surveyPointLat.show()
self.surveyPointLon.show()
self.degreeSignX.show()
self.degreeSignY.show()
self.label_survey.show()
self.offsetX.hide()
self.offsetY.hide()
self.label_offsets.hide()
self.offsetXDegreeSign.hide()
self.offsetYDegreeSign.hide()
self.description.setText(
"Use this option when you don't have to use a specific CRS.\
\n\nThis will change your Project CRS to a new custom CRS.\
\n\nHint: right-click on the canvas -> Copy Coordinate -> EPSG:4326. "
)
elif index == 0: # offsets
self.surveyPointLat.hide()
self.surveyPointLon.hide()
self.degreeSignX.hide()
self.degreeSignY.hide()
self.label_survey.hide()
self.offsetX.show()
self.offsetY.show()
self.label_offsets.show()
# if self.dataStorage.currentOriginalUnits == 'degrees':
self.offsetXDegreeSign.show()
self.offsetYDegreeSign.show()
units = self.dataStorage.currentOriginalUnits
print(units)
if units == "degrees":
self.offsetXDegreeSign.setText("°")
self.offsetYDegreeSign.setText("°")
elif units is not None:
self.offsetXDegreeSign.setText(str(units).lower()[0])
self.offsetYDegreeSign.setText(str(units).lower()[0])
else:
self.offsetXDegreeSign.hide()
self.offsetYDegreeSign.hide()
try:
authid = self.dataStorage.currentCRS.authid()
except:
try:
authid = self.dataStorage.currentCRS.name
except:
authid = str(self.dataStorage.currentCRS)
text = f"Use this option when your project requires a use of a specific CRS. \
\n\nThis will only affect Speckle data properties, not your Project CRS.\
\n\nHint: your current project CRS is '{authid}' and using units '{self.dataStorage.currentOriginalUnits}'."
if units == "degrees":
text += "\nThis CRS is not recommended if data was sent or needs to be \
\nreceived in a non-GIS application."
self.description.setText(text)
self.populateSurveyPoint()
self.populateOffsets()
self.populateRotation()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateModeDropdown(self):
if not self:
return
try:
self.modeDropdown.clear()
self.modeDropdown.addItems(
[
"Add offsets / rotation to the current Project CRS",
"Create custom CRS",
]
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def populateSurveyPoint(self):
if not self:
return
try:
self.surveyPointLat.clear()
self.surveyPointLon.clear()
if (
self.dataStorage.custom_lat is not None
and self.dataStorage.custom_lon is not None
):
self.surveyPointLat.setText(str(self.dataStorage.custom_lat))
self.surveyPointLon.setText(str(self.dataStorage.custom_lon))
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def populateRotation(self):
if not self:
return
try:
self.rotation.clear()
if self.dataStorage.crs_rotation is not None:
self.rotation.setText(str(self.dataStorage.crs_rotation))
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def populateOffsets(self):
try:
self.offsetX.clear()
self.offsetY.clear()
if (
self.dataStorage.crs_offset_x is not None
and self.dataStorage.crs_offset_y is not None
):
self.offsetX.setText(str(self.dataStorage.crs_offset_x))
self.offsetY.setText(str(self.dataStorage.crs_offset_y))
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=self)
return
def onOkClicked(self):
return
try:
self.close()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
+115
View File
@@ -0,0 +1,115 @@
import os
import urllib3
import requests
import requests_toolbelt
from specklepy.logging import metrics
try:
from specklepy_qt_ui.qt_ui.DataStorage import DataStorage
except ModuleNotFoundError:
from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from PyQt5 import QtWidgets, uic, QtCore
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
os.path.join(os.path.join(os.path.dirname(__file__), "ui", "dependencies.ui"))
)
class DependenciesUpgradeDialog(QtWidgets.QWidget, FORM_CLASS):
report_label: QtWidgets.QLabel = None
report_widget: QtWidgets.QTextEdit = None
btn_cancel: QtWidgets.QPushButton = None
btn_upgrade: QtWidgets.QPushButton = None
report_text: str = ""
dataStorage: DataStorage = None
def __init__(self, parent=None):
super(DependenciesUpgradeDialog, self).__init__(
parent, QtCore.Qt.WindowStaysOnTopHint
)
self.setupUi(self)
self.runAllSetup()
def runAllSetup(self):
self.setWindowTitle("Upgrade Python dependencies (Speckle)")
self.setMinimumWidth(900)
self.setMinimumHeight(600)
self.report_label.setWordWrap(True)
self.btn_cancel.clicked.connect(self.onOkClicked)
self.btn_upgrade.clicked.connect(self.upgradeDependencies)
self.btn_upgrade.setEnabled(True)
self.report_text = f"""Speckle plugin requires changes in versions of some Python libraries:
\nurllib3: from {urllib3.__version__} to 1.26.16
requests: from {requests.__version__} to 2.31.0
requests_toolbelt: from {requests_toolbelt.__version__} to 0.10.1
\nYou can use the button below run the upgrade automatically.
To do it manually, you can run 2 following commands from QGIS Plugins panel->Python Console, and then restart QGIS:
\n\ndef upgradeDependencies():
\timport subprocess
\tfrom speckle.utils.utils import get_qgis_python_path as path
\tresult = subprocess.run([path(), "-m", "pip", "install", "requests==2.31.0"],shell=True,timeout=1000,)
\tprint(result.returncode)
\tresult = subprocess.run([path(), "-m", "pip", "install", "urllib3==1.26.16"],shell=True,timeout=1000,)
\tprint(result.returncode)
\tresult = subprocess.run([path(), "-m", "pip", "install", "requests_toolbelt==0.10.1"],shell=True,timeout=1000,)
\tprint(result.returncode)
\nupgradeDependencies()
\n\n"""
self.report_widget.setText(self.report_text)
return
def onOkClicked(self):
self.close()
def upgradeDependencies(self):
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Resolve dependencies",
"connector_version": str(self.dataStorage.plugin_version),
},
)
except Exception as e:
print(e)
self.report_widget.setText("It might take a moment...")
self.btn_upgrade.setEnabled(False)
res1, res2, res3 = self.runSubprocess()
if res1.returncode == res2.returncode == res3.returncode == 0:
self.report_widget.setText(
"Libraries successfully upgraded. Please restart QGIS for Speckle to use the upgraded libraries."
)
else:
self.report_widget.setText(
f"Something went wrong. Here are the error logs: \n\n{res1}\n\n{res2}"
)
def runSubprocess(self):
import subprocess
try:
from speckle.utils.utils import get_qgis_python_path as path
except ModuleNotFoundError:
from speckle.speckle.utils.utils import get_qgis_python_path as path
result1 = subprocess.run(
[path(), "-m", "pip", "install", "requests==2.31.0"],
timeout=1000,
capture_output=True,
)
result2 = subprocess.run(
[path(), "-m", "pip", "install", "urllib3==1.26.16"],
timeout=1000,
capture_output=True,
)
result3 = subprocess.run(
[path(), "-m", "pip", "install", "requests_toolbelt==0.10.1"],
timeout=1000,
capture_output=True,
)
return result1, result2, result3
+171
View File
@@ -0,0 +1,171 @@
import inspect
import os
from typing import List, Tuple, Union
try:
from specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from specklepy_qt_ui.qt_ui.utils.global_resources import COLOR
from specklepy_qt_ui.qt_ui.utils.utils import SYMBOL
except ModuleNotFoundError:
from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from speckle.specklepy_qt_ui.qt_ui.utils.global_resources import COLOR
from speckle.specklepy_qt_ui.qt_ui.utils.utils import SYMBOL
# from specklepy_qt_ui.qt_ui.utils.logger import logToUser
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget
from PyQt5.QtCore import pyqtSignal
from specklepy.core.api.client import SpeckleClient
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
os.path.join(os.path.join(os.path.dirname(__file__), "ui", "report.ui"))
)
class ReportDialog(QtWidgets.QWidget, FORM_CLASS):
report_label: QtWidgets.QLabel = None
report_text: QtWidgets.QTextEdit = None
dataStorage: DataStorage = None
def __init__(self, parent=None):
super(ReportDialog, self).__init__(parent, QtCore.Qt.WindowStaysOnTopHint)
self.setupUi(self)
self.runAllSetup()
def runAllSetup(self):
self.setWindowTitle("Report (Speckle)")
self.setMinimumWidth(500)
self.setMinimumHeight(600)
self.report_label.setWordWrap(True)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(
self.onOkClicked
)
return
# self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).setText("More Info")
def assembleReport(self):
try:
if self.dataStorage is None:
return
reportList: List[dict] = self.dataStorage.latestActionReport
if reportList is None:
return
operation = ""
total_layers = 0
total_objects = 0
text = ""
sending = True
# details
last_report = ""
last_report += "Details:" + "\n"
for item in reportList:
line = ""
try: # if sending
some_id = item["feature_id"].replace(SYMBOL, "\\")
line += f'{some_id}: {item["obj_type"]}'
operation = f"Sent at {self.dataStorage.latestActionTime}"
except: # if receiving
sending = False
some_id = item[list(item.keys())[0]].replace(SYMBOL, "\\")
line += f'{some_id}: {item["obj_type"]}' # f'{item["speckle_id"]}: {item["obj_type"]}'
operation = f"Received at {self.dataStorage.latestActionTime}"
# edit based on the type
if "Layer" in item["obj_type"]:
total_layers += 1
if item["errors"] != "":
line += f', errors: {item["errors"]}'
line = "⚠️" + line[1:]
line = "\n" + line
else:
if item["errors"] != "":
line += f', errors: {item["errors"]}'
line = "" + line[1:]
else:
total_objects += 1
line = "__ " + line
last_report += line + "\n"
text += f"Operation: {operation}\n"
text += f"Total: {total_layers} layer{'' if str(total_layers).endswith('1') else 's'}, {total_objects} feature{'' if str(total_objects).endswith('1') else 's'}\n\n"
if sending is False:
try:
text += f"Host application: {self.dataStorage.latestHostApp}\n\n"
except:
pass
# layers and transformations (if applicable)
text += "Layers and transformations (if applicable):" + "\n"
for i, layer in enumerate(self.dataStorage.latestActionLayers):
# print(self.dataStorage.latestActionLayers)
name = layer # if isinstance(layer, str) else layer.name()
try:
transformExists = 0
for item in self.dataStorage.savedTransforms:
layer_name = item.split(" -> ")[0].split(" ('")[0]
transform_name = item.split(" -> ")[1]
if layer_name == name:
text += (
f"{i+1}. {layer_name} -> '{transform_name}'" + "\n"
)
transformExists += 1
break
if transformExists == 0:
text += f"{i+1}. {name} \n"
except Exception as e:
print(e)
text += "\n"
# add info about the offsets
try:
crs = self.dataStorage.project.crs()
text += "Project CRS: " + crs.authid() + "\n"
crs_keyword = "CRS"
except AttributeError:
crs = self.dataStorage.project.activeMap.spatialReference
crs_keyword = "Spatial Reference"
text += f"Project {crs_keyword}: " + crs.name + "\n"
units = self.dataStorage.latestActionUnits
text += (
f"Project {crs_keyword} units: "
+ units
+ f"{' (not supported, treated as Meters)' if 'degrees' in units else ''}"
+ "\n"
)
try:
text += f"Project {crs_keyword} WKT: \n" + crs.toWkt() + "\n\n"
except:
text += f"Project {crs_keyword} WKT: \n" + crs.exportToString() + "\n\n"
text += (
f"{crs_keyword} offsets: x={self.dataStorage.crs_offset_x}, y={self.dataStorage.crs_offset_y}"
+ "\n"
)
text += f"{crs_keyword} rotation: {self.dataStorage.crs_rotation}°" + "\n\n"
text += last_report
return operation, total_layers, total_objects, text
except Exception as e:
print(e)
return
def applyReport(self):
result = self.assembleReport()
if result is None:
print("no report generated")
return
operation, total_layers, total_objects, report = result
# self.report_label.setText(f"Operation: {operation}\nTotal: {total_layers} layer{'' if str(total_layers).endswith('1') else 's'}, {total_objects} feature{'' if str(total_objects).endswith('1') else 's'}")
self.report_text.setText(report)
def onOkClicked(self):
self.close()
+17
View File
@@ -0,0 +1,17 @@
import os
from PyQt5 import QtWidgets, uic
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
os.path.join( os.path.join(os.path.dirname(__file__), "ui", "streamlist_dialog.ui") )
)
class StreamListDialog(QtWidgets.QWidget, FORM_CLASS):
streams_add_button: QtWidgets.QPushButton
streams_reload_button: QtWidgets.QPushButton
streams_remove_button: QtWidgets.QPushButton
def __init__(self, parent=None):
super(StreamListDialog, self).__init__(parent)
self.setupUi(self)
+88
View File
@@ -0,0 +1,88 @@
import os
try:
from specklepy_qt_ui.qt_ui.DataStorage import DataStorage
except ModuleNotFoundError:
from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from PyQt5 import QtWidgets, uic, QtCore
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
os.path.join(os.path.join(os.path.dirname(__file__), "ui", "transforms.ui") )
)
class MappingSendDialog(QtWidgets.QWidget, FORM_CLASS):
dialog_button: QtWidgets.QPushButton = None
more_info: QtWidgets.QPushButton = None
layerDropdown: QtWidgets.QComboBox
transformDropdown: QtWidgets.QComboBox
addTransform: QtWidgets.QPushButton
removeTransform: QtWidgets.QPushButton
transformationsList: QtWidgets.QListWidget
elevationLayerDropdown: QtWidgets.QComboBox
attrDropdown: QtWidgets.QComboBox
dataStorage: DataStorage = None
def __init__(self, parent=None):
super(MappingSendDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint)
self.setupUi(self)
self.runAllSetup()
def runAllSetup(self):
self.setMinimumWidth(600)
self.addTransform.setStyleSheet("QPushButton {color: black; padding:3px;padding-left:5px;border: none; } QPushButton:hover { background-color: lightgrey}")
self.removeTransform.setStyleSheet("QPushButton {color: black; padding:3px;padding-left:5px;border: none; } QPushButton:hover { background-color: lightgrey}")
self.addTransform.clicked.connect(self.onAddTransform)
self.removeTransform.clicked.connect(self.onRemoveTransform)
self.transformDropdown.currentIndexChanged.connect(self.populateLayersByTransform)
self.transformDropdown.currentIndexChanged.connect(self.populateAttributesByLayer)
self.layerDropdown.currentIndexChanged.connect(self.populateAttributesByLayer)
#self.dialog_button.clicked.connect(self.saveElevationLayer)
self.dialog_button.clicked.connect(self.onOkClicked)
self.more_info.clicked.connect(self.onMoreInfo)
return
def runSetup(self):
return
def populateSavedTransforms(self, dataStorage): #, savedTransforms: Union[List, None] = None, getLayer: Union[str, None] = None, getTransform: Union[str, None] = None):
return
def onAddTransform(self):
return
def onRemoveTransform(self):
return
def onOkClicked(self):
try:
self.close()
except: pass
def populateLayers(self):
return
def populateLayersByTransform(self):
return
def populateAttributesByLayer(self):
return
def populateTransforms(self):
return
def populateSavedElevationLayer(self, dataStorage): #, savedTransforms: Union[List, None] = None, getLayer: Union[str, None] = None, getTransform: Union[str, None] = None):
return
def saveElevationLayer(self):
return
def onMoreInfo(self):
return