diff --git a/cura/Settings/CuraFormulaFunctions.py b/cura/Settings/CuraFormulaFunctions.py
index 9ef80bd3d4..a8b416eeb5 100644
--- a/cura/Settings/CuraFormulaFunctions.py
+++ b/cura/Settings/CuraFormulaFunctions.py
@@ -42,7 +42,14 @@ class CuraFormulaFunctions:
try:
extruder_stack = global_stack.extruders[str(extruder_position)]
except KeyError:
- Logger.log("w", "Value for %s of extruder %s was requested, but that extruder is not available" % (property_key, extruder_position))
+ if extruder_position != 0:
+ Logger.log("w", "Value for %s of extruder %s was requested, but that extruder is not available. Returning the result form extruder 0 instead" % (property_key, extruder_position))
+ # This fixes a very specific fringe case; If a profile was created for a custom printer and one of the
+ # extruder settings has been set to non zero and the profile is loaded for a machine that has only a single extruder
+ # it would cause all kinds of issues (and eventually a crash).
+ # See https://github.com/Ultimaker/Cura/issues/5535
+ return self.getValueInExtruder(0, property_key, context)
+ Logger.log("w", "Value for %s of extruder %s was requested, but that extruder is not available. " % (property_key, extruder_position))
return None
value = extruder_stack.getRawProperty(property_key, "value", context = context)
diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py
index 17a732c4b9..5c05d28739 100755
--- a/cura/Settings/GlobalStack.py
+++ b/cura/Settings/GlobalStack.py
@@ -85,7 +85,15 @@ class GlobalStack(CuraContainerStack):
# Requesting it from the metadata actually gets them as strings (as that's what you get from serializing).
# But we do want them returned as a list of ints (so the rest of the code can directly compare)
connection_types = self.getMetaDataEntry("connection_type", "").split(",")
- return [int(connection_type) for connection_type in connection_types if connection_type != ""]
+ result = []
+ for connection_type in connection_types:
+ if connection_type != "":
+ try:
+ result.append(int(connection_type))
+ except ValueError:
+ # We got invalid data, probably a None.
+ pass
+ return result
## \sa configuredConnectionTypes
def addConfiguredConnectionType(self, connection_type: int) -> None:
diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml
index 795622cf82..72dd6f91a2 100644
--- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml
+++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml
@@ -14,7 +14,7 @@ Rectangle
Column
{
height: childrenRect.height + 2 * padding
- spacing: UM.Theme.getSize("toolbox_showcase_spacing").width
+ spacing: UM.Theme.getSize("default_margin").width
width: parent.width
padding: UM.Theme.getSize("wide_margin").height
Label
diff --git a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml
index cde87c5bc4..7a7d2be48a 100644
--- a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml
+++ b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml
@@ -61,7 +61,7 @@ Button
{
target: label
font: UM.Theme.getFont("medium_bold")
- color: UM.Theme.getColor("toolbox_header_button_text_active")
+ color: UM.Theme.getColor("action_button_text")
}
}
]
diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py
index d98cdded9c..688538522e 100644
--- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py
+++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py
@@ -145,9 +145,17 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
## Checks whether the given network key is found in the cloud's host name
def matchesNetworkKey(self, network_key: str) -> bool:
- # A network key looks like "ultimakersystem-aabbccdd0011._ultimaker._tcp.local."
+ # Typically, a network key looks like "ultimakersystem-aabbccdd0011._ultimaker._tcp.local."
# the host name should then be "ultimakersystem-aabbccdd0011"
- return network_key.startswith(self.clusterData.host_name)
+ if network_key.startswith(self.clusterData.host_name):
+ return True
+
+ # However, for manually added printers, the local IP address is used in lieu of a proper
+ # network key, so check for that as well
+ if self.clusterData.host_internal_ip is not None and network_key.find(self.clusterData.host_internal_ip):
+ return True
+
+ return False
## Set all the interface elements and texts for this output device.
def _setInterfaceElements(self) -> None:
diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py
index 9c0853e7c9..48a4d5f031 100644
--- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py
+++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py
@@ -16,13 +16,14 @@ class CloudClusterResponse(BaseCloudModel):
# \param status: The status of the cluster authentication (active or inactive).
# \param host_version: The firmware version of the cluster host. This is where the Stardust client is running on.
def __init__(self, cluster_id: str, host_guid: str, host_name: str, is_online: bool, status: str,
- host_version: Optional[str] = None, **kwargs) -> None:
+ host_internal_ip: Optional[str] = None, host_version: Optional[str] = None, **kwargs) -> None:
self.cluster_id = cluster_id
self.host_guid = host_guid
self.host_name = host_name
self.status = status
self.is_online = is_online
self.host_version = host_version
+ self.host_internal_ip = host_internal_ip
super().__init__(**kwargs)
# Validates the model, raising an exception if the model is invalid.
diff --git a/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py
index ac0b980a32..5c1948b977 100644
--- a/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py
+++ b/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py
@@ -77,13 +77,15 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
self.setIconName("print")
- if PluginRegistry.getInstance() is not None:
+ self._output_controller = LegacyUM3PrinterOutputController(self)
+
+ def _createMonitorViewFromQML(self) -> None:
+ if self._monitor_view_qml_path is None and PluginRegistry.getInstance() is not None:
self._monitor_view_qml_path = os.path.join(
PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"),
"resources", "qml", "MonitorStage.qml"
)
-
- self._output_controller = LegacyUM3PrinterOutputController(self)
+ super()._createMonitorViewFromQML()
def _onAuthenticationStateChanged(self):
# We only accept commands if we are authenticated.
diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py
index e24ca1694e..869b39440c 100644
--- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py
+++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py
@@ -7,6 +7,7 @@ from UM.OutputDevice.OutputDeviceManager import OutputDeviceManager
from cura.UltimakerCloudAuthentication import CuraCloudAPIRoot
from ...src.Cloud import CloudApiClient
from ...src.Cloud import CloudOutputDeviceManager
+from ...src.Cloud.Models.CloudClusterResponse import CloudClusterResponse
from .Fixtures import parseFixture, readFixture
from .NetworkManagerMock import NetworkManagerMock, FakeSignal
@@ -55,7 +56,9 @@ class TestCloudOutputDeviceManager(TestCase):
devices = self.device_manager.getOutputDevices()
# TODO: Check active device
- response_clusters = self.clusters_response.get("data", [])
+ response_clusters = []
+ for cluster in self.clusters_response.get("data", []):
+ response_clusters.append(CloudClusterResponse(**cluster).toDict())
manager_clusters = sorted([device.clusterData.toDict() for device in self.manager._remote_clusters.values()],
key=lambda cluster: cluster['cluster_id'], reverse=True)
self.assertEqual(response_clusters, manager_clusters)
@@ -97,7 +100,7 @@ class TestCloudOutputDeviceManager(TestCase):
self.assertTrue(self.device_manager.getOutputDevice(cluster1["cluster_id"]).isConnected())
self.assertIsNone(self.device_manager.getOutputDevice(cluster2["cluster_id"]))
- self.assertEquals([], active_machine_mock.setMetaDataEntry.mock_calls)
+ self.assertEqual([], active_machine_mock.setMetaDataEntry.mock_calls)
def test_device_connects_by_network_key(self):
active_machine_mock = self.app.getGlobalContainerStack.return_value
diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py
index 952d38dcf4..2cab110861 100644
--- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py
+++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py
@@ -208,7 +208,7 @@ class TestSendMaterialJob(TestCase):
self.assertEqual(1, device_mock.createFormPart.call_count)
self.assertEqual(1, device_mock.postFormWithParts.call_count)
- self.assertEquals(
+ self.assertEqual(
[call.createFormPart("name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"", ""),
call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)],
device_mock.method_calls)
@@ -238,7 +238,7 @@ class TestSendMaterialJob(TestCase):
self.assertEqual(1, device_mock.createFormPart.call_count)
self.assertEqual(1, device_mock.postFormWithParts.call_count)
- self.assertEquals(
+ self.assertEqual(
[call.createFormPart("name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"", ""),
call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)],
device_mock.method_calls)
diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json
index 45ff92fea8..cb28a439ee 100644
--- a/resources/definitions/fdmprinter.def.json
+++ b/resources/definitions/fdmprinter.def.json
@@ -4092,7 +4092,7 @@
"description": "Amount of offset applied to all support polygons in each layer. Positive values can smooth out the support areas and result in more sturdy support.",
"unit": "mm",
"type": "float",
- "default_value": 0.2,
+ "default_value": 0,
"limit_to_extruder": "support_infill_extruder_nr",
"minimum_value_warning": "-1 * machine_nozzle_size",
"maximum_value_warning": "10 * machine_nozzle_size",
diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml
index f4505c620c..7e76768cb4 100644
--- a/resources/qml/ActionPanel/OutputProcessWidget.qml
+++ b/resources/qml/ActionPanel/OutputProcessWidget.qml
@@ -68,6 +68,7 @@ Column
property var printMaterialLengths: PrintInformation.materialLengths
property var printMaterialWeights: PrintInformation.materialWeights
+ property var printMaterialCosts: PrintInformation.materialCosts
text:
{
@@ -77,6 +78,7 @@ Column
}
var totalLengths = 0
var totalWeights = 0
+ var totalCosts = 0.0
if (printMaterialLengths)
{
for(var index = 0; index < printMaterialLengths.length; index++)
@@ -85,9 +87,16 @@ Column
{
totalLengths += printMaterialLengths[index]
totalWeights += Math.round(printMaterialWeights[index])
+ var cost = printMaterialCosts[index] == undefined ? 0.0 : printMaterialCosts[index]
+ totalCosts += cost
}
}
}
+ if(totalCosts > 0)
+ {
+ var costString = "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(totalCosts.toFixed(2))
+ return totalWeights + "g · " + totalLengths.toFixed(2) + "m · " + costString
+ }
return totalWeights + "g · " + totalLengths.toFixed(2) + "m"
}
source: UM.Theme.getIcon("spool")
diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml
index 121f604362..2cf3b0ed58 100755
--- a/resources/themes/cura-light/styles.qml
+++ b/resources/themes/cura-light/styles.qml
@@ -103,33 +103,29 @@ QtObject
// This property will be back-propagated when the width of the label is calculated
property var buttonWidth: 0
- background: Item
+ background: Rectangle
{
+ id: backgroundRectangle
implicitHeight: control.height
implicitWidth: buttonWidth
- Rectangle
- {
- id: buttonFace
- implicitHeight: parent.height
- implicitWidth: parent.width
- radius: UM.Theme.getSize("action_button_radius").width
+ radius: UM.Theme.getSize("action_button_radius").width
- color:
+ color:
+ {
+ if (control.checked)
{
- if (control.checked)
+ return UM.Theme.getColor("main_window_header_button_background_active")
+ }
+ else
+ {
+ if (control.hovered)
{
- return UM.Theme.getColor("main_window_header_button_background_active")
- }
- else
- {
- if (control.hovered)
- {
- return UM.Theme.getColor("main_window_header_button_background_hovered")
- }
- return UM.Theme.getColor("main_window_header_button_background_inactive")
+ return UM.Theme.getColor("main_window_header_button_background_hovered")
}
+ return UM.Theme.getColor("main_window_header_button_background_inactive")
}
}
+
}
label: Item
@@ -168,6 +164,8 @@ QtObject
buttonWidth = width
}
}
+
+
}
}
@@ -398,73 +396,6 @@ QtObject
}
}
- // Combobox with items with colored rectangles
- property Component combobox_color: Component
- {
-
- ComboBoxStyle
- {
-
- background: Rectangle
- {
- color: !enabled ? UM.Theme.getColor("setting_control_disabled") : control._hovered ? UM.Theme.getColor("setting_control_highlight") : UM.Theme.getColor("setting_control")
- border.width: UM.Theme.getSize("default_lining").width
- border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : control._hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border")
- radius: UM.Theme.getSize("setting_control_radius").width
- }
-
- label: Item
- {
- Label
- {
- anchors.left: parent.left
- anchors.leftMargin: UM.Theme.getSize("default_lining").width
- anchors.right: swatch.left
- anchors.rightMargin: UM.Theme.getSize("default_lining").width
- anchors.verticalCenter: parent.verticalCenter
-
- text: control.currentText
- font: UM.Theme.getFont("default")
- color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text")
-
- elide: Text.ElideRight
- verticalAlignment: Text.AlignVCenter
- }
-
- UM.RecolorImage
- {
- id: swatch
- height: Math.round(control.height / 2)
- width: height
- anchors.right: downArrow.left
- anchors.verticalCenter: parent.verticalCenter
- anchors.rightMargin: UM.Theme.getSize("default_margin").width
-
- sourceSize.width: width
- sourceSize.height: height
- source: UM.Theme.getIcon("extruder_button")
- color: (control.color_override !== "") ? control.color_override : control.color
- }
-
- UM.RecolorImage
- {
- id: downArrow
- anchors.right: parent.right
- anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2
- anchors.verticalCenter: parent.verticalCenter
-
- source: UM.Theme.getIcon("arrow_bottom")
- width: UM.Theme.getSize("standard_arrow").width
- height: UM.Theme.getSize("standard_arrow").height
- sourceSize.width: width + 5 * screenScaleFactor
- sourceSize.height: width + 5 * screenScaleFactor
-
- color: UM.Theme.getColor("setting_control_button")
- }
- }
- }
- }
-
property Component checkbox: Component
{
CheckBoxStyle
diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json
index 1cccb288c0..f287e60310 100644
--- a/resources/themes/cura-light/theme.json
+++ b/resources/themes/cura-light/theme.json
@@ -393,7 +393,6 @@
"printer_config_matched": [50, 130, 255, 255],
"printer_config_mismatch": [127, 127, 127, 255],
- "toolbox_header_button_text_active": [0, 0, 0, 255],
"toolbox_header_button_text_inactive": [0, 0, 0, 255],
"favorites_header_bar": [245, 245, 245, 255],
@@ -592,10 +591,8 @@
"toolbox_thumbnail_large": [12.0, 10.0],
"toolbox_footer": [1.0, 4.5],
"toolbox_footer_button": [8.0, 2.5],
- "toolbox_showcase_spacing": [1.0, 1.0],
"toolbox_header_tab": [8.0, 4.0],
"toolbox_detail_header": [1.0, 14.0],
- "toolbox_detail_tile": [1.0, 8.0],
"toolbox_back_column": [6.0, 1.0],
"toolbox_back_button": [6.0, 2.0],
"toolbox_installed_tile": [1.0, 8.0],
@@ -603,7 +600,6 @@
"toolbox_heading_label": [1.0, 3.8],
"toolbox_header": [1.0, 4.0],
"toolbox_header_highlight": [0.25, 0.25],
- "toolbox_progress_bar": [8.0, 0.5],
"toolbox_chart_row": [1.0, 2.0],
"toolbox_action_button": [8.0, 2.5],
"toolbox_loader": [2.0, 2.0],