mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-21 13:47:51 -06:00
Adds rough first version of rating stars
It's not fully polished just yet CURA-6013
This commit is contained in:
parent
ab8a2a9b2b
commit
f4220da550
8 changed files with 180 additions and 17 deletions
|
@ -44,7 +44,7 @@ class Account(QObject):
|
||||||
OAUTH_SERVER_URL= self._oauth_root,
|
OAUTH_SERVER_URL= self._oauth_root,
|
||||||
CALLBACK_PORT=self._callback_port,
|
CALLBACK_PORT=self._callback_port,
|
||||||
CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port),
|
CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port),
|
||||||
CLIENT_ID="um---------------ultimaker_cura_drive_plugin",
|
CLIENT_ID="um----------------------------ultimaker_cura",
|
||||||
CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write",
|
CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write",
|
||||||
AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data",
|
AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data",
|
||||||
AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root),
|
AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root),
|
||||||
|
|
101
plugins/Toolbox/resources/qml/RatingWidget.qml
Normal file
101
plugins/Toolbox/resources/qml/RatingWidget.qml
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
import QtQuick 2.2
|
||||||
|
import QtQuick.Controls 2.0
|
||||||
|
import UM 1.0 as UM
|
||||||
|
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: ratingWidget
|
||||||
|
|
||||||
|
property real rating: 0
|
||||||
|
property int indexHovered: -1
|
||||||
|
property string packageId: ""
|
||||||
|
property int numRatings: 0
|
||||||
|
property int userRating: 0
|
||||||
|
width: contentRow.width
|
||||||
|
height: contentRow.height
|
||||||
|
MouseArea
|
||||||
|
{
|
||||||
|
id: mouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: ratingWidget.enabled
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
onExited:
|
||||||
|
{
|
||||||
|
ratingWidget.indexHovered = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
Row
|
||||||
|
{
|
||||||
|
id: contentRow
|
||||||
|
height: childrenRect.height
|
||||||
|
Repeater
|
||||||
|
{
|
||||||
|
model: 5 // We need to get 5 stars
|
||||||
|
Button
|
||||||
|
{
|
||||||
|
id: control
|
||||||
|
hoverEnabled: true
|
||||||
|
onHoveredChanged:
|
||||||
|
{
|
||||||
|
if(hovered)
|
||||||
|
{
|
||||||
|
indexHovered = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property bool isStarFilled:
|
||||||
|
{
|
||||||
|
// If the entire widget is hovered, override the actual rating.
|
||||||
|
if(ratingWidget.indexHovered >= 0)
|
||||||
|
{
|
||||||
|
return indexHovered >= index
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ratingWidget.userRating > 0)
|
||||||
|
{
|
||||||
|
return userRating >= index +1
|
||||||
|
}
|
||||||
|
|
||||||
|
return rating >= index + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {}
|
||||||
|
height: UM.Theme.getSize("rating_star").height
|
||||||
|
width: UM.Theme.getSize("rating_star").width
|
||||||
|
background: UM.RecolorImage
|
||||||
|
{
|
||||||
|
source: UM.Theme.getIcon(control.isStarFilled ? "star_filled" : "star_empty")
|
||||||
|
|
||||||
|
// Unfilled stars should always have the default color. Only filled stars should change on hover
|
||||||
|
color:
|
||||||
|
{
|
||||||
|
if(!enabled)
|
||||||
|
{
|
||||||
|
return "#5a5a5a"
|
||||||
|
}
|
||||||
|
if((ratingWidget.indexHovered >= 0 || ratingWidget.userRating > 0) && isStarFilled)
|
||||||
|
{
|
||||||
|
return UM.Theme.getColor("primary")
|
||||||
|
}
|
||||||
|
return "#5a5a5a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClicked:
|
||||||
|
{
|
||||||
|
if(userRating == 0)
|
||||||
|
{
|
||||||
|
//User didn't vote yet, locally fake it
|
||||||
|
numRatings += 1
|
||||||
|
}
|
||||||
|
userRating = index + 1 // Fake local data
|
||||||
|
toolbox.ratePackage(ratingWidget.packageId, index + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
text: "(" + numRatings + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -84,6 +84,16 @@ Item
|
||||||
color: UM.Theme.getColor("text_medium")
|
color: UM.Theme.getColor("text_medium")
|
||||||
font: UM.Theme.getFont("default")
|
font: UM.Theme.getFont("default")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RatingWidget
|
||||||
|
{
|
||||||
|
visible: model.type == "plugin"
|
||||||
|
packageId: model.id
|
||||||
|
rating: model.average_rating != undefined ? model.average_rating : 0
|
||||||
|
numRatings: model.num_ratings != undefined ? model.num_ratings : 0
|
||||||
|
userRating: model.user_rating
|
||||||
|
enabled: installedPackages != 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MouseArea
|
MouseArea
|
||||||
|
|
|
@ -41,6 +41,9 @@ class PackagesModel(ListModel):
|
||||||
self.addRoleName(Qt.UserRole + 20, "links")
|
self.addRoleName(Qt.UserRole + 20, "links")
|
||||||
self.addRoleName(Qt.UserRole + 21, "website")
|
self.addRoleName(Qt.UserRole + 21, "website")
|
||||||
self.addRoleName(Qt.UserRole + 22, "login_required")
|
self.addRoleName(Qt.UserRole + 22, "login_required")
|
||||||
|
self.addRoleName(Qt.UserRole + 23, "average_rating")
|
||||||
|
self.addRoleName(Qt.UserRole + 24, "num_ratings")
|
||||||
|
self.addRoleName(Qt.UserRole + 25, "user_rating")
|
||||||
|
|
||||||
# List of filters for queries. The result is the union of the each list of results.
|
# List of filters for queries. The result is the union of the each list of results.
|
||||||
self._filter = {} # type: Dict[str, str]
|
self._filter = {} # type: Dict[str, str]
|
||||||
|
@ -101,7 +104,10 @@ class PackagesModel(ListModel):
|
||||||
"tags": package["tags"] if "tags" in package else [],
|
"tags": package["tags"] if "tags" in package else [],
|
||||||
"links": links_dict,
|
"links": links_dict,
|
||||||
"website": package["website"] if "website" in package else None,
|
"website": package["website"] if "website" in package else None,
|
||||||
"login_required": "login-required" in package.get("tags", [])
|
"login_required": "login-required" in package.get("tags", []),
|
||||||
|
"average_rating": package.get("rating", {}).get("average", 0),
|
||||||
|
"num_ratings": package.get("rating", {}).get("count", 0),
|
||||||
|
"user_rating": package.get("rating", {}).get("user", 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
# Filter on all the key-word arguments.
|
# Filter on all the key-word arguments.
|
||||||
|
|
|
@ -22,7 +22,8 @@ from cura.CuraApplication import CuraApplication
|
||||||
|
|
||||||
from .AuthorsModel import AuthorsModel
|
from .AuthorsModel import AuthorsModel
|
||||||
from .PackagesModel import PackagesModel
|
from .PackagesModel import PackagesModel
|
||||||
|
from cura.CuraVersion import CuraVersion
|
||||||
|
from cura.API import CuraAPI
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from cura.Settings.GlobalStack import GlobalStack
|
from cura.Settings.GlobalStack import GlobalStack
|
||||||
|
|
||||||
|
@ -50,17 +51,10 @@ class Toolbox(QObject, Extension):
|
||||||
self._download_progress = 0 # type: float
|
self._download_progress = 0 # type: float
|
||||||
self._is_downloading = False # type: bool
|
self._is_downloading = False # type: bool
|
||||||
self._network_manager = None # type: Optional[QNetworkAccessManager]
|
self._network_manager = None # type: Optional[QNetworkAccessManager]
|
||||||
self._request_header = [
|
self._request_headers = [] # type: List[Tuple(bytes, bytes)]
|
||||||
b"User-Agent",
|
self._updateRequestHeader()
|
||||||
str.encode(
|
|
||||||
"%s/%s (%s %s)" % (
|
|
||||||
self._application.getApplicationName(),
|
|
||||||
self._application.getVersion(),
|
|
||||||
platform.system(),
|
|
||||||
platform.machine(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
self._request_urls = {} # type: Dict[str, QUrl]
|
self._request_urls = {} # type: Dict[str, QUrl]
|
||||||
self._to_update = [] # type: List[str] # Package_ids that are waiting to be updated
|
self._to_update = [] # type: List[str] # Package_ids that are waiting to be updated
|
||||||
self._old_plugin_ids = set() # type: Set[str]
|
self._old_plugin_ids = set() # type: Set[str]
|
||||||
|
@ -115,6 +109,7 @@ class Toolbox(QObject, Extension):
|
||||||
self._restart_dialog_message = "" # type: str
|
self._restart_dialog_message = "" # type: str
|
||||||
|
|
||||||
self._application.initializationFinished.connect(self._onAppInitialized)
|
self._application.initializationFinished.connect(self._onAppInitialized)
|
||||||
|
self._application.getCuraAPI().account.loginStateChanged.connect(self._updateRequestHeader)
|
||||||
|
|
||||||
# Signals:
|
# Signals:
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
|
@ -134,12 +129,38 @@ class Toolbox(QObject, Extension):
|
||||||
showLicenseDialog = pyqtSignal()
|
showLicenseDialog = pyqtSignal()
|
||||||
uninstallVariablesChanged = pyqtSignal()
|
uninstallVariablesChanged = pyqtSignal()
|
||||||
|
|
||||||
|
def _updateRequestHeader(self):
|
||||||
|
self._request_headers = [
|
||||||
|
(b"User-Agent",
|
||||||
|
str.encode(
|
||||||
|
"%s/%s (%s %s)" % (
|
||||||
|
self._application.getApplicationName(),
|
||||||
|
self._application.getVersion(),
|
||||||
|
platform.system(),
|
||||||
|
platform.machine(),
|
||||||
|
)
|
||||||
|
))
|
||||||
|
]
|
||||||
|
access_token = self._application.getCuraAPI().account.accessToken
|
||||||
|
if access_token:
|
||||||
|
self._request_headers.append((b"Authorization", "Bearer {}".format(access_token).encode()))
|
||||||
|
|
||||||
def _resetUninstallVariables(self) -> None:
|
def _resetUninstallVariables(self) -> None:
|
||||||
self._package_id_to_uninstall = None # type: Optional[str]
|
self._package_id_to_uninstall = None # type: Optional[str]
|
||||||
self._package_name_to_uninstall = ""
|
self._package_name_to_uninstall = ""
|
||||||
self._package_used_materials = [] # type: List[Tuple[GlobalStack, str, str]]
|
self._package_used_materials = [] # type: List[Tuple[GlobalStack, str, str]]
|
||||||
self._package_used_qualities = [] # type: List[Tuple[GlobalStack, str, str]]
|
self._package_used_qualities = [] # type: List[Tuple[GlobalStack, str, str]]
|
||||||
|
|
||||||
|
@pyqtSlot(str, int)
|
||||||
|
def ratePackage(self, package_id: str, rating: int) -> None:
|
||||||
|
url = QUrl("{base_url}/packages/{package_id}/ratings".format(base_url=self._api_url, package_id = package_id))
|
||||||
|
|
||||||
|
self._rate_request = QNetworkRequest(url)
|
||||||
|
for header_name, header_value in self._request_headers:
|
||||||
|
cast(QNetworkRequest, self._rate_request).setRawHeader(header_name, header_value)
|
||||||
|
data = "{\"data\": {\"cura_version\": \"%s\", \"rating\": %i}}" % (Version(CuraVersion), rating)
|
||||||
|
self._rate_reply = cast(QNetworkAccessManager, self._network_manager).put(self._rate_request, data.encode())
|
||||||
|
|
||||||
@pyqtSlot(result = str)
|
@pyqtSlot(result = str)
|
||||||
def getLicenseDialogPluginName(self) -> str:
|
def getLicenseDialogPluginName(self) -> str:
|
||||||
return self._license_dialog_plugin_name
|
return self._license_dialog_plugin_name
|
||||||
|
@ -563,7 +584,8 @@ class Toolbox(QObject, Extension):
|
||||||
def _makeRequestByType(self, request_type: str) -> None:
|
def _makeRequestByType(self, request_type: str) -> None:
|
||||||
Logger.log("i", "Requesting %s metadata from server.", request_type)
|
Logger.log("i", "Requesting %s metadata from server.", request_type)
|
||||||
request = QNetworkRequest(self._request_urls[request_type])
|
request = QNetworkRequest(self._request_urls[request_type])
|
||||||
request.setRawHeader(*self._request_header)
|
for header_name, header_value in self._request_headers:
|
||||||
|
request.setRawHeader(header_name, header_value)
|
||||||
if self._network_manager:
|
if self._network_manager:
|
||||||
self._network_manager.get(request)
|
self._network_manager.get(request)
|
||||||
|
|
||||||
|
@ -578,7 +600,8 @@ class Toolbox(QObject, Extension):
|
||||||
if hasattr(QNetworkRequest, "RedirectPolicyAttribute"):
|
if hasattr(QNetworkRequest, "RedirectPolicyAttribute"):
|
||||||
# Patch for Qt 5.9+
|
# Patch for Qt 5.9+
|
||||||
cast(QNetworkRequest, self._download_request).setAttribute(QNetworkRequest.RedirectPolicyAttribute, True)
|
cast(QNetworkRequest, self._download_request).setAttribute(QNetworkRequest.RedirectPolicyAttribute, True)
|
||||||
cast(QNetworkRequest, self._download_request).setRawHeader(*self._request_header)
|
for header_name, header_value in self._request_headers:
|
||||||
|
cast(QNetworkRequest, self._download_request).setRawHeader(header_name, header_value)
|
||||||
self._download_reply = cast(QNetworkAccessManager, self._network_manager).get(self._download_request)
|
self._download_reply = cast(QNetworkAccessManager, self._network_manager).get(self._download_request)
|
||||||
self.setDownloadProgress(0)
|
self.setDownloadProgress(0)
|
||||||
self.setIsDownloading(True)
|
self.setIsDownloading(True)
|
||||||
|
@ -660,7 +683,7 @@ class Toolbox(QObject, Extension):
|
||||||
else:
|
else:
|
||||||
self.setViewPage("errored")
|
self.setViewPage("errored")
|
||||||
self.resetDownload()
|
self.resetDownload()
|
||||||
else:
|
elif reply.operation() == QNetworkAccessManager.PutOperation:
|
||||||
# Ignore any operation that is not a get operation
|
# Ignore any operation that is not a get operation
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
11
resources/themes/cura-light/icons/star_empty.svg
Normal file
11
resources/themes/cura-light/icons/star_empty.svg
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="14px" height="13px" viewBox="0 0 14 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Star Copy 8</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<g id="Toolbox-VIP-material" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Marketplace-hover-" transform="translate(-140.000000, -457.000000)" stroke="#666666">
|
||||||
|
<path d="M150.450431,468.749111 L149.791458,464.907 L152.582915,462.186001 L148.725216,461.625444 L147,458.129776 L145.274784,461.625444 L141.417085,462.186001 L144.208542,464.907 L143.549569,468.749111 L147,466.935112 L150.450431,468.749111 Z" id="Star-Copy-8"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 844 B |
11
resources/themes/cura-light/icons/star_filled.svg
Normal file
11
resources/themes/cura-light/icons/star_filled.svg
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="14px" height="13px" viewBox="0 0 14 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
|
||||||
|
<title>Star Copy 7</title>
|
||||||
|
<desc>Created with Sketch.</desc>
|
||||||
|
<g id="Toolbox-VIP-material" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="Marketplace-hover-" transform="translate(-127.000000, -457.000000)" fill="#666666" stroke="#666666">
|
||||||
|
<path d="M137.450431,468.749111 L136.791458,464.907 L139.582915,462.186001 L135.725216,461.625444 L134,458.129776 L132.274784,461.625444 L128.417085,462.186001 L131.208542,464.907 L130.549569,468.749111 L134,466.935112 L137.450431,468.749111 Z" id="Star-Copy-7"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 859 B |
|
@ -394,6 +394,7 @@
|
||||||
"section": [0.0, 2.2],
|
"section": [0.0, 2.2],
|
||||||
"section_icon": [1.6, 1.6],
|
"section_icon": [1.6, 1.6],
|
||||||
"section_icon_column": [2.8, 0.0],
|
"section_icon_column": [2.8, 0.0],
|
||||||
|
"rating_star": [1.0, 1.0],
|
||||||
|
|
||||||
"setting": [25.0, 1.8],
|
"setting": [25.0, 1.8],
|
||||||
"setting_control": [10.0, 2.0],
|
"setting_control": [10.0, 2.0],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue