mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-08 07:27:29 -06:00
Added author and material views
This commit is contained in:
parent
91001455ad
commit
817b724994
10 changed files with 172 additions and 43 deletions
|
@ -12,10 +12,8 @@ Item
|
|||
id: base
|
||||
anchors
|
||||
{
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
bottomMargin: UM.Theme.getSize("default_margin").height
|
||||
leftMargin: UM.Theme.getSize("double_margin").width
|
||||
rightMargin: UM.Theme.getSize("double_margin").width
|
||||
topMargin: UM.Theme.getSize("double_margin").height
|
||||
bottomMargin: UM.Theme.getSize("double_margin").height
|
||||
}
|
||||
ScrollView
|
||||
{
|
||||
|
@ -24,6 +22,8 @@ Item
|
|||
style: UM.Theme.styles.scrollview
|
||||
Column
|
||||
{
|
||||
anchors.right: base.right
|
||||
anchors.rightMargin: UM.Theme.getSize("double_margin").width
|
||||
height: childrenRect.height
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
Repeater
|
||||
|
|
|
@ -11,6 +11,7 @@ import UM 1.1 as UM
|
|||
|
||||
Item
|
||||
{
|
||||
property var details: manager.packagesModel.items[0]
|
||||
id: base
|
||||
anchors.fill: parent
|
||||
Item
|
||||
|
@ -82,7 +83,7 @@ Item
|
|||
width: UM.Theme.getSize("toolbox_thumbnail_medium").width
|
||||
height: UM.Theme.getSize("toolbox_thumbnail_medium").height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: manager.detailData["icon_url"] || "../images/logobot.svg"
|
||||
source: details.icon_url || "../images/logobot.svg"
|
||||
anchors
|
||||
{
|
||||
top: parent.top
|
||||
|
@ -104,21 +105,21 @@ Item
|
|||
spacing: Math.floor(UM.Theme.getSize("default_margin").height/2)
|
||||
Label
|
||||
{
|
||||
text: manager.detailData["name"]
|
||||
text: details.name
|
||||
font: UM.Theme.getFont("large")
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: manager.detailData["description"]
|
||||
text: details.description
|
||||
font: UM.Theme.getFont("default")
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
}
|
||||
Label
|
||||
{
|
||||
text: "Author: " + manager.detailData["author"]["name"]
|
||||
text: "Author: " + details.author_name
|
||||
font: UM.Theme.getFont("small")
|
||||
wrapMode: Text.WordWrap
|
||||
width: parent.width
|
||||
|
@ -131,8 +132,10 @@ Item
|
|||
{
|
||||
right: header.right
|
||||
top: header.bottom
|
||||
|
||||
left: header.left
|
||||
bottom: base.bottom
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ import UM 1.1 as UM
|
|||
|
||||
Rectangle
|
||||
{
|
||||
width: base.width
|
||||
height: UM.Theme.getSize("base_unit").height * 12
|
||||
color: "steelblue"
|
||||
width: base.width - UM.Theme.getSize("double_margin").width
|
||||
height: UM.Theme.getSize("base_unit").height * 8
|
||||
color: "transparent"
|
||||
Column
|
||||
{
|
||||
anchors
|
||||
|
@ -20,7 +20,6 @@ Rectangle
|
|||
right: controls.left
|
||||
rightMargin: UM.Theme.getSize("default_margin").width
|
||||
top: parent.top
|
||||
leftMargin: UM.Theme.getSize("default_margin").width
|
||||
topMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
Label
|
||||
|
|
|
@ -34,7 +34,7 @@ Column
|
|||
|
||||
Repeater
|
||||
{
|
||||
model: manager.packagesModel
|
||||
model: manager.viewCategory == "material" ? manager.authorsModel : manager.packagesModel
|
||||
delegate: ToolboxDownloadsGridTile
|
||||
{
|
||||
Layout.preferredWidth: (grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns
|
||||
|
|
|
@ -70,16 +70,16 @@ Item
|
|||
onClicked: {
|
||||
if ( manager.viewCategory == "material" )
|
||||
{
|
||||
console.log("filtering by " + model.author)
|
||||
manager.viewSelection = model.author.name
|
||||
manager.viewSelection = model.name
|
||||
manager.viewPage = "author"
|
||||
manager.filterPackages("author", model.author)
|
||||
manager.filterPackages("author_name", model.name)
|
||||
}
|
||||
else
|
||||
{
|
||||
manager.viewSelection = model.id
|
||||
manager.viewPage = "detail"
|
||||
manager.filterPackages("id", model.id)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ Column
|
|||
Label
|
||||
{
|
||||
id: heading
|
||||
text: "Top Downloads"
|
||||
text: "Showcase"
|
||||
width: parent.width
|
||||
color: UM.Theme.getColor("text_medium")
|
||||
font: UM.Theme.getFont("medium")
|
||||
|
|
|
@ -55,7 +55,8 @@ Rectangle {
|
|||
}
|
||||
onClicked:
|
||||
{
|
||||
manager.filterPackagesByType("plugin")
|
||||
manager.filterPackages("type", "plugin")
|
||||
manager.filterAuthors("type", "plugin")
|
||||
manager.viewCategory = "plugin"
|
||||
manager.viewPage = "overview"
|
||||
}
|
||||
|
@ -91,7 +92,8 @@ Rectangle {
|
|||
}
|
||||
onClicked:
|
||||
{
|
||||
manager.filterPackagesByType("material")
|
||||
manager.filterPackages("type", "material")
|
||||
manager.filterAuthors("type", "material")
|
||||
manager.viewCategory = "material"
|
||||
manager.viewPage = "overview"
|
||||
}
|
||||
|
|
88
plugins/Toolbox/src/AuthorsModel.py
Normal file
88
plugins/Toolbox/src/AuthorsModel.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
# 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 AuthorsModel(ListModel):
|
||||
NameRole = Qt.UserRole + 1
|
||||
EmailRole = Qt.UserRole + 2
|
||||
WebsiteRole = Qt.UserRole + 3
|
||||
TypeRole = Qt.UserRole + 4
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._authors_metadata = None
|
||||
|
||||
self.addRoleName(AuthorsModel.NameRole, "name")
|
||||
self.addRoleName(AuthorsModel.EmailRole, "email")
|
||||
self.addRoleName(AuthorsModel.WebsiteRole, "website")
|
||||
self.addRoleName(AuthorsModel.TypeRole, "type")
|
||||
|
||||
# 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._authors_metadata = data
|
||||
self._update()
|
||||
|
||||
def _update(self):
|
||||
items = []
|
||||
|
||||
for author in self._authors_metadata:
|
||||
items.append({
|
||||
"name": author["name"],
|
||||
"email": author["email"],
|
||||
"website": author["website"],
|
||||
"type": author["type"]
|
||||
})
|
||||
|
||||
# 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()
|
|
@ -14,10 +14,11 @@ class CuraPackageModel(ListModel):
|
|||
TypeRole = Qt.UserRole + 2
|
||||
NameRole = Qt.UserRole + 3
|
||||
VersionRole = Qt.UserRole + 4
|
||||
AuthorRole = Qt.UserRole + 5
|
||||
DescriptionRole = Qt.UserRole + 6
|
||||
IconURLRole = Qt.UserRole + 7
|
||||
ImageURLsRole = Qt.UserRole + 8
|
||||
AuthorNameRole = Qt.UserRole + 5
|
||||
AuthorEmailRole = Qt.UserRole + 6
|
||||
DescriptionRole = Qt.UserRole + 7
|
||||
IconURLRole = Qt.UserRole + 8
|
||||
ImageURLsRole = Qt.UserRole + 9
|
||||
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
@ -28,7 +29,8 @@ class CuraPackageModel(ListModel):
|
|||
self.addRoleName(CuraPackageModel.TypeRole, "type")
|
||||
self.addRoleName(CuraPackageModel.NameRole, "name")
|
||||
self.addRoleName(CuraPackageModel.VersionRole, "version")
|
||||
self.addRoleName(CuraPackageModel.AuthorRole, "author")
|
||||
self.addRoleName(CuraPackageModel.AuthorNameRole, "author_name")
|
||||
self.addRoleName(CuraPackageModel.AuthorEmailRole, "author_email")
|
||||
self.addRoleName(CuraPackageModel.DescriptionRole, "description")
|
||||
self.addRoleName(CuraPackageModel.IconURLRole, "icon_url")
|
||||
self.addRoleName(CuraPackageModel.ImageURLsRole, "image_urls")
|
||||
|
@ -49,7 +51,8 @@ class CuraPackageModel(ListModel):
|
|||
"type": package["package_type"],
|
||||
"name": package["display_name"],
|
||||
"version": package["package_version"],
|
||||
"author": package["author"],
|
||||
"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"]
|
||||
|
|
|
@ -22,6 +22,7 @@ import platform
|
|||
import zipfile
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from .AuthorsModel import AuthorsModel
|
||||
from .CuraPackageModel import CuraPackageModel
|
||||
|
||||
i18n_catalog = i18nCatalog("cura")
|
||||
|
@ -32,7 +33,7 @@ class Toolbox(QObject, Extension):
|
|||
super().__init__(parent)
|
||||
|
||||
self._api_version = 1
|
||||
self._api_url = "https://api-staging.ultimaker.com/cura-packages/v%s/" % self._api_version
|
||||
self._api_url = "https://api-staging.ultimaker.com/cura-packages/v%s" % self._api_version
|
||||
|
||||
self._package_list_request = None
|
||||
self._download_plugin_request = None
|
||||
|
@ -45,6 +46,8 @@ class Toolbox(QObject, Extension):
|
|||
|
||||
self._packages_metadata = [] # Stores the remote information of the packages
|
||||
self._packages_model = None # Model that list the remote available packages
|
||||
self._showcase_model = None
|
||||
self._authors_model = None
|
||||
|
||||
|
||||
# These properties are for keeping track of the UI state:
|
||||
|
@ -112,10 +115,14 @@ class Toolbox(QObject, Extension):
|
|||
|
||||
showLicenseDialog = pyqtSignal()
|
||||
showRestartDialog = pyqtSignal()
|
||||
|
||||
packagesMetadataChanged = pyqtSignal()
|
||||
authorsMetadataChanged = pyqtSignal()
|
||||
|
||||
onDownloadProgressChanged = pyqtSignal()
|
||||
onIsDownloadingChanged = pyqtSignal()
|
||||
restartRequiredChanged = pyqtSignal()
|
||||
|
||||
viewChanged = pyqtSignal()
|
||||
detailViewChanged = pyqtSignal()
|
||||
filterChanged = pyqtSignal()
|
||||
|
@ -161,7 +168,7 @@ class Toolbox(QObject, Extension):
|
|||
|
||||
def requestPackageList(self):
|
||||
Logger.log("i", "Requesting package list")
|
||||
url = QUrl("{base_url}packages?cura_version={version}".format(base_url = self._api_url, version = self._packages_version_number))
|
||||
url = QUrl("{base_url}/cura/v{version}/packages".format(base_url = self._api_url, version = self._packages_version_number))
|
||||
self._package_list_request = QNetworkRequest(url)
|
||||
self._package_list_request.setRawHeader(*self._request_header)
|
||||
self._network_manager.get(self._package_list_request)
|
||||
|
@ -355,17 +362,16 @@ class Toolbox(QObject, Extension):
|
|||
for item in self._packages_metadata:
|
||||
if item["id"] == plugin["id"]:
|
||||
plugin["update_url"] = item["file_location"]
|
||||
|
||||
# if self._current_view == "plugins":
|
||||
# self.filterPackagesByType("plugin")
|
||||
# elif self._current_view == "materials":
|
||||
# self.filterPackagesByType("material")
|
||||
return self._plugins_model
|
||||
|
||||
@pyqtProperty(QObject, notify = packagesMetadataChanged)
|
||||
def packagesModel(self):
|
||||
return self._packages_model
|
||||
|
||||
@pyqtProperty(QObject, notify = authorsMetadataChanged)
|
||||
def authorsModel(self):
|
||||
return self._authors_model
|
||||
|
||||
@pyqtProperty(bool, notify = packagesMetadataChanged)
|
||||
def dataReady(self):
|
||||
return self._packages_model is not None
|
||||
|
@ -425,16 +431,35 @@ class Toolbox(QObject, Extension):
|
|||
return
|
||||
|
||||
if reply.operation() == QNetworkAccessManager.GetOperation:
|
||||
if reply_url == "{base_url}packages?cura_version={version}".format(base_url = self._api_url, version = self._packages_version_number):
|
||||
if reply_url == "{base_url}/cura/v{version}/packages".format(base_url = self._api_url, version = self._packages_version_number):
|
||||
try:
|
||||
json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
|
||||
|
||||
print(json_data)
|
||||
# Add metadata to the manager:
|
||||
self._packages_metadata = json_data["data"]
|
||||
|
||||
|
||||
# Create packages model with all packages:
|
||||
if not self._packages_model:
|
||||
self._packages_model = CuraPackageModel()
|
||||
self._packages_metadata = json_data["data"]
|
||||
self._packages_model.setPackagesMetaData(self._packages_metadata)
|
||||
self.packagesMetadataChanged.emit()
|
||||
|
||||
# Create authors model with all authors:
|
||||
if not self._authors_model:
|
||||
self._authors_model = AuthorsModel()
|
||||
# In the future, this will be its own API call.
|
||||
self._authors_metadata = []
|
||||
for package in self._packages_metadata:
|
||||
package["author"]["type"] = package["package_type"]
|
||||
print(package["author"])
|
||||
if package["author"] not in self._authors_metadata:
|
||||
self._authors_metadata.append(package["author"])
|
||||
self._authors_model.setMetaData(self._authors_metadata)
|
||||
self.authorsMetadataChanged.emit()
|
||||
|
||||
|
||||
|
||||
except json.decoder.JSONDecodeError:
|
||||
Logger.log("w", "Received an invalid print job state message: Not valid JSON.")
|
||||
return
|
||||
|
@ -469,6 +494,7 @@ class Toolbox(QObject, Extension):
|
|||
CuraApplication.getInstance().windowClosed()
|
||||
|
||||
|
||||
|
||||
# Getter & Setter for self._view_category
|
||||
def setViewCategory(self, category = "plugins"):
|
||||
self._view_category = category
|
||||
|
@ -494,19 +520,13 @@ class Toolbox(QObject, Extension):
|
|||
return self._view_selection
|
||||
|
||||
|
||||
# Filtering
|
||||
@pyqtSlot(str)
|
||||
def filterPackagesByType(self, type):
|
||||
if not self._packages_model:
|
||||
return
|
||||
self._packages_model.setFilter({"type": type})
|
||||
self.filterChanged.emit()
|
||||
|
||||
# Filtering
|
||||
@pyqtSlot(str, str)
|
||||
def filterPackages(self, filterType, parameter):
|
||||
if not self._packages_model:
|
||||
return
|
||||
self._packages_model.setFilter({filterType: parameter})
|
||||
self._packages_model.setFilter({ filterType: parameter })
|
||||
self.filterChanged.emit()
|
||||
|
||||
@pyqtSlot()
|
||||
|
@ -515,3 +535,17 @@ class Toolbox(QObject, Extension):
|
|||
return
|
||||
self._packages_model.setFilter({})
|
||||
self.filterChanged.emit()
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def filterAuthors(self, filterType, parameter):
|
||||
if not self._authors_model:
|
||||
return
|
||||
self._authors_model.setFilter({ filterType: parameter })
|
||||
self.filterChanged.emit()
|
||||
|
||||
@pyqtSlot()
|
||||
def unfilterAuthors(self):
|
||||
if not self._authors_model:
|
||||
return
|
||||
self._authors_model.setFilter({})
|
||||
self.filterChanged.emit()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue