Merge branch 'Ultimaker:main' into main

This commit is contained in:
Stefano Serioli 2022-06-15 18:31:23 +02:00 committed by GitHub
commit 25a7c3e263
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 780 additions and 169 deletions

View File

@ -115,6 +115,8 @@ from . import CuraActions
from . import PlatformPhysics from . import PlatformPhysics
from . import PrintJobPreviewImageProvider from . import PrintJobPreviewImageProvider
from .AutoSave import AutoSave from .AutoSave import AutoSave
from .Machines.Models.ActiveIntentQualitiesModel import ActiveIntentQualitiesModel
from .Machines.Models.IntentSelectionModel import IntentSelectionModel
from .SingleInstance import SingleInstance from .SingleInstance import SingleInstance
if TYPE_CHECKING: if TYPE_CHECKING:
@ -1192,6 +1194,8 @@ class CuraApplication(QtApplication):
qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel") qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel")
qmlRegisterType(IntentModel, "Cura", 1, 6, "IntentModel") qmlRegisterType(IntentModel, "Cura", 1, 6, "IntentModel")
qmlRegisterType(IntentCategoryModel, "Cura", 1, 6, "IntentCategoryModel") qmlRegisterType(IntentCategoryModel, "Cura", 1, 6, "IntentCategoryModel")
qmlRegisterType(IntentSelectionModel, "Cura", 1, 7, "IntentSelectionModel")
qmlRegisterType(ActiveIntentQualitiesModel, "Cura", 1, 7, "ActiveIntentQualitiesModel")
self.processEvents() self.processEvents()
qmlRegisterType(MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler") qmlRegisterType(MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler")

View File

@ -0,0 +1,128 @@
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional, Set, Dict, List, Any
from PyQt6.QtCore import Qt, QObject, QTimer
import cura.CuraApplication
from UM.Logger import Logger
from UM.Qt.ListModel import ListModel
from cura.Machines.ContainerTree import ContainerTree
from cura.Machines.Models.MachineModelUtils import fetchLayerHeight
from cura.Machines.MaterialNode import MaterialNode
from cura.Machines.QualityGroup import QualityGroup
from cura.Settings.IntentManager import IntentManager
class ActiveIntentQualitiesModel(ListModel):
NameRole = Qt.ItemDataRole.UserRole + 1
DisplayTextRole = Qt.ItemDataRole.UserRole + 2
QualityTypeRole = Qt.ItemDataRole.UserRole + 3
LayerHeightRole = Qt.ItemDataRole.UserRole + 4
IntentCategeoryRole = Qt.ItemDataRole.UserRole + 5
def __init__(self, parent: Optional[QObject] = None) -> None:
super().__init__(parent)
self.addRoleName(self.NameRole, "name")
self.addRoleName(self.QualityTypeRole, "quality_type")
self.addRoleName(self.LayerHeightRole, "layer_height")
self.addRoleName(self.DisplayTextRole, "display_text")
self.addRoleName(self.IntentCategeoryRole, "intent_category")
self._intent_category = ""
IntentManager.intentCategoryChangedSignal.connect(self._update)
machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager()
machine_manager.activeQualityGroupChanged.connect(self._update)
self._update_timer = QTimer()
self._update_timer.setInterval(100)
self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self._update)
self._update()
def _updateDelayed(self):
self._update_timer.start()
def _onChanged(self, container: ContainerStack) -> None:
if container.getMetaDataEntry("type") == "intent":
self._updateDelayed()
def _update(self):
active_extruder_stack = cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeStack
if active_extruder_stack:
self._intent_category = active_extruder_stack.intent.getMetaDataEntry("intent_category", "")
new_items: List[Dict[str, Any]] = []
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if not global_stack:
self.setItems(new_items)
return
quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
material_nodes = self._getActiveMaterials()
added_quality_type_set: Set[str] = set()
for material_node in material_nodes:
intents = self._getIntentsForMaterial(material_node, quality_groups)
for intent in intents:
if intent["quality_type"] not in added_quality_type_set:
new_items.append(intent)
added_quality_type_set.add(intent["quality_type"])
new_items = sorted(new_items, key=lambda x: x["layer_height"])
self.setItems(new_items)
def _getActiveMaterials(self) -> Set["MaterialNode"]:
"""Get the active materials for all extruders. No duplicates will be returned"""
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if global_stack is None:
return set()
container_tree = ContainerTree.getInstance()
machine_node = container_tree.machines[global_stack.definition.getId()]
nodes: Set[MaterialNode] = set()
for extruder in global_stack.extruderList:
active_variant_name = extruder.variant.getMetaDataEntry("name")
if active_variant_name not in machine_node.variants:
Logger.log("w", "Could not find the variant %s", active_variant_name)
continue
active_variant_node = machine_node.variants[active_variant_name]
active_material_node = active_variant_node.materials.get(extruder.material.getMetaDataEntry("base_file"))
if active_material_node is None:
Logger.log("w", "Could not find the material %s", extruder.material.getMetaDataEntry("base_file"))
continue
nodes.add(active_material_node)
return nodes
def _getIntentsForMaterial(self, active_material_node: "MaterialNode", quality_groups: Dict[str, "QualityGroup"]) -> List[Dict[str, Any]]:
extruder_intents: List[Dict[str, Any]] = []
for quality_id, quality_node in active_material_node.qualities.items():
if quality_node.quality_type not in quality_groups: # Don't add the empty quality type (or anything else that would crash, defensively).
continue
quality_group = quality_groups[quality_node.quality_type]
if not quality_group.is_available:
continue
layer_height = fetchLayerHeight(quality_group)
for intent_id, intent_node in quality_node.intents.items():
if intent_node.intent_category != self._intent_category:
continue
extruder_intents.append({"name": quality_group.name,
"display_text": f"<b>{quality_group.name}</b> - {layer_height}mm",
"quality_type": quality_group.quality_type,
"layer_height": layer_height,
"intent_category": self._intent_category
})
return extruder_intents

