Merge remote-tracking branch 'origin/CURA-8587_disable_update_install_and_uninstall' into CURA-8587_disable_update_install_and_uninstall

# Conflicts:
#	plugins/Marketplace/resources/qml/PackageCard.qml
#	plugins/Marketplace/resources/qml/PackagePage.qml
This commit is contained in:
casper 2021-12-08 20:01:55 +01:00
commit 8708fd0f3a
6 changed files with 128 additions and 196 deletions

View file

@ -66,9 +66,7 @@ class LocalPackageList(PackageList):
section_title = self.PACKAGE_CATEGORIES[bundled_or_installed][package_type] section_title = self.PACKAGE_CATEGORIES[bundled_or_installed][package_type]
package = PackageModel(package_info, section_title = section_title, parent = self) package = PackageModel(package_info, section_title = section_title, parent = self)
self._connectManageButtonSignals(package) self._connectManageButtonSignals(package)
package.can_downgrade = self._manager.canDowngrade(package_id) package.setCanDowngrade(self._manager.canDowngrade(package_id))
if package_id in self._manager.getPackagesToRemove() or package_id in self._manager.getPackagesToInstall():
package.installation_status_changed = True
return package return package
def checkForUpdates(self, packages: List[Dict[str, Any]]): def checkForUpdates(self, packages: List[Dict[str, Any]]):

View file

@ -18,7 +18,7 @@ from cura.CuraApplication import CuraApplication
from cura.CuraPackageManager import CuraPackageManager from cura.CuraPackageManager import CuraPackageManager
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope # To make requests to the Ultimaker API with correct authorization. from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope # To make requests to the Ultimaker API with correct authorization.
from .PackageModel import PackageModel, ManageState from .PackageModel import PackageModel
from .Constants import USER_PACKAGES_URL from .Constants import USER_PACKAGES_URL
if TYPE_CHECKING: if TYPE_CHECKING:
@ -166,7 +166,6 @@ class PackageList(ListModel):
dialog.deleteLater() dialog.deleteLater()
# reset package card # reset package card
package = self.getPackageModel(package_id) package = self.getPackageModel(package_id)
package.is_installing = ManageState.FAILED
def _requestInstall(self, package_id: str, update: bool = False) -> None: def _requestInstall(self, package_id: str, update: bool = False) -> None:
package_path = self._to_install[package_id] package_path = self._to_install[package_id]
@ -184,12 +183,8 @@ class PackageList(ListModel):
package_path = self._to_install.pop(package_id) package_path = self._to_install.pop(package_id)
to_be_installed = self._manager.installPackage(package_path) is not None to_be_installed = self._manager.installPackage(package_path) is not None
package = self.getPackageModel(package_id) package = self.getPackageModel(package_id)
if package.can_update and to_be_installed: # TODO handle failure
package.can_update = False package.isRecentlyInstalledChanged.emit(update)
if update:
package.is_updating = ManageState.HALTED
else:
package.is_installing = ManageState.HALTED
self.subscribeUserToPackage(package_id, str(package.sdk_version)) self.subscribeUserToPackage(package_id, str(package.sdk_version))
def download(self, package_id: str, url: str, update: bool = False) -> None: def download(self, package_id: str, url: str, update: bool = False) -> None:
@ -239,10 +234,7 @@ class PackageList(ListModel):
Logger.error(f"Failed to download package: {package_id} due to {reply_string}") Logger.error(f"Failed to download package: {package_id} due to {reply_string}")
try: try:
package = self.getPackageModel(package_id) package = self.getPackageModel(package_id)
if update: # TODO: handle error
package.is_updating = ManageState.FAILED
else:
package.is_installing = ManageState.FAILED
except RuntimeError: except RuntimeError:
# Setting the ownership of this object to not qml can still result in a RuntimeError. Which can occur when quickly toggling # Setting the ownership of this object to not qml can still result in a RuntimeError. Which can occur when quickly toggling
# between de-/constructing Remote or Local PackageLists. This try-except is here to prevent a hard crash when the wrapped C++ object # between de-/constructing Remote or Local PackageLists. This try-except is here to prevent a hard crash when the wrapped C++ object
@ -285,7 +277,6 @@ class PackageList(ListModel):
:param package_id: the package identification string :param package_id: the package identification string
""" """
package = self.getPackageModel(package_id) package = self.getPackageModel(package_id)
package.is_installing = ManageState.PROCESSING
url = package.download_url url = package.download_url
self.download(package_id, url, False) self.download(package_id, url, False)
@ -295,10 +286,9 @@ class PackageList(ListModel):
:param package_id: the package identification string :param package_id: the package identification string
""" """
package = self.getPackageModel(package_id) package = self.getPackageModel(package_id)
package.is_installing = ManageState.PROCESSING
self._manager.removePackage(package_id) self._manager.removePackage(package_id)
self.unsunscribeUserFromPackage(package_id) self.unsunscribeUserFromPackage(package_id)
package.is_installing = ManageState.HALTED package.isRecentlyInstalledChanged.emit(False)
def updatePackage(self, package_id: str) -> None: def updatePackage(self, package_id: str) -> None:
"""Update a package from the Marketplace """Update a package from the Marketplace
@ -306,7 +296,6 @@ class PackageList(ListModel):
:param package_id: the package identification string :param package_id: the package identification string
""" """
package = self.getPackageModel(package_id) package = self.getPackageModel(package_id)
package.is_updating = ManageState.PROCESSING
self._manager.removePackage(package_id, force_add = True) self._manager.removePackage(package_id, force_add = True)
url = package.download_url url = package.download_url
self.download(package_id, url, True) self.download(package_id, url, True)
@ -317,10 +306,8 @@ class PackageList(ListModel):
:param package_id: the package identification string :param package_id: the package identification string
""" """
package = self.getPackageModel(package_id) package = self.getPackageModel(package_id)
package.is_enabling = ManageState.PROCESSING
self._plugin_registry.enablePlugin(package_id) self._plugin_registry.enablePlugin(package_id)
package.is_active = True package.is_active = True
package.is_enabling = ManageState.HALTED
def disablePackage(self, package_id: str) -> None: def disablePackage(self, package_id: str) -> None:
"""Disable a package in the plugin registry """Disable a package in the plugin registry
@ -328,7 +315,5 @@ class PackageList(ListModel):
:param package_id: the package identification string :param package_id: the package identification string
""" """
package = self.getPackageModel(package_id) package = self.getPackageModel(package_id)
package.is_enabling = ManageState.PROCESSING
self._plugin_registry.disablePlugin(package_id) self._plugin_registry.disablePlugin(package_id)
package.is_active = False package.is_active = False
package.is_enabling = ManageState.HALTED

