mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-08 23:46:22 -06:00
Merge branch 'master' into CURA-8112_Inform_the_user_about_their_subscription_limits
This commit is contained in:
commit
11d86b98ef
9 changed files with 244 additions and 51 deletions
|
@ -0,0 +1,62 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 378.13 348.13">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-2,.cls-6{fill:#c5dbfb;}
|
||||||
|
.cls-3,.cls-5,.cls-8{fill:#fff;}
|
||||||
|
.cls-3{stroke:#c5dbfb;}
|
||||||
|
.cls-10,.cls-3,.cls-4,.cls-6,.cls-7,.cls-8{stroke-miterlimit:10;stroke-width:2px;}
|
||||||
|
.cls-4,.cls-7{fill:#f3f8fe;}
|
||||||
|
.cls-4{stroke:#f3f8fe;}
|
||||||
|
.cls-6,.cls-7,.cls-8{stroke:#061884;}
|
||||||
|
.cls-10{fill:#196ef0;stroke:#196ef0;}
|
||||||
|
.cls-11{fill:#061884;}
|
||||||
|
</style>
|
||||||
|
<clipPath id="clip-path">
|
||||||
|
<circle fill="none" cx="155" cy="125" r="80" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<path class="cls-2" d="M43,17V3H83a2,2,0,0,1,2,2V17Z" />
|
||||||
|
<path class="cls-3" d="M3,1H40L56,17H87a2,2,0,0,1,2,2V67a2,2,0,0,1-2,2H3a2,2,0,0,1-2-2V3A2,2,0,0,1,3,1Z" />
|
||||||
|
<path class="cls-2" d="M153,17V3h40a2,2,0,0,1,2,2V17Z" />
|
||||||
|
<path class="cls-3" d="M113,1h37l16,16h31a2,2,0,0,1,2,2V67a2,2,0,0,1-2,2H113a2,2,0,0,1-2-2V3A2,2,0,0,1,113,1Z" />
|
||||||
|
<path class="cls-2" d="M263,17V3h40a2,2,0,0,1,2,2V17Z" />
|
||||||
|
<path class="cls-3" d="M223,1h37l16,16h31a2,2,0,0,1,2,2V67a2,2,0,0,1-2,2H223a2,2,0,0,1-2-2V3A2,2,0,0,1,223,1Z" />
|
||||||
|
<path class="cls-2" d="M43,107V93H83a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path class="cls-3" d="M3,91H40l16,16H87a2,2,0,0,1,2,2v48a2,2,0,0,1-2,2H3a2,2,0,0,1-2-2V93A2,2,0,0,1,3,91Z" />
|
||||||
|
<path class="cls-4" d="M153,107V93h40a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path class="cls-3" d="M113,91h37l16,16h31a2,2,0,0,1,2,2v48a2,2,0,0,1-2,2H113a2,2,0,0,1-2-2V93A2,2,0,0,1,113,91Z" />
|
||||||
|
<path class="cls-2" d="M263,107V93h40a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path class="cls-3" d="M223,91h37l16,16h31a2,2,0,0,1,2,2v48a2,2,0,0,1-2,2H223a2,2,0,0,1-2-2V93A2,2,0,0,1,223,91Z" />
|
||||||
|
<path class="cls-2" d="M43,197V183H83a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path class="cls-3" d="M3,181H40l16,16H87a2,2,0,0,1,2,2v48a2,2,0,0,1-2,2H3a2,2,0,0,1-2-2V183A2,2,0,0,1,3,181Z" />
|
||||||
|
<path class="cls-4" d="M153,197V183h40a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path class="cls-3" d="M113,181h37l16,16h31a2,2,0,0,1,2,2v48a2,2,0,0,1-2,2H113a2,2,0,0,1-2-2V183A2,2,0,0,1,113,181Z" />
|
||||||
|
<path class="cls-2" d="M263,197V183h40a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path class="cls-3" d="M223,181h37l16,16h31a2,2,0,0,1,2,2v48a2,2,0,0,1-2,2H223a2,2,0,0,1-2-2V183A2,2,0,0,1,223,181Z" />
|
||||||
|
<circle class="cls-5" cx="155" cy="125" r="100" />
|
||||||
|
<path class="cls-6" d="M351.12,322.62h20a10,10,0,0,1,10,10v7a0,0,0,0,1,0,0h-40a0,0,0,0,1,0,0v-7A10,10,0,0,1,351.12,322.62Z" transform="translate(850.61 309.91) rotate(135)" />
|
||||||
|
<rect class="cls-7" x="293.75" y="225.25" width="40" height="117" transform="translate(-108.74 304.96) rotate(-45)" />
|
||||||
|
<polyline class="cls-7" points="213.69 199.25 252.58 238.14 267.43 223.29 228.54 184.4" />
|
||||||
|
<circle class="cls-8" cx="155" cy="125" r="95" />
|
||||||
|
<circle class="cls-8" cx="155" cy="125" r="85" />
|
||||||
|
<path class="cls-6" d="M256.37,227.87h20a10,10,0,0,1,10,10v7a0,0,0,0,1,0,0h-40a0,0,0,0,1,0,0v-7a10,10,0,0,1,10-10Z" transform="translate(-89.12 257.58) rotate(-45)" />
|
||||||
|
<g clip-path="url(#clip-path)">
|
||||||
|
<path class="cls-10" d="M43,17V3H83a2,2,0,0,1,2,2V17Z" />
|
||||||
|
<path class="cls-8" d="M3,1H40L56,17H87a2,2,0,0,1,2,2V67a2,2,0,0,1-2,2H3a2,2,0,0,1-2-2V3A2,2,0,0,1,3,1Z" />
|
||||||
|
<path class="cls-10" d="M153,17V3h40a2,2,0,0,1,2,2V17Z" />
|
||||||
|
<path class="cls-8" d="M113,1h37l16,16h31a2,2,0,0,1,2,2V67a2,2,0,0,1-2,2H113a2,2,0,0,1-2-2V3A2,2,0,0,1,113,1Z" />
|
||||||
|
<path class="cls-10" d="M263,17V3h40a2,2,0,0,1,2,2V17Z" />
|
||||||
|
<path class="cls-8" d="M223,1h37l16,16h31a2,2,0,0,1,2,2V67a2,2,0,0,1-2,2H223a2,2,0,0,1-2-2V3A2,2,0,0,1,223,1Z" />
|
||||||
|
<path class="cls-10" d="M43,107V93H83a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path class="cls-8" d="M3,91H40l16,16H87a2,2,0,0,1,2,2v48a2,2,0,0,1-2,2H3a2,2,0,0,1-2-2V93A2,2,0,0,1,3,91Z" />
|
||||||
|
<path class="cls-10" d="M263,107V93h40a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path class="cls-8" d="M223,91h37l16,16h31a2,2,0,0,1,2,2v48a2,2,0,0,1-2,2H223a2,2,0,0,1-2-2V93A2,2,0,0,1,223,91Z" />
|
||||||
|
<path class="cls-10" d="M43,197V183H83a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path class="cls-8" d="M3,181H40l16,16H87a2,2,0,0,1,2,2v48a2,2,0,0,1-2,2H3a2,2,0,0,1-2-2V183A2,2,0,0,1,3,181Z" />
|
||||||
|
<path class="cls-10" d="M153,197V183h40a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path class="cls-8" d="M113,181h37l16,16h31a2,2,0,0,1,2,2v48a2,2,0,0,1-2,2H113a2,2,0,0,1-2-2V183A2,2,0,0,1,113,181Z" />
|
||||||
|
<path class="cls-10" d="M263,197V183h40a2,2,0,0,1,2,2v12Z" />
|
||||||
|
<path class="cls-8" d="M223,181h37l16,16h31a2,2,0,0,1,2,2v48a2,2,0,0,1-2,2H223a2,2,0,0,1-2-2V183A2,2,0,0,1,223,181Z" />
|
||||||
|
<path class="cls-11" d="M149.18,133.69v-3.48a14.36,14.36,0,0,1,1.74-7.25,20.17,20.17,0,0,1,6.4-6.17A25.87,25.87,0,0,0,163,112a7,7,0,0,0,1.48-4.34,4.13,4.13,0,0,0-1.93-3.62,9,9,0,0,0-5.14-1.3,24.94,24.94,0,0,0-7.34,1.16,45.2,45.2,0,0,0-7.78,3.31l-5.37-10.64a48.41,48.41,0,0,1,9.89-4.21,40.25,40.25,0,0,1,11.67-1.61q9.57,0,14.9,4.43a14.16,14.16,0,0,1,5.32,11.41,15.41,15.41,0,0,1-2.55,9,30.38,30.38,0,0,1-7.92,7.34A32.11,32.11,0,0,0,163,127.3a5.91,5.91,0,0,0-1.34,4v2.41Zm-1.61,15.12q0-4.38,2.46-6.12a10,10,0,0,1,5.95-1.75,9.69,9.69,0,0,1,5.77,1.75q2.46,1.74,2.46,6.12,0,4.22-2.46,6a9.42,9.42,0,0,1-5.77,1.84,9.69,9.69,0,0,1-5.95-1.84Q147.57,153,147.57,148.81Z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.1 KiB |
|
@ -1,10 +1,12 @@
|
||||||
// Copyright (C) 2021 Ultimaker B.V.
|
// Copyright (C) 2021 Ultimaker B.V.
|
||||||
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
import QtQuick.Controls 1.4 as OldControls // TableView doesn't exist in the QtQuick Controls 2.x in 5.10, so use the old one
|
import QtQuick.Controls 1.4 as OldControls // TableView doesn't exist in the QtQuick Controls 2.x in 5.10, so use the old one
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Controls.Styles 1.4
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
import UM 1.2 as UM
|
import UM 1.2 as UM
|
||||||
import Cura 1.6 as Cura
|
import Cura 1.6 as Cura
|
||||||
|
@ -29,24 +31,35 @@ Item
|
||||||
margins: UM.Theme.getSize("default_margin").width
|
margins: UM.Theme.getSize("default_margin").width
|
||||||
}
|
}
|
||||||
|
|
||||||
Label
|
RowLayout
|
||||||
{
|
{
|
||||||
id: selectProjectLabel
|
id: headerRow
|
||||||
|
|
||||||
text: "Select Project"
|
anchors
|
||||||
font: UM.Theme.getFont("medium")
|
{
|
||||||
color: UM.Theme.getColor("small_button_text")
|
top: parent.top
|
||||||
anchors.top: parent.top
|
left: parent.left
|
||||||
anchors.left: parent.left
|
right: parent.right
|
||||||
visible: projectListContainer.visible
|
}
|
||||||
|
height: childrenRect.height
|
||||||
|
spacing: UM.Theme.getSize("default_margin").width
|
||||||
|
|
||||||
|
Cura.TextField
|
||||||
|
{
|
||||||
|
id: searchBar
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: createNewProjectButton.height
|
||||||
|
|
||||||
|
onTextEdited: manager.projectFilter = text //Update the search filter when editing this text field.
|
||||||
|
|
||||||
|
leftIcon: UM.Theme.getIcon("Magnifier")
|
||||||
|
placeholderText: "Search"
|
||||||
}
|
}
|
||||||
|
|
||||||
Cura.SecondaryButton
|
Cura.SecondaryButton
|
||||||
{
|
{
|
||||||
id: createNewProjectButton
|
id: createNewProjectButton
|
||||||
|
|
||||||
anchors.verticalCenter: selectProjectLabel.verticalCenter
|
|
||||||
anchors.right: parent.right
|
|
||||||
text: "New Library project"
|
text: "New Library project"
|
||||||
visible: createNewProjectButtonVisible && manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
|
visible: createNewProjectButtonVisible && manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
|
||||||
|
|
||||||
|
@ -62,8 +75,6 @@ Item
|
||||||
{
|
{
|
||||||
id: upgradePlanButton
|
id: upgradePlanButton
|
||||||
|
|
||||||
anchors.verticalCenter: selectProjectLabel.verticalCenter
|
|
||||||
anchors.right: parent.right
|
|
||||||
text: "Upgrade plan"
|
text: "Upgrade plan"
|
||||||
iconSource: UM.Theme.getIcon("LinkExternal")
|
iconSource: UM.Theme.getIcon("LinkExternal")
|
||||||
visible: createNewProjectButtonVisible && !manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
|
visible: createNewProjectButtonVisible && !manager.userAccountCanCreateNewLibraryProject && (manager.retrievingProjectsStatus == DF.RetrievalStatus.Success || manager.retrievingProjectsStatus == DF.RetrievalStatus.Failed)
|
||||||
|
@ -72,6 +83,7 @@ Item
|
||||||
|
|
||||||
onClicked: Qt.openUrlExternally("https://ultimaker.com/software/enterprise-software")
|
onClicked: Qt.openUrlExternally("https://ultimaker.com/software/enterprise-software")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item
|
Item
|
||||||
{
|
{
|
||||||
|
@ -93,7 +105,7 @@ Item
|
||||||
{
|
{
|
||||||
id: digitalFactoryImage
|
id: digitalFactoryImage
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
source: "../images/digital_factory.svg"
|
source: searchBar.text === "" ? "../images/digital_factory.svg" : "../images/projects_not_found.svg"
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
width: parent.width - 2 * UM.Theme.getSize("thick_margin").width
|
width: parent.width - 2 * UM.Theme.getSize("thick_margin").width
|
||||||
sourceSize.width: width
|
sourceSize.width: width
|
||||||
|
@ -104,8 +116,9 @@ Item
|
||||||
{
|
{
|
||||||
id: noLibraryProjectsLabel
|
id: noLibraryProjectsLabel
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: "It appears that you don't have any projects in the Library yet."
|
text: searchBar.text === "" ? "It appears that you don't have any projects in the Library yet." : "No projects found that match the search query."
|
||||||
font: UM.Theme.getFont("medium")
|
font: UM.Theme.getFont("medium")
|
||||||
|
color: UM.Theme.getColor("text")
|
||||||
}
|
}
|
||||||
|
|
||||||
Cura.TertiaryButton
|
Cura.TertiaryButton
|
||||||
|
@ -114,6 +127,7 @@ Item
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: "Visit Digital Library"
|
text: "Visit Digital Library"
|
||||||
onClicked: Qt.openUrlExternally(CuraApplication.ultimakerDigitalFactoryUrl + "/app/library")
|
onClicked: Qt.openUrlExternally(CuraApplication.ultimakerDigitalFactoryUrl + "/app/library")
|
||||||
|
visible: searchBar.text === "" //Show the link to Digital Library when there are no projects in the user's Library.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +137,7 @@ Item
|
||||||
id: projectListContainer
|
id: projectListContainer
|
||||||
anchors
|
anchors
|
||||||
{
|
{
|
||||||
top: selectProjectLabel.bottom
|
top: headerRow.bottom
|
||||||
topMargin: UM.Theme.getSize("default_margin").height
|
topMargin: UM.Theme.getSize("default_margin").height
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
left: parent.left
|
left: parent.left
|
||||||
|
|
|
@ -132,7 +132,7 @@ class DigitalFactoryApiClient:
|
||||||
error_callback = failed,
|
error_callback = failed,
|
||||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||||
|
|
||||||
def getProjectsFirstPage(self, on_finished: Callable[[List[DigitalFactoryProjectResponse]], Any], failed: Callable) -> None:
|
def getProjectsFirstPage(self, search_filter: str, on_finished: Callable[[List[DigitalFactoryProjectResponse]], Any], failed: Callable) -> None:
|
||||||
"""
|
"""
|
||||||
Retrieves digital factory projects for the user that is currently logged in.
|
Retrieves digital factory projects for the user that is currently logged in.
|
||||||
|
|
||||||
|
@ -140,13 +140,18 @@ class DigitalFactoryApiClient:
|
||||||
according to the limit set in the pagination manager. If there is no projects pagination manager, this function
|
according to the limit set in the pagination manager. If there is no projects pagination manager, this function
|
||||||
leaves the project limit to the default set on the server side (999999).
|
leaves the project limit to the default set on the server side (999999).
|
||||||
|
|
||||||
|
:param search_filter: Text to filter the search results. If given an empty string, results are not filtered.
|
||||||
:param on_finished: The function to be called after the result is parsed.
|
:param on_finished: The function to be called after the result is parsed.
|
||||||
:param failed: The function to be called if the request fails.
|
:param failed: The function to be called if the request fails.
|
||||||
"""
|
"""
|
||||||
url = "{}/projects".format(self.CURA_API_ROOT)
|
url = f"{self.CURA_API_ROOT}/projects"
|
||||||
|
query_character = "?"
|
||||||
if self._projects_pagination_mgr:
|
if self._projects_pagination_mgr:
|
||||||
self._projects_pagination_mgr.reset() # reset to clear all the links and response metadata
|
self._projects_pagination_mgr.reset() # reset to clear all the links and response metadata
|
||||||
url += "?limit={}".format(self._projects_pagination_mgr.limit)
|
url += f"{query_character}limit={self._projects_pagination_mgr.limit}"
|
||||||
|
query_character = "&"
|
||||||
|
if search_filter != "":
|
||||||
|
url += f"{query_character}search={search_filter}"
|
||||||
|
|
||||||
self._http.get(url,
|
self._http.get(url,
|
||||||
scope = self._scope,
|
scope = self._scope,
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
# Copyright (c) 2021 Ultimaker B.V.
|
# Copyright (c) 2021 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
@ -8,7 +10,7 @@ from enum import IntEnum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, List, Dict, Any, cast
|
from typing import Optional, List, Dict, Any, cast
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSignal, QObject, pyqtSlot, pyqtProperty, Q_ENUMS, QUrl
|
from PyQt5.QtCore import pyqtSignal, QObject, pyqtSlot, pyqtProperty, Q_ENUMS, QTimer, QUrl
|
||||||
from PyQt5.QtNetwork import QNetworkReply
|
from PyQt5.QtNetwork import QNetworkReply
|
||||||
from PyQt5.QtQml import qmlRegisterType, qmlRegisterUncreatableType
|
from PyQt5.QtQml import qmlRegisterType, qmlRegisterUncreatableType
|
||||||
|
|
||||||
|
@ -119,6 +121,11 @@ class DigitalFactoryController(QObject):
|
||||||
self._project_model = DigitalFactoryProjectModel()
|
self._project_model = DigitalFactoryProjectModel()
|
||||||
self._selected_project_idx = -1
|
self._selected_project_idx = -1
|
||||||
self._project_creation_error_text = "Something went wrong while creating a new project. Please try again."
|
self._project_creation_error_text = "Something went wrong while creating a new project. Please try again."
|
||||||
|
self._project_filter = ""
|
||||||
|
self._project_filter_change_timer = QTimer()
|
||||||
|
self._project_filter_change_timer.setInterval(200)
|
||||||
|
self._project_filter_change_timer.setSingleShot(True)
|
||||||
|
self._project_filter_change_timer.timeout.connect(self._applyProjectFilter)
|
||||||
|
|
||||||
# Initialize the file model
|
# Initialize the file model
|
||||||
self._file_model = DigitalFactoryFileModel()
|
self._file_model = DigitalFactoryFileModel()
|
||||||
|
@ -178,7 +185,7 @@ class DigitalFactoryController(QObject):
|
||||||
if preselected_project_id:
|
if preselected_project_id:
|
||||||
self._api.getProject(preselected_project_id, on_finished = self.setProjectAsPreselected, failed = self._onGetProjectFailed)
|
self._api.getProject(preselected_project_id, on_finished = self.setProjectAsPreselected, failed = self._onGetProjectFailed)
|
||||||
else:
|
else:
|
||||||
self._api.getProjectsFirstPage(on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
self._api.getProjectsFirstPage(search_filter = self._project_filter, on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||||
|
|
||||||
def setProjectAsPreselected(self, df_project: DigitalFactoryProjectResponse) -> None:
|
def setProjectAsPreselected(self, df_project: DigitalFactoryProjectResponse) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -304,6 +311,38 @@ class DigitalFactoryController(QObject):
|
||||||
self._selected_file_indices = file_indices
|
self._selected_file_indices = file_indices
|
||||||
self.selectedFileIndicesChanged.emit(file_indices)
|
self.selectedFileIndicesChanged.emit(file_indices)
|
||||||
|
|
||||||
|
def setProjectFilter(self, new_filter: str) -> None:
|
||||||
|
"""
|
||||||
|
Called when the user wants to change the search filter for projects.
|
||||||
|
|
||||||
|
The filter is not immediately applied. There is some delay to allow the user to finish typing.
|
||||||
|
:param new_filter: The new filter that the user wants to apply.
|
||||||
|
"""
|
||||||
|
self._project_filter = new_filter
|
||||||
|
self._project_filter_change_timer.start()
|
||||||
|
|
||||||
|
"""
|
||||||
|
Signal to notify Qt that the applied filter has changed.
|
||||||
|
"""
|
||||||
|
projectFilterChanged = pyqtSignal()
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify = projectFilterChanged, fset = setProjectFilter)
|
||||||
|
def projectFilter(self) -> str:
|
||||||
|
"""
|
||||||
|
The current search filter being applied to the project list.
|
||||||
|
:return: The current search filter being applied to the project list.
|
||||||
|
"""
|
||||||
|
return self._project_filter
|
||||||
|
|
||||||
|
def _applyProjectFilter(self) -> None:
|
||||||
|
"""
|
||||||
|
Actually apply the current filter to search for projects with the user-defined search string.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.clear()
|
||||||
|
self.projectFilterChanged.emit()
|
||||||
|
self._api.getProjectsFirstPage(search_filter = self._project_filter, on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||||
|
|
||||||
@pyqtProperty(QObject, constant = True)
|
@pyqtProperty(QObject, constant = True)
|
||||||
def digitalFactoryProjectModel(self) -> "DigitalFactoryProjectModel":
|
def digitalFactoryProjectModel(self) -> "DigitalFactoryProjectModel":
|
||||||
return self._project_model
|
return self._project_model
|
||||||
|
@ -518,7 +557,7 @@ class DigitalFactoryController(QObject):
|
||||||
# false, we also need to clean it from the projects model
|
# false, we also need to clean it from the projects model
|
||||||
self._project_model.clearProjects()
|
self._project_model.clearProjects()
|
||||||
self.setSelectedProjectIndex(-1)
|
self.setSelectedProjectIndex(-1)
|
||||||
self._api.getProjectsFirstPage(on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
self._api.getProjectsFirstPage(search_filter = self._project_filter, on_finished = self._onGetProjectsFirstPageFinished, failed = self._onGetProjectsFailed)
|
||||||
self._api.checkUserCanCreateNewLibraryProject(callback = self.setCanCreateNewLibraryProject)
|
self._api.checkUserCanCreateNewLibraryProject(callback = self.setCanCreateNewLibraryProject)
|
||||||
self.setRetrievingProjectsStatus(RetrievalStatus.InProgress)
|
self.setRetrievingProjectsStatus(RetrievalStatus.InProgress)
|
||||||
self._has_preselected_project = new_has_preselected_project
|
self._has_preselected_project = new_has_preselected_project
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# Copyright (c) 2021 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -37,7 +40,7 @@ def test_getProjectsFirstPage(api_client):
|
||||||
failed_callback = MagicMock()
|
failed_callback = MagicMock()
|
||||||
|
|
||||||
# Call
|
# Call
|
||||||
api_client.getProjectsFirstPage(on_finished = finished_callback, failed = failed_callback)
|
api_client.getProjectsFirstPage(search_filter = "filter", on_finished = finished_callback, failed = failed_callback)
|
||||||
|
|
||||||
# Asserts
|
# Asserts
|
||||||
pagination_manager.reset.assert_called_once() # Should be called since we asked for new set of projects
|
pagination_manager.reset.assert_called_once() # Should be called since we asked for new set of projects
|
||||||
|
@ -45,16 +48,16 @@ def test_getProjectsFirstPage(api_client):
|
||||||
args = http_manager.get.call_args_list[0]
|
args = http_manager.get.call_args_list[0]
|
||||||
|
|
||||||
# Ensure that it's called with the right limit
|
# Ensure that it's called with the right limit
|
||||||
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=20"
|
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=20&search=filter"
|
||||||
|
|
||||||
# Change the limit & try again
|
# Change the limit & try again
|
||||||
http_manager.get.reset_mock()
|
http_manager.get.reset_mock()
|
||||||
pagination_manager.limit = 80
|
pagination_manager.limit = 80
|
||||||
api_client.getProjectsFirstPage(on_finished = finished_callback, failed = failed_callback)
|
api_client.getProjectsFirstPage(search_filter = "filter", on_finished = finished_callback, failed = failed_callback)
|
||||||
args = http_manager.get.call_args_list[0]
|
args = http_manager.get.call_args_list[0]
|
||||||
|
|
||||||
# Ensure that it's called with the right limit
|
# Ensure that it's called with the right limit
|
||||||
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=80"
|
assert args[0][0] == "https://api.ultimaker.com/cura/v1/projects?limit=80&search=filter"
|
||||||
|
|
||||||
|
|
||||||
def test_getMoreProjects_noNewProjects(api_client):
|
def test_getMoreProjects_noNewProjects(api_client):
|
||||||
|
|
35
resources/definitions/atom2.def.json
Normal file
35
resources/definitions/atom2.def.json
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "Atom 2",
|
||||||
|
"version": 2,
|
||||||
|
"inherits": "fdmprinter",
|
||||||
|
"metadata": {
|
||||||
|
"visible": true,
|
||||||
|
"author": "Victor (Yu Chieh) Lin",
|
||||||
|
"manufacturer": "Layer One",
|
||||||
|
"file_formats": "text/x-gcode",
|
||||||
|
"platform_offset": [0,0,0],
|
||||||
|
"machine_extruder_trains": { "0": "atom2_extruder_0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"overrides": {
|
||||||
|
"machine_name": { "default_value": "Atom 2" },
|
||||||
|
"machine_shape": { "default_value": "elliptic" },
|
||||||
|
"machine_width": { "default_value": 210 },
|
||||||
|
"machine_depth": { "default_value": 210 },
|
||||||
|
"machine_height": { "default_value": 320 },
|
||||||
|
"machine_extruder_count": { "default_value": 1 },
|
||||||
|
"machine_heated_bed": { "default_value": false },
|
||||||
|
"machine_center_is_zero": { "default_value": true },
|
||||||
|
|
||||||
|
"machine_start_gcode": { "default_value": "G21\nG90 \nM107\nG28\nG92 E0\nG1 F200 E3\nG92 E0" },
|
||||||
|
"machine_end_gcode": { "default_value": "M104 S0\nG28\nG91\nG1 E-6 F300\nM84\nG90" },
|
||||||
|
|
||||||
|
"layer_height": { "default_value": 0.2 },
|
||||||
|
"default_material_print_temperature": { "default_value": 210 },
|
||||||
|
"speed_print": { "default_value": 32 },
|
||||||
|
"optimize_wall_printing_order": { "value": "True" },
|
||||||
|
"infill_sparse_density": { "default_value": 10 },
|
||||||
|
"brim_width": { "default_value": 4 }
|
||||||
|
}
|
||||||
|
}
|
15
resources/extruders/atom2_extruder_0.def.json
Normal file
15
resources/extruders/atom2_extruder_0.def.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"name": "Extruder 1",
|
||||||
|
"inherits": "fdmextruder",
|
||||||
|
"metadata": {
|
||||||
|
"machine": "atom2",
|
||||||
|
"position": "0"
|
||||||
|
},
|
||||||
|
|
||||||
|
"overrides": {
|
||||||
|
"extruder_nr": { "default_value": 0 },
|
||||||
|
"machine_nozzle_size": { "default_value": 0.4 },
|
||||||
|
"material_diameter": { "default_value": 1.75 }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) 2019 Ultimaker B.V.
|
// Copyright (c) 2021 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.10
|
import QtQuick 2.10
|
||||||
|
@ -15,6 +15,8 @@ TextField
|
||||||
{
|
{
|
||||||
id: textField
|
id: textField
|
||||||
|
|
||||||
|
property alias leftIcon: iconLeft.source
|
||||||
|
|
||||||
UM.I18nCatalog { id: catalog; name: "cura" }
|
UM.I18nCatalog { id: catalog; name: "cura" }
|
||||||
|
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
@ -22,6 +24,7 @@ TextField
|
||||||
font: UM.Theme.getFont("default")
|
font: UM.Theme.getFont("default")
|
||||||
color: UM.Theme.getColor("text")
|
color: UM.Theme.getColor("text")
|
||||||
renderType: Text.NativeRendering
|
renderType: Text.NativeRendering
|
||||||
|
leftPadding: iconLeft.visible ? iconLeft.width + UM.Theme.getSize("default_margin").width * 2 : UM.Theme.getSize("thin_margin").width
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State
|
State
|
||||||
|
@ -52,7 +55,6 @@ TextField
|
||||||
|
|
||||||
color: UM.Theme.getColor("main_background")
|
color: UM.Theme.getColor("main_background")
|
||||||
|
|
||||||
anchors.margins: Math.round(UM.Theme.getSize("default_lining").width)
|
|
||||||
radius: UM.Theme.getSize("setting_control_radius").width
|
radius: UM.Theme.getSize("setting_control_radius").width
|
||||||
|
|
||||||
border.color:
|
border.color:
|
||||||
|
@ -67,5 +69,23 @@ TextField
|
||||||
}
|
}
|
||||||
return UM.Theme.getColor("setting_control_border")
|
return UM.Theme.getColor("setting_control_border")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Optional icon added on the left hand side.
|
||||||
|
UM.RecolorImage
|
||||||
|
{
|
||||||
|
id: iconLeft
|
||||||
|
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: UM.Theme.getSize("default_margin").width
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: source != ""
|
||||||
|
height: UM.Theme.getSize("small_button_icon").height
|
||||||
|
width: visible ? height : 0
|
||||||
|
color: textField.color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
.st0{fill:#231F20;}
|
.st0{fill:#231F20;}
|
||||||
</style>
|
</style>
|
||||||
<g id="Layer_1_1_">
|
<g id="Layer_1_1_">
|
||||||
<path class="st0" d="M15,26C8.9,26,4,21.1,4,15S8.9,4,15,4s11,4.9,11,11S21.1,26,15,26z M15,7c-4.4,0-8,3.6-8,8s3.6,8,8,8
|
<path class="st0" d="M15,26C8.9,26,4,21.1,4,15S8.9,4,15,4s11,4.9,11,11S21.1,26,15,26z M15,8c-3.8,0-7,3.2-7,7s3.2,7,7,7
|
||||||
s8-3.6,8-8S19.4,7,15,7z"/>
|
s7-3.1,7-7S18.9,8,15,8z"/>
|
||||||
</g>
|
</g>
|
||||||
<g id="Comments">
|
<g id="Comments">
|
||||||
</g>
|
</g>
|
||||||
|
|
Before Width: | Height: | Size: 603 B After Width: | Height: | Size: 603 B |
Loading…
Add table
Add a link
Reference in a new issue