View File

@ -0,0 +1,129 @@
# Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import collections
from typing import OrderedDict, Optional
from PyQt6.QtCore import Qt, QTimer, QObject
import cura
from UM import i18nCatalog
from UM.Logger import Logger
from UM.Qt.ListModel import ListModel
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.Interfaces import ContainerInterface
from cura.Settings.IntentManager import IntentManager
catalog = i18nCatalog("cura")
class IntentSelectionModel(ListModel):
NameRole = Qt.ItemDataRole.UserRole + 1
IntentCategoryRole = Qt.ItemDataRole.UserRole + 2
WeightRole = Qt.ItemDataRole.UserRole + 3
DescriptionRole = Qt.ItemDataRole.UserRole + 4
IconRole = Qt.ItemDataRole.UserRole + 5
def __init__(self, parent: Optional[QObject] = None) -> None:
super().__init__(parent)
self.addRoleName(self.NameRole, "name")
self.addRoleName(self.IntentCategoryRole, "intent_category")
self.addRoleName(self.WeightRole, "weight")
self.addRoleName(self.DescriptionRole, "description")
self.addRoleName(self.IconRole, "icon")
application = cura.CuraApplication.CuraApplication.getInstance()
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChange)
ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChange)
machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager()
machine_manager.activeMaterialChanged.connect(self._update)
machine_manager.activeVariantChanged.connect(self._update)
machine_manager.extruderChanged.connect(self._update)
extruder_manager = application.getExtruderManager()
extruder_manager.extrudersChanged.connect(self._update)
self._update_timer: QTimer = QTimer()
self._update_timer.setInterval(100)
self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self._update)
self._onChange()
@staticmethod
def _getDefaultProfileInformation() -> OrderedDict[str, dict]:
""" Default information user-visible string. Ordered by weight. """
default_profile_information = collections.OrderedDict()
default_profile_information["default"] = {
"name": catalog.i18nc("@label", "Default"),
"icon": "GearCheck"
}
default_profile_information["visual"] = {
"name": catalog.i18nc("@label", "Visual"),
"description": catalog.i18nc("@text", "The visual profile is designed to print visual prototypes and models with the intent of high visual and surface quality."),
"icon" : "Visual"
}
default_profile_information["engineering"] = {
"name": catalog.i18nc("@label", "Engineering"),
"description": catalog.i18nc("@text", "The engineering profile is designed to print functional prototypes and end-use parts with the intent of better accuracy and for closer tolerances."),
"icon": "Nut"
}
default_profile_information["quick"] = {
"name": catalog.i18nc("@label", "Draft"),
"description": catalog.i18nc("@text", "The draft profile is designed to print initial prototypes and concept validation with the intent of significant print time reduction."),
"icon": "SpeedOMeter"
}
return default_profile_information
def _onContainerChange(self, container: ContainerInterface) -> None:
"""Updates the list of intents if an intent profile was added or removed."""
if container.getMetaDataEntry("type") == "intent":
self._update()
def _onChange(self) -> None:
self._update_timer.start()
def _update(self) -> None:
Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__))
global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
if global_stack is None:
self.setItems([])
Logger.log("d", "No active GlobalStack, set quality profile model as empty.")
return
# Check for material compatibility
if not cura.CuraApplication.CuraApplication.getInstance().getMachineManager().activeMaterialsCompatible():
Logger.log("d", "No active material compatibility, set quality profile model as empty.")
self.setItems([])
return
default_profile_info = self._getDefaultProfileInformation()
available_categories = IntentManager.getInstance().currentAvailableIntentCategories()
result = []
for i, category in enumerate(available_categories):
profile_info = default_profile_info.get(category, {})
try:
weight = list(default_profile_info.keys()).index(category)
except ValueError:
weight = len(available_categories) + i
result.append({
"name": profile_info.get("name", category.title()),
"description": profile_info.get("description", None),
"icon" : profile_info.get("icon", ""),
"intent_category": category,
"weight": weight,
})
result.sort(key=lambda k: k["weight"])
self.setItems(result)

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019 Ultimaker B.V. # Copyright (c) 2022 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot from PyQt6.QtCore import QObject, pyqtProperty, pyqtSignal, pyqtSlot
@ -8,6 +8,7 @@ from UM.Logger import Logger
from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.InstanceContainer import InstanceContainer
import cura.CuraApplication import cura.CuraApplication
from UM.Signal import Signal
from cura.Machines.ContainerTree import ContainerTree from cura.Machines.ContainerTree import ContainerTree
from cura.Settings.cura_empty_instance_containers import empty_intent_container from cura.Settings.cura_empty_instance_containers import empty_intent_container
@ -29,6 +30,7 @@ class IntentManager(QObject):
return cls.__instance return cls.__instance
intentCategoryChanged = pyqtSignal() #Triggered when we switch categories. intentCategoryChanged = pyqtSignal() #Triggered when we switch categories.
intentCategoryChangedSignal = Signal()
def intentMetadatas(self, definition_id: str, nozzle_name: str, material_base_file: str) -> List[Dict[str, Any]]: def intentMetadatas(self, definition_id: str, nozzle_name: str, material_base_file: str) -> List[Dict[str, Any]]:
"""Gets the metadata dictionaries of all intent profiles for a given """Gets the metadata dictionaries of all intent profiles for a given
@ -189,3 +191,4 @@ class IntentManager(QObject):
application.getMachineManager().setQualityGroupByQualityType(quality_type) application.getMachineManager().setQualityGroupByQualityType(quality_type)
if old_intent_category != intent_category: if old_intent_category != intent_category:
self.intentCategoryChanged.emit() self.intentCategoryChanged.emit()
self.intentCategoryChangedSignal.emit()