View file

@ -13,13 +13,6 @@ from UM.i18n import i18nCatalog # To translate placeholder names if data is not
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
class ManageState(Enum):
PROCESSING = 1
HALTED = 0
FAILED = -1
class PackageModel(QObject): class PackageModel(QObject):
""" """
Represents a package, containing all the relevant information to be displayed about a package. Represents a package, containing all the relevant information to be displayed about a package.
@ -69,19 +62,45 @@ class PackageModel(QObject):
if not self._icon_url or self._icon_url == "": if not self._icon_url or self._icon_url == "":
self._icon_url = author_data.get("icon_url", "") self._icon_url = author_data.get("icon_url", "")
self._is_installing: ManageState = ManageState.HALTED self._is_installing = False
self._installation_status_changed = False self._install_status_changing = False
self._is_recently_installed = False
self._is_recently_updated = False self._is_recently_updated = False
self._is_recently_enabled = False
self._can_update = False self._can_update = False
self._is_updating: ManageState = ManageState.HALTED self._is_updating = False
self._is_enabling: ManageState = ManageState.HALTED
self._can_downgrade = False self._can_downgrade = False
self._section_title = section_title self._section_title = section_title
self.sdk_version = package_data.get("sdk_version_semver", "") self.sdk_version = package_data.get("sdk_version_semver", "")
# Note that there's a lot more info in the package_data than just these specified here. # Note that there's a lot more info in the package_data than just these specified here.
def install_clicked(package_id):
self._install_status_changing = True
self.setIsInstalling(True)
self.installPackageTriggered.connect(install_clicked)
def uninstall_clicked(package_id):
self._install_status_changing = False
self.setIsInstalling(True)
self.uninstallPackageTriggered.connect(uninstall_clicked)
def update_clicked(package_id):
self.setIsUpdating(True)
self.updatePackageTriggered.connect(update_clicked)
def finished_installed(is_updating):
if is_updating:
self._is_recently_installed = True
self.setIsUpdating(False)
else:
self._is_recently_updated
self.setIsInstalling(False)
self.isRecentlyInstalledChanged.connect(finished_installed)
def __eq__(self, other: object): def __eq__(self, other: object):
if isinstance(other, PackageModel): if isinstance(other, PackageModel):
return other == self return other == self
@ -278,6 +297,10 @@ class PackageModel(QObject):
def isCompatibleAirManager(self) -> bool: def isCompatibleAirManager(self) -> bool:
return self._is_compatible_air_manager return self._is_compatible_air_manager
@pyqtProperty(bool, constant = True)
def isBundled(self) -> bool:
return self._is_bundled
# --- manage buttons signals --- # --- manage buttons signals ---
stateManageButtonChanged = pyqtSignal() stateManageButtonChanged = pyqtSignal()
@ -292,33 +315,14 @@ class PackageModel(QObject):
disablePackageTriggered = pyqtSignal(str) disablePackageTriggered = pyqtSignal(str)
recentlyInstalledChanged = pyqtSignal(bool) isRecentlyInstalledChanged = pyqtSignal(bool)
# --- enabling --- # --- enabling ---
@pyqtProperty(str, notify = stateManageButtonChanged) @pyqtProperty(bool, notify = stateManageButtonChanged)
def stateManageEnableButton(self) -> str: def stateManageEnableButton(self) -> bool:
"""The state of the manage Enable Button of this package""" """The state of the manage Enable Button of this package"""
if self._is_enabling == ManageState.PROCESSING: return not (self._is_installed and self._is_active)
return "busy"
if self._package_type == "material" or not self._is_installed:
return "hidden"
if self._is_installed and self._is_active:
return "secondary"
return "primary"
@property
def is_enabling(self) -> ManageState:
"""Flag if the package is being enabled/disabled"""
return self._is_enabling
@is_enabling.setter
def is_enabling(self, value: ManageState) -> None:
if value != self._is_enabling:
self._is_enabling = value
if value == ManageState.HALTED:
self._is_recently_enabled = True
self.stateManageButtonChanged.emit()
@property @property
def is_active(self) -> bool: def is_active(self) -> bool:
@ -333,85 +337,67 @@ class PackageModel(QObject):
# --- Installing --- # --- Installing ---
@pyqtProperty(str, notify = stateManageButtonChanged) @pyqtProperty(bool, notify = stateManageButtonChanged)
def stateManageInstallButton(self) -> str: def stateManageInstallButton(self) -> bool:
"""The state of the Manage Install package card""" """The state of the Manage Install package card"""
if self._is_installing == ManageState.PROCESSING: return not self._is_installed
return "busy"
if self._installation_status_changed:
return "confirmed"
if self._is_installed:
if self._is_bundled and not self._can_downgrade:
return "hidden"
else:
return "secondary"
else:
return "primary"
@property def setIsInstalling(self, value: bool) -> None:
def is_installing(self) -> ManageState:
"""Flag is we're currently installing, when setting this to ``None`` in indicates a failed installation"""
return self._is_installing
@is_installing.setter
def is_installing(self, value: ManageState) -> None:
if value != self._is_installing: if value != self._is_installing:
self._is_installing = value self._is_installing = value
if value == ManageState.HALTED:
self._installation_status_changed = True
self.stateManageButtonChanged.emit() self.stateManageButtonChanged.emit()
@property @pyqtProperty(bool, fset = setIsInstalling, notify = stateManageButtonChanged)
def installation_status_changed(self): def isInstalling(self) -> bool:
return self._installation_status_changed return self._is_installing
@installation_status_changed.setter def setInstallStatusChanging(self, value: bool) -> None:
def installation_status_changed(self, value): if value != self._install_status_changing:
if value != self._installation_status_changed: self._install_status_changing = value
self._installation_status_changed = value
self.stateManageButtonChanged.emit() self.stateManageButtonChanged.emit()
@pyqtProperty(bool, fset = setInstallStatusChanging, notify = stateManageButtonChanged)
def installStatusChanging(self) -> bool:
return self._install_status_changing
@pyqtProperty(bool, notify = stateManageButtonChanged) @pyqtProperty(bool, notify = stateManageButtonChanged)
def installationStatus(self): def isInstalled(self) -> bool:
return self._package_id in CuraApplication.getInstance().getPackageManager().getPackagesToInstall() return self._package_id in CuraApplication.getInstance().getPackageManager().getPackagesToInstall()
@property @pyqtProperty(bool, notify = stateManageButtonChanged)
def can_downgrade(self) -> bool: def isUninstalled(self) -> bool:
"""Flag if the installed package can be downgraded to a bundled version""" return self._package_id in CuraApplication.getInstance().getPackageManager().getPackagesToRemove()
return self._can_downgrade
@can_downgrade.setter def setCanDowngrade(self, value: bool) -> None:
def can_downgrade(self, value: bool) -> None:
if value != self._can_downgrade: if value != self._can_downgrade:
self._can_downgrade = value self._can_downgrade = value
self.stateManageButtonChanged.emit() self.stateManageButtonChanged.emit()
@pyqtProperty(bool, fset = setCanDowngrade, notify = stateManageButtonChanged)
def canDowngrade(self) -> bool:
"""Flag if the installed package can be downgraded to a bundled version"""
return self._can_downgrade
# --- Updating --- # --- Updating ---
@pyqtProperty(str, notify = stateManageButtonChanged) def setIsUpdating(self, value):
def stateManageUpdateButton(self) -> str:
"""The state of the manage Update button for this card """
if self._is_updating == ManageState.PROCESSING:
return "busy"
if self._is_recently_updated:
return "confirmed"
if self._can_update:
return "primary"
return "hidden"
@property
def is_updating(self) -> ManageState:
"""Flag indicating if the package is being updated"""
return self._is_updating
@is_updating.setter
def is_updating(self, value: ManageState) -> None:
if value != self._is_updating: if value != self._is_updating:
self._is_updating = value self._is_updating = value
if value == ManageState.HALTED:
self._is_recently_updated = True
self.stateManageButtonChanged.emit() self.stateManageButtonChanged.emit()
@pyqtProperty(bool, fset = setIsUpdating, notify = stateManageButtonChanged)
def isUpdating(self):
return self._is_updating
def setIsUpdated(self, value):
if value != self._is_recently_updated:
self._is_recently_updated = value
self.stateManageButtonChanged.emit()
@pyqtProperty(bool, fset = setIsUpdated, notify = stateManageButtonChanged)
def isUpdated(self):
return self._is_recently_updated
@property @property
def can_update(self) -> bool: def can_update(self) -> bool:
"""Flag indicating if the package can be updated""" """Flag indicating if the package can be updated"""

View file

@ -123,8 +123,6 @@ class RemotePackageList(PackageList):
try: try:
package = PackageModel(package_data, parent = self) package = PackageModel(package_data, parent = self)
self._connectManageButtonSignals(package) self._connectManageButtonSignals(package)
if package_id in self._manager.getPackagesToRemove() or package_id in self._manager.getPackagesToInstall():
package.installation_status_changed = True
self.appendItem({"package": package}) # Add it to this list model. self.appendItem({"package": package}) # Add it to this list model.
except RuntimeError: except RuntimeError:
# Setting the ownership of this object to not qml can still result in a RuntimeError. Which can occur when quickly toggling # Setting the ownership of this object to not qml can still result in a RuntimeError. Which can occur when quickly toggling

View file

@ -11,7 +11,7 @@ import Cura 1.6 as Cura
Item Item
{ {
id: manageButton id: manageButton
property string button_style property bool button_style
property string text property string text
property bool busy property bool busy
property bool confirmed property bool confirmed
@ -117,19 +117,10 @@ Item
sourceComponent: sourceComponent:
{ {
switch (manageButton.button_style) if (busy) { return manageButton.busyButton; }
{ else if (confirmed) { return manageButton.confirmButton; }
case "primary": else if (manageButton.button_style) { return manageButton.primaryButton; }
return manageButton.primaryButton; else { return manageButton.secondaryButton; }
case "secondary":
return manageButton.secondaryButton;
case "busy":
return manageButton.busyButton;
case "confirmed":
return manageButton.confirmButton;
default:
return;
}
} }
} }
} }

View file

@ -181,34 +181,16 @@ Item
ManageButton ManageButton
{ {
id: enableManageButton id: enableManageButton
visible: !(installManageButton.confirmed || updateManageButton.confirmed) || enableManageButton.confirmed visible: showManageButtons && !(installManageButton.confirmed || updateManageButton.confirmed)
enabled: !(installManageButton.busy || updateManageButton.busy)
busy: false
confirmed: false
button_style: packageData.stateManageEnableButton button_style: packageData.stateManageEnableButton
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
busy: packageData.enableManageButton == "busy"
confirmed: packageData.enableManageButton == "confirmed" text: packageData.stateManageEnableButton ? catalog.i18nc("@button", "Enable") : catalog.i18nc("@button", "Disable")
text: {
switch (packageData.stateManageEnableButton) {
case "primary":
return catalog.i18nc("@button", "Enable");
case "secondary":
return catalog.i18nc("@button", "Disable");
case "busy":
if (packageData.installationStatus) {
return catalog.i18nc("@button", "Enabling...");
} else {
return catalog.i18nc("@button", "Disabling...");
}
case "confirmed":
if (packageData.installationStatus) {
return catalog.i18nc("@button", "Enabled");
} else {
return catalog.i18nc("@button", "Disabled");
}
default:
return "";
}
}
enabled: !installManageButton.busy && !updateManageButton.busy
onClicked: onClicked:
{ {
@ -226,34 +208,31 @@ Item
ManageButton ManageButton
{ {
id: installManageButton id: installManageButton
visible: (showManageButtons || installManageButton.confirmed) && !(enableManageButton.confirmed || updateManageButton.confirmed) visible: (showManageButtons || confirmed) && ((packageData.isBundled && packageData.canDowngrade) || !packageData.isBundled || !updateManageButton.confirmed)
enabled: !packageData.isUpdating
busy: packageData.isInstalling
confirmed: packageData.isInstalled || packageData.isUninstalled
button_style: packageData.stateManageInstallButton button_style: packageData.stateManageInstallButton
busy: packageData.stateManageInstallButton == "busy"
confirmed: packageData.stateManageInstallButton == "confirmed"
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
text: {
switch (packageData.stateManageInstallButton) { text:
case "primary": {
return catalog.i18nc("@button", "Install"); if (packageData.stateManageInstallButton)
case "secondary": {
return catalog.i18nc("@button", "Uninstall"); if (packageData.isInstalling) { return catalog.i18nc("@button", "Installing..."); }
case "busy": else if (packageData.isInstalled) { return catalog.i18nc("@button", "Installed"); }
if (packageData.installationStatus) { else { return catalog.i18nc("@button", "Install"); }
return catalog.i18nc("@button", "Installing..."); }
} else { else
return catalog.i18nc("@button", "Uninstalling..."); {
} if (packageData.isInstalling) { return catalog.i18nc("@button", "Uninstalling..."); }
case "confirmed": else if (packageData.isUninstalled) { return catalog.i18nc("@button", "Uninstalled"); }
if (packageData.installationStatus) { else { return catalog.i18nc("@button", "Uninstall"); }
return catalog.i18nc("@button", "Installed");
} else {
return catalog.i18nc("@button", "Uninstalled");
}
default:
return "";
} }
} }
enabled: !enableManageButton.busy && !updateManageButton.busy
onClicked: onClicked:
{ {
@ -271,25 +250,20 @@ Item
ManageButton ManageButton
{ {
id: updateManageButton id: updateManageButton
visible: showManageButtons && (!installManageButton.confirmed || updateManageButton.confirmed) visible: (showManageButtons && confirmed) && !installManageButton.confirmed
enabled: !installManageButton.busy
button_style: packageData.stateManageUpdateButton busy: packageData.isUpdating
busy: packageData.stateManageUpdateButton == "busy" confirmed: packageData.isUpdated
confirmed: packageData.stateManageUpdateButton == "confirmed"
button_style: true
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
enabled: !installManageButton.busy && !enableManageButton.busy
text: { text:
switch (packageData.stateManageInstallButton) { {
case "primary": if (packageData.isUpdating) { return catalog.i18nc("@button", "Updating..."); }
return catalog.i18nc("@button", "Update"); else if (packageData.isUpdated) { return catalog.i18nc("@button", "Updated"); }
case "busy": else { return catalog.i18nc("@button", "Update"); }
return catalog.i18nc("@button", "Updating...");
case "confirmed":
return catalog.i18nc("@button", "Updated");
default:
return "";
}
} }
onClicked: packageData.updatePackageTriggered(packageData.packageId) onClicked: packageData.updatePackageTriggered(packageData.packageId)