Open separate license dialog with each plugin install

Previously the license dialog was instanciated once and re-used for each
install. As the dialog is only shown after the plugin is downloaded it
was possible to click install for multiple plugins. Plugins that finish
downloading later would override the dialog of earlier downloaded
plugins. Clicking "accept" would then only install the latest downloaded
plugin.

Now for each install a separate dialog is shown. Accepting the license
agreement would only install the recently accepted plugin.

Note: in the current form the license agreement does not show any
identification to what plugin triggered the dialog. As multiple dialogs
can be shown at once this might be unclear.

cura 8587
This commit is contained in:
casper 2021-12-06 19:34:02 +01:00
parent 20fb08145d
commit 71a43060a6
3 changed files with 35 additions and 61 deletions

View file

@ -1,31 +0,0 @@
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
# Model for the LicenseDialog
class LicenseModel(QObject):
packageIdChanged = pyqtSignal()
licenseTextChanged = pyqtSignal()
def __init__(self) -> None:
super().__init__()
self._license_text = ""
self._package_id = ""
@pyqtProperty(str, notify=packageIdChanged)
def packageId(self) -> str:
return self._package_id
def setPackageId(self, name: str) -> None:
self._package_id = name
self.packageIdChanged.emit()
@pyqtProperty(str, notify=licenseTextChanged)
def licenseText(self) -> str:
return self._license_text
def setLicenseText(self, license_text: str) -> None:
if self._license_text != license_text:
self._license_text = license_text
self.licenseTextChanged.emit()

View file

@ -20,7 +20,6 @@ from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope # To ma
from .PackageModel import PackageModel from .PackageModel import PackageModel
from .Constants import USER_PACKAGES_URL from .Constants import USER_PACKAGES_URL
from .LicenseModel import LicenseModel
if TYPE_CHECKING: if TYPE_CHECKING:
from PyQt5.QtCore import QObject from PyQt5.QtCore import QObject
@ -52,19 +51,7 @@ class PackageList(ListModel):
self._ongoing_request: Optional[HttpRequestData] = None self._ongoing_request: Optional[HttpRequestData] = None
self._scope = JsonDecoratorScope(UltimakerCloudScope(CuraApplication.getInstance())) self._scope = JsonDecoratorScope(UltimakerCloudScope(CuraApplication.getInstance()))
self._license_dialogs: Dict[str, QObject] = {}
self._license_model = LicenseModel()
plugin_path = self._plugin_registry.getPluginPath("Marketplace")
if plugin_path is None:
plugin_path = os.path.dirname(__file__)
# create a QML component for the license dialog
license_dialog_component_path = os.path.join(plugin_path, "resources", "qml", "LicenseDialog.qml")
self._license_dialog = CuraApplication.getInstance().createQmlComponent(license_dialog_component_path, {
"licenseModel": self._license_model,
"handler": self
})
@pyqtSlot() @pyqtSlot()
def updatePackages(self) -> None: def updatePackages(self) -> None:
@ -137,24 +124,42 @@ class PackageList(ListModel):
canInstallChanged = pyqtSignal(str, bool) canInstallChanged = pyqtSignal(str, bool)
def _openLicenseDialog(self, plugin_name: str, license_content: str) -> None: def _openLicenseDialog(self, package_id: str, license_content: str) -> None:
Logger.debug(f"Prompting license for {plugin_name}") Logger.debug(f"Prompting license for {package_id}")
self._license_model.setPackageId(plugin_name)
self._license_model.setLicenseText(license_content)
self._license_dialog.show()
@pyqtSlot() plugin_path = self._plugin_registry.getPluginPath("Marketplace")
def onLicenseAccepted(self) -> None: if plugin_path is None:
package_id = self._license_model.packageId plugin_path = os.path.dirname(__file__)
# create a QML component for the license dialog
license_dialog_component_path = os.path.join(plugin_path, "resources", "qml", "LicenseDialog.qml")
dialog = CuraApplication.getInstance().createQmlComponent(license_dialog_component_path, {
"licenseContent": license_content,
"packageId": package_id,
"handler": self
})
dialog.show()
# place dialog in class such that it does not get remove by garbage collector
self._license_dialogs[package_id] = dialog
@pyqtSlot(str)
def onLicenseAccepted(self, package_id: str) -> None:
Logger.debug(f"Accepted license for {package_id}") Logger.debug(f"Accepted license for {package_id}")
self._license_dialog.close() # close dialog
dialog = self._license_dialogs.pop(package_id)
if dialog is not None:
dialog.deleteLater()
# install relevant package
self._install(package_id) self._install(package_id)
@pyqtSlot() @pyqtSlot(str)
def onLicenseDeclined(self) -> None: def onLicenseDeclined(self, package_id: str) -> None:
package_id = self._license_model.packageId
Logger.debug(f"Declined license for {package_id}") Logger.debug(f"Declined license for {package_id}")
self._license_dialog.close() # close dialog
dialog = self._license_dialogs.pop(package_id)
if dialog is not None:
dialog.deleteLater()
# reset package card
package = self.getPackageModel(package_id) package = self.getPackageModel(package_id)
package.is_installing = False package.is_installing = False

View file

@ -63,7 +63,7 @@ UM.Dialog
Layout.fillHeight: true Layout.fillHeight: true
anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.topMargin: UM.Theme.getSize("default_margin").height
textArea.text: licenseModel.licenseText textArea.text: licenseContent
textArea.readOnly: true textArea.readOnly: true
} }
@ -73,7 +73,7 @@ UM.Dialog
Cura.PrimaryButton Cura.PrimaryButton
{ {
text: catalog.i18nc("@button", "Accept") text: catalog.i18nc("@button", "Accept")
onClicked: { handler.onLicenseAccepted() } onClicked: { handler.onLicenseAccepted(packageId) }
} }
] ]
@ -82,7 +82,7 @@ UM.Dialog
Cura.SecondaryButton Cura.SecondaryButton
{ {
text: catalog.i18nc("@button", "Decline") text: catalog.i18nc("@button", "Decline")
onClicked: { handler.onLicenseDeclined() } onClicked: { handler.onLicenseDeclined(packageId) }
} }
] ]
} }