View File

@ -1778,3 +1778,31 @@ class MachineManager(QObject):
abbr_machine += stripped_word abbr_machine += stripped_word
return abbr_machine return abbr_machine
@pyqtSlot(str, str, result = bool)
def intentCategoryHasQuality(self, intent_category: str, quality_type: str) -> bool:
""" Checks if there are any quality groups for active extruders that have an intent category """
quality_groups = ContainerTree.getInstance().getCurrentQualityGroups()
if quality_type in quality_groups:
quality_group = quality_groups[quality_type]
for node in quality_group.nodes_for_extruders.values():
if any(intent.intent_category == intent_category for intent in node.intents.values()):
return True
return False
@pyqtSlot(str, result = str)
def getDefaultQualityTypeForIntent(self, intent_category) -> str:
""" If there is an intent category for the default machine quality return it, otherwise return the first quality for this intent category """
machine = ContainerTree.getInstance().machines.get(self._global_container_stack.definition.getId())
if self.intentCategoryHasQuality(intent_category, machine.preferred_quality_type):
return machine.preferred_quality_type
for quality_type, quality_group in ContainerTree.getInstance().getCurrentQualityGroups().items():
for node in quality_group.nodes_for_extruders.values():
if any(intent.intent_category == intent_category for intent in node.intents.values()):
return quality_type
return ""

View File

@ -22,6 +22,7 @@ Item
property alias elide: label.elide property alias elide: label.elide
property real margin: UM.Theme.getSize("narrow_margin").width property real margin: UM.Theme.getSize("narrow_margin").width
property alias wrapMode: label.wrapMode property alias wrapMode: label.wrapMode
property real spacing: UM.Theme.getSize("narrow_margin").width
// These properties can be used in combination with layouts. // These properties can be used in combination with layouts.
readonly property real contentWidth: icon.width + margin + label.contentWidth readonly property real contentWidth: icon.width + margin + label.contentWidth
@ -61,6 +62,7 @@ Item
top: parent.top top: parent.top
bottom: parent.bottom bottom: parent.bottom
rightMargin: 0 rightMargin: 0
leftMargin: spacing
margins: margin margins: margin
} }
} }

View File

@ -0,0 +1,108 @@
import QtQuick 2.10
import UM 1.6 as UM
import Cura 1.6 as Cura
Rectangle
{
height: visible ? UM.Theme.getSize("action_button_icon").height : 0
visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.MachineManager.hasCustomQuality
anchors.topMargin: UM.Theme.getSize("default_margin")
anchors.bottomMargin: UM.Theme.getSize("default_margin")
Rectangle
{
id: warningIcon
color: UM.Theme.getColor("um_yellow_5")
height: UM.Theme.getSize("action_button_icon").height
width: height
radius: width
anchors
{
left: parent.left
verticalCenter: parent.verticalCenter
}
UM.ColorImage
{
height: UM.Theme.getSize("action_button_icon").height
width: height
source: UM.Theme.getIcon("Warning", "low")
}
}
UM.Label
{
id: warning
anchors
{
left: warningIcon.right
verticalCenter: parent.verticalCenter
leftMargin: UM.Theme.getSize("thin_margin").width
}
text: ""
states: [
State
{
name: "settings changed and custom quality"
when: Cura.SimpleModeSettingsManager.isProfileCustomized && Cura.MachineManager.hasCustomQuality
PropertyChanges
{
target: warning
text: {
var profile_name = Cura.MachineManager.activeQualityChangesGroup.name
return "<b>%1</b> %2".arg(profile_name).arg(catalog.i18nc("@info", "custom profile is active and you overwrote some settings."))
}
}
},
State
{
name: "custom quality"
when: Cura.MachineManager.hasCustomQuality
PropertyChanges
{
target: warning
text: {
var profile_name = Cura.MachineManager.activeQualityChangesGroup.name
return "<b>%1</b> %2".arg(profile_name).arg(catalog.i18nc("@info", "custom profile is overriding some settings."))
}
}
},
State
{
name: "settings changed"
when: Cura.SimpleModeSettingsManager.isProfileCustomized
PropertyChanges
{
target: warning
text: catalog.i18nc("@info", "Some settings were changed.")
}
}
]
}
UM.SimpleButton
{
id: resetToDefaultQualityButton
height: UM.Theme.getSize("action_button_icon").height
width: height
iconSource: UM.Theme.getIcon("ArrowReset")
anchors
{
right: parent.right
verticalCenter: parent.verticalCenter
}
color: UM.Theme.getColor("accent_1")
onClicked:
{
Cura.MachineManager.resetToUseDefaultQuality()
}
}
}

View File

@ -1,10 +1,11 @@
// Copyright (c) 2018 Ultimaker B.V. //Copyright (c) 2022 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher. //Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.10 import QtQuick 2.10
import QtQuick.Layouts 1.1
import UM 1.2 as UM import UM 1.6 as UM
import Cura 1.0 as Cura import Cura 1.6 as Cura
Item Item
{ {
@ -13,11 +14,11 @@ Item
height: childrenRect.height + 2 * padding height: childrenRect.height + 2 * padding
property bool settingsEnabled: Cura.ExtruderManager.activeExtruderStackId || extrudersEnabledCount.properties.value == 1 property bool settingsEnabled: Cura.ExtruderManager.activeExtruderStackId || extrudersEnabledCount.properties.value == 1
property real padding: UM.Theme.getSize("thick_margin").width property real padding: UM.Theme.getSize("default_margin").width
Column ColumnLayout
{ {
spacing: UM.Theme.getSize("wide_margin").height spacing: UM.Theme.getSize("default_margin").height
anchors anchors
{ {
@ -30,11 +31,53 @@ Item
// TODO // TODO
property real firstColumnWidth: Math.round(width / 3) property real firstColumnWidth: Math.round(width / 3)
UM.Label
{
text: catalog.i18nc("@label", "Profiles")
font: UM.Theme.getFont("medium")
}
RecommendedQualityProfileSelector RecommendedQualityProfileSelector
{ {
width: parent.width width: parent.width
// TODO Create a reusable component with these properties to not define them separately for each component visible: recommendedResolutionSelector.visible
labelColumnWidth: parent.firstColumnWidth }
RecommendedResolutionSelector
{
id: recommendedResolutionSelector
Layout.fillWidth: true
width: parent.width
}
UnsupportedProfileIndication
{
width: parent.width
visible: !recommendedResolutionSelector.visible
}
ProfileWarningReset
{
width: parent.width
Layout.fillWidth: true
}
//Line between the sections.
Rectangle
{
width: parent.width
height: UM.Theme.getSize("default_lining").height
Layout.topMargin: UM.Theme.getSize("narrow_margin").height
Layout.bottomMargin: UM.Theme.getSize("narrow_margin").height
Layout.fillWidth: true
color: UM.Theme.getColor("lining")
}
UM.Label
{
text: catalog.i18nc("@label", "Print settings")
font: UM.Theme.getFont("medium")
} }
RecommendedInfillDensitySelector RecommendedInfillDensitySelector
@ -42,6 +85,9 @@ Item
width: parent.width width: parent.width
// TODO Create a reusable component with these properties to not define them separately for each component // TODO Create a reusable component with these properties to not define them separately for each component
labelColumnWidth: parent.firstColumnWidth labelColumnWidth: parent.firstColumnWidth
Layout.fillWidth: true
Layout.leftMargin: UM.Theme.getSize("default_margin").width
Layout.rightMargin: UM.Theme.getSize("default_margin").width
} }
RecommendedSupportSelector RecommendedSupportSelector
@ -49,6 +95,7 @@ Item
width: parent.width width: parent.width
// TODO Create a reusable component with these properties to not define them separately for each component // TODO Create a reusable component with these properties to not define them separately for each component
labelColumnWidth: parent.firstColumnWidth labelColumnWidth: parent.firstColumnWidth
Layout.leftMargin: UM.Theme.getSize("default_margin").width
} }
RecommendedAdhesionSelector RecommendedAdhesionSelector
@ -56,6 +103,7 @@ Item
width: parent.width width: parent.width
// TODO Create a reusable component with these properties to not define them separately for each component // TODO Create a reusable component with these properties to not define them separately for each component
labelColumnWidth: parent.firstColumnWidth labelColumnWidth: parent.firstColumnWidth
Layout.leftMargin: UM.Theme.getSize("default_margin").width
} }
} }

View File

