diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py
index 1e9199d525..81d3f733b4 100755
--- a/cura/Settings/ExtruderManager.py
+++ b/cura/Settings/ExtruderManager.py
@@ -12,6 +12,7 @@ from UM.Scene.SceneNode import SceneNode
from UM.Scene.Selection import Selection
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Settings.ContainerRegistry import ContainerRegistry # Finding containers by ID.
+from cura.Machines.ContainerTree import ContainerTree
from typing import Any, cast, Dict, List, Optional, TYPE_CHECKING, Union
@@ -403,6 +404,32 @@ class ExtruderManager(QObject):
raise IndexError(msg)
extruder_stack_0.definition = extruder_definition
+ @pyqtSlot("QVariant", result = bool)
+ def getExtruderHasQualityForMaterial(self, extruder_stack: "ExtruderStack") -> bool:
+ """Checks if quality nodes exist for the variant/material combination."""
+ application = cura.CuraApplication.CuraApplication.getInstance()
+ global_stack = application.getGlobalContainerStack()
+ if not global_stack or not extruder_stack:
+ return False
+
+ if not global_stack.getMetaDataEntry("has_materials"):
+ return True
+
+ machine_node = ContainerTree.getInstance().machines[global_stack.definition.getId()]
+
+ active_variant_name = extruder_stack.variant.getMetaDataEntry("name")
+ if active_variant_name not in machine_node.variants:
+ Logger.log("w", "Could not find the variant %s", active_variant_name)
+ return True
+ active_variant_node = machine_node.variants[active_variant_name]
+ active_material_node = active_variant_node.materials[extruder_stack.material.getMetaDataEntry("base_file")]
+
+ active_material_node_qualities = active_material_node.qualities
+ if not active_material_node_qualities:
+ return False
+ return list(active_material_node_qualities.keys())[0] != "empty_quality"
+
+
@pyqtSlot(str, result="QVariant")
def getInstanceExtruderValues(self, key: str) -> List:
"""Get all extruder values for a certain setting.
diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml
index b23a8c0811..57d003b65b 100644
--- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml
+++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml
@@ -6,7 +6,7 @@ import QtQuick.Controls 2.3
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.3
-import UM 1.2 as UM
+import UM 1.4 as UM
import Cura 1.0 as Cura
@@ -50,10 +50,16 @@ Cura.ExpandablePopup
model: extrudersModel
delegate: Item
{
+ id: extruderItem
+
Layout.preferredWidth: Math.round(parent.width / extrudersModel.count)
Layout.maximumWidth: Math.round(parent.width / extrudersModel.count)
Layout.fillHeight: true
+ property var extruderStack: Cura.MachineManager.activeMachine.extruders[model.index]
+ property bool valueWarning: !Cura.ExtruderManager.getExtruderHasQualityForMaterial(extruderStack)
+ property bool valueError: Cura.ContainerManager.getContainerMetaDataEntry(extruderStack.material.id, "compatible", "") != "True"
+
// Extruder icon. Shows extruder index and has the same color as the active material.
Cura.ExtruderIcon
{
@@ -63,6 +69,109 @@ Cura.ExpandablePopup
anchors.verticalCenter: parent.verticalCenter
}
+ MouseArea // Connection status tooltip hover area
+ {
+ id: tooltipHoverArea
+ anchors.fill: parent
+ hoverEnabled: tooltip.text != ""
+ acceptedButtons: Qt.NoButton // react to hover only, don't steal clicks
+
+ onEntered:
+ {
+ base.mouseArea.entered() // we want both this and the outer area to be entered
+ tooltip.show()
+ }
+ onExited: { tooltip.hide() }
+ }
+
+ Cura.ToolTip
+ {
+ id: tooltip
+ x: 0
+ y: parent.height + UM.Theme.getSize("default_margin").height
+ width: UM.Theme.getSize("tooltip").width
+ targetPoint: Qt.point(Math.round(extruderIcon.width / 2), 0)
+ text:
+ {
+ if (extruderItem.valueError)
+ {
+ return catalog.i18nc("@tooltip", "The configuration of this extruder is not allowed, and prohibits slicing.")
+ }
+ if (extruderItem.valueWarning)
+ {
+ return catalog.i18nc("@tooltip", "There are no profiles matching the configuration of this extruder.")
+ }
+ return ""
+ }
+ }
+
+ // Warning icon that indicates if no qualities are available for the variant/material combination for this extruder
+ UM.RecolorImage
+ {
+ id: badge
+ anchors
+ {
+ top: parent.top
+ topMargin: - Math.round(height * 1 / 6)
+ left: parent.left
+ leftMargin: extruderIcon.width - Math.round(width * 5 / 6)
+ }
+
+ width: UM.Theme.getSize("icon_indicator").width
+ height: UM.Theme.getSize("icon_indicator").height
+
+ visible: extruderItem.valueError || extruderItem.valueWarning
+
+ source:
+ {
+ if (extruderItem.valueError)
+ {
+ return UM.Theme.getIcon("ErrorBadge", "low")
+ }
+ if (extruderItem.valueWarning)
+ {
+ return UM.Theme.getIcon("WarningBadge", "low")
+ }
+ return ""
+ }
+
+ color:
+ {
+ if (extruderItem.valueError)
+ {
+ return UM.Theme.getColor("error")
+ }
+ if (extruderItem.valueWarning)
+ {
+ return UM.Theme.getColor("warning")
+ }
+ return "transparent"
+ }
+
+ // Make a themable circle in the background so we can change it in other themes
+ Rectangle
+ {
+ id: iconBackground
+ anchors.centerIn: parent
+ width: parent.width - 1.5 //1.5 pixels smaller, (at least sqrt(2), regardless of screen pixel scale) so that the circle doesn't show up behind the icon due to anti-aliasing.
+ height: parent.height - 1.5
+ radius: width / 2
+ z: parent.z - 1
+ color:
+ {
+ if (extruderItem.valueError)
+ {
+ return UM.Theme.getColor("error_badge_background")
+ }
+ if (extruderItem.valueWarning)
+ {
+ return UM.Theme.getColor("warning_badge_background")
+ }
+ return "transparent"
+ }
+ }
+ }
+
Column
{
opacity: model.enabled ? 1 : UM.Theme.getColor("extruder_disabled").a
diff --git a/resources/themes/cura-light/icons/low/ErrorBadge.svg b/resources/themes/cura-light/icons/low/ErrorBadge.svg
new file mode 100644
index 0000000000..a4df126394
--- /dev/null
+++ b/resources/themes/cura-light/icons/low/ErrorBadge.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/resources/themes/cura-light/icons/low/WarningBadge.svg b/resources/themes/cura-light/icons/low/WarningBadge.svg
new file mode 100644
index 0000000000..63a77919d4
--- /dev/null
+++ b/resources/themes/cura-light/icons/low/WarningBadge.svg
@@ -0,0 +1,4 @@
+
+
diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json
index 9dc3d8d114..f59231d960 100644
--- a/resources/themes/cura-light/theme.json
+++ b/resources/themes/cura-light/theme.json
@@ -472,7 +472,9 @@
"monitor_carousel_dot_current": [119, 119, 119, 255],
"cloud_unavailable": [153, 153, 153, 255],
- "connection_badge_background": [255, 255, 255, 255]
+ "connection_badge_background": [255, 255, 255, 255],
+ "warning_badge_background": [0, 0, 0, 255],
+ "error_badge_background": [255, 255, 255, 255]
},
"sizes": {