Skip to content

Commit

Permalink
feat: UI improvements working
Browse files Browse the repository at this point in the history
  • Loading branch information
AlanRynne committed Nov 23, 2021
1 parent 5bf7135 commit 1453562
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 108 deletions.
2 changes: 1 addition & 1 deletion speckle-qgsi.pyproject
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"files": ["ui/speckle_qgis_dialog_base.ui","ui/main_form.ui","ui/speckle_qgis_dialog.py","ui/Accounts.py","ui/main_form.py","ui/streamlist_dialog.py","ui/streamlist_dialog.ui"]
"files": ["ui/streamlist_dialog.py","ui/speckle_qgis_dialog_base.ui","ui/main_form.ui","ui/speckle_qgis_dialog.py","ui/Accounts.py","ui/main_form.py","ui/streamlist_dialog.ui","ui/add_stream_modal.ui"]
}
158 changes: 100 additions & 58 deletions speckle_qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication, Qt
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction, QDockWidget
from qgis.PyQt.QtWidgets import QAction, QDockWidget, QListWidget
from qgis.core import Qgis, QgsProject

# Initialize Qt resources from file resources.py
Expand All @@ -30,11 +30,11 @@
from ui.speckle_qgis_dialog import SpeckleQGISDialog

from specklepy.api import operations
from specklepy.api.client import SpeckleClient
from specklepy.api.credentials import Account, get_local_accounts, StreamWrapper
from specklepy.api.client import SpeckleClient, SpeckleException
from specklepy.api.credentials import Account, get_local_accounts, StreamWrapper, get_default_account
from specklepy.transports.server import ServerTransport
from specklepy.objects import Base

from specklepy.api.models import Stream
from speckle.logging import *
from speckle.converter.geometry import *
from speckle.converter.layers import convertSelectedLayers, getLayers
Expand All @@ -44,9 +44,13 @@ class SpeckleQGIS:
"""Speckle Connector Plugin for QGIS"""

dockwidget: QDockWidget = None
speckle_account: Account = None
speckle_client: SpeckleClient = None
add_stream_modal: AddStreamModalDialog = None
current_streams: [(StreamWrapper, Stream)] = []

active_stream: (StreamWrapper, Stream) = None

qgis_project: QgsProject