@ -3,180 +3,46 @@
import QtQuick 2.10 import QtQuick 2.10
import QtQuick.Controls 2.3 import QtQuick.Controls 2.3
import QtQuick.Layouts 2.10
import UM 1.5 as UM import UM 1.5 as UM
import Cura 1.6 as Cura import Cura 1.7 as Cura
import ".." import ".."
Item Item
{ {
id: qualityRow id: qualityRow
height: childrenRect.height height: childrenRect.height
visible: intentSelectionRepeater.count > 1 //Only show selector if there's more options than just "default".
property real labelColumnWidth: Math.round(width / 3) RowLayout
property real settingsColumnWidth: width - labelColumnWidth
// Here are the elements that are shown in the left column
Column
{ {
anchors id: intentRow
{ width: parent.width
left: parent.left
right: parent.right
}
spacing: UM.Theme.getSize("default_margin").height
ButtonGroup
{
id: activeProfileButtonGroup
exclusive: true
onClicked: Cura.IntentManager.selectIntent(button.modelData.intent_category, button.modelData.quality_type)
}
Item
{
height: childrenRect.height
anchors
{
left: parent.left
right: parent.right
}
Cura.IconWithText
{
id: profileLabel
source: UM.Theme.getIcon("PrintQuality")
text: catalog.i18nc("@label", "Profiles")
font: UM.Theme.getFont("medium")
width: labelColumnWidth
iconSize: UM.Theme.getSize("medium_button_icon").width
}
UM.SimpleButton
{
id: resetToDefaultQualityButton
visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.MachineManager.hasCustomQuality
height: visible ? UM.Theme.getSize("print_setup_icon").height : 0
width: height
anchors
{
right: profileLabel.right
rightMargin: UM.Theme.getSize("default_margin").width
leftMargin: UM.Theme.getSize("default_margin").width
verticalCenter: parent.verticalCenter
}
color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
iconSource: UM.Theme.getIcon("ArrowReset")
onClicked:
{
// if the current profile is user-created, switch to a built-in quality
Cura.MachineManager.resetToUseDefaultQuality()
}
onEntered:
{
var tooltipContent = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.")
base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, 0), tooltipContent)
}
onExited: base.hideTooltip()
}
Cura.LabelBar
{
id: labelbar
anchors
{
left: profileLabel.right
right: parent.right
verticalCenter: profileLabel.verticalCenter
}
model: Cura.QualityProfilesDropDownMenuModel
modelKey: "layer_height"
}
}
Repeater Repeater
{ {
model: Cura.IntentCategoryModel {} id: intentSelectionRepeater
Item model: Cura.IntentSelectionModel {}
{
anchors
{
left: parent.left
right: parent.right
}
height: intentCategoryLabel.height
UM.Label RecommendedQualityProfileSelectorButton
{ {
id: intentCategoryLabel profileName: model.name
text: model.name icon: model.icon
width: labelColumnWidth - UM.Theme.getSize("section_icon").width
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("section_icon").width + UM.Theme.getSize("narrow_margin").width
font: UM.Theme.getFont("medium")
elide: Text.ElideRight
}
Cura.RadioCheckbar
{
anchors
{
left: intentCategoryLabel.right
right: parent.right
}
dataModel: model["qualities"]
buttonGroup: activeProfileButtonGroup
function checkedFunction(modelItem) selected: Cura.MachineManager.activeIntentCategory == model.intent_category
{
if(Cura.MachineManager.hasCustomQuality)
{
// When user created profile is active, no quality tickbox should be active.
return false
}
if(modelItem === null) onClicked: {
var qualityType
if (Cura.MachineManager.intentCategoryHasQuality(model.intent_category, Cura.MachineManager.activeQualityType))
{ {
return false qualityType = Cura.MachineManager.activeQualityType
} } else {
return Cura.MachineManager.activeQualityType == modelItem.quality_type && Cura.MachineManager.activeIntentCategory == modelItem.intent_category qualityType = Cura.MachineManager.getDefaultQualityTypeForIntent(model.intent_category)
} print(Cura.MachineManager.getDefaultQualityTypeForIntent(model.intent_category))
isCheckedFunction: checkedFunction
}
MouseArea // Intent description tooltip hover area
{
id: intentDescriptionHoverArea
anchors.fill: parent
hoverEnabled: true
enabled: model.description !== undefined
acceptedButtons: Qt.NoButton // react to hover only, don't steal clicks
Timer
{
id: intentTooltipTimer
interval: 500
running: false
repeat: false
onTriggered: base.showTooltip(
intentCategoryLabel,
Qt.point(-(intentCategoryLabel.x - qualityRow.x) - UM.Theme.getSize("thick_margin").width, 0),
model.description
)
}
onEntered: intentTooltipTimer.start()
onExited:
{
base.hideTooltip()
intentTooltipTimer.stop()
} }
Cura.IntentManager.selectIntent(model.intent_category, qualityType)
} }
} }
} }

View File

@ -0,0 +1,93 @@
// Copyright (c) 2022 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Layouts 2.10
import UM 1.5 as UM
import Cura 1.7 as Cura
Rectangle
{
id: base
height: 60
Layout.fillWidth: true
color: mouseArea.containsMouse || selected ? UM.Theme.getColor("background_3") : UM.Theme.getColor("background_1")
property bool selected: false
property string profileName: ""
property string icon: ""
signal clicked()
MouseArea
{
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: base.clicked()
}
Item
{
width: intentIcon.width
anchors
{
top: parent.top
bottom: qualityLabel.top
horizontalCenter: parent.horizontalCenter
topMargin: UM.Theme.getSize("narrow_margin").height
}
Item
{
id: intentIcon
width: UM.Theme.getSize("recommended_button_icon").width
height: UM.Theme.getSize("recommended_button_icon").height
UM.ColorImage
{
anchors.fill: parent
anchors.centerIn: parent
visible: icon != ""
source: UM.Theme.getIcon(icon)
color: UM.Theme.getColor("icon")
}
Rectangle
{
id: circle
anchors.fill: parent
radius: width
anchors.verticalCenter: parent.verticalCenter
visible: icon == ""
border.width: UM.Theme.getSize("thick_lining").width
border.color: UM.Theme.getColor("text")
UM.Label
{
id: initialLabel
anchors.centerIn: parent
text: profileName.charAt(0).toUpperCase()
font: UM.Theme.getFont("small_bold")
horizontalAlignment: Text.AlignHCenter
}
}
}
}
UM.Label
{
id: qualityLabel
text: profileName
anchors
{
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
bottomMargin: UM.Theme.getSize("narrow_margin").height
}
}
}

View File

@ -0,0 +1,84 @@
// Copyright (c) 2022 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.10
import UM 1.6 as UM
import Cura 1.7 as Cura
Item
{
id: recommendedResolutionSelector
height: childrenRect.height
property real labelColumnWidth: Math.round(width / 3)
property string _previousResolution: "" //Internal variable to detect changes.
Component.onCompleted: _previousResolution = Cura.MachineManager.activeQualityType;
visible: visibilityPreset.count > 0 //Only show if there are quality types to select from.
Cura.IconWithText
{
id: resolutionTitle
anchors.top: parent.top
anchors.left: parent.left
source: UM.Theme.getIcon("PrintQuality")
text: catalog.i18nc("@label", "Resolution")
width: labelColumnWidth
height: parent.height
spacing: UM.Theme.getSize("thick_margin").width
iconSize: UM.Theme.getSize("medium_button_icon").width
}
Cura.ComboBox
{
id: visibilityPreset
implicitHeight: UM.Theme.getSize("combobox").height
implicitWidth: UM.Theme.getSize("combobox").width
anchors
{
top: parent.top
right: parent.right
}
textRole: "display_text"
textFormat: Text.StyledText
model: Cura.ActiveIntentQualitiesModel{}
currentIndex:
{
var current_quality_type = Cura.MachineManager.activeQualityType
var index = 0
for (var i = 0; i < model.count; i++)
{
if (model.getItem(i).quality_type == current_quality_type)
{
index = i
break
}
}
return index
}
onActivated:
{
var selected_item = model.getItem(currentIndex)
Cura.IntentManager.selectIntent(selected_item.intent_category, selected_item.quality_type)
}
Connections
{
target: Cura.IntentManager
function onIntentCategoryChanged()
{
if(recommendedResolutionSelector._previousResolution !== Cura.MachineManager.activeQualityType)
{
visibilityPreset.pulse();
}
recommendedResolutionSelector._previousResolution = Cura.MachineManager.activeQualityType;
}
}
}
}

View File

@ -0,0 +1,54 @@
//Copyright (c) 2022 Ultimaker B.V.
//Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.15
import Cura 1.6 as Cura
import UM 1.6 as UM
//Message showing the user that the configuration they have selected has no profiles.
Column
{
spacing: UM.Theme.getSize("default_margin").height
Row
{
width: parent.width
spacing: UM.Theme.getSize("thin_margin").width
UM.StatusIcon
{
width: UM.Theme.getSize("notification_icon").width
status: UM.StatusIcon.Status.ERROR
}
UM.Label
{
width: parent.width
font: UM.Theme.getFont("default_bold")
text: catalog.i18nc("@error", "Configuration not supported")
}
}
UM.Label
{
width: parent.width
text: catalog.i18nc("@message:text %1 is the name the printer uses for 'nozzle'.", "No profiles are available for the selected material/%1 configuration. Please change your configuration."
).arg(Cura.MachineManager.activeDefinitionVariantsName)
}
Cura.TertiaryButton
{
anchors.right: parent.right
text: catalog.i18nc("@button:label", "Learn more")
textFont: UM.Theme.getFont("default")
iconSource: UM.Theme.getIcon("LinkExternal")
isIconOnRightSide: true
onClicked: Qt.openUrlExternally("https://support.ultimaker.com/hc/en-us/articles/360012909099")
}
}

View File

@ -17,6 +17,8 @@ ComboBox
property var defaultTextOnEmptyModel: catalog.i18nc("@label", "No items to select from") // Text displayed in the combobox when the model is empty property var defaultTextOnEmptyModel: catalog.i18nc("@label", "No items to select from") // Text displayed in the combobox when the model is empty
property var defaultTextOnEmptyIndex: "" // Text displayed in the combobox when the model has items but no item is selected property var defaultTextOnEmptyIndex: "" // Text displayed in the combobox when the model has items but no item is selected
property alias textFormat: contentLabel.textFormat
enabled: delegateModel.count > 0 enabled: delegateModel.count > 0
onVisibleChanged: { popup.close() } onVisibleChanged: { popup.close() }
@ -52,7 +54,34 @@ ComboBox
} }
] ]
background: UM.UnderlineBackground{} background: UM.UnderlineBackground
{
//Rectangle for highlighting when this combobox needs to pulse.
Rectangle
{
anchors.fill: parent
opacity: 0
color: UM.Theme.getColor("warning")
SequentialAnimation on opacity
{
id: pulseAnimation
running: false
loops: 1
alwaysRunToEnd: true
PropertyAnimation
{
to: 1
duration: 300
}
PropertyAnimation
{
to: 0
duration : 2000
}
}
}
}
indicator: UM.ColorImage indicator: UM.ColorImage
{ {
@ -146,7 +175,7 @@ ComboBox
anchors.rightMargin: UM.Theme.getSize("setting_unit_margin").width anchors.rightMargin: UM.Theme.getSize("setting_unit_margin").width
text: delegateItem.text text: delegateItem.text
textFormat: Text.PlainText textFormat: control.textFormat
color: UM.Theme.getColor("setting_control_text") color: UM.Theme.getColor("setting_control_text")
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.NoWrap wrapMode: Text.NoWrap
@ -162,4 +191,9 @@ ComboBox
text: delegateLabel.truncated ? delegateItem.text : "" text: delegateLabel.truncated ? delegateItem.text : ""
} }
} }
function pulse()
{
pulseAnimation.restart();
}
} }

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M19 12C19 11.895 18.9888 11.7928 18.9843 11.6889L21.2354 9.9966L18.3525 5.0034L15.7525 6.1005C15.5837 5.9927 15.4112 5.8905 15.2325 5.7972L14.8828 3H9.11725L8.76725 5.7972C8.58555 5.8923 8.40965 5.9964 8.23725 6.1066L5.64725 5.0034L2.76465 9.9966L5.01525 11.7C5.01095 11.8 5.00005 11.8988 5.00005 12C5.00005 12.105 5.01125 12.2072 5.01575 12.3111L2.76465 14.0034L5.64755 18.9966L8.24755 17.8994C8.41635 18.0072 8.58885 18.1094 8.76755 18.2028L9.11725 21H14.8828L15.2328 18.2028C15.4145 18.1077 15.5904 18.0036 15.7628 17.8934L18.3528 18.9966L21.2357 14.0034L18.9848 12.3C18.9891 12.2 19 12.1012 19 12ZM18.62 14.5327L17.5028 16.4673L15.4513 15.6018C14.8703 16.1531 14.1649 16.5564 13.3949 16.7773L13.1172 19H10.8828L10.605 16.7773C9.8351 16.5564 9.12973 16.1531 8.54865 15.6018L6.49715 16.4673L5.38005 14.5327L7.15795 13.1865C6.94757 12.4095 6.94757 11.5905 7.15795 10.8135L5.38005 9.4673L6.49715 7.5327L8.54865 8.3982C9.12973 7.84686 9.8351 7.44364 10.605 7.2227L10.8828 5H13.1172L13.395 7.2227C14.165 7.44364 14.8704 7.84686 15.4514 8.3982L17.5029 7.5327L18.62 9.4673L16.8421 10.8135C17.0525 11.5905 17.0525 12.4095 16.8421 13.1865L18.62 14.5327Z"/>
<path d="M11 15.414L8.29297 12.707L9.70697 11.293L11 12.586L14.293 9.29297L15.707 10.707L11 15.414Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<g>
<path d="M17,3.3H7L2,12l5,8.7h10l5-8.7L17,3.3z M15.8,18.7H8.2L4.3,12l3.8-6.7h7.7l3.8,6.7L15.8,18.7z"/>
<path d="M12,7c-2.8,0-5,2.2-5,5s2.2,5,5,5s5-2.2,5-5S14.8,7,12,7z M12,15c-1.7,0-3-1.3-3-3s1.3-3,3-3s3,1.3,3,3S13.7,15,12,15z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 498 B

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<g>
<path d="M18,3H6C4.3,3,3,4.3,3,6v12c0,1.7,1.3,3,3,3h12c1.7,0,3-1.3,3-3V6C21,4.3,19.7,3,18,3z M19,19H5v-2.6l4-4l5,5l3-3l2,2V19z
M19,13.6l-2-2l-3,3l-5-5l-4,4V5h14V13.6z"/>
<circle cx="15.5" cy="8.5" r="1.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 478 B

View File

@ -133,6 +133,11 @@
"weight": 400, "weight": 400,
"family": "Noto Sans" "family": "Noto Sans"
}, },
"small_bold": {
"size": 0.9,
"weight": 700,
"family": "Noto Sans"
},
"small_ja_JP": { "small_ja_JP": {
"size": 0.9, "size": 0.9,
"weight": 400, "weight": 400,
@ -637,6 +642,10 @@
"marketplace_large_icon": [4.0, 4.0], "marketplace_large_icon": [4.0, 4.0],
"preferences_page_list_item": [8.0, 2.0] "preferences_page_list_item": [8.0, 2.0],
"recommended_button_icon": [1.7, 1.7],
"reset_profile_icon": [1, 1]
} }
} }