mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 06:57:28 -06:00
Add license dialog to the Marketplace
cura 8587
This commit is contained in:
parent
325783ca46
commit
09e221d64a
3 changed files with 214 additions and 1 deletions
66
plugins/Marketplace/LicenseModel.py
Normal file
66
plugins/Marketplace/LicenseModel.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal
|
||||||
|
from UM.i18n import i18nCatalog
|
||||||
|
|
||||||
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
# Model for the LicenseDialog
|
||||||
|
class LicenseModel(QObject):
|
||||||
|
DEFAULT_DECLINE_BUTTON_TEXT = catalog.i18nc("@button", "Decline")
|
||||||
|
ACCEPT_BUTTON_TEXT = catalog.i18nc("@button", "Agree")
|
||||||
|
|
||||||
|
dialogTitleChanged = pyqtSignal()
|
||||||
|
packageNameChanged = pyqtSignal()
|
||||||
|
licenseTextChanged = pyqtSignal()
|
||||||
|
iconChanged = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, decline_button_text: str = DEFAULT_DECLINE_BUTTON_TEXT) -> None:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self._dialogTitle = ""
|
||||||
|
self._license_text = ""
|
||||||
|
self._package_name = ""
|
||||||
|
self._icon_url = ""
|
||||||
|
self._decline_button_text = decline_button_text
|
||||||
|
|
||||||
|
@pyqtProperty(str, constant = True)
|
||||||
|
def acceptButtonText(self):
|
||||||
|
return self.ACCEPT_BUTTON_TEXT
|
||||||
|
|
||||||
|
@pyqtProperty(str, constant = True)
|
||||||
|
def declineButtonText(self):
|
||||||
|
return self._decline_button_text
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify=dialogTitleChanged)
|
||||||
|
def dialogTitle(self) -> str:
|
||||||
|
return self._dialogTitle
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify=packageNameChanged)
|
||||||
|
def packageName(self) -> str:
|
||||||
|
return self._package_name
|
||||||
|
|
||||||
|
def setPackageName(self, name: str) -> None:
|
||||||
|
self._package_name = name
|
||||||
|
self.packageNameChanged.emit()
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify=iconChanged)
|
||||||
|
def iconUrl(self) -> str:
|
||||||
|
return self._icon_url
|
||||||
|
|
||||||
|
def setIconUrl(self, url: str):
|
||||||
|
self._icon_url = url
|
||||||
|
self.iconChanged.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()
|
||||||
|
|
||||||
|
def _updateDialogTitle(self):
|
||||||
|
self._dialogTitle = catalog.i18nc("@title:window", "Plugin License Agreement")
|
||||||
|
if self._page_count > 1:
|
||||||
|
self._dialogTitle = self._dialogTitle + " ({}/{})".format(self._current_page_idx + 1, self._page_count)
|
||||||
|
self.dialogTitleChanged.emit()
|
|
@ -2,6 +2,7 @@
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
import tempfile
|
import tempfile
|
||||||
import json
|
import json
|
||||||
|
import os.path
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, Qt
|
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, Qt
|
||||||
from typing import Dict, Optional, Set, TYPE_CHECKING
|
from typing import Dict, Optional, Set, TYPE_CHECKING
|
||||||
|
@ -11,6 +12,7 @@ from UM.Qt.ListModel import ListModel
|
||||||
from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
|
from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
|
||||||
from UM.TaskManagement.HttpRequestManager import HttpRequestData, HttpRequestManager
|
from UM.TaskManagement.HttpRequestManager import HttpRequestData, HttpRequestManager
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
|
from UM.PluginRegistry import PluginRegistry
|
||||||
|
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
from cura import CuraPackageManager
|
from cura import CuraPackageManager
|
||||||
|
@ -18,6 +20,7 @@ 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
|
||||||
|
@ -42,12 +45,24 @@ class PackageList(ListModel):
|
||||||
self._has_more = False
|
self._has_more = False
|
||||||
self._has_footer = True
|
self._has_footer = True
|
||||||
self._to_install: Dict[str, str] = {}
|
self._to_install: Dict[str, str] = {}
|
||||||
self.canInstallChanged.connect(self._install)
|
self.canInstallChanged.connect(self._requestInstall)
|
||||||
self._local_packages: Set[str] = {p["package_id"] for p in self._manager.local_packages}
|
self._local_packages: Set[str] = {p["package_id"] for p in self._manager.local_packages}
|
||||||
|
|
||||||
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_model = LicenseModel()
|
||||||
|
|
||||||
|
plugin_path = PluginRegistry.getInstance().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
|
||||||
|
})
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def updatePackages(self) -> None:
|
def updatePackages(self) -> None:
|
||||||
""" A Qt slot which will update the List from a source. Actual implementation should be done in the child class"""
|
""" A Qt slot which will update the List from a source. Actual implementation should be done in the child class"""
|
||||||
|
@ -119,6 +134,28 @@ class PackageList(ListModel):
|
||||||
|
|
||||||
canInstallChanged = pyqtSignal(str, bool)
|
canInstallChanged = pyqtSignal(str, bool)
|
||||||
|
|
||||||
|
def _openLicenseDialog(self, plugin_name: str, license_content: str, icon_url: str) -> None:
|
||||||
|
self._license_model.setIconUrl(icon_url)
|
||||||
|
self._license_model.setPackageName(plugin_name)
|
||||||
|
self._license_model.setLicenseText(license_content)
|
||||||
|
self._license_dialog.show()
|
||||||
|
|
||||||
|
def _requestInstall(self, package_id: str, update: bool = False) -> None:
|
||||||
|
Logger.debug(f"Request installing {package_id}")
|
||||||
|
|
||||||
|
package_path = self._to_install.pop(package_id)
|
||||||
|
license_content = self._manager.getPackageLicense(package_path)
|
||||||
|
|
||||||
|
if not update and license_content is not None:
|
||||||
|
# open dialog, prompting the using to accept the plugin license
|
||||||
|
package = self.getPackageModel(package_id)
|
||||||
|
plugin_name = package.displayName
|
||||||
|
icon_url = package.iconUrl
|
||||||
|
self._openLicenseDialog(plugin_name, license_content, icon_url)
|
||||||
|
else:
|
||||||
|
# Otherwise continue the installation
|
||||||
|
self._install(package_id, update)
|
||||||
|
|
||||||
def _install(self, package_id: str, update: bool = False) -> None:
|
def _install(self, package_id: str, update: bool = False) -> None:
|
||||||
package_path = self._to_install.pop(package_id)
|
package_path = self._to_install.pop(package_id)
|
||||||
Logger.debug(f"Installing {package_id}")
|
Logger.debug(f"Installing {package_id}")
|
||||||
|
|
110
plugins/Marketplace/resources/qml/LicenseDialog.qml
Normal file
110
plugins/Marketplace/resources/qml/LicenseDialog.qml
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Toolbox is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.10
|
||||||
|
import QtQuick.Dialogs 1.1
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 2.3
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
|
||||||
|
import UM 1.1 as UM
|
||||||
|
import Cura 1.6 as Cura
|
||||||
|
|
||||||
|
UM.Dialog
|
||||||
|
{
|
||||||
|
id: licenseDialog
|
||||||
|
title: licenseModel.dialogTitle
|
||||||
|
minimumWidth: UM.Theme.getSize("license_window_minimum").width
|
||||||
|
minimumHeight: UM.Theme.getSize("license_window_minimum").height
|
||||||
|
width: minimumWidth
|
||||||
|
height: minimumHeight
|
||||||
|
backgroundColor: UM.Theme.getColor("main_background")
|
||||||
|
margin: screenScaleFactor * 10
|
||||||
|
|
||||||
|
ColumnLayout
|
||||||
|
{
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: UM.Theme.getSize("thick_margin").height
|
||||||
|
|
||||||
|
UM.I18nCatalog{id: catalog; name: "cura"}
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: licenseHeader
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: catalog.i18nc("@label", "You need to accept the license to install the package")
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: packageRow
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: childrenRect.height
|
||||||
|
spacing: UM.Theme.getSize("default_margin").width
|
||||||
|
leftPadding: UM.Theme.getSize("narrow_margin").width
|
||||||
|
|
||||||
|
Image
|
||||||
|
{
|
||||||
|
id: icon
|
||||||
|
width: 30 * screenScaleFactor
|
||||||
|
height: width
|
||||||
|
sourceSize.width: width
|
||||||
|
sourceSize.height: height
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
source: licenseModel.iconUrl || "../../images/placeholder.svg"
|
||||||
|
mipmap: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: packageName
|
||||||
|
text: licenseModel.packageName
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
|
font.bold: true
|
||||||
|
anchors.verticalCenter: icon.verticalCenter
|
||||||
|
height: contentHeight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
renderType: Text.NativeRendering
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Cura.ScrollableTextArea
|
||||||
|
{
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||||
|
|
||||||
|
textArea.text: licenseModel.licenseText
|
||||||
|
textArea.readOnly: true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
rightButtons:
|
||||||
|
[
|
||||||
|
Cura.PrimaryButton
|
||||||
|
{
|
||||||
|
leftPadding: UM.Theme.getSize("dialog_primary_button_padding").width
|
||||||
|
rightPadding: UM.Theme.getSize("dialog_primary_button_padding").width
|
||||||
|
|
||||||
|
text: licenseModel.acceptButtonText
|
||||||
|
onClicked: { handler.onLicenseAccepted() }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
leftButtons:
|
||||||
|
[
|
||||||
|
Cura.SecondaryButton
|
||||||
|
{
|
||||||
|
id: declineButton
|
||||||
|
text: licenseModel.declineButtonText
|
||||||
|
onClicked: { handler.onLicenseDeclined() }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue