ui and sending working

This commit is contained in:
KatKatKateryna
2023-12-07 21:11:26 +08:00
parent 8df1a6d760
commit 7770f6c1d1
11 changed files with 794 additions and 410 deletions
+3 -3
View File
@@ -5,8 +5,8 @@ import json
import os
try:
from speckle.converter.layers.CRS import CRS
from speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
from speckle.speckle.converter.layers.CRS import CRS
from speckle.speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
except:
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.CRS import CRS
from speckle_toolbox.esri.toolboxes.speckle.converter.layers.Layer import Layer, VectorLayer, RasterLayer
@@ -73,7 +73,7 @@ for k, grp in enumerate(sym.renderer.groups):
from speckle.converter.layers.symbology import get_polygon_simpleRenderer
from speckle.speckle.converter.layers.symbology import get_polygon_simpleRenderer
from arcpy._mp import ArcGISProject
aprx = ArcGISProject('CURRENT')
@@ -10,38 +10,42 @@ from subprocess_call import subprocess_call
import sys
ENV_NEW_NAME = "arcgispro-py3-speckle"
def setup():
pythonExec = get_python_path() # import numpy; import os; print(os.path.abspath(numpy.__file__))
return pythonExec # None if not successful
def get_python_path(): # create a full copy of default env
#print("Get Python path")
# or: import site; site.getsitepackages()[0]
# import specklepy; import os; print(os.path.abspath(specklepy.__file__)) ##currentPythonExec = sysconfig.get_paths()['data'] + r"\python.exe"
def get_default_python():
pythonExec = os.environ["ProgramFiles"] + r'\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\python.exe' #(r"%PROGRAMFILES%\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\python.exe") #os.path.dirname(sys.executable) + "\\python.exe" # default python.exe
#print(pythonExec)
if not os.path.exists(pythonExec):
pythonExec = os.getenv('APPDATA').replace("Roaming", "Local") + r'\Programs\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\python.exe'
if not os.path.exists(pythonExec): return None
return pythonExec
def get_python_path(): # create a full copy of default env
#print("Get Python path")
# or: import site; site.getsitepackages()[0]
# import specklepy; import os; print(os.path.abspath(specklepy.__file__)) ##currentPythonExec = sysconfig.get_paths()['data'] + r"\python.exe"
def_exec = get_default_python()
#print(os.getenv('APPDATA') + r'\Programs\ArcGIS\Pro\bin\Python\envs\arcgispro-py3\python.exe')
if sys.platform == "win32":
env_new_name = "arcgispro-py3-speckle"
newExec = clone_env(pythonExec, env_new_name) # only if doesn't exist yet
newExec = clone_env(def_exec) # only if doesn't exist yet
if not os.path.exists(newExec): return None
activate_env(env_new_name)
activate_env()
return newExec
else: return None
def clone_env(pythonExec_old: str, env_new_name: str):
def clone_env(pythonExec_old: str):
install_folder = os.getenv('APPDATA').replace("\\Roaming","") + r"\Local\ESRI\conda\envs" #r"%LOCALAPPDATA%\ESRI\conda\envs"
if not os.path.exists(install_folder): os.makedirs(install_folder)
default_env = pythonExec_old.replace("Pro\\bin\\Python\\envs\\arcgispro-py3\\python.exe","Pro\\bin\\Python\\envs\\arcgispro-py3") # + "\\" + 'arcgispro-py3'
conda_exe = pythonExec_old.replace("Pro\\bin\\Python\\envs\\arcgispro-py3\\python.exe","Pro\\bin\\Python\\Scripts\\conda.exe") #%PROGRAMFILES%\ArcGIS\Pro\bin\Python\Scripts\conda.exe #base: %PROGRAMDATA%\Anaconda3\condabin\conda.bat
new_env = install_folder + "\\" + env_new_name # %LOCALAPPDATA%\ESRI\conda\envs\...
new_env = install_folder + "\\" + ENV_NEW_NAME # %LOCALAPPDATA%\ESRI\conda\envs\...
if os.path.exists(conda_exe) and os.path.exists(default_env) and os.path.exists(new_env) and not os.path.exists(new_env + "\\python.exe"):
# conda environment invalid: delete it's folder
@@ -60,9 +64,9 @@ def clone_env(pythonExec_old: str, env_new_name: str):
print(new_env + "\\python.exe")
return new_env + "\\python.exe"
def activate_env(env_new_name: str):
def activate_env():
# using Popen, because process does not return result; subprocess.run will hang indefinitely
variable = subprocess.Popen((f'proswap {env_new_name}'),stdout = subprocess.PIPE,stderr = subprocess.PIPE,text = True,shell = True)
variable = subprocess.Popen((f'proswap {ENV_NEW_NAME}'),stdout = subprocess.PIPE,stderr = subprocess.PIPE,text = True,shell = True)
# activate new env : https://support.esri.com/en/technical-article/000024206
@@ -152,10 +156,16 @@ def installDependencies(pythonExec: str, pkgName: str, pkgVersion: str):
return True
pythonPath = setup()
print(pythonPath)
if pythonPath is not None:
#def_exec = get_default_python()
#conda_exe = def_exec.replace("Pro\\bin\\Python\\envs\\arcgispro-py3\\python.exe","Pro\\bin\\Python\\Scripts\\conda.exe") #%PROGRAMFILES%\ArcGIS\Pro\bin\Python\Scripts\conda.exe #base: %PROGRAMDATA%\Anaconda3\condabin\conda.bat
#subprocess_call([conda_exe, 'proup','-n', ENV_NEW_NAME])
clearToolbox(pythonPath)
installToolbox(pythonPath)
installDependencies(pythonPath, "specklepy", "2.9.0" )
installDependencies(pythonPath, "specklepy", "2.17.12" )
installDependencies(pythonPath, "panda3d", "1.10.11" )
installDependencies(pythonPath, "PyQt5", "5.15.9" )
+2 -77
View File
@@ -1,77 +1,2 @@
<?xml version="1.0"?>
<metadata xml:lang="en"><Esri><CreaDate>20220718</CreaDate><CreaTime>13500100</CreaTime><ArcGISFormat>1.0</ArcGISFormat><SyncOnce>TRUE</SyncOnce><ModDate>20230912</ModDate><ModTime>150318</ModTime><scaleRange><minScale>150000000</minScale><maxScale>5000</maxScale></scaleRange><ArcGISProfile>ItemDescription</ArcGISProfile></Esri><toolbox name="Speckle" alias="speckle_toolbox_"><arcToolboxHelpPath>c:\program files\arcgis\pro\Resources\Help\gp</arcToolboxHelpPath><toolsets/></toolbox><dataIdInfo><idCitation><resTitle>Speckle</resTitle></idCitation><idPurp>Speckle connector for ArcGIS</idPurp><searchKeys><keyword>speckle3d</keyword></searchKeys></dataIdInfo><distInfo><distributor><distorFormat><formatName>ArcToolbox Toolbox</formatName></distorFormat></distributor></distInfo><mdHrLv><ScopeCd value="005"></ScopeCd></mdHrLv><Binary><Thumbnail><Data EsriPropertyType="PictureX">/9j/4AAQSkZJRgABAQEAYABgAAD/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdC
IFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA9tYAAQAA
AADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFj
cHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAA
ABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAACVAAAAHBkbWRkAAACxAAAAIh2dWVkAAAD
TAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAAABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJD
AAAEPAAACAxnVFJDAAAEPAAACAxiVFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5
OCBIZXdsZXR0LVBhY2thcmQgQ29tcGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEA
AAAAAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAA
AAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAA
AA+EAAC2z2Rlc2MAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAFklFQyBo
dHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAABkZXNjAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAt
IHNSR0IAAAAAAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAt
IHNSR0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcg
Q29uZGl0aW9uIGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENv
bmRpdGlvbiBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAA
ABOk/gAUXy4AEM8UAAPtzAAEEwsAA1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVhcwAAAAAA
AAABAAAAAAAAAAAAAAAAAAAAAAAAAo8AAAACc2lnIAAAAABDUlQgY3VydgAAAAAAAAQAAAAABQAK
AA8AFAAZAB4AIwAoAC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUA
mgCfAKQAqQCuALIAtwC8AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEy
ATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMC
DAIUAh0CJgIvAjgCQQJLAlQCXQJnAnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMh
Ay0DOANDA08DWgNmA3IDfgOKA5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4E
jASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3
BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDII
RghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpUCmoKgQqY
Cq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcMwAzZDPMNDQ0mDUAN
Wg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9eD3oPlg+zD88P7BAJECYQQxBh
EH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT
5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReu
F9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9oc
AhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCY
IMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZcl
xyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2
K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIx
SjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDec
N9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+
oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXe
RiJGZ0arRvBHNUd7R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN
3E4lTm5Ot08AT0lPk0/dUCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYP
VlxWqVb3V0RXklfgWC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1f
D19hX7NgBWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/
aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfBy
S3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyB
fOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuH
n4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/jmaOzo82j56QBpBukNaRP5GokhGSepLj
k02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6f
HZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1
q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm4
0blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZG
xsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG1EnU
y9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM4lPi2+Nj
4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/8ozz
GfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t////2wBDAAMCAgMC
AgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIU
FRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU
FBQUFBQUFBQUFBQUFBT/wAARCAAgACADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAEC
AwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0Kx
wRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1
dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ
2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QA
tREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYk
NOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaH
iImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq
8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9Kfif8SNJ+EngjUPFeui4OlWLwrObWMPIolmSINtJGQDI
Ccc4BwCcA8rbftG+F7y3iuLe31CeCVBJHLGkTK6kZDAiTBBHeuZ/bq/5NW8b/wDbj/6XW9fmzpP/
ACC7P/rin/oIr9T4V4XwueYKVetJqSk18rRfl3Z8NxLnlfJfZypK/MfpTq37XHhmxvpbeFLVhGdj
C71KKGRWHDKUG7GD716V8OfihoHxP0l7vRr+2uZ4Nq3drDOsj27HOA209Dg4bvg9CCB+S1fZf/BO
v/moH/cP/wDbmvX4j4OwOV5XUxlBvmhy/O8ktfvvofO5DxVjMxzKGFrJcs7/ACsm/wBD1H9ur/k1
bxv/ANuP/pdb1+bOk/8AILs/+uKf+giv2Y1DT7XVrC5sb62hvLK5iaGe2uIxJHLGwIZGU8MpBIIP
BBrw/Rf2JPhRpLXQk0e81GGWTdDDdahMFtU7RxmNlJUDAy5ZuOWNeTwlxVg8jwlShioybcuZctne
6Stq1ta+/wDwfoOKMjxOcqlHDNK173dvyTPzhr7L/wCCdf8AzUD/ALh//tzXsv8Awx58If8AoUf/
ACpXn/x6uz+HXwd8IfCf+0P+EV0j+yv7Q8v7T/pM03mbN2z/AFjtjG9umOtepxDxpl+bZZVwVCE1
KXLa6jbSSfST7djwMi4Sx2WZjSxdacHGN72bvrFrrFd+5//Z</Data></Thumbnail></Binary><mdDateSt Sync="TRUE">20220725</mdDateSt></metadata>
<?xml version="1.0" encoding="UTF-8"?>
<metadata xml:lang="en"><Esri><CreaDate>20231206</CreaDate><CreaTime>23391500</CreaTime><ArcGISFormat>1.0</ArcGISFormat><SyncOnce>TRUE</SyncOnce><ModDate>20231206</ModDate><ModTime>233915</ModTime></Esri><toolbox name="Speckle" alias="speckle_toolbox_"><arcToolboxHelpPath>c:\program files\arcgis\pro\Resources\Help\gp</arcToolboxHelpPath><toolsets/></toolbox><dataIdInfo><idCitation><resTitle>Speckle</resTitle></idCitation></dataIdInfo><distInfo><distributor><distorFormat><formatName>ArcToolbox Toolbox</formatName></distorFormat></distributor></distInfo></metadata>
@@ -33,13 +33,14 @@ try:
from speckle.speckle.converter.layers.Layer import (Layer, VectorLayer, RasterLayer)
from speckle.speckle.converter.layers import convertSelectedLayers, getLayers
from speckle.speckle.converter.layers.utils import findAndClearLayerGroup
from speckle.speckle.ui.validation import tryGetStream, validateBranch, validateCommit, validateStream, validateTransport
from speckle.speckle.ui.validation import tryGetStream, tryGetClient, validateBranch, validateCommit, validateStream, validateTransport
from speckle.speckle.ui.add_stream_modal import AddStreamModalDialog
from speckle.speckle.ui.create_stream import CreateStreamModalDialog
from speckle.speckle.ui.create_branch import CreateBranchModalDialog
from speckle.speckle.ui.speckle_qgis_dialog import SpeckleGISDialog
from speckle.speckle.ui.logger import logToUser, logToUserWithAction
from speckle.speckle.plugin_utils.helpers import removeSpecialCharacters, getAppName
from speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.plugin_utils.object_utils import callback, traverseObject
@@ -47,13 +48,14 @@ except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers import convertSelectedLayers, getLayers
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.emptyLayerTemplates import createGroupLayer
from speckle_toolbox.esri.toolboxes.speckle.speckle.converter.layers.utils import findAndClearLayerGroup
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.validation import tryGetStream, validateBranch, validateCommit, validateStream, validateTransport
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.validation import tryGetStream, tryGetClient, validateBranch, validateCommit, validateStream, validateTransport
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.add_stream_modal import AddStreamModalDialog
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.create_stream import CreateStreamModalDialog
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.create_branch import CreateBranchModalDialog
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.speckle_qgis_dialog import SpeckleGISDialog
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser, logToUserWithAction
from speckle_toolbox.esri.toolboxes.speckle.speckle.plugin_utils.helpers import removeSpecialCharacters, getAppName
from speckle_toolbox.esri.toolboxes.speckle.specklepy_qt_ui.qt_ui.DataStorage import DataStorage
# Import the code for the dialog
@@ -138,6 +140,7 @@ class SpeckleGIS:
version: str
gis_version: str
dataStorage: DataStorage
dockwidget: Optional[SpeckleGISDialog]
add_stream_modal: AddStreamModalDialog
create_stream_modal: CreateStreamModalDialog
@@ -168,6 +171,7 @@ class SpeckleGIS:
self.gis_version = full_version
# Save reference to the QGIS interface
self.dataStorage = None
self.dockwidget = None
#self.iface = None
self.gis_project = ArcGISProject('CURRENT') #QgsProject.instance()
@@ -385,9 +389,13 @@ class SpeckleGIS:
streamWrapper = self.active_stream[0]
streamName = self.active_stream[1].name
streamId = streamWrapper.stream_id
client = streamWrapper.get_client()
# client = streamWrapper.get_client()
client, stream = tryGetClient(
streamWrapper, self.dataStorage, False, self.dockwidget
)
stream = validateStream(streamWrapper)
stream = validateStream(stream, self.dockwidget)
if stream == None: return
branchName = str(self.dockwidget.streamBranchDropdown.currentText())
@@ -472,14 +480,19 @@ class SpeckleGIS:
# Get the stream wrapper
streamWrapper = self.active_stream[0]
streamId = streamWrapper.stream_id
client = streamWrapper.get_client()
#client = streamWrapper.get_client()
client, stream = tryGetClient(
streamWrapper, self.dataStorage, False, self.dockwidget
)
# Ensure the stream actually exists
print("ON RECEIVE 2")
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3], plugin = self.dockwidget)
return
try:
stream = validateStream(streamWrapper)
stream = validateStream(stream, self.dockwidget)
if stream == None: return
branchName = str(self.dockwidget.streamBranchDropdown.currentText())
@@ -559,9 +572,12 @@ class SpeckleGIS:
try:
from speckle.ui.project_vars import get_project_streams, get_survey_point, get_project_layer_selection
except:
from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import get_project_streams, get_survey_point, get_project_layer_selection
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.project_vars import get_project_streams, get_survey_point, get_project_layer_selection
self.is_setup = self.check_for_accounts()
self.dataStorage = DataStorage()
self.dataStorage.plugin_version = self.version
self.is_setup = self.dataStorage.check_for_accounts()
if self.dockwidget is not None:
self.active_stream = None
get_project_streams(self)
@@ -593,11 +609,11 @@ class SpeckleGIS:
"""Run method that performs all the real work"""
print("run plugin")
try:
from speckle.ui.speckle_qgis_dialog import SpeckleGISDialog
from speckle.ui.project_vars import get_project_streams, get_survey_point, get_project_layer_selection
from speckle.speckle.ui.speckle_qgis_dialog import SpeckleGISDialog
from speckle.speckle.ui.project_vars import get_project_streams, get_survey_point, get_project_layer_selection
except:
from speckle_toolbox.esri.toolboxes.speckle.ui.speckle_qgis_dialog import SpeckleGISDialog
from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import get_project_streams, get_survey_point, get_project_layer_selection
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.speckle_qgis_dialog import SpeckleGISDialog
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.project_vars import get_project_streams, get_survey_point, get_project_layer_selection
try:
# Create the dialog with elements (after translation) and keep reference
# Only create GUI ONCE in callback, so that it will only load when the plugin is started
@@ -606,6 +622,11 @@ class SpeckleGIS:
if self.pluginIsActive:
self.reloadUI()
else:
self.dataStorage = DataStorage()
self.dataStorage.plugin_version = self.version
self.is_setup = self.dataStorage.check_for_accounts()
self.pluginIsActive = True
if self.dockwidget is None:
self.dockwidget = SpeckleGISDialog()
@@ -639,7 +660,7 @@ class SpeckleGIS:
try:
from speckle.ui.project_vars import set_survey_point
except:
from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import set_survey_point
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.project_vars import set_survey_point
set_survey_point(self)
def onStreamCreateClicked(self):
@@ -693,7 +714,7 @@ class SpeckleGIS:
if isinstance(br_id, GraphQLException):
logToUser(br_id.message, level=2, func = inspect.stack()[0][3])
self.active_stream = (sw, tryGetStream(sw))
self.active_stream = (sw, tryGetStream(sw, self.dataStorage))
self.current_streams[0] = self.active_stream
self.dockwidget.populateActiveStreamBranchDropdown(self)
@@ -706,14 +727,14 @@ class SpeckleGIS:
def handleStreamAdd(self, sw: StreamWrapper):
try:
from speckle.ui.project_vars import set_project_streams
from speckle.speckle.ui.project_vars import set_project_streams
except:
from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import set_project_streams
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.project_vars import set_project_streams
streamExists = 0
index = 0
try:
stream = tryGetStream(sw)
stream = tryGetStream(sw, self.dataStorage)
for st in self.current_streams:
if isinstance(stream, Stream) and st[0].stream_id == stream.id:
@@ -33,50 +33,51 @@ except:
FIELDS = ["project_streams","project_layer_selection", "lat_lon"]
def get_project_streams(self: SpeckleGIS, content: str = None):
def get_project_streams(plugin: SpeckleGIS, content: str = None):
try:
print("get proj streams")
print("GET proj streams")
project = self.gis_project
project = plugin.gis_project
table = findOrCreateSpeckleTable(project)
if table is None: return
logToUser(table, level=0, func = inspect.stack()[0][3])
rows = arcpy.da.SearchCursor(table, "project_streams")
saved_streams = []
for x in rows:
logToUser(x, level=0, func = inspect.stack()[0][3])
saved_streams.append(x[0])
temp = []
######### need to check whether saved streams are available (account reachable)
if len(saved_streams) > 0:
for url in saved_streams:
if url=="": continue
try:
sw = StreamWrapper(url)
try:
stream = tryGetStream(sw)
stream = tryGetStream(sw, plugin.dataStorage)
except SpeckleException as e:
logToUser(e.message, level=2, func = inspect.stack()[0][3])
stream = None
#strId = stream.id # will cause exception if invalid
temp.append((sw, stream))
except SpeckleException as e:
logToUser(e.message, 2)
logToUser(e.message, level=2, func = inspect.stack()[0][3])
#except GraphQLException as e:
# logger.logToUser(e.message, Qgis.Warning)
self.current_streams = temp
plugin.current_streams = temp
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def set_project_streams(self: SpeckleGIS):
def set_project_streams(plugin: SpeckleGIS):
try:
print("SET proj streams")
project = self.gis_project
project = plugin.gis_project
table = findOrCreateSpeckleTable(project)
print("SET proj streams 2")
value = [stream[0].stream_url for stream in self.current_streams] #",".join()
value = [stream[0].stream_url for stream in plugin.current_streams] #",".join()
print(value)
logToUser(value, level=0, func = inspect.stack()[0][3])
if table is not None:
proj_layers = []
@@ -106,10 +107,10 @@ def set_project_streams(self: SpeckleGIS):
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
def get_project_layer_selection(self: SpeckleGIS):
def get_project_layer_selection(plugin: SpeckleGIS):
try:
print("GET project layer selection from the table")
project = self.gis_project
project = plugin.gis_project
table = findOrCreateSpeckleTable(project)
if table is None: return
@@ -134,7 +135,7 @@ def get_project_layer_selection(self: SpeckleGIS):
break
if found == 0:
logToUser(f'Saved layer not found: "{layerPath}"', level=1, func = inspect.stack()[0][3])
self.current_layers = temp
plugin.current_layers = temp
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
@@ -287,12 +288,12 @@ def findOrCreateSpeckleTable(project: ArcGISProject) -> Union[str, None]:
arcpy.management.AddField(table, "lat_lon", "TEXT")
cursor = arcpy.da.InsertCursor(table, FIELDS )
cursor.insertRow(["",""])
cursor.insertRow(["","",""])
del cursor
except Exception as e:
logToUser("Error creating a table: " + str(e), level=1, func = inspect.stack()[0][3])
return None
raise e
else:
#print("table already exists")
# make sure fileds exist
@@ -354,266 +355,3 @@ def findOrCreateRow(table:str, fields: List[str]):
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
r'''
class speckleInputsClass:
#def __init__(self):
print("CREATING speckle inputs first time________")
instances = []
accounts: List[Account] = get_local_accounts()
account = None
streams_default: Optional[List[Stream]] = None
project = None
active_map = None
saved_streams: List[Optional[Tuple[StreamWrapper, Stream]]] = []
stream_file_path: str = ""
all_layers: List[arcLayer] = []
clients: List[SpeckleClient] = []
for acc in accounts:
if acc.isDefault: account = acc
new_client = SpeckleClient(
acc.serverInfo.url,
acc.serverInfo.url.startswith("https")
)
new_client.authenticate_with_token(token=acc.token)
clients.append(new_client)
speckle_client = None
if account:
speckle_client = SpeckleClient(
account.serverInfo.url,
account.serverInfo.url.startswith("https")
)
speckle_client.authenticate_with_token(token=account.token)
streams_default = speckle_client.stream.search("")
def __init__(self) -> None:
print("___start speckle inputs________")
self.all_layers = []
try:
aprx = ArcGISProject('CURRENT')
self.project = aprx
# following will fail if no project found
self.active_map = aprx.activeMap
if self.active_map is not None and isinstance(self.active_map, Map): # if project loaded
for layer in self.active_map.listLayers():
try: geomType = arcpy.Describe(layer.dataSource).shapeType.lower()
except: geomType = '' #print(arcpy.Describe(layer.dataSource)) #and arcpy.Describe(layer.dataSource).shapeType.lower() != "multipatch")
if (layer.isFeatureLayer and geomType != "multipatch") or layer.isRasterLayer: self.all_layers.append(layer) #type: 'arcpy._mp.Layer'
self.stream_file_path: str = aprx.filePath.replace("aprx","gdb") + "\\speckle_streams.txt"
if os.path.exists(self.stream_file_path):
try:
f = open(self.stream_file_path, "r")
content = f.read()
self.saved_streams = self.getProjectStreams(content)
f.close()
except: pass
elif len(self.stream_file_path) >10:
f = open(self.stream_file_path, "x")
f.close()
f = open(self.stream_file_path, "w")
content = ""
f.write(content)
f.close()
except: self.project = None; print("Project not found")
self.instances.append(self)
def getProjectStreams(self, content: str = None):
print("get proj streams")
if not content:
content = self.stream_file_path
try:
f = open(self.stream_file_path, "r")
content = f.read()
f.close()
except: pass
######### need to check whether saved streams are available (account reachable)
if content:
streamsTuples = []
for i, url in enumerate(content.split(",")):
streamExists = 0
index = 0
try:
#print(url)
sw = StreamWrapper(url)
stream = self.tryGetStream(sw)
for st in streamsTuples:
if isinstance(stream, Stream) and st[0].stream_id == stream.id:
streamExists = 1;
break
index += 1
if streamExists == 1: del streamsTuples[index]
streamsTuples.insert(0,(sw, stream))
except SpeckleException as e:
arcpy.AddMessage(str(e.args))
return streamsTuples
else: return []
def tryGetStream (self,sw: StreamWrapper) -> Stream:
if isinstance(sw, StreamWrapper):
steamId = sw.stream_id
try: steamId = sw.stream_id.split("/streams/")[1].split("/")[0]
except: pass
client = sw.get_client()
stream = client.stream.get(id = steamId, branch_limit = 100, commit_limit = 100)
if isinstance(stream, GraphQLException):
raise SpeckleException(stream.errors[0]['message'])
return stream
else:
raise SpeckleException('Invalid StreamWrapper provided')
class toolboxInputsClass:
print("CREATING UI inputs first time________")
instances = []
lat: float = 0.0
lon: float = 0.0
active_stream: Optional[Stream] = None
active_stream_wrapper: Optional[StreamWrapper] = None
active_branch: Optional[Branch] = None
active_commit = None
selected_layers: List[Any] = []
messageSpeckle: str = ""
action: int = 1 #send
project = None
stream_file_path: str = ""
# Get the target item's Metadata object
def __init__(self) -> None:
print("___start UI inputs________")
try:
aprx = ArcGISProject('CURRENT')
project = aprx
self.stream_file_path: str = aprx.filePath.replace("aprx","gdb") + "\\speckle_streams.txt"
if os.path.exists(self.stream_file_path):
try:
f = open(self.stream_file_path, "r")
content = f.read()
self.lat, self.lon = self.get_survey_point(content)
f.close()
except: pass
except: print("Project not found")
try:
aprx = ArcGISProject('CURRENT')
self.project = aprx
except: self.project = None; print("Project not found"); arcpy.AddWarning("Project not found")
self.instances.append(self)
def setProjectStreams(self, wr: StreamWrapper, add = True):
# ERROR 032659 Error queueing metrics request:
print("SET proj streams")
if os.path.exists(self.stream_file_path) and ".gdb\\speckle_streams.txt" in self.stream_file_path:
new_content = ""
f = open(self.stream_file_path, "r")
existing_content = f.read()
f.close()
f = open(self.stream_file_path, "w")
if str(wr.stream_url) in existing_content:
new_content = existing_content.replace(str(wr.stream_url) + "," , "")
else:
new_content = existing_content
if add == True: new_content += str(wr.stream_url) + "," # add stream
else: pass # remove stream
f.write(new_content)
f.close()
elif ".gdb\\speckle_streams.txt" in self.stream_file_path:
f = open(self.stream_file_path, "x")
f.close()
f = open(self.stream_file_path, "w")
f.write(str(wr.stream_url) + ",")
f.close()
def get_survey_point(self, content = None) -> Tuple[float]:
# get from saved project
print("get survey point")
x = y = 0
if not content:
content = None
if os.path.exists(self.stream_file_path) and ".gdb\\speckle_streams.txt" in self.stream_file_path:
try:
f = open(self.stream_file_path, "r")
content = f.read()
f.close()
except: pass
if content:
for i, coords in enumerate(content.split(",")):
if "speckle_sr_origin_" in coords:
try:
x, y = [float(c) for c in coords.replace("speckle_sr_origin_","").split(";")]
except: pass
return (x, y)
def set_survey_point(self, coords: List[float]):
# from widget (2 strings) to local vars + update SR of the map
print("SET survey point")
if len(coords) == 2:
pt = "speckle_sr_origin_" + str(coords[0]) + ";" + str(coords[1])
if os.path.exists(self.stream_file_path) and ".gdb\\speckle_streams.txt" in self.stream_file_path:
new_content = ""
f = open(self.stream_file_path, "r")
existing_content = f.read()
f.close()
f = open(self.stream_file_path, "w")
if pt in existing_content:
new_content = existing_content.replace( pt , "")
else:
new_content = existing_content
new_content += pt + "," # add point
f.write(new_content)
f.close()
elif ".gdb\\speckle_streams.txt" in self.stream_file_path:
f = open(self.stream_file_path, "x")
f.close()
f = open(self.stream_file_path, "w")
f.write(pt + ",")
f.close()
# save to project; crearte SR
self.lat, self.lon = coords[0], coords[1]
newCrsString = "+proj=tmerc +ellps=WGS84 +datum=WGS84 +units=m +no_defs +lon_0=" + str(self.lon) + " lat_0=" + str(self.lat) + " +x_0=0 +y_0=0 +k_0=1"
newCrs = osr.SpatialReference()
newCrs.ImportFromProj4(newCrsString)
newCrs.MorphToESRI() # converts the WKT to an ESRI-compatible format
validate = True if len(newCrs.ExportToWkt())>10 else False
if validate:
newProjSR = arcpy.SpatialReference()
newProjSR.loadFromString(newCrs.ExportToWkt())
#source = osr.SpatialReference()
#source.ImportFromWkt(self.project.activeMap.spatialReference.exportToString())
#transform = osr.CoordinateTransformation(source, newCrs)
self.project.activeMap.spatialReference = newProjSR
arcpy.AddMessage("Custom project CRS successfully applied")
else:
arcpy.AddWarning("Custom CRS could not be created")
else:
arcpy.AddWarning("Custom CRS could not be created: not enough coordinates provided")
return True
'''
@@ -429,9 +429,9 @@ class SpeckleGISDialog(QMainWindow):
print("populate layer dropdown / clicked save selection")
if not self: return
try:
from speckle.ui.project_vars import set_project_layer_selection
from speckle.speckle.ui.project_vars import set_project_layer_selection
except:
from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import set_project_layer_selection
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.project_vars import set_project_layer_selection
try:
self.layersWidget.clear()
@@ -531,9 +531,9 @@ class SpeckleGISDialog(QMainWindow):
def populateProjectStreams(self, plugin):
try:
from speckle.ui.project_vars import set_project_streams
from speckle.speckle.ui.project_vars import set_project_streams
except:
from speckle_toolbox.esri.toolboxes.speckle.ui.project_vars import set_project_streams
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.project_vars import set_project_streams
try:
if not self: return
@@ -13,30 +13,101 @@ try:
from speckle.speckle.ui.logger import logToUser
except:
from speckle_toolbox.esri.toolboxes.speckle.speckle.ui.logger import logToUser
def tryGetStream (sw: StreamWrapper) -> Union[Stream, None]:
try:
client = sw.get_client()
stream = client.stream.get(id = sw.stream_id, branch_limit = 100, commit_limit = 100)
if isinstance(stream, GraphQLException):
raise SpeckleException(stream.errors[0]['message'])
return stream
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None
def validateStream(streamWrapper: StreamWrapper) -> Union[Stream, None]:
def tryGetStream(
sw: StreamWrapper, dataStorage, write=False, dockwidget=None
) -> Union[Stream, None]:
try:
# print("tryGetStream")
client, stream = tryGetClient(sw, dataStorage, write, dockwidget)
return stream
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=dockwidget)
return None
def tryGetClient(sw: StreamWrapper, dataStorage, write=False, dockwidget=None):
# only streams with write access
try:
client = None
savedRole = None
savedStreamId = None
for acc in dataStorage.accounts:
# only check accounts on selected server
if acc.serverInfo.url in sw.server_url:
client = SpeckleClient(
acc.serverInfo.url, acc.serverInfo.url.startswith("https")
)
try:
client.authenticate_with_account(acc)
if client.account.token is not None:
break
except SpeckleException as ex:
if "already connected" in ex.message:
logToUser(
"Dependencies versioning error.\nClick here for details.",
url="dependencies_error",
level=2,
plugin=dockwidget,
)
return
else:
raise ex
# if token still not found
if client is None or client.account.token is None:
for acc in dataStorage.accounts:
client = sw.get_client()
if client is not None:
break
if client is not None:
stream = client.stream.get(
id=sw.stream_id, branch_limit=100, commit_limit=100
)
if isinstance(stream, Stream):
# print(stream.role)
if write == False:
# try get stream, only read access needed
# print("only read access needed")
return client, stream
else:
# check write access
# print("write access needed")
if stream.role is None or (
isinstance(stream.role, str) and "reviewer" in stream.role
):
savedRole = stream.role
savedStreamId = stream.id
else:
return client, stream
if savedRole is not None and savedStreamId is not None:
logToUser(
f"You don't have write access to the stream '{savedStreamId}'. You role is '{savedRole}'",
level=2,
func=inspect.stack()[0][3],
plugin=dockwidget,
)
return None, None
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3], plugin=dockwidget)
return None, None
def validateStream(stream: Stream, dockwidget) -> Union[Stream, None]:
try:
stream = tryGetStream(streamWrapper)
if isinstance(stream, SpeckleException): return None
if isinstance(stream, SpeckleException):
return None
if stream.branches is None:
logToUser("Stream has no branches", level=2, func = inspect.stack()[0][3])
logToUser("Stream has no branches", level=1, plugin=dockwidget)
return None
return stream
except Exception as e:
logToUser(str(e), level=2, func = inspect.stack()[0][3])
return None
logToUser(e, level=2, plugin=dockwidget)
return
def validateBranch(stream: Stream, branchName: str, checkCommits: bool) -> Union[Branch, None]:
@@ -0,0 +1,111 @@
from typing import Dict, List
from numpy import double
from import UpdateSavedStreams
from import UpdateSelectedStream
from specklepy_qt_ui.qt_ui.ConnectorBindings import ConnectorBindings
from specklepy_qt_ui.qt_ui.Models.StreamState import StreamState
class QGISBindings(ConnectorBindings):
def __init__(self):
pass
def UpdateSavedStreams(self, streams: List[StreamState]):
UpdateSavedStreams(streams)
def UpdateSelectedStream(self):
UpdateSelectedStream()
def Open3DView(self, viewCoordinates: List[double], viewName: str = ""):
'''Opens a 3D view in the host application
viewCoordinates: First three values are the camera position, second three the target.
viewName: Id or Name of the view'''
return
def GetHostAppNameVersion(self)-> str:
'''Gets the current host application name with version.'''
return
def GetHostAppName(self) -> str:
'''Gets the current host application name.'''
return
def GetFileName(self) -> str:
'''Gets the current opened/focused file's name.
Make sure to check regarding unsaved/temporary files.'''
return
def GetDocumentId(self) -> str:
'''Gets the current opened/focused file's id.
Generate one in here if the host app does not provide one.'''
return
def GetDocumentLocation(self) -> str:
'''Gets the current opened/focused file's locations.
Make sure to check regarding unsaved/temporary files.'''
return
def ResetDocument(self):
'''Clears the document state of selections and previews'''
return
def GetActiveViewName(self) -> str:
'''Gets the current opened/focused file's view, if applicable.'''
return
def GetStreamsInFile(self) -> List[StreamState]:
'''Returns the serialised clients present in the current open host file.'''
return
def WriteStreamsToFile(self, streams: List[StreamState]):
'''Writes serialised clients to the current open host file.'''
return
def AddNewStream(self, state: StreamState):
'''Adds a new client and persists the info to the host file'''
return
def PersistAndUpdateStreamInFile(self, state: StreamState):
'''Persists the stream info to the host file; if maintaining a local in memory copy, make sure to update it too.'''
return
def SendStream(self, state: StreamState, progress: ProgressViewModel) -> str:
'''Pushes a client's stream'''
return
def PreviewSend(self, state: StreamState, progress: ProgressViewModel):
'''Previews a send operation'''
def ReceiveStream(self, state: StreamState, progress: ProgressViewModel) -> StreamState:
'''Receives stream data from the server'''
def PreviewReceive(self, state: StreamState, progress: ProgressViewModel) -> StreamState:
'''Previews a receive operation'''
def GetSelectedObjects(self) -> List[str]:
'''Adds the current selection to the provided client.'''
def GetObjectsInView(self) -> List[str]:
'''Gets a list of objects in the currently active view'''
def SelectClientObjects(self, objs: List[str], deselect: bool = False):
'''clients should be able to select/preview/hover one way or another their associated objects'''
def GetSelectionFilters(self) -> List[ISelectionFilter]:
'''Should return a list of filters that the application supports.'''
def GetReceiveModes(self) -> List[ReceiveMode]:
'''Should return a list of receive modes that the application supports.'''
def GetCustomStreamMenuItems(self) -> List[MenuItem]:
'''Return a list of custom menu items for stream cards.'''
def GetSettings(self) -> List[ISetting]:
return
def ImportFamilyCommand(self, Mapping: Dict[str, List[MappingValue]] ) -> Dict[str, List[MappingValue]] :
'''Imports family symbols in Revit'''
return
@@ -0,0 +1,104 @@
import threading
from specklepy_qt_ui.qt_ui.dockwidget_main import SpeckleQGISDialog as SpeckleQGISDialog_UI
import specklepy_qt_ui.qt_ui
from speckle.ui_widgets.widget_transforms import MappingSendDialogQGIS
from PyQt5 import QtWidgets, uic
import os
import inspect
from specklepy.logging.exceptions import (SpeckleException, GraphQLException)
from specklepy.logging import metrics
from PyQt5 import QtWidgets, uic
from PyQt5.QtWidgets import QCheckBox, QListWidgetItem, QHBoxLayout, QWidget
from PyQt5.QtCore import pyqtSignal
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.DataStorage import DataStorage
FORM_CLASS, _ = uic.loadUiType(
os.path.join(os.path.dirname(specklepy_qt_ui.qt_ui.__file__), os.path.join("ui", "dockwidget_main.ui") )
)
class SpeckleQGISDialog(SpeckleQGISDialog_UI, FORM_CLASS):
def __init__(self, parent=None):
"""Constructor."""
super(SpeckleQGISDialog_UI, self).__init__(parent)
self.setupUi(self)
self.runAllSetup()
def createMappingDialog(self):
if self.mappingSendDialog is None:
self.mappingSendDialog = MappingSendDialogQGIS(None)
self.mappingSendDialog.dataStorage = self.dataStorage
self.mappingSendDialog.runSetup()
def completeStreamSection(self, plugin):
try:
self.streams_remove_button.clicked.connect( lambda: self.onStreamRemoveButtonClicked(plugin) )
self.streamList.currentIndexChanged.connect( lambda: self.onActiveStreamChanged(plugin) )
self.streamBranchDropdown.currentIndexChanged.connect( lambda: self.populateActiveCommitDropdown(plugin) )
return
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def onStreamRemoveButtonClicked(self, plugin):
try:
from speckle.utils.project_vars import set_project_streams
if not self: return
index = self.streamList.currentIndex()
if len(plugin.current_streams) > 0: plugin.current_streams.pop(index)
plugin.active_stream = None
self.streamBranchDropdown.clear()
self.commitDropdown.clear()
#self.streamIdField.setText("")
set_project_streams(plugin)
self.populateProjectStreams(plugin)
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def populateProjectStreams(self, plugin):
try:
from speckle.utils.project_vars import set_project_streams
if not self: return
self.streamList.clear()
for stream in plugin.current_streams:
self.streamList.addItems(
[f"Stream not accessible - {stream[0].stream_id}" if stream[1] is None or isinstance(stream[1], SpeckleException) else f"{stream[1].name}, {stream[1].id} | {stream[0].stream_url.split('/streams')[0].split('/projects')[0]}"]
)
if len(plugin.current_streams)==0: self.streamList.addItems([""])
self.streamList.addItems(["Create New Stream"])
set_project_streams(plugin)
index = self.streamList.currentIndex()
if index == -1: self.streams_remove_button.setEnabled(False)
else: self.streams_remove_button.setEnabled(True)
if len(plugin.current_streams)>0: plugin.active_stream = plugin.current_streams[0]
except Exception as e:
logToUser(e, level = 2, func = inspect.stack()[0][3], plugin=self)
return
def cancelOperations(self):
#print("____cancelOperations______")
for t in threading.enumerate():
#print(t.name)
if 'speckle_' in t.name:
#print(f"thread to kill: {t}")
t.kill()
t.join()
# not printed if same thread
#print("Remaining threads: ")
#print(threading.enumerate())
@@ -0,0 +1,404 @@
import inspect
import os
from typing import Any, List, Tuple, Union
from speckle.converter.layers import getAllLayers
from speckle.converter.layers.utils import getElevationLayer, getLayerGeomType
from specklepy_qt_ui.qt_ui.widget_transforms import MappingSendDialog
from specklepy_qt_ui.qt_ui.utils.logger import displayUserMsg
from specklepy_qt_ui.qt_ui.DataStorage import DataStorage
from speckle.utils.panel_logging import logToUser
from qgis.core import QgsVectorLayer, QgsRasterLayer, QgsIconUtils
from PyQt5 import QtWidgets, uic, QtCore
from PyQt5.QtWidgets import QListWidgetItem
from specklepy.logging import metrics
from osgeo import gdal
import webbrowser
import specklepy_qt_ui.qt_ui
FORM_CLASS, _ = uic.loadUiType(
os.path.join(
os.path.join(
os.path.dirname(specklepy_qt_ui.qt_ui.__file__), "ui", "transforms.ui"
)
)
)
class MappingSendDialogQGIS(MappingSendDialog, FORM_CLASS):
def __init__(self, parent=None):
super(MappingSendDialog, self).__init__(parent, QtCore.Qt.WindowStaysOnTopHint)
self.setupUi(self)
self.runAllSetup()
def runSetup(self):
self.attr_label.setEnabled(False)
self.attrDropdown.setEnabled(False)
self.dialog_button.setText("Apply")
self.populateTransforms()
self.populateLayersByTransform()
self.populateSavedTransforms(self.dataStorage)
self.populateSavedElevationLayer(self.dataStorage)
# self.elevationLayerDropdown.currentIndexChanged.connect(self.saveElevationLayer)
def populateSavedTransforms(
self, dataStorage
): # , savedTransforms: Union[List, None] = None, getLayer: Union[str, None] = None, getTransform: Union[str, None] = None):
if dataStorage is not None:
self.dataStorage = dataStorage # making sure lists are synced
self.transformationsList.clear()
vals = self.dataStorage.savedTransforms
all_l_names = [l.name() for l in self.dataStorage.all_layers]
for item in vals:
layer_name = item.split(" -> ")[0].split(" ('")[0]
transform_name = item.split(" -> ")[1]
layer = None
for l in self.dataStorage.all_layers:
if layer_name == l.name():
layer = l
if layer is None:
logToUser(
f"Layer '{layer_name}' not found in the project.\nTransformation is removed.",
level=2,
)
self.dataStorage.savedTransforms.remove(item)
else:
if transform_name not in self.dataStorage.transformsCatalog:
displayUserMsg(
f"Saved transformation '{transform_name}' is not valid.\nTransformation is removed.",
level=1,
)
self.dataStorage.savedTransforms.remove(item)
elif all_l_names.count(layer.name()) > 1:
displayUserMsg(
f"Layer name '{layer.name()}' is used for more than 1 layer in the project.\nTransformation is removed.",
level=1,
)
self.dataStorage.savedTransforms.remove(item)
else:
listItem = QListWidgetItem(item)
icon = QgsIconUtils().iconForLayer(layer)
listItem.setIcon(icon)
self.transformationsList.addItem(listItem)
def onAddTransform(self):
from speckle.utils.project_vars import set_transformations
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
if (
len(self.layerDropdown.currentText()) > 1
and len(self.transformDropdown.currentText()) > 1
):
listItem = (
str(self.layerDropdown.currentText())
+ " -> "
+ str(self.transformDropdown.currentText())
)
layer_name = listItem.split(" -> ")[0].split(" ('")[0]
transform_name = listItem.split(" -> ")[1].lower()
exists = 0
for record in self.dataStorage.savedTransforms:
current_layer_name = record.split(" -> ")[0].split(" ('")[0]
current_transf_name = record.split(" -> ")[1].lower()
if layer_name == current_layer_name: # in layers
exists += 1
displayUserMsg(
"Selected layer already has a transformation applied", level=1
)
break
if exists == 0:
layer = None
for l in self.dataStorage.all_layers:
if layer_name == l.name():
layer = l
if layer is not None:
if (
"attribute" in transform_name
and self.attrDropdown.currentText() != ""
):
listItem = (
str(self.layerDropdown.currentText())
+ " ('"
+ str(self.attrDropdown.currentText())
+ "') -> "
+ str(self.transformDropdown.currentText())
)
self.dataStorage.savedTransforms.append(listItem)
self.populateSavedTransforms(self.dataStorage)
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Transformation on Send Add",
"Transformation": listItem.split(" -> ")[1],
"connector_version": str(
self.dataStorage.plugin_version
),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
set_transformations(self.dataStorage)
def onRemoveTransform(self):
from speckle.utils.project_vars import set_transformations
if self.transformationsList.currentItem() is not None:
listItem = self.transformationsList.currentItem().text()
# print(listItem)
if listItem in self.dataStorage.savedTransforms:
self.dataStorage.savedTransforms.remove(listItem)
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Transformation on Send Remove",
"Transformation": listItem.split(" -> ")[1],
"connector_version": str(self.dataStorage.plugin_version),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
self.populateSavedTransforms(self.dataStorage)
set_transformations(self.dataStorage)
def onOkClicked(self):
try:
self.saveElevationLayer()
self.close()
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateLayers(self):
try:
self.layerDropdown.clear()
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
for i, layer in enumerate(self.dataStorage.all_layers):
listItem = layer.name()
self.layerDropdown.addItem(listItem)
icon = QgsIconUtils().iconForLayer(layer)
self.layerDropdown.setItemIcon(i, icon)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateLayersByTransform(self):
try:
self.layerDropdown.clear()
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
transform = str(self.transformDropdown.currentText())
layers_dropdown = []
for i, layer in enumerate(self.dataStorage.all_layers):
listItem = None
if "extrude" in transform.lower():
if isinstance(layer, QgsVectorLayer):
geom_type = getLayerGeomType(layer)
if "polygon" in geom_type.lower():
listItem = layer.name()
elif "elevation" in transform.lower():
if isinstance(layer, QgsRasterLayer):
# avoiding tiling layers
ds = gdal.Open(layer.source(), gdal.GA_ReadOnly)
if ds is None:
continue
# for satellites
if "texture" in transform.lower():
listItem = layer.name()
# for elevation to mesh
elif "mesh" in transform.lower():
try:
if layer.bandCount() == 1:
listItem = layer.name()
except:
pass
if listItem is not None:
layers_dropdown.append(listItem)
self.layerDropdown.addItem(listItem)
icon = QgsIconUtils().iconForLayer(layer)
self.layerDropdown.setItemIcon(len(layers_dropdown) - 1, icon)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateAttributesByLayer(self):
try:
self.attrDropdown.clear()
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
layer_name = str(self.layerDropdown.currentText())
transform_name = self.transformDropdown.currentText()
layerForAttributes = None
for i, layer in enumerate(self.dataStorage.all_layers):
if layer_name == layer.name():
if isinstance(layer, QgsVectorLayer):
geom_type = getLayerGeomType(layer)
if "polygon" in geom_type.lower():
layerForAttributes = layer
break
if layerForAttributes is not None and "attribute" in transform_name:
self.attr_label.setEnabled(True)
self.attrDropdown.setEnabled(True)
if "ignore" not in transform_name:
self.attrDropdown.addItem("Random height")
for field in layerForAttributes.fields():
field_type = field.type()
if field_type in [2, 6, 10]:
self.attrDropdown.addItem(str(field.name()))
else:
self.attr_label.setEnabled(False)
self.attrDropdown.setEnabled(False)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateTransforms(self):
try:
self.transformDropdown.clear()
for item in self.dataStorage.transformsCatalog:
self.transformDropdown.addItem(item)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def populateSavedElevationLayer(
self, dataStorage
): # , savedTransforms: Union[List, None] = None, getLayer: Union[str, None] = None, getTransform: Union[str, None] = None):
try:
if dataStorage is not None:
self.dataStorage = dataStorage # making sure lists are synced
elevationLayer = getElevationLayer(self.dataStorage)
self.elevationLayerDropdown.clear()
root = self.dataStorage.project.layerTreeRoot()
self.dataStorage.all_layers = getAllLayers(root)
self.elevationLayerDropdown.addItem("")
setAsindex = 0
countRaster = 1
for i, layer in enumerate(self.dataStorage.all_layers):
if isinstance(layer, QgsRasterLayer):
# avoiding tiling layers
ds = gdal.Open(layer.source(), gdal.GA_ReadOnly)
if ds is None:
continue
elif layer.bandCount() != 1:
continue
listItem = layer.name()
self.elevationLayerDropdown.addItem(listItem)
icon = QgsIconUtils().iconForLayer(layer)
self.elevationLayerDropdown.setItemIcon(countRaster, icon)
if elevationLayer is not None:
if listItem == elevationLayer.name():
setAsindex = countRaster
countRaster += 1
self.elevationLayerDropdown.setCurrentIndex(setAsindex)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
return
def saveElevationLayer(self):
# print("saveElevationLayer")
from speckle.utils.project_vars import set_elevationLayer
root = self.dataStorage.project.layerTreeRoot()
layer = None
if self.dataStorage is None:
return
layerName = str(self.elevationLayerDropdown.currentText())
try:
if self.dataStorage.elevationLayer.name() == layerName:
return
except:
pass
if len(layerName) < 1:
layer = None
else:
self.dataStorage.all_layers = getAllLayers(root)
all_l_names = [l.name() for l in self.dataStorage.all_layers]
# print(all_l_names)
for l in self.dataStorage.all_layers:
if layerName == l.name():
layer = l
try:
# print(layerName)
if all_l_names.count(layer.name()) > 1:
displayUserMsg(
f"Layer name '{layer.name()}' is used for more than 1 layer in the project",
level=1,
)
layer = None
break
else:
self.dataStorage.elevationLayer = layer
set_elevationLayer(self.dataStorage)
logToUser(
f"Elevation layer '{layerName}' successfully set",
level=0,
)
break
except:
displayUserMsg(
f"Layer '{layer.name()}' is not found in the project",
level=1,
)
layer = None
break
try:
metrics.track(
"Connector Action",
self.dataStorage.active_account,
{
"name": "Add transformation on Send",
"Transformation": "Set Layer as Elevation",
"connector_version": str(self.dataStorage.plugin_version),
},
)
except Exception as e:
logToUser(e, level=2, func=inspect.stack()[0][3])
def onMoreInfo(self):
webbrowser.open("https://speckle.guide/user/qgis.html#transformations")