Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9cd480fb6e | |||
| 3b1d62c322 | |||
| 4ee673d9cd | |||
| ed66a0fb2e | |||
| 18435bf4af | |||
| 61af1d7676 | |||
| bb081a9b52 | |||
| 563b54aa60 | |||
| 0b099810e5 | |||
| 30c3514bde | |||
| 97adaca60a | |||
| 66f45e8147 | |||
| 8d505db4fd | |||
| 5a85a97785 | |||
| 04e4f43972 | |||
| ef07bedc26 | |||
| 97f2afedfb | |||
| ae83a21179 | |||
| 240efef89a | |||
| 6db462df3c | |||
| 4600fa5167 | |||
| 876068a39a | |||
| d0c464d736 | |||
| ea76011ed2 | |||
| 51b1de4587 | |||
| 2a577de45c | |||
| 937ff8a878 | |||
| d2451a41d8 | |||
| 912ba2d734 | |||
| 99a1c99422 | |||
| 62733b13d8 | |||
| 3ebe5c478d | |||
| fd340a2026 | |||
| b73753d15c | |||
| 90285387f9 | |||
| f773b1b7c6 | |||
| 91ee94a965 | |||
| 3fc8428e5a | |||
| 2d42f13f49 | |||
| 2e1afa7f9c | |||
| 65a1f4730c | |||
| 1c4dc0c07e | |||
| 11c6129e81 | |||
| ca6b63fea8 | |||
| eb7f90dacb | |||
| 483ffcf3c2 | |||
| ff5c3f3ad4 | |||
| 239ca78102 | |||
| 1cfad1ab47 | |||
| 1db92e6a21 | |||
| 1b784e6699 | |||
| 21ca787be9 | |||
| 54c81046cf | |||
| 31d66e2a50 | |||
| b0956c215a | |||
| 26ebf81727 | |||
| dec8d1c64c | |||
| 213db29bc0 | |||
| db271e879d | |||
| 7c69b4fbc0 | |||
| 8bd86ca029 | |||
| 2b6f82e13f | |||
| e30ba5d5ce | |||
| 5a9e69ea2f | |||
| 4075fe3dde | |||
| fa6855cd7c | |||
| 12e17283fb | |||
| 9abe5538c9 | |||
| 9bda01eea6 | |||
| 74da8af536 | |||
| f2a263b399 | |||
| 7052a5cd81 | |||
| c3c0f6a202 | |||
| dd429e2996 | |||
| bbcc946a2a | |||
| 17a0d311d5 | |||
| 2dd3b5e179 | |||
| 8c07bf8f41 | |||
| 01f7c0c8cb | |||
| 15f6fa50a6 | |||
| a4694c36f9 | |||
| 5974a94759 | |||
| 6cfee19bed | |||
| 0e8ca45a0c | |||
| 42a25eb085 | |||
| 2ed4e2d0ab | |||
| beb04e0cc5 | |||
| 52ae8b96ba | |||
| fb36569263 | |||
| 098cceac0a | |||
| c57b502e03 | |||
| de2c90e9dc | |||
| e33bec3e89 | |||
| fc893148b8 | |||
| e4f9e0bf11 | |||
| 2295e145c7 | |||
| 7b475233c7 | |||
| 4b17aef8d3 | |||
| 720ed755e9 | |||
| d124a3d6f7 | |||
| 1abafd2ecf | |||
| 1146a55614 | |||
| 8da2270781 | |||
| a5d85b1b64 | |||
| c796136b5b | |||
| f12f468dd9 | |||
| 1425f2874b | |||
| dfb20fa4ad | |||
| df86bab6ce | |||
| 4f2b1a5545 |
@@ -0,0 +1 @@
|
||||
*.pyc
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
# specklepy_qt_ui
|
||||
|
||||
Python-based Qt UI for Speckle connectors
|
||||
Based on PyQt5
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 345 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 400 B |
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
"""
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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
|
||||