From 3774fdbd0209e5bfca735160ba7fc05a9324670d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 28 Jan 2019 14:29:41 +0100 Subject: [PATCH 1/6] Refactored the "connection_type" metadata entry so it can support multiple types. After a lot of discussion and finding out what the hell was going on, we figured out we made a pretty big derp by only setting a single connection_type in the metadata of the machine. What it's actually doing is describing what connection types have been configured (and not just randomly displaying whatever output device set the value last) --- cura/GlobalStacksModel.py | 21 +++++------ cura/Settings/GlobalStack.py | 26 +++++++++++++ cura/Settings/MachineManager.py | 8 +++- .../src/DiscoverUM3Action.py | 37 +++++++++++-------- .../src/UM3OutputDevicePlugin.py | 20 +++++----- 5 files changed, 75 insertions(+), 37 deletions(-) diff --git a/cura/GlobalStacksModel.py b/cura/GlobalStacksModel.py index 289a03d1c4..406e7b6bb3 100644 --- a/cura/GlobalStacksModel.py +++ b/cura/GlobalStacksModel.py @@ -5,10 +5,8 @@ from UM.Qt.ListModel import ListModel from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal -from UM.Settings.ContainerRegistry import ContainerRegistry -from UM.Settings.ContainerStack import ContainerStack - from cura.PrinterOutputDevice import ConnectionType +from cura.Settings.CuraContainerRegistry import CuraContainerRegistry from cura.Settings.GlobalStack import GlobalStack @@ -25,14 +23,13 @@ class GlobalStacksModel(ListModel): self.addRoleName(self.NameRole, "name") self.addRoleName(self.IdRole, "id") self.addRoleName(self.HasRemoteConnectionRole, "hasRemoteConnection") - self.addRoleName(self.ConnectionTypeRole, "connectionType") self.addRoleName(self.MetaDataRole, "metadata") self._container_stacks = [] # Listen to changes - ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged) - ContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged) - ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged) + CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged) + CuraContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged) + CuraContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged) self._filter_dict = {} self._update() @@ -45,11 +42,14 @@ class GlobalStacksModel(ListModel): def _update(self) -> None: items = [] - container_stacks = ContainerRegistry.getInstance().findContainerStacks(type = "machine") + container_stacks = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine") for container_stack in container_stacks: - connection_type = int(container_stack.getMetaDataEntry("connection_type", ConnectionType.NotConnected.value)) - has_remote_connection = connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] + has_remote_connection = False + + for connection_type in container_stack.configuredConnectionTypes: + has_remote_connection |= connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] + if container_stack.getMetaDataEntry("hidden", False) in ["True", True]: continue @@ -57,7 +57,6 @@ class GlobalStacksModel(ListModel): items.append({"name": container_stack.getMetaDataEntry("connect_group_name", container_stack.getName()), "id": container_stack.getId(), "hasRemoteConnection": has_remote_connection, - "connectionType": connection_type, "metadata": container_stack.getMetaData().copy()}) items.sort(key=lambda i: not i["hasRemoteConnection"]) self.setItems(items) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index 44ceee9511..b65b0fcbf4 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -42,7 +42,12 @@ class GlobalStack(CuraContainerStack): # Per thread we have our own resolving_settings, or strange things sometimes occur. self._resolving_settings = defaultdict(set) #type: Dict[str, Set[str]] # keys are thread names + # Since the metadatachanged is defined in container stack, we can't use it here as a notifier for pyqt + # properties. So we need to tie them together like this. + self.metaDataChanged.connect(self.configuredConnectionTypesChanged) + extrudersChanged = pyqtSignal() + configuredConnectionTypesChanged = pyqtSignal() ## Get the list of extruders of this stack. # @@ -63,6 +68,27 @@ class GlobalStack(CuraContainerStack): def getLoadingPriority(cls) -> int: return 2 + @pyqtProperty("QVariantList", notify=configuredConnectionTypesChanged) + def configuredConnectionTypes(self): + # 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 != ""] + + def addConfiguredConnectionType(self, connection_type): + configured_connection_types = self.configuredConnectionTypes + if connection_type not in configured_connection_types: + # Store the values as a string. + configured_connection_types.append(str(connection_type)) + self.setMetaDataEntry("connection_type", ",".join(configured_connection_types)) + + def removeConfiguredConnectionType(self, connection_type): + configured_connection_types = self.configuredConnectionTypes + if connection_type in self.configured_connection_types: + # Store the values as a string. + configured_connection_types.remove(str(connection_type)) + self.setMetaDataEntry("connection_type", ",".join(configured_connection_types)) + @classmethod def getConfigurationTypeFromSerialized(cls, serialized: str) -> Optional[str]: configuration_type = super().getConfigurationTypeFromSerialized(serialized) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 5f33be1c54..ae74d76734 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -528,8 +528,12 @@ class MachineManager(QObject): @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasRemoteConnection(self) -> bool: if self._global_container_stack: - connection_type = int(self._global_container_stack.getMetaDataEntry("connection_type", ConnectionType.NotConnected.value)) - return connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] + has_remote_connection = False + + for connection_type in self._global_container_stack.configuredConnectionTypes: + has_remote_connection |= connection_type in [ConnectionType.NetworkConnection.value, + ConnectionType.CloudConnection.value] + return has_remote_connection return False @pyqtProperty(bool, notify = printerConnectedStatusChanged) diff --git a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py index b688ee9d7d..7072aef4cc 100644 --- a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py +++ b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py @@ -13,6 +13,7 @@ from UM.i18n import i18nCatalog from cura.CuraApplication import CuraApplication from cura.MachineAction import MachineAction +from cura.Settings.CuraContainerRegistry import CuraContainerRegistry from .UM3OutputDevicePlugin import UM3OutputDevicePlugin @@ -133,23 +134,29 @@ class DiscoverUM3Action(MachineAction): return meta_data = global_container_stack.getMetaData() - if "um_network_key" in meta_data: - previous_network_key = meta_data["um_network_key"] - global_container_stack.setMetaDataEntry("um_network_key", printer_device.key) - # Delete old authentication data. - Logger.log("d", "Removing old authentication id %s for device %s", - global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key) - global_container_stack.removeMetaDataEntry("network_authentication_id") - global_container_stack.removeMetaDataEntry("network_authentication_key") - CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = printer_device.key) - if "connection_type" in meta_data: - previous_connection_type = meta_data["connection_type"] - global_container_stack.setMetaDataEntry("connection_type", printer_device.connectionType.value) - CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "connection_type", value = previous_connection_type, new_value = printer_device.connectionType.value) - else: + if "um_network_key" in meta_data: # Global stack already had a connection, but it's changed. + old_network_key = meta_data["um_network_key"] + # Since we might have a bunch of hidden stacks, we also need to change it there. + metadata_filter = {"um_network_key": old_network_key} + containers = CuraContainerRegistry.getInstance().findContainerStacks(type="machine", **metadata_filter) + + for container in containers: + container.setMetaDataEntry("um_network_key", printer_device.key) + + # Delete old authentication data. + Logger.log("d", "Removing old authentication id %s for device %s", + global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key) + + container.removeMetaDataEntry("network_authentication_id") + container.removeMetaDataEntry("network_authentication_key") + + # Ensure that these containers do know that they are configured for network connection + container.addConfiguredConnectionType(printer_device.connectionType.value) + + else: # Global stack didn't have a connection yet, configure it. global_container_stack.setMetaDataEntry("um_network_key", printer_device.key) - global_container_stack.setMetaDataEntry("connection_type", printer_device.connectionType.value) + global_container_stack.addConfiguredConnectionType(printer_device.connectionType.value) if self._network_plugin: # Ensure that the connection states are refreshed. diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 4a510903dd..74d2d87b3b 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -9,7 +9,7 @@ from zeroconf import Zeroconf, ServiceBrowser, ServiceStateChange, ServiceInfo from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager from PyQt5.QtCore import QUrl -from UM.Application import Application +from cura.CuraApplication import CuraApplication from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from UM.Logger import Logger from UM.Signal import Signal, signalemitter @@ -41,7 +41,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self.addDeviceSignal.connect(self._onAddDevice) self.removeDeviceSignal.connect(self._onRemoveDevice) - Application.getInstance().globalContainerStackChanged.connect(self.reCheckConnections) + CuraApplication.getInstance().globalContainerStackChanged.connect(self.reCheckConnections) self._discovered_devices = {} @@ -56,7 +56,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/" # Get list of manual instances from preferences - self._preferences = Application.getInstance().getPreferences() + self._preferences = CuraApplication.getInstance().getPreferences() self._preferences.addPreference("um3networkprinting/manual_instances", "") # A comma-separated list of ip adresses or hostnames @@ -108,7 +108,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self.resetLastManualDevice() def reCheckConnections(self): - active_machine = Application.getInstance().getGlobalContainerStack() + active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return @@ -118,7 +118,8 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): if key == um_network_key: if not self._discovered_devices[key].isConnected(): Logger.log("d", "Attempting to connect with [%s]" % key) - active_machine.setMetaDataEntry("connection_type", self._discovered_devices[key].connectionType.value) + # It should already be set, but if it actually connects we know for sure it's supported! + active_machine.addConfiguredConnectionType(self._discovered_devices[key].connectionType.value) self._discovered_devices[key].connect() self._discovered_devices[key].connectionStateChanged.connect(self._onDeviceConnectionStateChanged) else: @@ -134,7 +135,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): return if self._discovered_devices[key].isConnected(): # Sometimes the status changes after changing the global container and maybe the device doesn't belong to this machine - um_network_key = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("um_network_key") + um_network_key = CuraApplication.getInstance().getGlobalContainerStack().getMetaDataEntry("um_network_key") if key == um_network_key: self.getOutputDeviceManager().addOutputDevice(self._discovered_devices[key]) else: @@ -287,9 +288,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._discovered_devices[device.getId()] = device self.discoveredDevicesChanged.emit() - global_container_stack = Application.getInstance().getGlobalContainerStack() + global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() if global_container_stack and device.getId() == global_container_stack.getMetaDataEntry("um_network_key"): - global_container_stack.setMetaDataEntry("connection_type", device.connectionType.value) + # Ensure that the configured connection type is set. + global_container_stack.addConfiguredConnectionType(device.connectionType.value) device.connect() device.connectionStateChanged.connect(self._onDeviceConnectionStateChanged) @@ -306,7 +308,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._service_changed_request_event.wait(timeout = 5.0) # Stop if the application is shutting down - if Application.getInstance().isShuttingDown(): + if CuraApplication.getInstance().isShuttingDown(): return self._service_changed_request_event.clear() From e2e856a5cd321ef89a704b96082ba191bdb2fa3c Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 28 Jan 2019 14:41:54 +0100 Subject: [PATCH 2/6] Add adhesion warning message back CURA-6070 --- .../ConfigurationMenu/CustomConfiguration.qml | 51 +++++++++++++++++++ resources/themes/cura-light/theme.json | 2 +- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 5cecda4e5c..09ea8300ad 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -288,6 +288,57 @@ Item menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } } } + + Row + { + height: UM.Theme.getSize("print_setup_big_item").height + visible: buildplateCompatibilityError || buildplateCompatibilityWarning + + property bool buildplateCompatibilityError: !Cura.MachineManager.variantBuildplateCompatible && !Cura.MachineManager.variantBuildplateUsable + property bool buildplateCompatibilityWarning: Cura.MachineManager.variantBuildplateUsable + + // This is a space holder aligning the warning messages. + Label + { + text: "" + width: selectors.textWidth + renderType: Text.NativeRendering + } + + Item + { + width: selectors.controlWidth + height: parent.height + + UM.RecolorImage + { + id: warningImage + anchors.left: parent.left + source: UM.Theme.getIcon("warning") + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + sourceSize.width: width + sourceSize.height: height + color: UM.Theme.getColor("material_compatibility_warning") + visible: !Cura.MachineManager.isCurrentSetupSupported || buildplateCompatibilityError || buildplateCompatibilityWarning + } + + Label + { + id: materialCompatibilityLabel + anchors.left: warningImage.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + verticalAlignment: Text.AlignVCenter + width: selectors.controlWidth - warningImage.width - UM.Theme.getSize("default_margin").width + text: catalog.i18nc("@label", "Use glue for better adhesion with this material combination.") + font: UM.Theme.getFont("very_small") + color: UM.Theme.getColor("text") + visible: CuraSDKVersion == "dev" ? false : buildplateCompatibilityError || buildplateCompatibilityWarning + wrapMode: Text.WordWrap + renderType: Text.NativeRendering + } + } + } } } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index b12385c962..a54b63b4c9 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -294,7 +294,7 @@ "setting_validation_ok": [255, 255, 255, 255], "setting_filter_field" : [153, 153, 153, 255], - "material_compatibility_warning": [0, 0, 0, 255], + "material_compatibility_warning": [243, 166, 59, 255], "progressbar_background": [245, 245, 245, 255], "progressbar_control": [50, 130, 255, 255], From bc8a08bb44b107ccfd1ed39a94d9cd631abb116f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 28 Jan 2019 14:57:24 +0100 Subject: [PATCH 3/6] Add documentation to the configuredConnectionTypes --- cura/Settings/GlobalStack.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index b65b0fcbf4..2bb2cc7298 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -68,6 +68,11 @@ class GlobalStack(CuraContainerStack): def getLoadingPriority(cls) -> int: return 2 + # The configured connection types can be used to find out if the global stack is configured to be connected with + # a printer, without having to know all the details as to how this is exactly done (and without actually setting + # the stack to be active). This data can then in turn also be used when the global stack is active; If we can't + # get a network connection, but it is configured to have one, we can display a different icon to indicate the + # difference. @pyqtProperty("QVariantList", notify=configuredConnectionTypesChanged) def configuredConnectionTypes(self): # Requesting it from the metadata actually gets them as strings (as that's what you get from serializing). @@ -75,6 +80,7 @@ class GlobalStack(CuraContainerStack): connection_types = self.getMetaDataEntry("connection_type", "").split(",") return [int(connection_type) for connection_type in connection_types if connection_type != ""] + # \sa configuredConnectionTypes def addConfiguredConnectionType(self, connection_type): configured_connection_types = self.configuredConnectionTypes if connection_type not in configured_connection_types: @@ -82,6 +88,7 @@ class GlobalStack(CuraContainerStack): configured_connection_types.append(str(connection_type)) self.setMetaDataEntry("connection_type", ",".join(configured_connection_types)) + # \sa configuredConnectionTypes def removeConfiguredConnectionType(self, connection_type): configured_connection_types = self.configuredConnectionTypes if connection_type in self.configured_connection_types: From 9d8cf5ddde7d645ded51178f2b0596a178cc00bb Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 28 Jan 2019 16:40:45 +0100 Subject: [PATCH 4/6] Apply division by zero prevention to the denominator Rather than the numerator, which could not cause a division by zero exception. --- cura/PrinterOutput/PrintJobOutputModel.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cura/PrinterOutput/PrintJobOutputModel.py b/cura/PrinterOutput/PrintJobOutputModel.py index a77ac81909..eb0d846415 100644 --- a/cura/PrinterOutput/PrintJobOutputModel.py +++ b/cura/PrinterOutput/PrintJobOutputModel.py @@ -132,8 +132,7 @@ class PrintJobOutputModel(QObject): @pyqtProperty(float, notify = timeElapsedChanged) def progress(self) -> float: - time_elapsed = max(float(self.timeElapsed), 1.0) # Prevent a division by zero exception - result = time_elapsed / self.timeTotal + result = float(self.timeElapsed) / max(self.timeTotal, 1.0) # Prevent a division by zero exception. return min(result, 1.0) # Never get a progress past 1.0 @pyqtProperty(str, notify=stateChanged) From 14300e745f2102605d6a93260d9e218e1d6ac15e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 30 Jan 2019 12:52:58 +0100 Subject: [PATCH 5/6] Use Doxygen-style comments for documentation According to our style. Not just single line comments without anything else. Contributes to issue CURA-6159. --- cura/Settings/GlobalStack.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index 2bb2cc7298..896caffe42 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -68,11 +68,14 @@ class GlobalStack(CuraContainerStack): def getLoadingPriority(cls) -> int: return 2 - # The configured connection types can be used to find out if the global stack is configured to be connected with - # a printer, without having to know all the details as to how this is exactly done (and without actually setting - # the stack to be active). This data can then in turn also be used when the global stack is active; If we can't - # get a network connection, but it is configured to have one, we can display a different icon to indicate the - # difference. + ## The configured connection types can be used to find out if the global + # stack is configured to be connected with a printer, without having to + # know all the details as to how this is exactly done (and without + # actually setting the stack to be active). + # + # This data can then in turn also be used when the global stack is active; + # If we can't get a network connection, but it is configured to have one, + # we can display a different icon to indicate the difference. @pyqtProperty("QVariantList", notify=configuredConnectionTypesChanged) def configuredConnectionTypes(self): # Requesting it from the metadata actually gets them as strings (as that's what you get from serializing). @@ -80,7 +83,7 @@ class GlobalStack(CuraContainerStack): connection_types = self.getMetaDataEntry("connection_type", "").split(",") return [int(connection_type) for connection_type in connection_types if connection_type != ""] - # \sa configuredConnectionTypes + ## \sa configuredConnectionTypes def addConfiguredConnectionType(self, connection_type): configured_connection_types = self.configuredConnectionTypes if connection_type not in configured_connection_types: @@ -88,7 +91,7 @@ class GlobalStack(CuraContainerStack): configured_connection_types.append(str(connection_type)) self.setMetaDataEntry("connection_type", ",".join(configured_connection_types)) - # \sa configuredConnectionTypes + ## \sa configuredConnectionTypes def removeConfiguredConnectionType(self, connection_type): configured_connection_types = self.configuredConnectionTypes if connection_type in self.configured_connection_types: From 430be551d39c1d056b90b59108d7f3f4c23d1a3f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 30 Jan 2019 12:59:27 +0100 Subject: [PATCH 6/6] Fix link to properties of row This fixes a QML warning and probably fixes the visibility of these elements. --- .../qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 09ea8300ad..f33f638119 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -291,6 +291,7 @@ Item Row { + id: warnings height: UM.Theme.getSize("print_setup_big_item").height visible: buildplateCompatibilityError || buildplateCompatibilityWarning @@ -320,7 +321,7 @@ Item sourceSize.width: width sourceSize.height: height color: UM.Theme.getColor("material_compatibility_warning") - visible: !Cura.MachineManager.isCurrentSetupSupported || buildplateCompatibilityError || buildplateCompatibilityWarning + visible: !Cura.MachineManager.isCurrentSetupSupported || warnings.buildplateCompatibilityError || warnings.buildplateCompatibilityWarning } Label @@ -333,7 +334,7 @@ Item text: catalog.i18nc("@label", "Use glue for better adhesion with this material combination.") font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text") - visible: CuraSDKVersion == "dev" ? false : buildplateCompatibilityError || buildplateCompatibilityWarning + visible: CuraSDKVersion == "dev" ? false : warnings.buildplateCompatibilityError || warnings.buildplateCompatibilityWarning wrapMode: Text.WordWrap renderType: Text.NativeRendering }