def __init__(self, iface):
"""Constructor.
Expand All @@ -58,6 +62,10 @@ def __init__(self, iface):
# Save reference to the QGIS interface

self.iface = iface
self.qgis_project = QgsProject().instance()
self.qgis_project.fileNameChanged.connect(self.reloadUI)
self.qgis_project.homePathChanged.connect(self.reloadUI)

# initialize plugin directory
self.plugin_dir = os.path.dirname(__file__)
# initialize locale
Expand All @@ -78,7 +86,6 @@ def __init__(self, iface):
# Check if plugin was started the first time in current QGIS session
# Must be set in initGui() to survive plugin reloads
self.pluginIsActive = False
self.add_stream_modal = AddStreamModalDialog()

# noinspection PyMethodMayBeStatic

Expand Down Expand Up @@ -202,15 +209,6 @@ def unload(self):
self.iface.removePluginWebMenu(self.tr("&SpeckleQGIS"), action)
self.iface.removeToolBarIcon(action)

def onAccountSelected(self, ix):
self.speckle_account = self.speckle_accounts[ix]
# initialise the client
self.speckle_client = SpeckleClient(
host=self.speckle_account.serverInfo.url or ""
) # or whatever your host is
# client = SpeckleClient(host="localhost:3000", use_ssl=False) or use local server
self.speckle_client.authenticate(token=self.speckle_account.token)

def onSendButtonClicked(self):
# creating our parent base object
project = QgsProject.instance()
Expand All @@ -228,23 +226,20 @@ def onSendButtonClicked(self):
if not self.dockwidget.streamIdField.text():
logger.logToUser("Please enter a Stream Url/ID.", Qgis.Warning)
return
# Get the stream id/url
try:
streamWrapper = StreamWrapper(self.dockwidget.streamIdField.text())
streamId = streamWrapper.stream_id
except Exception as error:
# Not a url, we assume it's an id!
streamId = self.dockwidget.streamIdField.text()

# Get the stream wrapper
streamWrapper = self.active_stream[0]
streamId = streamWrapper.stream_id
client = streamWrapper.get_client()
# Ensure the stream actually exists
try:
self.speckle_client.stream.get(streamId)
client.stream.get(streamId)
except Exception as error:
logger.logToUser(str(error), Qgis.Critical)
return

# next create a server transport - this is the vehicle through which you will send and receive
transport = ServerTransport(client=self.speckle_client, stream_id=streamId)
transport = ServerTransport(client=client, stream_id=streamId)

try:
# this serialises the block and sends it to the transport
Expand All @@ -253,18 +248,21 @@ def onSendButtonClicked(self):
logger.logToUser("Error sending data", Qgis.Critical)
return

message = self.dockwidget.messageInput.text()
try:
# you can now create a commit on your stream with this object
self.speckle_client.commit.create(
client.commit.create(
stream_id=streamId,
object_id=hash,
message="This was sent from QGIS!!",
branch_name= self.dockwidget.streamBranchDropdown.currentText(),
message= "Sent objects from QGIS" if len(message) == 0 else message,
source_application="QGIS",
)
logger.logToUser("Successfully sent data to stream: " + streamId)
except:
self.dockwidget.messageInput.setText("")
except Exception as e:
logger.logToUser("Error creating commit", Qgis.Critical)
return


def populateLayerDropdown(self):
# Fetch the currently loaded layers
Expand All @@ -275,28 +273,30 @@ def populateLayerDropdown(self):
# Populate the comboBox with names of all the loaded layers
self.dockwidget.layersWidget.addItems([layer.name() for layer in layers])

def populateAccountsDropdown(self):
# Populate the accounts comboBox
self.speckle_accounts = get_local_accounts()
self.dockwidget.accountListField.clear()
self.dockwidget.accountListField.addItems(
[
f"{acc.userInfo.name} - {acc.serverInfo.url}"
for acc in self.speckle_accounts
]
)

def populateProjectStreams(self):

self.dockwidget.streamList.clear()
self.dockwidget.streamList.addItems([
f"{stream.name} - {stream.id}" for stream in self.speckle_client.stream.list()
f"{stream[1].name} - {stream[1].id}" for stream in self.current_streams
])

def populateActiveStreamBranchDropdown(self):
logger.log("populating branch from stream")
self.dockwidget.streamBranchDropdown.clear()
if(self.active_stream is None):
return
self.dockwidget.streamBranchDropdown.addItems([
f"{branch.name}" for branch in self.active_stream[1].branches.items
])

def reloadUI(self):
self.populateAccountsDropdown()
self.populateLayerDropdown()
self.populateProjectStreams()
if(self.dockwidget is not None):
self.active_stream = None
self.get_project_streams()
self.populateLayerDropdown()
self.populateProjectStreams()
self.dockwidget.streamIdField.clear()
self.dockwidget.streamBranchDropdown.clear()

def run(self):
"""Run method that performs all the real work"""
Expand All @@ -310,24 +310,20 @@ def run(self):
self.dockwidget = SpeckleQGISDialog()

# Setup events on first load only!
self.dockwidget.accountListField.currentIndexChanged.connect(
self.onAccountSelected
)
self.dockwidget.sendButton.clicked.connect(self.onSendButtonClicked)
self.dockwidget.reloadButton.clicked.connect(self.reloadUI)
# connect to provide cleanup on closing of dockwidget
self.dockwidget.closingPlugin.connect(self.onClosePlugin)

# Connect streams section events
self.dockwidget.streams_add_button.clicked.connect(self.onStreamAdd)
self.dockwidget.streams_remove_button.clicked.connect(self.onStreamRemove)
self.dockwidget.streams_reload_button.clicked.connect(self.onStreamReload)
self.dockwidget.streams_add_button.clicked.connect(self.onStreamAddButtonClicked)
self.dockwidget.streams_remove_button.clicked.connect(self.onStreamRemoveButtonClicked)
self.dockwidget.streamList.itemSelectionChanged.connect(self.onActiveStreamChanged)

self.get_project_streams()

# Populate the UI dropdowns
self.populateLayerDropdown()
self.populateAccountsDropdown()
self.populateProjectStreams()

# Setup reload of UI dropdowns when layers change.
Expand All @@ -339,15 +335,61 @@ def run(self):
self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dockwidget)
self.dockwidget.show()

def onStreamAdd(self, signal):
def onStreamAddButtonClicked(self):
logger.log("on stream add")
self.add_stream_modal = AddStreamModalDialog(None)
self.add_stream_modal.handleStreamAdd.connect(self.handleStreamAdd)
self.add_stream_modal.show()

def onStreamRemove(self, signal):
def onStreamRemoveButtonClicked(self):
logger.log("on stream remove")
index = self.dockwidget.streamList.currentIndex().row()
self.current_streams.pop(index)
self.active_stream = None
self.dockwidget.streamBranchDropdown.clear()
self.dockwidget.streamIdField.setText("")

def onStreamReload(signal):
logger.log("on stream reload")
def onActiveStreamChanged(signal):
self.set_project_streams()
self.populateProjectStreams()

def onActiveStreamChanged(self):
logger.log("on active stream changed")
if(len(self.current_streams) == 0):
return
index = self.dockwidget.streamList.currentRow()
if(index == -1):
return
self.active_stream = self.current_streams[index]
self.dockwidget.streamIdField.setText(self.dockwidget.streamList.currentItem().text())
self.populateActiveStreamBranchDropdown()

def handleStreamAdd(self, sw: StreamWrapper):
logger.log("handling stream addition")
client = sw.get_client()
stream = client.stream.get(sw.stream_id)
self.current_streams.append((sw, stream))
self.add_stream_modal.handleStreamAdd.disconnect(self.handleStreamAdd)
self.set_project_streams()
self.populateProjectStreams()

# Persist added streams in project
def set_project_streams(self):
proj = QgsProject().instance()
value = ",".join([stream[0].stream_url for stream in self.current_streams])
proj.writeEntry("speckle-qgis", "project_streams", value)

def get_project_streams(self):
proj = QgsProject().instance()
saved_streams = proj.readEntry("speckle-qgis", "project_streams", "")
if saved_streams[1] and len(saved_streams[0]) != 0:
temp = []
for url in saved_streams[0].split(","):
try:
sw = StreamWrapper(url)
stream = sw.get_client().stream.get(sw.stream_id)
temp.append((sw,stream))
except SpeckleException as e:
logger.logToUser(e.message, Qgis.Warning)

self.current_streams = temp

80 changes: 76 additions & 4 deletions ui/add_stream_modal.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,79 @@
import os
from qgis.PyQt import QtWidgets, uic, QtGui, QtCore
import ui.speckle_qgis_dialog
from qgis.PyQt import QtWidgets, uic, QtCore
from qgis.PyQt.QtCore import pyqtSignal
from specklepy.api.models import Stream
from specklepy.api.client import SpeckleClient
from speckle.utils import logger
from specklepy.api.credentials import get_local_accounts, StreamWrapper

# This loads your .ui file so that PyQt can populate your plugin with the elements from Qt Designer
FORM_CLASS, _ = uic.loadUiType(
os.path.join(os.path.dirname(__file__), "add_stream_modal.ui")
)

class AddStreamModalDialog(QtWidgets.QWidget, FORM_CLASS):

search_button: QtWidgets.QPushButton = None
search_text_field: QtWidgets.QLineEdit = None
search_results_list: QtWidgets.QListWidget = None
dialog_button_box: QtWidgets.QDialogButtonBox = None
accounts_dropdown: QtWidgets.QComboBox

stream_results: [Stream] = []
speckle_client: SpeckleClient = None

#Events
handleStreamAdd = pyqtSignal(StreamWrapper)

def __init__(self, parent=None, speckle_client: SpeckleClient = None):
super(AddStreamModalDialog,self).__init__(parent,QtCore.Qt.WindowStaysOnTopHint)
self.speckle_client = speckle_client
self.setupUi(self)
self.setWindowTitle("Add Speckle stream")

self.search_button.clicked.connect(self.onSearchClicked)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Ok).clicked.connect(self.onOkClicked)
self.dialog_button_box.button(QtWidgets.QDialogButtonBox.Cancel).clicked.connect(self.onCancelClicked)
self.accounts_dropdown.currentIndexChanged.connect(self.onAccountSelected)
self.populate_accounts_dropdown()

def onSearchClicked(self):
logger.log("search clicked")
query = self.search_text_field.text()
results = self.speckle_client.stream.search(query)
self.stream_results = results
self.populateResultsList()

def populateResultsList(self):
self.search_results_list.clear()
self.search_results_list.addItems([
f"{stream.name} - {stream.id} (last updated at XXX by YY)" for stream in self.stream_results
])

def onOkClicked(self):
index = self.search_results_list.currentIndex().row()
stream = self.stream_results[index]
acc = get_local_accounts()[self.accounts_dropdown.currentIndex()]
self.handleStreamAdd.emit(StreamWrapper(f"{acc.serverInfo.url}/streams/{stream.id}?u={acc.userInfo.id}"))
self.close()

def onCancelClicked(self):
self.close()

def onAccountSelected(self, index):
account = self.speckle_accounts[index]
self.speckle_client = SpeckleClient(account.serverInfo.url, account.serverInfo.url.startswith("https"))
self.speckle_client.authenticate(token=account.token)

def populate_accounts_dropdown(self):
# Populate the accounts comboBox
self.speckle_accounts = get_local_accounts()
self.accounts_dropdown.clear()
self.accounts_dropdown.addItems(
[
f"{acc.userInfo.name} - {acc.serverInfo.url}"
for acc in self.speckle_accounts
]
)

class AddStreamModalDialog(QtWidgets.QDialog):
def __init__(self):
super(AddStreamModalDialog,self).__init__(None,QtCore.Qt.WindowStaysOnTopHint)
Loading

0 comments on commit 1453562

Please sign in to comment.