diff --git a/plugins/Toolbox/Toolbox/CuraPackageModel.py b/plugins/Toolbox/Toolbox/CuraPackageModel.py index f8d6174d66..facf21cc6f 100644 --- a/plugins/Toolbox/Toolbox/CuraPackageModel.py +++ b/plugins/Toolbox/Toolbox/CuraPackageModel.py @@ -1,6 +1,7 @@ # 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 @@ -33,7 +34,7 @@ class CuraPackageModel(ListModel): self.addRoleName(CuraPackageModel.ImageURLsRole, "image_urls") # List of filters for queries. The result is the union of the each list of results. - self._filter = None # type: Dict[str,str] + self._filter = {} # type: Dict[str,str] def setPackagesMetaData(self, data): self._packages_metadata = data @@ -54,18 +55,46 @@ class CuraPackageModel(ListModel): "image_urls": package["image_urls"] }) - items.sort(key = lambda k: k["name"]) - self.setItems(items) + # 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) - filterChanged = pyqtSignal() + # 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.filterChanged.emit() + self._update() - @pyqtProperty("QVariantMap", fset = setFilter, notify = filterChanged) + @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/Toolbox/Toolbox.py b/plugins/Toolbox/Toolbox/Toolbox.py index 858a21d18a..c1cdcd5741 100644 --- a/plugins/Toolbox/Toolbox/Toolbox.py +++ b/plugins/Toolbox/Toolbox/Toolbox.py @@ -43,12 +43,12 @@ class Toolbox(QObject, Extension): self._plugin_registry = Application.getInstance().getPluginRegistry() self._packages_version_number = self._plugin_registry.APIVersion - self._packages_metadata = [] - self._packages_model = None + self._packages_metadata = [] # Stores the remote information of the packages + self._packages_model = None # Model that list the remote available packages - # Can be 'installed' or 'available' - self._view = "available" - self._detail_view = "" + # Nowadays can be 'plugins', 'materials' or 'installed' + self._current_view = "plugins" + self._detail_view = False self._restart_required = False @@ -91,6 +91,7 @@ class Toolbox(QObject, Extension): restartRequiredChanged = pyqtSignal() viewChanged = pyqtSignal() detailViewChanged = pyqtSignal() + filterChanged = pyqtSignal() @pyqtSlot(result = str) def getLicenseDialogPluginName(self): @@ -282,28 +283,31 @@ class Toolbox(QObject, Extension): self.setIsDownloading(False) @pyqtSlot(str) - def setView(self, view = "available"): - self._view = view + def filterPackagesByType(self, type): + if not self._packages_model: + return + self._packages_model.setFilter({"type": type}) + self.filterChanged.emit() + + def setCurrentView(self, view = "plugins"): + self._current_view = view self.viewChanged.emit() - self.packagesMetadataChanged.emit() - @pyqtProperty(str, notify = viewChanged) - def viewing(self): - return self._view + @pyqtProperty(str, fset = setCurrentView, notify = viewChanged) + def currentView(self): + return self._current_view - @pyqtSlot(str) - def setDetailView(self, item = ""): + def setDetailView(self, item = False): self._detail_view = item self.detailViewChanged.emit() - self.packagesMetadataChanged.emit() - @pyqtProperty(str, notify = detailViewChanged) + @pyqtProperty(bool, fset = setDetailView, notify = detailViewChanged) def detailView(self): return self._detail_view @pyqtProperty(QObject, notify = packagesMetadataChanged) def pluginsModel(self): - self._plugins_model = PluginsModel(None, self._view) + self._plugins_model = PluginsModel(None, self._current_view) # self._plugins_model.update() # Check each plugin the registry for matching plugin from server @@ -383,10 +387,10 @@ class Toolbox(QObject, Extension): json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) # Add metadata to the manager: - self._packages_metadata = json_data + self._packages_metadata = json_data["data"] if not self._packages_model: self._packages_model = CuraPackageModel() - self._packages_model.setPackagesMetaData(self._packages_metadata["data"]) + self._packages_model.setPackagesMetaData(self._packages_metadata) # self._plugin_registry.addExternalPlugins(self._packages_metadata) self.packagesMetadataChanged.emit() except json.decoder.JSONDecodeError: diff --git a/plugins/Toolbox/resources/qml/PluginBrowser.qml b/plugins/Toolbox/resources/qml/PluginBrowser.qml index 643d26bfbe..a45f717d22 100644 --- a/plugins/Toolbox/resources/qml/PluginBrowser.qml +++ b/plugins/Toolbox/resources/qml/PluginBrowser.qml @@ -14,7 +14,8 @@ import UM 1.1 as UM Window { id: base - title: catalog.i18nc("@title:tab", "Plugins"); + title: catalog.i18nc("@title:tab", "Toolbox"); + modality: Qt.ApplicationModal width: 800 * screenScaleFactor height: 640 * screenScaleFactor minimumWidth: 800 * screenScaleFactor @@ -42,18 +43,17 @@ Window ToolboxViewDownloads { id: viewDownloads - visible: manager.viewing == "available" && manager.detailView == "" ? true : false + visible: manager.currentView != "installed" && !manager.detailView } - ToolboxViewDetail { id: viewDetail - visible: manager.viewing == "available" && manager.detailView != "" ? true : false + visible: manager.currentView != "installed" && manager.detailView } ToolboxViewInstalled { id: installedPluginList - visible: manager.viewing == "installed" ? true : false + visible: manager.currentView == "installed" } } SectionShadow diff --git a/plugins/Toolbox/resources/qml/ToolboxGrid.qml b/plugins/Toolbox/resources/qml/ToolboxGrid.qml index 53c4443493..1ec87e8217 100644 --- a/plugins/Toolbox/resources/qml/ToolboxGrid.qml +++ b/plugins/Toolbox/resources/qml/ToolboxGrid.qml @@ -39,13 +39,10 @@ Rectangle columnSpacing: UM.Theme.getSize("base_unit").width rowSpacing: UM.Theme.getSize("base_unit").height - ToolboxGridTile {} - ToolboxGridTile {} - ToolboxGridTile {} - ToolboxGridTile {} - ToolboxGridTile {} - ToolboxGridTile {} - ToolboxGridTile {} - ToolboxGridTile {} + Repeater + { + model: manager.packagesModel + delegate: ToolboxGridTile {} + } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxGridTile.qml index 160e59bf15..36c7b93223 100644 --- a/plugins/Toolbox/resources/qml/ToolboxGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxGridTile.qml @@ -33,7 +33,7 @@ Item Label { id: name - text: "Auto Orientation" + text: model.name width: parent.width wrapMode: Text.WordWrap height: UM.Theme.getSize("base_unit").height * 2 @@ -44,7 +44,7 @@ Item Label { id: info - text: "Automatically orientate your model." + text: model.description width: parent.width wrapMode: Text.WordWrap color: UM.Theme.getColor("text_medium") @@ -55,8 +55,6 @@ Item MouseArea { anchors.fill: parent - onClicked: { - manager.setDetailView("thingy") - } + onClicked: manager.detailView = true } } diff --git a/plugins/Toolbox/resources/qml/ToolboxHeader.qml b/plugins/Toolbox/resources/qml/ToolboxHeader.qml index cce078035d..106b988e9e 100644 --- a/plugins/Toolbox/resources/qml/ToolboxHeader.qml +++ b/plugins/Toolbox/resources/qml/ToolboxHeader.qml @@ -32,7 +32,7 @@ Rectangle { implicitWidth: 96 implicitHeight: 48 Rectangle { - visible: manager.viewing == "available" ? true : false + visible: manager.currentView == "plugins" color: UM.Theme.getColor("primary") anchors.bottom: parent.bottom width: parent.width @@ -49,7 +49,11 @@ Rectangle { horizontalAlignment: Text.AlignHCenter } } - onClicked: manager.setView("available") + onClicked: + { + manager.filterPackagesByType("plugin") + manager.currentView = "plugins" + } } Button { @@ -60,7 +64,7 @@ Rectangle { implicitWidth: 96 implicitHeight: 48 Rectangle { - visible: manager.viewing == "available" ? true : false + visible: manager.currentView == "materials" color: UM.Theme.getColor("primary") anchors.bottom: parent.bottom width: parent.width @@ -77,7 +81,11 @@ Rectangle { horizontalAlignment: Text.AlignHCenter } } - onClicked: manager.setView("available") + onClicked: + { + manager.filterPackagesByType("material") + manager.currentView = "materials" + } } } @@ -91,7 +99,7 @@ Rectangle { implicitWidth: 96 implicitHeight: 48 Rectangle { - visible: manager.viewing == "installed" ? true : false + visible: manager.currentView == "installed" color: UM.Theme.getColor("primary") anchors.bottom: parent.bottom width: parent.width @@ -108,6 +116,6 @@ Rectangle { horizontalAlignment: Text.AlignHCenter } } - onClicked: manager.setView("installed") + onClicked: manager.currentView = "installed" } } diff --git a/plugins/Toolbox/resources/qml/ToolboxViewDetail.qml b/plugins/Toolbox/resources/qml/ToolboxViewDetail.qml index 10ad984bdc..c0fe5790dd 100644 --- a/plugins/Toolbox/resources/qml/ToolboxViewDetail.qml +++ b/plugins/Toolbox/resources/qml/ToolboxViewDetail.qml @@ -29,9 +29,7 @@ Item Button { text: "Back" - onClicked: { - manager.setDetailView("") - } + onClicked: manager.detailView = false } } ScrollView