widget for upgrading dependencies; line breaks in notifications; formatting

This commit is contained in:
KatKatKateryna
2023-10-28 01:04:26 +01:00
parent 11c6129e81
commit 1c4dc0c07e
7 changed files with 749 additions and 304 deletions
+182 -99
View File
@@ -1,4 +1,3 @@
import inspect
from typing import Any, List
@@ -11,13 +10,21 @@ from specklepy.logging import metrics
from specklepy.core.api.credentials import Account
from specklepy_qt_ui.qt_ui.global_resources import (
BACKGR_COLOR, BACKGR_COLOR_LIGHT, BACKGR_COLOR_GREY, BACKGR_COLOR_TRANSPARENT, BACKGR_COLOR_HIGHLIGHT,
NEW_GREY, NEW_GREY_HIGHLIGHT, BACKGR_ERROR_COLOR, BACKGR_ERROR_COLOR_LIGHT
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_report import ReportDialog
from specklepy_qt_ui.qt_ui.widget_dependencies_upgrade import DependenciesUpgradeDialog
from specklepy_qt_ui.qt_ui.widget_report import ReportDialog
class LogWidget(QWidget):
dataStorage = None
msgs: List[str] = []
used_btns: List[int] = []
@@ -31,54 +38,54 @@ class LogWidget(QWidget):
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)
# print(self.parentWidget)
self.max_msg = 8
# create a temporary floating button
width = 0 #parent.frameSize().width()
height = 0 # parent.frameSize().height()
# 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.layout.setAlignment(Qt.AlignBottom)
self.setGeometry(0, 0, width, height)
self.createBtns()
self.hide()
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 = 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()
# print("Mouse Release Event")
self.hide()
# self.parentWidget.hideError()
def hide(self):
#print("___HIDE LOG WIDGET")
# 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)
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
@@ -86,21 +93,25 @@ class LogWidget(QWidget):
self.msgs.clear()
def addButton(self, obj: dict):
#print("Add button")
# print("Add button")
text: str = obj["text"]
level: int = obj["level"]
url: str = obj["url"]
url: str = obj["url"]
blue: bool = obj["blue"]
report: bool = obj["report"]
self.setGeometry(
0,
0,
self.parentWidget.frameSize().width(),
self.parentWidget.frameSize().height(),
)
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)
# print(btn)
btn.setAccessibleName(url)
#print(btn)
# print(btn)
btn.setText(text)
self.resizeToText(btn)
@@ -108,45 +119,79 @@ class LogWidget(QWidget):
boxLayout = QHBoxLayout(widget)
spacer = QPushButton("")
spacer.setStyleSheet("QPushButton {padding:0px;"+ f"{BACKGR_COLOR_TRANSPARENT}" + "}")
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
# 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}" + " }")
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
if report is True:
# color report btn
reportList = self.dataStorage.latestActionReport
if reportList is not None:
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
# 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
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.layout.addWidget(widget) #, alignment=Qt.AlignCenter)
self.reportBtn = reportBtn
self.layout.addWidget(widget) # , alignment=Qt.AlignCenter)
self.msgs.append(text)
self.used_btns.append(1)
@@ -156,91 +201,129 @@ class LogWidget(QWidget):
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)})
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])
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 = ""):
def btnClicked(self, url=""):
try:
btn = self.sender()
url = btn.accessibleName()
if url == "" or not isinstance(url, str):
if url == "" or not isinstance(url, str):
return
elif isinstance(url, str):
if url.startswith("http"):
self.openURL(url)
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.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)
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:
except Exception as e:
print(e)
pass #logger.logToUser(str(e), level=2, func = inspect.stack()[0][3])
#self.hide()
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
#print(index)
#print(self.btns)
index = len(self.used_btns) # get the next "free" button
# print(index)
# print(self.btns)
if index >= len(self.btns):
if index >= len(self.btns):
# remove first button
print(self.layout.itemAt(0).widget())
self.layout.itemAt(0).widget().setParent(None)
#self.used_btns.clear()
# self.used_btns.clear()
self.createBtns()
index = 0
index = 0
btn = self.btns[index]
#print(btn)
return btn, index
# print(btn)
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
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}" + "}")
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
# if len(text.split("\n"))>2:
height = len(text.split("\n")) * 25 + 20
btn.setMinimumHeight(height)
return btn
except Exception as e:
return btn
except Exception as e:
print(e)
return btn
return btn
+75 -61
View File
@@ -1,74 +1,88 @@
from PyQt5.QtWidgets import QMessageBox
from PyQt5 import QtCore
from specklepy_qt_ui.qt_ui.utils import splitTextIntoLines
def logToUser(msg: str, func=None, level: int = 2, plugin = None, url = "", blue = False, report = False):
#print("Log to user")
msg = str(msg)
#if func is not None and func != "None":
# print(msg + " " + url + "::" + str(func))
#else:
# print(msg + " " + url )
dockwidget = plugin
try:
if url == "" and blue is False: # only for info messages
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 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: # only for info messages
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
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 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")
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)
# print("Create window")
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
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
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
+99
View File
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DependenciesUpgradeDialog</class>
<widget class="QWidget" name="ReportDialog">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="search_form">
<property name="leftMargin">
<number>10</number>
</property>
<property name="topMargin">
<number>10</number>
</property>
<property name="rightMargin">
<number>10</number>
</property>
<property name="bottomMargin">
<number>10</number>
</property>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout">
<item row="2" >
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="report_label">
<property name="text">
<string> </string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="report_widget">
<property name="text">
<string>Option: </string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QWidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btn_upgrade">
<property name="text">
<string>Upgrade dependencies automatically</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+48 -30
View File
@@ -1,59 +1,77 @@
from textwrap import wrap
def splitTextIntoLines(text: str = "", number: int= 40) -> str:
#print("__splitTextIntoLines")
#print(text)
def splitTextIntoLines(text: str = "", number: int = 40) -> str:
# print("__splitTextIntoLines")
# print(text)
msg = ""
try:
if len(text)>number:
if len(text) > number:
try:
lines = wrap(text, number)
for i, x in enumerate(lines):
msg += x
if i!= len(lines) - 1:
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:
except Exception as e:
print(e)
else:
msg = text
except Exception as e:
print(e)
print(text)
return msg
def constructCommitURL(streamWrapper, branch_id: str = None, commit_id: str = None) -> str:
import requests
def constructCommitURL(
streamWrapper, branch_id: str = None, commit_id: str = None
) -> str:
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']
url = streamUrl.replace("streams", "projects") + "/models/" + branch_id + "@" + commit_id
url = streamUrl
# check for frontend2
try:
header = r.headers["x-speckle-frontend-2"]
url = (
streamUrl.replace("streams", "projects")
+ "/models/"
+ branch_id
+ "@"
+ commit_id
)
except:
url = streamUrl.replace("projects", "streams") + "/commits/" + commit_id
return url
return url
except:
pass
pass
def constructCommitURLfromServerCommit(serverURL: str, stream_id: str) -> str:
import requests
import requests
r = requests.get(serverURL)
# check for frontend2
try:
header = r.headers['x-speckle-frontend-2']
#url = streamUrl.replace("streams", "projects") + "/models/" + branch_id + "@" + commit_id
url = serverURL + "/projects/" + stream_id # replace with 'projects' after it's implemented in Specklepy
# check for frontend2
try:
header = r.headers["x-speckle-frontend-2"]
# url = streamUrl.replace("streams", "projects") + "/models/" + branch_id + "@" + commit_id
url = (
serverURL + "/projects/" + stream_id
) # replace with 'projects' after it's implemented in Specklepy
except:
url = serverURL + "/streams/" + stream_id
return url
return url
#def removeSpecialCharacters(text: str) -> str:
# def removeSpecialCharacters(text: str) -> str:
# new_text = text.replace("[","_").replace("]","_").replace(" ","_").replace("-","_").replace("(","_").replace(")","_").replace(":","_").replace("\\","_").replace("/","_").replace("\"","_").replace("&","_").replace("@","_").replace("$","_").replace("%","_").replace("^","_")
# return new_text
+205 -93
View File
@@ -12,7 +12,7 @@ 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.credentials import get_local_accounts # , StreamWrapper
from specklepy.core.api.wrapper import StreamWrapper
from specklepy.logging import metrics
@@ -20,11 +20,11 @@ from specklepy_qt_ui.qt_ui.utils import constructCommitURLfromServerCommit
# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
os.path.join(os.path.join(os.path.dirname(__file__), "ui", "add_stream_modal.ui") )
os.path.join(os.path.join(os.path.dirname(__file__), "ui", "add_stream_modal.ui"))
)
class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):
class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):
search_button: QtWidgets.QPushButton = None
search_text_field: QtWidgets.QLineEdit = None
search_results_list: QtWidgets.QListWidget = None
@@ -35,192 +35,305 @@ class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):
branch_result: Branch = None
commit_result: Commit = None
speckle_client: Union[SpeckleClient, None] = None
dataStorage: DataStorage = None
dataStorage: DataStorage = None
#Events
# Events
handleStreamAdd = pyqtSignal(object)
def __init__(self, parent=None, speckle_client: SpeckleClient = None):
super(AddStreamModalDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint)
super(AddStreamModalDialog, self).__init__(
parent, QtCore.Qt.WindowStaysOnTopHint
)
self.speckle_client = speckle_client
self.setupUi(self)
self.setWindowTitle("Add Speckle stream")
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(False)
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.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)
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])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def getAllStreams(self):
try:
query = ""
sw = None
sw = None
results = []
if self.speckle_client is not None:
if self.speckle_client is not None:
results = self.speckle_client.stream.search(query)
try: metrics.track("Connector Action", self.dataStorage.active_account, {"name": "Stream 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])
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Stream 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])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onSearchClicked(self):
try:
query = self.search_text_field.text()
sw = None
sw = None
streams = []
branch = None
commit = None
#print("_____ onSearchClicked___")
if "http" in query and len(query.split("/")) >= 3: # URL
# print("_____ onSearchClicked___")
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):
stream = sw.get_client().stream.get(
id=sw.stream_id, branch_limit=100, commit_limit=100
)
if isinstance(stream, Stream):
streams = [stream]
if "/branches/" in query:
#print("branches")
branch_name = query.split("/branches/")[len(query.split("/branches/"))-1].split("/")[0].split("?")[0].split("&")[0].split("@")[0]
#print(branch_name)
#print(stream)
#print(len(stream.branches.items))
# print("branches")
branch_name = (
query.split("/branches/")[
len(query.split("/branches/")) - 1
]
.split("/")[0]
.split("?")[0]
.split("&")[0]
.split("@")[0]
)
# print(branch_name)
# print(stream)
# print(len(stream.branches.items))
for br in stream.branches.items:
name = urllib.parse.quote(br.name)
#print(name)
# print(name)
if name == branch_name:
branch = br
break
break
elif "/commits/" in query:
#print("commits")
commit_id = query.split("/commits/")[len(query.split("/commits/"))-1].split("/")[0].split("?")[0].split("&")[0].split("@")[0]
# print("commits")
commit_id = (
query.split("/commits/")[len(query.split("/commits/")) - 1]
.split("/")[0]
.split("?")[0]
.split("&")[0]
.split("@")[0]
)
for br in stream.branches.items:
for com in br.commits.items:
if com.id == commit_id:
branch = br
commit = com
#print(branch)
#print(commit)
commit = com
# print(branch)
# print(commit)
break
elif isinstance(stream, Exception):
print(stream)
#if "/commits/" in query:
# if "/commits/" in query:
# branch_id = query.split("/commits/")[len(query.split("/commits/"))-1].split("/")[0].split("?")[0].split("&")[0].split("@")[0]
# for com in stream.
# if br.id == branch_id:
# branch = br
# break
try: metrics.track("Connector Action", self.dataStorage.active_account, {"name": "Stream 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:
# break
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Stream 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": "Stream 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])
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Stream 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])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateResultsList(self, sw = None):
def populateResultsList(self, sw=None):
try:
self.search_results_list.clear()
if isinstance(self.stream_results, SpeckleException):
logToUser("Some streams cannot be accessed", level = 1, func = inspect.stack()[0][3])
return
if isinstance(self.stream_results, SpeckleException):
logToUser(
"Some streams 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:
else:
host = self.speckle_client.account.serverInfo.url
if isinstance(stream, SpeckleException):
logToUser("Some streams 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
])
if isinstance(stream, SpeckleException):
logToUser(
"Some streams 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])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onOkClicked(self):
try:
if isinstance(self.stream_results, SpeckleException):
logToUser("Selected stream cannot be accessed: "+ str(self.stream_results.message), level = 1, func = inspect.stack()[0][3])
logToUser(
"Selected stream 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])
#url = item.text().split(" | ")[1] + "/streams/" + 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}"))
url = constructCommitURLfromServerCommit(
item.text().split(" | ")[1],
item.text().split(", ")[1].split(" | ")[0],
)
# url = item.text().split(" | ")[1] + "/streams/" + 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 streams cannot be accessed: " + str(e), level = 1, func = inspect.stack()[0][3])
return
logToUser(
"Some streams cannot be accessed: " + str(e),
level=1,
func=inspect.stack()[0][3],
)
return
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onCancelClicked(self):
try:
self.close()
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onAccountSelected(self, index = 0):
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] )
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"))
self.speckle_client.authenticate_with_token(token=account.token)
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])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populate_accounts_dropdown(self):
@@ -235,6 +348,5 @@ class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):
]
)
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
+42 -21
View File
@@ -6,15 +6,20 @@ from specklepy_qt_ui.qt_ui.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.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") )
os.path.join(os.path.join(os.path.dirname(__file__), "ui", "create_stream.ui"))
)
class CreateStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):
class CreateStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):
name_field: QtWidgets.QLineEdit = None
description_field: QtWidgets.QLineEdit = None
dialog_button_box: QtWidgets.QDialogButtonBox = None
@@ -23,31 +28,41 @@ class CreateStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):
speckle_client: Union[SpeckleClient, None] = None
#Events
# Events
handleStreamCreate = pyqtSignal(Account, str, str, bool)
def __init__(self, parent=None, speckle_client: SpeckleClient = None):
super(CreateStreamModalDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint)
super(CreateStreamModalDialog, self).__init__(
parent, QtCore.Qt.WindowStaysOnTopHint
)
self.speckle_client = speckle_client
self.setupUi(self)
self.setWindowTitle("Create New Stream")
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.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)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(
True
)
else:
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).setEnabled(
False
)
return
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onOkClicked(self):
@@ -56,27 +71,34 @@ class CreateStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):
name = self.name_field.text()
description = self.description_field.text()
public = self.public_toggle.isChecked()
self.handleStreamCreate.emit(acc,name,description,public)
self.handleStreamCreate.emit(acc, name, description, public)
self.close()
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def onCancelClicked(self):
try:
#self.handleCancelStreamCreate.emit()
# self.handleCancelStreamCreate.emit()
self.close()
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def 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)
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])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populate_accounts_dropdown(self):
@@ -91,6 +113,5 @@ class CreateStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):
]
)
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3])
logToUser(e, level=2, func=inspect.stack()[0][3])
return
+98
View File
@@ -0,0 +1,98 @@
import os
import urllib3
import requests
import requests_toolbelt
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 = ""
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(400)
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):
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.stdout}\n\n{res2.stdout}"
)
def runSubprocess(self):
import subprocess
from speckle.utils.utils import get_qgis_python_path as path
result1 = subprocess.run(
[path(), "-m", "pip", "install", "requests==2.31.0"],
shell=True,
timeout=1000,
capture_output=True,
text=True,
)
result2 = subprocess.run(
[path(), "-m", "pip", "install", "urllib3==1.26.16"],
shell=True,
timeout=1000,
capture_output=True,
text=True,
)
result3 = subprocess.run(
[path(), "-m", "pip", "install", "requests_toolbelt==0.10.1"],
shell=True,
timeout=1000,
capture_output=True,
text=True,
)
return result1, result2, result3