Added author and material views

This commit is contained in:
Ian Paschal 2018-04-09 15:55:56 +02:00
parent 91001455ad
commit 817b724994
10 changed files with 172 additions and 43 deletions

View file

@ -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

View file

@ -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
}
}
}

View file

@ -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

View file

@ -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

View file

@ -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)
}
}
}

View file

@ -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")

View file

@ -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"
}

View 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()

View file

@ -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"]

View file

@ -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()