diff --git a/scripts/debugging_panel.py b/scripts/debugging_panel.py
index 5ad8f6c..4681545 100644
--- a/scripts/debugging_panel.py
+++ b/scripts/debugging_panel.py
@@ -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')
diff --git a/speckle_arcgis_installer/conda_clone_activate.py b/speckle_arcgis_installer/conda_clone_activate.py
index cf73db3..d03cf11 100644
--- a/speckle_arcgis_installer/conda_clone_activate.py
+++ b/speckle_arcgis_installer/conda_clone_activate.py
@@ -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" )
diff --git a/speckle_toolbox/esri/toolboxes/Speckle.pyt.xml b/speckle_toolbox/esri/toolboxes/Speckle.pyt.xml
index 12662eb..4d0341b 100644
--- a/speckle_toolbox/esri/toolboxes/Speckle.pyt.xml
+++ b/speckle_toolbox/esri/toolboxes/Speckle.pyt.xml
@@ -1,77 +1,2 @@
-
-20220718135001001.0TRUE202309121503181500000005000ItemDescriptionc:\program files\arcgis\pro\Resources\Help\gpSpeckleSpeckle connector for ArcGISspeckle3dArcToolbox Toolbox/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//Z20220725
+
+20231206233915001.0TRUE20231206233915c:\program files\arcgis\pro\Resources\Help\gpSpeckleArcToolbox Toolbox
diff --git a/speckle_toolbox/esri/toolboxes/speckle/speckle/speckle_arcgis.py b/speckle_toolbox/esri/toolboxes/speckle/speckle/speckle_arcgis.py
index 9708c48..2169e80 100644
--- a/speckle_toolbox/esri/toolboxes/speckle/speckle/speckle_arcgis.py
+++ b/speckle_toolbox/esri/toolboxes/speckle/speckle/speckle_arcgis.py
@@ -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:
diff --git a/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/project_vars.py b/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/project_vars.py
index 2bc2b86..75f3cf7 100644
--- a/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/project_vars.py
+++ b/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/project_vars.py
@@ -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
-'''
-
\ No newline at end of file
diff --git a/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/speckle_qgis_dialog.py b/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/speckle_qgis_dialog.py
index e10eb9d..5b1edf4 100644
--- a/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/speckle_qgis_dialog.py
+++ b/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/speckle_qgis_dialog.py
@@ -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
diff --git a/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/validation.py b/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/validation.py
index c5794fa..9dc2a1b 100644
--- a/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/validation.py
+++ b/speckle_toolbox/esri/toolboxes/speckle/speckle/ui/validation.py
@@ -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]:
diff --git a/speckle_toolbox/esri/toolboxes/speckle/ui_widgets/ConnectorBindings.py b/speckle_toolbox/esri/toolboxes/speckle/ui_widgets/ConnectorBindings.py
new file mode 100644
index 0000000..8896faa
--- /dev/null
+++ b/speckle_toolbox/esri/toolboxes/speckle/ui_widgets/ConnectorBindings.py
@@ -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
diff --git a/speckle_toolbox/esri/toolboxes/speckle/ui_widgets/__init__.py b/speckle_toolbox/esri/toolboxes/speckle/ui_widgets/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/speckle_toolbox/esri/toolboxes/speckle/ui_widgets/dockwidget_main.py b/speckle_toolbox/esri/toolboxes/speckle/ui_widgets/dockwidget_main.py
new file mode 100644
index 0000000..bb2a400
--- /dev/null
+++ b/speckle_toolbox/esri/toolboxes/speckle/ui_widgets/dockwidget_main.py
@@ -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())
+
\ No newline at end of file
diff --git a/speckle_toolbox/esri/toolboxes/speckle/ui_widgets/widget_transforms.py b/speckle_toolbox/esri/toolboxes/speckle/ui_widgets/widget_transforms.py
new file mode 100644
index 0000000..d25c891
--- /dev/null
+++ b/speckle_toolbox/esri/toolboxes/speckle/ui_widgets/widget_transforms.py
@@ -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")