mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-22 06:03:57 -06:00
Merge remote-tracking branch 'origin/4.0'
This commit is contained in:
commit
64150cc7cf
6 changed files with 70 additions and 36 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
import functools
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from cura.CuraApplication import CuraApplication
|
from cura.CuraApplication import CuraApplication
|
||||||
|
@ -6,7 +7,7 @@ from cura.CuraApplication import CuraApplication
|
||||||
#
|
#
|
||||||
# HACK:
|
# HACK:
|
||||||
#
|
#
|
||||||
# In project loading, when override the existing machine is selected, the stacks and containers that are correctly
|
# In project loading, when override the existing machine is selected, the stacks and containers that are currently
|
||||||
# active in the system will be overridden at runtime. Because the project loading is done in a different thread than
|
# active in the system will be overridden at runtime. Because the project loading is done in a different thread than
|
||||||
# the Qt thread, something else can kick in the middle of the process. One of them is the rendering. It will access
|
# the Qt thread, something else can kick in the middle of the process. One of them is the rendering. It will access
|
||||||
# the current stacks and container, which have not completely been updated yet, so Cura will crash in this case.
|
# the current stacks and container, which have not completely been updated yet, so Cura will crash in this case.
|
||||||
|
@ -22,7 +23,13 @@ class InterCallObject:
|
||||||
|
|
||||||
|
|
||||||
def call_on_qt_thread(func):
|
def call_on_qt_thread(func):
|
||||||
|
@functools.wraps(func)
|
||||||
def _call_on_qt_thread_wrapper(*args, **kwargs):
|
def _call_on_qt_thread_wrapper(*args, **kwargs):
|
||||||
|
# If the current thread is the main thread, which is the Qt thread, directly call the function.
|
||||||
|
current_thread = threading.current_thread()
|
||||||
|
if isinstance(current_thread, threading._MainThread):
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
def _handle_call(ico, *args, **kwargs):
|
def _handle_call(ico, *args, **kwargs):
|
||||||
ico.result = func(*args, **kwargs)
|
ico.result = func(*args, **kwargs)
|
||||||
ico.finish_event.set()
|
ico.finish_event.set()
|
||||||
|
|
|
@ -40,10 +40,13 @@ class DriveApiService:
|
||||||
if not access_token:
|
if not access_token:
|
||||||
Logger.log("w", "Could not get access token.")
|
Logger.log("w", "Could not get access token.")
|
||||||
return []
|
return []
|
||||||
|
try:
|
||||||
backup_list_request = requests.get(self.BACKUP_URL, headers = {
|
backup_list_request = requests.get(self.BACKUP_URL, headers = {
|
||||||
"Authorization": "Bearer {}".format(access_token)
|
"Authorization": "Bearer {}".format(access_token)
|
||||||
})
|
})
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
Logger.log("w", "Unable to connect with the server.")
|
||||||
|
return []
|
||||||
|
|
||||||
# HTTP status 300s mean redirection. 400s and 500s are errors.
|
# HTTP status 300s mean redirection. 400s and 500s are errors.
|
||||||
# Technically 300s are not errors, but the use case here relies on "requests" to handle redirects automatically.
|
# Technically 300s are not errors, but the use case here relies on "requests" to handle redirects automatically.
|
||||||
|
|
|
@ -61,8 +61,19 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
||||||
# \param cluster: The device response received from the cloud API.
|
# \param cluster: The device response received from the cloud API.
|
||||||
# \param parent: The optional parent of this output device.
|
# \param parent: The optional parent of this output device.
|
||||||
def __init__(self, api_client: CloudApiClient, cluster: CloudClusterResponse, parent: QObject = None) -> None:
|
def __init__(self, api_client: CloudApiClient, cluster: CloudClusterResponse, parent: QObject = None) -> None:
|
||||||
|
|
||||||
|
# The following properties are expected on each networked output device.
|
||||||
|
# Because the cloud connection does not off all of these, we manually construct this version here.
|
||||||
|
# An example of why this is needed is the selection of the compatible file type when exporting the tool path.
|
||||||
|
properties = {
|
||||||
|
b"address": b"",
|
||||||
|
b"name": cluster.host_name.encode() if cluster.host_name else b"",
|
||||||
|
b"firmware_version": cluster.host_version.encode() if cluster.host_version else b"",
|
||||||
|
b"printer_type": b""
|
||||||
|
}
|
||||||
|
|
||||||
super().__init__(device_id = cluster.cluster_id, address = "",
|
super().__init__(device_id = cluster.cluster_id, address = "",
|
||||||
connection_type = ConnectionType.CloudConnection, properties = {}, parent = parent)
|
connection_type = ConnectionType.CloudConnection, properties = properties, parent = parent)
|
||||||
self._api = api_client
|
self._api = api_client
|
||||||
self._cluster = cluster
|
self._cluster = cluster
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ class TestCloudOutputDevice(TestCase):
|
||||||
JOB_ID = "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE="
|
JOB_ID = "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE="
|
||||||
HOST_NAME = "ultimakersystem-ccbdd30044ec"
|
HOST_NAME = "ultimakersystem-ccbdd30044ec"
|
||||||
HOST_GUID = "e90ae0ac-1257-4403-91ee-a44c9b7e8050"
|
HOST_GUID = "e90ae0ac-1257-4403-91ee-a44c9b7e8050"
|
||||||
|
HOST_VERSION = "5.2.0"
|
||||||
|
|
||||||
STATUS_URL = "{}/connect/v1/clusters/{}/status".format(CuraCloudAPIRoot, CLUSTER_ID)
|
STATUS_URL = "{}/connect/v1/clusters/{}/status".format(CuraCloudAPIRoot, CLUSTER_ID)
|
||||||
PRINT_URL = "{}/connect/v1/clusters/{}/print/{}".format(CuraCloudAPIRoot, CLUSTER_ID, JOB_ID)
|
PRINT_URL = "{}/connect/v1/clusters/{}/print/{}".format(CuraCloudAPIRoot, CLUSTER_ID, JOB_ID)
|
||||||
|
@ -36,7 +37,7 @@ class TestCloudOutputDevice(TestCase):
|
||||||
patched_method.start()
|
patched_method.start()
|
||||||
|
|
||||||
self.cluster = CloudClusterResponse(self.CLUSTER_ID, self.HOST_GUID, self.HOST_NAME, is_online=True,
|
self.cluster = CloudClusterResponse(self.CLUSTER_ID, self.HOST_GUID, self.HOST_NAME, is_online=True,
|
||||||
status="active")
|
status="active", host_version=self.HOST_VERSION)
|
||||||
|
|
||||||
self.network = NetworkManagerMock()
|
self.network = NetworkManagerMock()
|
||||||
self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken")
|
self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken")
|
||||||
|
@ -56,6 +57,11 @@ class TestCloudOutputDevice(TestCase):
|
||||||
for patched_method in self.patches:
|
for patched_method in self.patches:
|
||||||
patched_method.stop()
|
patched_method.stop()
|
||||||
|
|
||||||
|
# We test for these in order to make sure the correct file type is selected depending on the firmware version.
|
||||||
|
def test_properties(self):
|
||||||
|
self.assertEqual(self.device.firmwareVersion, self.HOST_VERSION)
|
||||||
|
self.assertEqual(self.device.name, self.HOST_NAME)
|
||||||
|
|
||||||
def test_status(self):
|
def test_status(self):
|
||||||
self.device._update()
|
self.device._update()
|
||||||
self.network.flushReplies()
|
self.network.flushReplies()
|
||||||
|
@ -114,7 +120,7 @@ class TestCloudOutputDevice(TestCase):
|
||||||
|
|
||||||
def test_print_to_cloud(self):
|
def test_print_to_cloud(self):
|
||||||
active_machine_mock = self.app.getGlobalContainerStack.return_value
|
active_machine_mock = self.app.getGlobalContainerStack.return_value
|
||||||
active_machine_mock.getMetaDataEntry.side_effect = {"file_formats": "application/gzip"}.get
|
active_machine_mock.getMetaDataEntry.side_effect = {"file_formats": "application/x-ufp"}.get
|
||||||
|
|
||||||
request_upload_response = parseFixture("putJobUploadResponse")
|
request_upload_response = parseFixture("putJobUploadResponse")
|
||||||
request_print_response = parseFixture("postJobPrintResponse")
|
request_print_response = parseFixture("postJobPrintResponse")
|
||||||
|
@ -124,6 +130,10 @@ class TestCloudOutputDevice(TestCase):
|
||||||
|
|
||||||
file_handler = MagicMock()
|
file_handler = MagicMock()
|
||||||
file_handler.getSupportedFileTypesWrite.return_value = [{
|
file_handler.getSupportedFileTypesWrite.return_value = [{
|
||||||
|
"extension": "ufp",
|
||||||
|
"mime_type": "application/x-ufp",
|
||||||
|
"mode": 2
|
||||||
|
}, {
|
||||||
"extension": "gcode.gz",
|
"extension": "gcode.gz",
|
||||||
"mime_type": "application/gzip",
|
"mime_type": "application/gzip",
|
||||||
"mode": 2,
|
"mode": 2,
|
||||||
|
@ -137,10 +147,9 @@ class TestCloudOutputDevice(TestCase):
|
||||||
|
|
||||||
self.network.flushReplies()
|
self.network.flushReplies()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{"data": {"content_type": "application/gzip", "file_size": len(expected_mesh), "job_name": "FileName"}},
|
{"data": {"content_type": "application/x-ufp", "file_size": len(expected_mesh), "job_name": "FileName"}},
|
||||||
json.loads(self.network.getRequestBody("PUT", self.REQUEST_UPLOAD_URL).decode())
|
json.loads(self.network.getRequestBody("PUT", self.REQUEST_UPLOAD_URL).decode())
|
||||||
)
|
)
|
||||||
self.assertEqual(expected_mesh,
|
self.assertEqual(expected_mesh,
|
||||||
self.network.getRequestBody("PUT", request_upload_response["data"]["upload_url"]))
|
self.network.getRequestBody("PUT", request_upload_response["data"]["upload_url"]))
|
||||||
|
|
||||||
self.assertIsNone(self.network.getRequestBody("POST", self.PRINT_URL))
|
self.assertIsNone(self.network.getRequestBody("POST", self.PRINT_URL))
|
||||||
|
|
|
@ -21,26 +21,29 @@ UM.Dialog
|
||||||
|
|
||||||
Rectangle
|
Rectangle
|
||||||
{
|
{
|
||||||
|
id: header
|
||||||
width: parent.width + 2 * margin // margin from Dialog.qml
|
width: parent.width + 2 * margin // margin from Dialog.qml
|
||||||
height: version.y + version.height + margin
|
height: childrenRect.height + topPadding
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: - margin
|
anchors.topMargin: -margin
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
property real topPadding: UM.Theme.getSize("wide_margin").height
|
||||||
|
|
||||||
color: UM.Theme.getColor("main_window_header_background")
|
color: UM.Theme.getColor("main_window_header_background")
|
||||||
}
|
|
||||||
|
|
||||||
Image
|
Image
|
||||||
{
|
{
|
||||||
id: logo
|
id: logo
|
||||||
width: (base.minimumWidth * 0.85) | 0
|
width: (base.minimumWidth * 0.85) | 0
|
||||||
|
height: (width * (UM.Theme.getSize("logo").height / UM.Theme.getSize("logo").width)) | 0
|
||||||
source: UM.Theme.getImage("logo")
|
source: UM.Theme.getImage("logo")
|
||||||
sourceSize.width: width
|
sourceSize.width: width
|
||||||
sourceSize.height: height
|
sourceSize.height: height
|
||||||
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: ((base.minimumWidth - width) / 2) | 0
|
anchors.topMargin: parent.topPadding
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
UM.I18nCatalog{id: catalog; name: "cura"}
|
UM.I18nCatalog{id: catalog; name: "cura"}
|
||||||
|
@ -57,6 +60,7 @@ UM.Dialog
|
||||||
anchors.top: logo.bottom
|
anchors.top: logo.bottom
|
||||||
anchors.topMargin: (UM.Theme.getSize("default_margin").height / 2) | 0
|
anchors.topMargin: (UM.Theme.getSize("default_margin").height / 2) | 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Label
|
Label
|
||||||
{
|
{
|
||||||
|
@ -67,7 +71,7 @@ UM.Dialog
|
||||||
text: catalog.i18nc("@label","End-to-end solution for fused filament 3D printing.")
|
text: catalog.i18nc("@label","End-to-end solution for fused filament 3D printing.")
|
||||||
font: UM.Theme.getFont("system")
|
font: UM.Theme.getFont("system")
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
anchors.top: version.bottom
|
anchors.top: header.bottom
|
||||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -473,7 +473,7 @@
|
||||||
"default_lining": [0.08, 0.08],
|
"default_lining": [0.08, 0.08],
|
||||||
|
|
||||||
"default_arrow": [0.8, 0.8],
|
"default_arrow": [0.8, 0.8],
|
||||||
"logo": [8, 2.4],
|
"logo": [8, 1.75],
|
||||||
|
|
||||||
"wide_margin": [2.0, 2.0],
|
"wide_margin": [2.0, 2.0],
|
||||||
"thick_margin": [1.71, 1.43],
|
"thick_margin": [1.71, 1.43],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue