Updated documentation and typing

Contributes to CURA-8558
This commit is contained in:
j.spijker@ultimaker.com 2021-11-03 13:43:08 +01:00 committed by Jelle Spijker
parent 0218703592
commit edc71f12a3
No known key found for this signature in database
GPG key ID: 6662DC033BE6B99A
3 changed files with 46 additions and 29 deletions

View file

@ -2,10 +2,9 @@
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import pyqtSlot, Qt from PyQt5.QtCore import pyqtSlot, Qt
from typing import TYPE_CHECKING from typing import Any, Dict, Generator, TYPE_CHECKING
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Logger import Logger
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
@ -31,7 +30,7 @@ class LocalPackageList(PackageList):
"plugin": catalog.i18nc("@label:property", "Bundled Plugins"), "plugin": catalog.i18nc("@label:property", "Bundled Plugins"),
"material": catalog.i18nc("@label:property", "Bundled Materials") "material": catalog.i18nc("@label:property", "Bundled Materials")
} }
} } # The section headers to be used for the different package categories
def __init__(self, parent: "QObject" = None) -> None: def __init__(self, parent: "QObject" = None) -> None:
super().__init__(parent) super().__init__(parent)
@ -40,42 +39,58 @@ class LocalPackageList(PackageList):
@pyqtSlot() @pyqtSlot()
def updatePackages(self) -> None: def updatePackages(self) -> None:
""" """Update the list with local packages, these are materials or plugin, either bundled or user installed. The list
Make a request for the first paginated page of packages. will also contain **to be removed** or **to be installed** packages since the user might still want to interact
with these.
When the request is done, the list will get updated with the new package models.
""" """
self.setErrorMessage("") # Clear any previous errors. self.setErrorMessage("") # Clear any previous errors.
self.setIsLoading(True) self.setIsLoading(True)
self._getLocalPackages() self._getLocalPackages()
self.setIsLoading(True)
def _getLocalPackages(self) -> None: def _getLocalPackages(self) -> None:
""" Obtain the local packages.
The list is sorted per category as in the order of the PACKAGE_SECTION_HEADER dictionary, whereas the packages
for the sections are sorted alphabetically on the display name
"""
sorted_sections = {} sorted_sections = {}
# Filter the package list per section_title and sort these
for section in self._getSections(): for section in self._getSections():
packages = filter(lambda p: p["section_title"] == section, self._allPackageInfo()) packages = filter(lambda p: p["section_title"] == section, self._allPackageInfo())
sorted_sections[section] = sorted(packages, key = lambda p: p["display_name"]) sorted_sections[section] = sorted(packages, key = lambda p: p["display_name"])
# Create a PackageModel from the sorted package_info and append them to the list
for section in sorted_sections.values(): for section in sorted_sections.values():
for package_data in section: for package_data in section:
package = PackageModel(package_data, parent = self) package = PackageModel(package_data, parent = self)
self.appendItem({"package": package}) self.appendItem({"package": package})
self.setIsLoading(False) self.setIsLoading(False)
self.setHasMore(False) self.setHasMore(False) # All packages should have been loaded at this time
def _getSections(self): def _getSections(self) -> Generator[str]:
""" Flatten and order the PACKAGE_SECTION_HEADER such that it can be used in obtaining the packages in the
correct order"""
for package_type in self.PACKAGE_SECTION_HEADER.values(): for package_type in self.PACKAGE_SECTION_HEADER.values():
for section in package_type.values(): for section in package_type.values():
yield section yield section
def _allPackageInfo(self): def _allPackageInfo(self) -> Generator[Dict[str, Any]]:
""" A generator which returns a unordered list of package_info, the section_title is appended to the each
package_info"""
manager = self._application.getPackageManager() manager = self._application.getPackageManager()
# Get all the installed packages, add a section_title depending on package_type and user installed
for package_type, packages in manager.getAllInstalledPackagesInfo().items(): for package_type, packages in manager.getAllInstalledPackagesInfo().items():
for package_data in packages: for package_data in packages:
bundled_or_installed = "installed" if manager.isUserInstalledPackage(package_data["package_id"]) else "bundled" bundled_or_installed = "installed" if manager.isUserInstalledPackage(package_data["package_id"]) else "bundled"
package_data["section_title"] = self.PACKAGE_SECTION_HEADER[bundled_or_installed][package_type] package_data["section_title"] = self.PACKAGE_SECTION_HEADER[bundled_or_installed][package_type]
yield package_data yield package_data
# Get all to be removed package_info's. These packages are still used in the current session so the user might
# to interact with these in the list
for package_data in manager.getPackagesToRemove().values(): for package_data in manager.getPackagesToRemove().values():
yield package_data["package_info"] yield package_data["package_info"]

View file

@ -9,8 +9,8 @@ from typing import Optional, TYPE_CHECKING
from cura.ApplicationMetadata import CuraSDKVersion from cura.ApplicationMetadata import CuraSDKVersion
from cura.CuraApplication import CuraApplication # Creating QML objects and managing packages. from cura.CuraApplication import CuraApplication # Creating QML objects and managing packages.
from cura.UltimakerCloud import UltimakerCloudConstants from cura.UltimakerCloud import UltimakerCloudConstants
from UM.Extension import Extension # We are implementing the main object of an extension here. from UM.Extension import Extension # We are implementing the main object of an extension here.
from UM.Logger import Logger
from UM.PluginRegistry import PluginRegistry # To find out where we are stored (the proper way). from UM.PluginRegistry import PluginRegistry # To find out where we are stored (the proper way).
from .RemotePackageList import RemotePackageList # To register this type with QML. from .RemotePackageList import RemotePackageList # To register this type with QML.

View file

@ -14,6 +14,9 @@ catalog = i18nCatalog("cura")
class PackageList(ListModel): class PackageList(ListModel):
""" A List model for Packages, this class serves as parent class for more detailed implementations.
such as Packages obtained from Remote or Local source
"""
PackageRole = Qt.UserRole + 1 PackageRole = Qt.UserRole + 1
def __init__(self, parent: "QObject" = None) -> None: def __init__(self, parent: "QObject" = None) -> None:
@ -26,20 +29,20 @@ class PackageList(ListModel):
@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"""
Initialize the first page of packages pass
"""
self.setErrorMessage("") # Clear any previous errors.
self.isLoadingChanged.emit()
@pyqtSlot() @pyqtSlot()
def abortUpdating(self) -> None: def abortUpdating(self) -> None:
""" A Qt slot which allows the update process to be aborted. Override this for child classes with async/callback
updatePackges methods"""
pass pass
def reset(self) -> None: def reset(self) -> None:
""" Resets and clears the list"""
self.clear() self.clear()
isLoadingChanged = pyqtSignal() isLoadingChanged = pyqtSignal() # The signal for isLoading property
def setIsLoading(self, value: bool) -> None: def setIsLoading(self, value: bool) -> None:
if self._is_loading != value: if self._is_loading != value:
@ -48,13 +51,12 @@ class PackageList(ListModel):
@pyqtProperty(bool, fset = setIsLoading, notify = isLoadingChanged) @pyqtProperty(bool, fset = setIsLoading, notify = isLoadingChanged)
def isLoading(self) -> bool: def isLoading(self) -> bool:
""" """ Indicating if the the packages are loading
Gives whether the list is currently loading the first page or loading more pages. :return" ``True`` if the list is being obtained, otherwise ``False``
:return: ``True`` if the list is being gathered, or ``False`` if .
""" """
return self._is_loading return self._is_loading
hasMoreChanged = pyqtSignal() hasMoreChanged = pyqtSignal() # The signal for hasMore property
def setHasMore(self, value: bool) -> None: def setHasMore(self, value: bool) -> None:
if self._has_more != value: if self._has_more != value:
@ -63,24 +65,21 @@ class PackageList(ListModel):
@pyqtProperty(bool, fset = setHasMore, notify = hasMoreChanged) @pyqtProperty(bool, fset = setHasMore, notify = hasMoreChanged)
def hasMore(self) -> bool: def hasMore(self) -> bool:
""" """ Indicating if there are more packages available to load.
Returns whether there are more packages to load. :return: ``True`` if there are more packages to load, or ``False``.
:return: ``True`` if there are more packages to load, or ``False`` if we've reached the last page of the
pagination.
""" """
return self._has_more return self._has_more
errorMessageChanged = pyqtSignal() # The signal for errorMessage property
def setErrorMessage(self, error_message: str) -> None: def setErrorMessage(self, error_message: str) -> None:
if self._error_message != error_message: if self._error_message != error_message:
self._error_message = error_message self._error_message = error_message
self.errorMessageChanged.emit() self.errorMessageChanged.emit()
errorMessageChanged = pyqtSignal()
@pyqtProperty(str, notify = errorMessageChanged, fset = setErrorMessage) @pyqtProperty(str, notify = errorMessageChanged, fset = setErrorMessage)
def errorMessage(self) -> str: def errorMessage(self) -> str:
""" """ If an error occurred getting the list of packages, an error message will be held here.
If an error occurred getting the list of packages, an error message will be held here.
If no error occurred (yet), this will be an empty string. If no error occurred (yet), this will be an empty string.
:return: An error message, if any, or an empty string if everything went okay. :return: An error message, if any, or an empty string if everything went okay.
@ -89,4 +88,7 @@ class PackageList(ListModel):
@pyqtProperty(bool, constant = True) @pyqtProperty(bool, constant = True)
def hasFooter(self) -> bool: def hasFooter(self) -> bool:
""" Indicating if the PackageList should have a Footer visible. For paginated PackageLists
:return: ``True`` if a Footer should be displayed in the ListView, e.q.: paginated lists, ``False`` Otherwise
"""
return self._has_footer return self._has_footer