diff --git a/cura/CuraPackageManager.py b/cura/CuraPackageManager.py index abfad86b20..f787a8b29b 100644 --- a/cura/CuraPackageManager.py +++ b/cura/CuraPackageManager.py @@ -118,8 +118,8 @@ class CuraPackageManager(QObject): package_type = package_info["package_type"] if package_type not in installed_packages_dict: - installed_packages_dict[package_type] = {} - installed_packages_dict[package_type][package_id] = package_info + installed_packages_dict[package_type] = [] + installed_packages_dict[package_type].append( package_info ) # We also need to get information from the plugin registry such as if a plugin is active package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id) @@ -137,8 +137,8 @@ class CuraPackageManager(QObject): plugin_package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id) package_type = "plugin" if package_type not in installed_packages_dict: - installed_packages_dict[package_type] = {} - installed_packages_dict[package_type][package_id] = plugin_package_info + installed_packages_dict[package_type] = [] + installed_packages_dict[package_type].append( plugin_package_info ) return installed_packages_dict diff --git a/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml b/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml index 01f6d44033..faeac08b3c 100644 --- a/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml +++ b/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml @@ -42,8 +42,8 @@ Item onClicked: { toolbox.viewPage = "overview" - toolbox.filterPackages("type", toolbox.viewCategory) - toolbox.filterAuthors("type", toolbox.viewCategory) + toolbox.filterModelByProp("packages", "type", toolbox.viewCategory) + toolbox.filterModelByProp("authors", "type", toolbox.viewCategory) } style: ButtonStyle { diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 729d1e17d6..cac8be8ae9 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -92,15 +92,15 @@ Item { toolbox.viewSelection = model.name toolbox.viewPage = "author" - toolbox.filterAuthors("name", model.name) - toolbox.filterPackages("author_name", model.name) + toolbox.filterModelByProp("authors", "name", model.name) + toolbox.filterModelByProp("packages", "author_name", model.name) } else { toolbox.viewSelection = model.id toolbox.viewPage = "detail" - toolbox.filterAuthors("name", model.author_name) - toolbox.filterPackages("id", model.id) + toolbox.filterModelByProp("authors", "name", model.author_name) + toolbox.filterModelByProp("packages", "id", model.id) } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index b7888f257f..51ccc9b0e5 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -56,14 +56,14 @@ Item case "material": toolbox.viewSelection = model.name toolbox.viewPage = "author" - toolbox.filterAuthors("name", model.name) - toolbox.filterPackages("author_name", model.name) + toolbox.filterModelByProp("authors", "name", model.name) + toolbox.filterModelByProp("packages", "author_name", model.name) break default: toolbox.viewSelection = model.id toolbox.viewPage = "detail" - toolbox.filterAuthors("name", model.author_name) - toolbox.filterPackages("id", model.id) + toolbox.filterModelByProp("authors", "name", model.author_name) + toolbox.filterModelByProp("packages", "id", model.id) break } } diff --git a/plugins/Toolbox/resources/qml/ToolboxHeader.qml b/plugins/Toolbox/resources/qml/ToolboxHeader.qml index 060eefa493..5604814afd 100644 --- a/plugins/Toolbox/resources/qml/ToolboxHeader.qml +++ b/plugins/Toolbox/resources/qml/ToolboxHeader.qml @@ -6,9 +6,6 @@ import QtQuick.Dialogs 1.1 import QtQuick.Window 2.2 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 - -// TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles - import UM 1.1 as UM Rectangle { @@ -55,8 +52,8 @@ Rectangle { } onClicked: { - toolbox.filterPackages("type", "plugin") - toolbox.filterAuthors("type", "plugin") + toolbox.filterModelByProp("packages", "type", "plugin") + toolbox.filterModelByProp("authors", "type", "plugin") toolbox.viewCategory = "plugin" toolbox.viewPage = "overview" } @@ -92,8 +89,8 @@ Rectangle { } onClicked: { - toolbox.filterPackages("type", "material") - toolbox.filterAuthors("type", "material") + toolbox.filterModelByProp("packages", "type", "material") + toolbox.filterModelByProp("authors", "type", "material") toolbox.viewCategory = "material" toolbox.viewPage = "overview" } diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml index d8169906b5..cfcd8acf32 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml @@ -58,7 +58,7 @@ ScrollView Repeater { id: materialList - model: toolbox.packagesModel + model: toolbox.pluginsInstalledModel delegate: ToolboxInstalledTile {} } } @@ -93,7 +93,7 @@ ScrollView Repeater { id: pluginList - model: toolbox.packagesModel + model: toolbox.materialsInstalledModel delegate: ToolboxInstalledTile {} } } diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml index 25bcbab895..75314e9af5 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml @@ -103,7 +103,7 @@ Item Button { id: removeButton text: "Uninstall" - visible: model.can_uninstall && model.status == "installed" + // visible: model.can_uninstall && model.status == "installed" enabled: !toolbox.isDownloading style: ButtonStyle { @@ -131,7 +131,7 @@ Item Button { id: updateButton text: "Update" - enabled: model.can_update + // enabled: model.can_update style: ButtonStyle { background: Rectangle @@ -157,9 +157,9 @@ Item ProgressBar { id: progressbar - anchors.left: installButton.left - anchors.right: installButton.right - anchors.top: installButton.bottom + anchors.left: updateButton.left + anchors.right: updateButton.right + anchors.top: updateButton.bottom anchors.topMargin: 4 value: toolbox.isDownloading ? toolbox.downloadProgress : 0 visible: toolbox.isDownloading diff --git a/plugins/Toolbox/src/PackagesModel.py b/plugins/Toolbox/src/PackagesModel.py index 7bf023f707..dc1eb2cb78 100644 --- a/plugins/Toolbox/src/PackagesModel.py +++ b/plugins/Toolbox/src/PackagesModel.py @@ -52,7 +52,6 @@ class PackagesModel(ListModel): items = [] for package in self._metadata: - print(package) items.append({ "id": package["package_id"], "type": package["package_type"], @@ -62,9 +61,9 @@ class PackagesModel(ListModel): "author_email": package["author"]["email"], "description": package["description"], "icon_url": package["icon_url"] if "icon_url" in package else None, - "image_urls": package["image_urls"], - "download_url": package["download_url"], - "last_updated": package["last_updated"] + "image_urls": package["image_urls"] if "image_urls" in package else None, + "download_url": package["download_url"] if "download_url" in package else None, + "last_updated": package["last_updated"] if "last_updated" in package else None }) # Filter on all the key-word arguments. diff --git a/plugins/Toolbox/src/ShowcaseModel.py b/plugins/Toolbox/src/ShowcaseModel.py deleted file mode 100644 index 2dcfc45762..0000000000 --- a/plugins/Toolbox/src/ShowcaseModel.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -import re -from typing import Dict - -from PyQt5.QtCore import Qt, pyqtProperty, pyqtSignal - -from UM.Qt.ListModel import ListModel - -## Model that holds cura packages. By setting the filter property the instances held by this model can be changed. -class ShowcaseModel(ListModel): - IdRole = Qt.UserRole + 1 - TypeRole = Qt.UserRole + 2 - NameRole = Qt.UserRole + 3 - VersionRole = Qt.UserRole + 4 - AuthorNameRole = Qt.UserRole + 5 - AuthorEmailRole = Qt.UserRole + 6 - DescriptionRole = Qt.UserRole + 7 - IconURLRole = Qt.UserRole + 8 - ImageURLsRole = Qt.UserRole + 9 - DownloadURLRole = Qt.UserRole + 10 - LastUpdatedRole = Qt.UserRole + 11 - - def __init__(self, parent = None): - super().__init__(parent) - - self._metadata = None - - self.addRoleName(ShowcaseModel.IdRole, "id") - self.addRoleName(ShowcaseModel.TypeRole, "type") - self.addRoleName(ShowcaseModel.NameRole, "name") - self.addRoleName(ShowcaseModel.VersionRole, "version") - self.addRoleName(ShowcaseModel.AuthorNameRole, "author_name") - self.addRoleName(ShowcaseModel.AuthorEmailRole, "author_email") - self.addRoleName(ShowcaseModel.DescriptionRole, "description") - self.addRoleName(ShowcaseModel.IconURLRole, "icon_url") - self.addRoleName(ShowcaseModel.ImageURLsRole, "image_urls") - self.addRoleName(ShowcaseModel.DownloadURLRole, "download_url") - self.addRoleName(ShowcaseModel.LastUpdatedRole, "last_updated") - - # List of filters for queries. The result is the union of the each list of results. - self._filter = {} # type: Dict[str,str] - - def setMetadata(self, data): - self._metadata = data - self._update() - - def _update(self): - items = [] - - for package in self._metadata: - print(package) - items.append({ - "id": package["id"], - "type": package["type"], - "name": package["name"], - "version": package["package_version"], - "author_name": package["author"]["name"], - "author_email": package["author"]["email"], - "description": package["description"], - "icon_url": package["icon_url"] if "icon_url" in package else None, - "image_urls": package["image_urls"], - "download_url": package["download_url"], - "last_updated": package["last_updated"] - }) - - # Filter on all the key-word arguments. - for key, value in self._filter.items(): - if "*" in value: - key_filter = lambda candidate, key = key, value = value: self._matchRegExp(candidate, key, value) - else: - key_filter = lambda candidate, key = key, value = value: self._matchString(candidate, key, value) - items = filter(key_filter, items) - - # Execute all filters. - filtered_items = list(items) - - filtered_items.sort(key = lambda k: k["name"]) - self.setItems(filtered_items) - - ## Set the filter of this model based on a string. - # \param filter_dict \type{Dict} Dictionary to do the filtering by. - def setFilter(self, filter_dict: Dict[str, str]) -> None: - if filter_dict != self._filter: - self._filter = filter_dict - self._update() - - @pyqtProperty("QVariantMap", fset = setFilter, constant = True) - def filter(self) -> Dict[str, str]: - return self._filter - - # Check to see if a container matches with a regular expression - def _matchRegExp(self, metadata, property_name, value): - if property_name not in metadata: - return False - value = re.escape(value) #Escape for regex patterns. - value = "^" + value.replace("\\*", ".*") + "$" #Instead of (now escaped) asterisks, match on any string. Also add anchors for a complete match. - if self._ignore_case: - value_pattern = re.compile(value, re.IGNORECASE) - else: - value_pattern = re.compile(value) - - return value_pattern.match(str(metadata[property_name])) - - # Check to see if a container matches with a string - def _matchString(self, metadata, property_name, value): - if property_name not in metadata: - return False - return value.lower() == str(metadata[property_name]).lower() diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 92bd5eefec..dacf806fe6 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -21,7 +21,6 @@ from cura.Utils.VersionTools import compareSemanticVersions from cura.CuraApplication import CuraApplication from .AuthorsModel import AuthorsModel from .PackagesModel import PackagesModel -from .ShowcaseModel import ShowcaseModel i18n_catalog = i18nCatalog("cura") @@ -68,6 +67,8 @@ class Toolbox(QObject, Extension): "authors": [], "packages": [], "plugins_showcase": [], + "plugins_installed": [], + # TODO: Replace this with a proper API call: "materials_showcase": [ { "name": "DSM", @@ -81,7 +82,8 @@ class Toolbox(QObject, Extension): "website": "www.basf.de", "type": "material" } - ] + ], + "materials_installed": [] } # Models: @@ -185,8 +187,20 @@ class Toolbox(QObject, Extension): self._network_manager.finished.connect(self._onRequestFinished) self._network_manager.networkAccessibleChanged.connect(self._onNetworkAccesibleChanged) - self.makeRequestByType("packages") - self.makeRequestByType("plugins_showcase") + # Make remote requests: + self._makeRequestByType("packages") + self._makeRequestByType("plugins_showcase") + + # Gather installed packages: + all_packages = self._package_manager.getAllInstalledPackagesInfo() + if "plugin" in all_packages: + self._metadata["plugins_installed"] = all_packages["plugin"] + self._models["plugins_installed"].setMetadata(self._metadata["plugins_installed"]) + self.metadataChanged.emit() + if "material" in all_packages: + self._metadata["materials_installed"] = all_packages["material"] + self._models["materials_installed"].setMetadata(self._metadata["materials_installed"]) + self.metadataChanged.emit() if not self._dialog: self._dialog = self._createDialog("Toolbox.qml") @@ -248,7 +262,6 @@ class Toolbox(QObject, Extension): installed_plugin_data = self._package_manager.getInstalledPackageInfo(package_id) if installed_plugin_data is None: return False - installed_version = installed_plugin_data["package_version"] return compareSemanticVersions(version, installed_version) > 0 @@ -260,36 +273,15 @@ class Toolbox(QObject, Extension): return True return False - def _createNetworkManager(self): - if self._network_manager: - self._network_manager.finished.disconnect(self._onRequestFinished) - self._network_manager.networkAccessibleChanged.disconnect(self._onNetworkAccesibleChanged) - self._network_manager = QNetworkAccessManager() - self._network_manager.finished.connect(self._onRequestFinished) - self._network_manager.networkAccessibleChanged.connect(self._onNetworkAccesibleChanged) - # Make API Calls # -------------------------------------------------------------------------- - def makeRequestByType(self, type): + def _makeRequestByType(self, type): Logger.log("i", "Toolbox: Requesting %s metadata from server.", type) request = QNetworkRequest(self._request_urls[type]) request.setRawHeader(*self._request_header) self._network_manager.get(request) - - def _requestPackages(self): - Logger.log("i", "Toolbox: Requesting package list from server.") - self._get_packages_request = QNetworkRequest(self._request_urls["packages"]) - self._get_packages_request.setRawHeader(*self._request_header) - self._network_manager.get(self._get_packages_request) - - def _requestShowcase(self): - Logger.log("i", "Toolbox: Requesting showcase list from server.") - self._get_showcase_request = QNetworkRequest(self._request_urls["plugins_showcase"]) - self._get_showcase_request.setRawHeader(*self._request_header) - self._network_manager.get(self._get_showcase_request) - # TODO: Request authors and request material showcase @pyqtSlot(str) @@ -327,7 +319,6 @@ class Toolbox(QObject, Extension): self._download_reply.abort() self._download_reply = None - # TODO: This function is sooooo ugly. Needs a rework: def _onRequestFinished(self, reply): if reply.error() == QNetworkReply.TimeoutError: @@ -340,15 +331,16 @@ class Toolbox(QObject, Extension): self._download_plugin_reply.abort() self._download_plugin_reply = None return - elif reply.error() == QNetworkReply.HostNotFoundError: + + if reply.error() == QNetworkReply.HostNotFoundError: Logger.log("w", "Unable to reach server.") return if reply.operation() == QNetworkAccessManager.GetOperation: # TODO: In the future use the following to build any model from any - # request. Right now this doesn't work though as the packages - # request is also responsible for populating other models. + # request. Right now this doesn't work because the packages request + # is also responsible for populating other models. # for type, url in self._request_urls.items(): # if reply.url() == url: # try: @@ -368,7 +360,6 @@ class Toolbox(QObject, Extension): if reply.url() == self._request_urls["packages"]: try: json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) - print(json_data) # Create packages model with all packages: if not self._models["packages"]: self._models["packages"] = PackagesModel(self) @@ -408,8 +399,6 @@ class Toolbox(QObject, Extension): self._models["plugins_showcase"] = PackagesModel() self._metadata["plugins_showcase"] = json_data["data"] self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"]) - for package in self._models["plugins_showcase"].items: - print(package) self.metadataChanged.emit() self.setViewPage("overview") @@ -440,7 +429,6 @@ class Toolbox(QObject, Extension): def _onDownloadComplete(self, file_path): Logger.log("i", "Toolbox: Download complete.") - print(file_path) try: package_info = self._package_manager.getPackageInfo(file_path) except: @@ -551,32 +539,3 @@ class Toolbox(QObject, Extension): return self._models[modelType].setFilter({}) self.filterChanged.emit() - - # TODO: Eventually dump everything below here: - @pyqtSlot(str, str) - def filterPackages(self, filterType, parameter): - if not self._models["packages"]: - return - self._models["packages"].setFilter({ filterType: parameter }) - self.filterChanged.emit() - - @pyqtSlot() - def unfilterPackages(self): - if not self._models["packages"]: - return - self._models["packages"].setFilter({}) - self.filterChanged.emit() - - @pyqtSlot(str, str) - def filterAuthors(self, filterType, parameter): - if not self._models["authors"]: - return - self._models["authors"].setFilter({ filterType: parameter }) - self.filterChanged.emit() - - @pyqtSlot() - def unfilterAuthors(self): - if not self._models["authors"]: - return - self._models["authors"].setFilter({}) - self.filterChanged.emit()