mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 14:37:29 -06:00
Merge remote-tracking branch 'origin/5.7' into CURA-11772_fix_crash_on_open
This commit is contained in:
commit
e85e50cce4
59 changed files with 68925 additions and 71512 deletions
|
@ -24,29 +24,34 @@ UM.Dialog
|
|||
{
|
||||
height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").height
|
||||
color: UM.Theme.getColor("main_background")
|
||||
|
||||
UM.Label
|
||||
ColumnLayout
|
||||
{
|
||||
id: titleLabel
|
||||
text: manager.isUcp? catalog.i18nc("@action:title Don't translate 'Universal Cura Project'", "Summary - Open Universal Cura Project (UCP)"): catalog.i18nc("@action:title", "Summary - Cura Project")
|
||||
font: UM.Theme.getFont("large")
|
||||
id: headerColumn
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").height
|
||||
}
|
||||
|
||||
Cura.TertiaryButton
|
||||
{
|
||||
id: learnMoreButton
|
||||
visible: manager.isUcp
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").height
|
||||
text: catalog.i18nc("@button", "Learn more")
|
||||
iconSource: UM.Theme.getIcon("LinkExternal")
|
||||
isIconOnRightSide: true
|
||||
onClicked: Qt.openUrlExternally("https://support.ultimaker.com/s/article/000002979")
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.rightMargin: anchors.leftMargin
|
||||
RowLayout
|
||||
{
|
||||
UM.Label
|
||||
{
|
||||
id: titleLabel
|
||||
text: manager.isUcp? catalog.i18nc("@action:title Don't translate 'Universal Cura Project'", "Summary - Open Universal Cura Project (UCP)"): catalog.i18nc("@action:title", "Summary - Cura Project")
|
||||
font: UM.Theme.getFont("large")
|
||||
}
|
||||
Cura.TertiaryButton
|
||||
{
|
||||
id: learnMoreButton
|
||||
visible: manager.isUcp
|
||||
text: catalog.i18nc("@button", "Learn more")
|
||||
iconSource: UM.Theme.getIcon("LinkExternal")
|
||||
isIconOnRightSide: true
|
||||
onClicked: Qt.openUrlExternally("https://support.ultimaker.com/s/article/000002979")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,13 +6,14 @@ from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal
|
|||
|
||||
class SettingExport(QObject):
|
||||
|
||||
def __init__(self, id, name, value, selectable):
|
||||
def __init__(self, id, name, value, selectable, show):
|
||||
super().__init__()
|
||||
self.id = id
|
||||
self._name = name
|
||||
self._value = value
|
||||
self._selected = selectable
|
||||
self._selectable = selectable
|
||||
self._show_in_menu = show
|
||||
|
||||
@pyqtProperty(str, constant=True)
|
||||
def name(self):
|
||||
|
@ -36,3 +37,7 @@ class SettingExport(QObject):
|
|||
@pyqtProperty(bool, constant=True)
|
||||
def selectable(self):
|
||||
return self._selectable
|
||||
|
||||
@pyqtProperty(bool, constant=True)
|
||||
def isVisible(self):
|
||||
return self._show_in_menu
|
||||
|
|
|
@ -23,6 +23,7 @@ class SettingsExportGroup(QObject):
|
|||
self._category_details = category_details
|
||||
self._extruder_index = extruder_index
|
||||
self._extruder_color = extruder_color
|
||||
self._visible_settings = []
|
||||
|
||||
@pyqtProperty(str, constant=True)
|
||||
def name(self):
|
||||
|
@ -32,6 +33,12 @@ class SettingsExportGroup(QObject):
|
|||
def settings(self):
|
||||
return self._settings
|
||||
|
||||
@pyqtProperty(list, constant=True)
|
||||
def visibleSettings(self):
|
||||
if self._visible_settings == []:
|
||||
self._visible_settings = list(filter(lambda item : item.isVisible, self._settings))
|
||||
return self._visible_settings
|
||||
|
||||
@pyqtProperty(int, constant=True)
|
||||
def category(self):
|
||||
return self._category
|
||||
|
|
|
@ -117,6 +117,7 @@ class SettingsExportModel(QObject):
|
|||
is_exportable = any(key in SettingsExportModel.PER_MODEL_EXPORTABLE_SETTINGS_KEYS for key in user_keys)
|
||||
|
||||
for setting_to_export in user_keys:
|
||||
show_in_menu = setting_to_export not in SettingsExportModel.PER_MODEL_EXPORTABLE_SETTINGS_KEYS
|
||||
label = settings_stack.getProperty(setting_to_export, "label")
|
||||
value = settings_stack.getProperty(setting_to_export, "value")
|
||||
unit = settings_stack.getProperty(setting_to_export, "unit")
|
||||
|
@ -130,6 +131,7 @@ class SettingsExportModel(QObject):
|
|||
settings_export.append(SettingExport(setting_to_export,
|
||||
label,
|
||||
value,
|
||||
is_exportable or setting_to_export in exportable_settings))
|
||||
is_exportable or setting_to_export in exportable_settings,
|
||||
show_in_menu))
|
||||
|
||||
return settings_export
|
||||
|
|
|
@ -71,8 +71,8 @@ ColumnLayout
|
|||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: contentHeight
|
||||
spacing: 0
|
||||
model: modelData.settings
|
||||
visible: modelData.settings.length > 0
|
||||
model: modelData.visibleSettings
|
||||
visible: modelData.visibleSettings.length > 0
|
||||
|
||||
delegate: SettingSelection { }
|
||||
}
|
||||
|
@ -80,8 +80,7 @@ ColumnLayout
|
|||
UM.Label
|
||||
{
|
||||
UM.I18nCatalog { id: catalog; name: "cura" }
|
||||
|
||||
text: catalog.i18nc("@label", "No specific value has been set")
|
||||
visible: modelData.settings.length === 0
|
||||
visible: modelData.visibleSettings.length === 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import urllib.parse
|
|||
from json import JSONDecodeError
|
||||
from time import time
|
||||
from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any, cast
|
||||
from pathlib import Path
|
||||
|
||||
from PyQt6.QtCore import QUrl
|
||||
from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply
|
||||
|
@ -38,14 +39,17 @@ class CloudApiClient:
|
|||
|
||||
# The cloud URL to use for this remote cluster.
|
||||
ROOT_PATH = UltimakerCloudConstants.CuraCloudAPIRoot
|
||||
CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH)
|
||||
CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH)
|
||||
CLUSTER_API_ROOT = f"{ROOT_PATH}/connect/v1"
|
||||
CURA_API_ROOT = f"{ROOT_PATH}/cura/v1"
|
||||
|
||||
DEFAULT_REQUEST_TIMEOUT = 10 # seconds
|
||||
|
||||
# In order to avoid garbage collection we keep the callbacks in this list.
|
||||
_anti_gc_callbacks = [] # type: List[Callable[[Any], None]]
|
||||
|
||||
# Custom machine definition ID to cloud cluster name mapping
|
||||
_machine_id_to_name: Dict[str, str] = None
|
||||
|
||||
def __init__(self, app: CuraApplication, on_error: Callable[[List[CloudError]], None]) -> None:
|
||||
"""Initializes a new cloud API client.
|
||||
|
||||
|
@ -73,10 +77,10 @@ class CloudApiClient:
|
|||
|
||||
url = f"{self.CLUSTER_API_ROOT}/clusters?status=active"
|
||||
self._http.get(url,
|
||||
scope = self._scope,
|
||||
callback = self._parseCallback(on_finished, CloudClusterResponse, failed),
|
||||
error_callback = failed,
|
||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||
scope=self._scope,
|
||||
callback=self._parseCallback(on_finished, CloudClusterResponse, failed),
|
||||
error_callback=failed,
|
||||
timeout=self.DEFAULT_REQUEST_TIMEOUT)
|
||||
|
||||
def getClustersByMachineType(self, machine_type, on_finished: Callable[[List[CloudClusterWithConfigResponse]], Any], failed: Callable) -> None:
|
||||
# HACK: There is something weird going on with the API, as it reports printer types in formats like
|
||||
|
@ -84,13 +88,9 @@ class CloudApiClient:
|
|||
# conversion!
|
||||
# API points to "MakerBot Method" for a makerbot printertypes which we already changed to allign with other printer_type
|
||||
|
||||
method_x = {
|
||||
"ultimaker_method":"MakerBot Method",
|
||||
"ultimaker_methodx":"MakerBot Method X",
|
||||
"ultimaker_methodxl":"MakerBot Method XL"
|
||||
}
|
||||
if machine_type in method_x:
|
||||
machine_type = method_x[machine_type]
|
||||
machine_id_to_name = self.getMachineIDMap()
|
||||
if machine_type in machine_id_to_name:
|
||||
machine_type = machine_id_to_name[machine_type]
|
||||
else:
|
||||
machine_type = machine_type.replace("_plus", "+")
|
||||
machine_type = machine_type.replace("_", " ")
|
||||
|
@ -114,9 +114,9 @@ class CloudApiClient:
|
|||
|
||||
url = f"{self.CLUSTER_API_ROOT}/clusters/{cluster_id}/status"
|
||||
self._http.get(url,
|
||||
scope = self._scope,
|
||||
callback = self._parseCallback(on_finished, CloudClusterStatus),
|
||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||
scope=self._scope,
|
||||
callback=self._parseCallback(on_finished, CloudClusterStatus),
|
||||
timeout=self.DEFAULT_REQUEST_TIMEOUT)
|
||||
|
||||
def requestUpload(self, request: CloudPrintJobUploadRequest,
|
||||
on_finished: Callable[[CloudPrintJobResponse], Any]) -> None:
|
||||
|
@ -131,10 +131,10 @@ class CloudApiClient:
|
|||
data = json.dumps({"data": request.toDict()}).encode()
|
||||
|
||||
self._http.put(url,
|
||||
scope = self._scope,
|
||||
data = data,
|
||||
callback = self._parseCallback(on_finished, CloudPrintJobResponse),
|
||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||
scope=self._scope,
|
||||
data=data,
|
||||
callback=self._parseCallback(on_finished, CloudPrintJobResponse),
|
||||
timeout=self.DEFAULT_REQUEST_TIMEOUT)
|
||||
|
||||
def uploadToolPath(self, print_job: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any],
|
||||
on_progress: Callable[[int], Any], on_error: Callable[[], Any]):
|
||||
|
@ -160,11 +160,11 @@ class CloudApiClient:
|
|||
def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any], on_error) -> None:
|
||||
url = f"{self.CLUSTER_API_ROOT}/clusters/{cluster_id}/print/{job_id}"
|
||||
self._http.post(url,
|
||||
scope = self._scope,
|
||||
data = b"",
|
||||
callback = self._parseCallback(on_finished, CloudPrintResponse),
|
||||
error_callback = on_error,
|
||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||
scope=self._scope,
|
||||
data=b"",
|
||||
callback=self._parseCallback(on_finished, CloudPrintResponse),
|
||||
error_callback=on_error,
|
||||
timeout=self.DEFAULT_REQUEST_TIMEOUT)
|
||||
|
||||
def doPrintJobAction(self, cluster_id: str, cluster_job_id: str, action: str,
|
||||
data: Optional[Dict[str, Any]] = None) -> None:
|
||||
|
@ -174,14 +174,15 @@ class CloudApiClient:
|
|||
:param cluster_id: The ID of the cluster.
|
||||
:param cluster_job_id: The ID of the print job within the cluster.
|
||||
:param action: The name of the action to execute.
|
||||
:param data: Optional data to send with the POST request
|
||||
"""
|
||||
|
||||
body = json.dumps({"data": data}).encode() if data else b""
|
||||
url = f"{self.CLUSTER_API_ROOT}/clusters/{cluster_id}/print_jobs/{cluster_job_id}/action/{action}"
|
||||
self._http.post(url,
|
||||
scope = self._scope,
|
||||
data = body,
|
||||
timeout = self.DEFAULT_REQUEST_TIMEOUT)
|
||||
scope=self._scope,
|
||||
data=body,
|
||||
timeout=self.DEFAULT_REQUEST_TIMEOUT)
|
||||
|
||||
def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest:
|
||||
"""We override _createEmptyRequest in order to add the user credentials.
|
||||
|
@ -216,8 +217,11 @@ class CloudApiClient:
|
|||
Logger.logException("e", "Could not parse the stardust response: %s", error.toDict())
|
||||
return status_code, {"errors": [error.toDict()]}
|
||||
|
||||
def _parseResponse(self, response: Dict[str, Any], on_finished: Union[Callable[[CloudApiClientModel], Any],
|
||||
Callable[[List[CloudApiClientModel]], Any]], model_class: Type[CloudApiClientModel]) -> None:
|
||||
def _parseResponse(self,
|
||||
response: Dict[str, Any],
|
||||
on_finished: Union[Callable[[CloudApiClientModel], Any],
|
||||
Callable[[List[CloudApiClientModel]], Any]],
|
||||
model_class: Type[CloudApiClientModel]) -> None:
|
||||
"""Parses the given response and calls the correct callback depending on the result.
|
||||
|
||||
:param response: The response from the server, after being converted to a dict.
|
||||
|
@ -276,3 +280,14 @@ class CloudApiClient:
|
|||
|
||||
self._anti_gc_callbacks.append(parse)
|
||||
return parse
|
||||
|
||||
@classmethod
|
||||
def getMachineIDMap(cls) -> Dict[str, str]:
|
||||
if cls._machine_id_to_name is None:
|
||||
try:
|
||||
with open(Path(__file__).parent / "machine_id_to_name.json", "rt") as f:
|
||||
cls._machine_id_to_name = json.load(f)
|
||||
except Exception as e:
|
||||
Logger.logException("e", f"Could not load machine_id_to_name.json: '{e}'")
|
||||
cls._machine_id_to_name = {}
|
||||
return cls._machine_id_to_name
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"ultimaker_method": "MakerBot Method",
|
||||
"ultimaker_methodx": "MakerBot Method X",
|
||||
"ultimaker_methodxl": "MakerBot Method XL"
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue