diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 1680e7c6a6..2d1c35aca7 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -270,8 +270,9 @@ class CuraApplication(QtApplication): empty_quality_container = copy.deepcopy(empty_container) empty_quality_container._id = "empty_quality" empty_quality_container.setName("Not Supported") - empty_quality_container.addMetaDataEntry("quality_type", "normal") + empty_quality_container.addMetaDataEntry("quality_type", "not_supported") empty_quality_container.addMetaDataEntry("type", "quality") + empty_quality_container.addMetaDataEntry("supported", False) ContainerRegistry.getInstance().addContainer(empty_quality_container) empty_quality_changes_container = copy.deepcopy(empty_container) empty_quality_changes_container._id = "empty_quality_changes" diff --git a/cura/QualityManager.py b/cura/QualityManager.py index b6d47d919b..0312c50daf 100644 --- a/cura/QualityManager.py +++ b/cura/QualityManager.py @@ -87,7 +87,7 @@ class QualityManager: qualities = set(quality_type_dict.values()) for material_container in material_containers[1:]: next_quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_container) - qualities.update(set(next_quality_type_dict.values())) + qualities.intersection_update(set(next_quality_type_dict.values())) return list(qualities) @@ -178,12 +178,32 @@ class QualityManager: def findAllUsableQualitiesForMachineAndExtruders(self, global_container_stack: "GlobalStack", extruder_stacks: List["ExtruderStack"]) -> List[InstanceContainer]: global_machine_definition = global_container_stack.getBottom() + machine_manager = Application.getInstance().getMachineManager() + active_stack_id = machine_manager.activeStackId + + materials = [] + + # TODO: fix this if extruder_stacks: - # Multi-extruder machine detected. - materials = [stack.material for stack in extruder_stacks] + # Multi-extruder machine detected + for stack in extruder_stacks: + if stack.getId() == active_stack_id and machine_manager.newMaterial: + materials.append(machine_manager.newMaterial) + else: + materials.append(stack.material) else: - # Machine with one extruder. - materials = [global_container_stack.material] + # Machine with one extruder + if global_container_stack.getId() == active_stack_id and machine_manager.newMaterial: + materials.append(machine_manager.newMaterial) + else: + materials.append(global_container_stack.material) + + # if extruder_stacks: + # # Multi-extruder machine detected. + # materials = [stack.material for stack in extruder_stacks] + # else: + # # Machine with one extruder. + # materials = [global_container_stack.material] quality_types = self.findAllQualityTypesForMachineAndMaterials(global_machine_definition, materials) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index fc5c415f87..131160c8af 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -50,6 +50,7 @@ class MachineManager(QObject): # Used to store the new containers until after confirming the dialog self._new_variant_container = None self._new_material_container = None + self._new_quality_containers = [] self._error_check_timer = QTimer() self._error_check_timer.setInterval(250) @@ -70,10 +71,10 @@ class MachineManager(QObject): self._stacks_have_errors = None - self._empty_variant_container = ContainerRegistry.getInstance().getEmptyInstanceContainer() - self._empty_material_container = ContainerRegistry.getInstance().getEmptyInstanceContainer() - self._empty_quality_container = ContainerRegistry.getInstance().getEmptyInstanceContainer() - self._empty_quality_changes_container = ContainerRegistry.getInstance().getEmptyInstanceContainer() + self._empty_variant_container = ContainerRegistry.getInstance().findContainers(id = "empty_variant")[0] + self._empty_material_container = ContainerRegistry.getInstance().findContainers(id = "empty_material")[0] + self._empty_quality_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0] + self._empty_quality_changes_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality_changes")[0] self._onGlobalContainerChanged() @@ -147,6 +148,14 @@ class MachineManager(QObject): self.outputDevicesChanged.emit() + @property + def newVariant(self): + return self._new_variant_container + + @property + def newMaterial(self): + return self._new_material_container + @pyqtProperty("QVariantList", notify = outputDevicesChanged) def printerOutputDevices(self): return self._printer_output_devices @@ -818,6 +827,7 @@ class MachineManager(QObject): if old_material: preferred_material_name = old_material.getName() preferred_material_id = self._updateMaterialContainer(self._global_container_stack.getBottom(), self._global_container_stack, containers[0], preferred_material_name).id + Logger.log("d", "preferred_material_id=%s", preferred_material_id) self.setActiveMaterial(preferred_material_id) else: Logger.log("w", "While trying to set the active variant, no variant was found to replace.") @@ -833,8 +843,6 @@ class MachineManager(QObject): if not containers or not self._global_container_stack: return - Logger.log("d", "Attempting to change the active quality to %s", quality_id) - # Quality profile come in two flavours: type=quality and type=quality_changes # If we found a quality_changes profile then look up its parent quality profile. container_type = containers[0].getMetaDataEntry("type") @@ -854,20 +862,36 @@ class MachineManager(QObject): if new_quality_settings_list is None: return - name_changed_connect_stacks = [] # Connect these stacks to the name changed callback + # check if any of the stacks have a not supported profile + # if that is the case, all stacks should have a not supported state (otherwise it will show quality_type normal) + has_not_supported_quality = False + + # check all stacks for not supported + for setting_info in new_quality_settings_list: + if setting_info["quality"].getMetaDataEntry("quality_type") == "not_supported": + has_not_supported_quality = True + break + + # set all stacks to not supported if that's the case + if has_not_supported_quality: + for setting_info in new_quality_settings_list: + setting_info["quality"] = self._empty_quality_container + + self._new_quality_containers.clear() + + # store the upcoming quality profile changes per stack for later execution + # this prevents re-slicing before the user has made a choice in the discard or keep dialog + # (see _executeDelayedActiveContainerStackChanges) for setting_info in new_quality_settings_list: stack = setting_info["stack"] stack_quality = setting_info["quality"] stack_quality_changes = setting_info["quality_changes"] - name_changed_connect_stacks.append(stack_quality) - name_changed_connect_stacks.append(stack_quality_changes) - self._replaceQualityOrQualityChangesInStack(stack, stack_quality, postpone_emit=True) - self._replaceQualityOrQualityChangesInStack(stack, stack_quality_changes, postpone_emit=True) - - # Connect to onQualityNameChanged - for stack in name_changed_connect_stacks: - stack.nameChanged.connect(self._onQualityNameChanged) + self._new_quality_containers.append({ + "stack": stack, + "quality": stack_quality, + "quality_changes": stack_quality_changes + }) has_user_interaction = False @@ -890,13 +914,24 @@ class MachineManager(QObject): # before the user decided to keep or discard any of their changes using the dialog. # The Application.onDiscardOrKeepProfileChangesClosed signal triggers this method. def _executeDelayedActiveContainerStackChanges(self): + if self._new_variant_container is not None: + self._active_container_stack.variant = self._new_variant_container + self._new_variant_container = None + if self._new_material_container is not None: self._active_container_stack.material = self._new_material_container self._new_material_container = None - if self._new_variant_container is not None: - self._active_container_stack.variant = self._new_variant_container - self._new_variant_container = None + # apply the new quality to all stacks + if self._new_quality_containers: + for new_quality in self._new_quality_containers: + self._replaceQualityOrQualityChangesInStack(new_quality["stack"], new_quality["quality"], postpone_emit = True) + self._replaceQualityOrQualityChangesInStack(new_quality["stack"], new_quality["quality_changes"], postpone_emit = True) + + for new_quality in self._new_quality_containers: + new_quality["stack"].nameChanged.connect(self._onQualityNameChanged) + + self._new_quality_containers.clear() ## Cancel set changes for material and variant in the active container stack. # Used for ignoring any changes when switching between printers (setActiveMachine) @@ -926,8 +961,14 @@ class MachineManager(QObject): for stack in stacks: material = stack.material + + # TODO: fix this + if self._new_material_container and stack.getId() == self._active_container_stack.getId(): + material = self._new_material_container + quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material]) - if not quality: #No quality profile is found for this quality type. + if not quality: + # No quality profile is found for this quality type. quality = self._empty_quality_container result.append({"stack": stack, "quality": quality, "quality_changes": empty_quality_changes}) @@ -962,8 +1003,12 @@ class MachineManager(QObject): else: Logger.log("e", "Could not find the global quality changes container with name %s", quality_changes_name) return None + material = global_container_stack.material + if self._new_material_container and self._active_container_stack.getId() == global_container_stack.getId(): + material = self._new_material_container + # For the global stack, find a quality which matches the quality_type in # the quality changes profile and also satisfies any material constraints. quality_type = global_quality_changes.getMetaDataEntry("quality_type") @@ -990,6 +1035,10 @@ class MachineManager(QObject): quality_changes = self._empty_quality_changes_container material = stack.material + + if self._new_material_container and self._active_container_stack.getId() == stack.getId(): + material = self._new_material_container + quality = quality_manager.findQualityByQualityType(quality_type, global_machine_definition, [material]) if not quality: #No quality profile found for this quality type. quality = self._empty_quality_container diff --git a/cura/Settings/NotSupportedProfileContainer.py b/cura/Settings/NotSupportedProfileContainer.py deleted file mode 100644 index 156f13ba4a..0000000000 --- a/cura/Settings/NotSupportedProfileContainer.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -from UM.Signal import signalemitter - -from UM.Settings.InstanceContainer import InstanceContainer -from UM.Settings.ContainerRegistry import ContainerRegistry -from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase - - -## A container for not supported profiles. -# -# -@signalemitter -class NotSupportedProfileContainer(InstanceContainer): - - def __init__(self, container_id: str, machine_id: str, material_id: str, *args, **kwargs): - super().__init__(container_id, *args, **kwargs) - - # self._id = str(container_id) # type: str - # self._name = "Not supported" # type: str - - self.setMetaData({ - "setting_version": 3, - "supported": False, - "type": "quality", - "weight": "0", - "material": material_id - }) - - # register this container - ContainerRegistry.getInstance().addContainer(self) - - # set printer definition - definition = ContainerRegistry.getInstance().findDefinitionContainers(id = machine_id) - self.setDefinition(definition[0]) - - -# register the container mime type -not_support_instance_mime = MimeType( - name = "application/x-cura-notsupportedinstancecontainer", - comment = "Cura Not Supported Instance Container", - suffixes = [] -) - -MimeTypeDatabase.addMimeType(not_support_instance_mime) -ContainerRegistry.addContainerTypeByName(NotSupportedProfileContainer, "not_supported_instance", not_support_instance_mime.name) diff --git a/cura/Settings/ProfilesModel.py b/cura/Settings/ProfilesModel.py index 68d538c671..5bbc3f6116 100644 --- a/cura/Settings/ProfilesModel.py +++ b/cura/Settings/ProfilesModel.py @@ -6,12 +6,12 @@ from collections import OrderedDict from PyQt5.QtCore import Qt from UM.Application import Application +from UM.Logger import Logger from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.Models.InstanceContainersModel import InstanceContainersModel from cura.QualityManager import QualityManager from cura.Settings.ExtruderManager import ExtruderManager -from cura.Settings.NotSupportedProfileContainer import NotSupportedProfileContainer ## QML Model for listing the current list of valid quality profiles. @@ -20,14 +20,12 @@ class ProfilesModel(InstanceContainersModel): LayerHeightRole = Qt.UserRole + 1001 LayerHeightWithoutUnitRole = Qt.UserRole + 1002 AvailableRole = Qt.UserRole + 1003 - NotSupportedRole = Qt.UserRole + 1004 def __init__(self, parent = None): super().__init__(parent) self.addRoleName(self.LayerHeightRole, "layer_height") self.addRoleName(self.LayerHeightWithoutUnitRole, "layer_height_without_unit") self.addRoleName(self.AvailableRole, "available") - self.addRoleName(self.NotSupportedRole, "not_supported") Application.getInstance().globalContainerStackChanged.connect(self._update) @@ -74,15 +72,6 @@ class ProfilesModel(InstanceContainersModel): # The actual list of quality profiles come from the first extruder in the extruder list. result = QualityManager.getInstance().findAllUsableQualitiesForMachineAndExtruders(global_container_stack, extruder_stacks) - # If not qualities are found we dynamically create an empty container with name "Not Supported" - if len(result) == 0: - machine_id = global_container_stack.definition.getId() - material_id = extruder_stacks[0].material.getId() - container_id = machine_id + "_" + material_id + "_not_supported" - not_supported_container = NotSupportedProfileContainer(container_id, machine_id, material_id) - result.append(not_supported_container) - return result - # The usable quality types are set quality_type_set = set([x.getMetaDataEntry("quality_type") for x in result]) @@ -96,6 +85,12 @@ class ProfilesModel(InstanceContainersModel): if quality.getMetaDataEntry("quality_type") not in quality_type_set: result.append(quality) + # if still profiles are found, add a single empty_quality ("Not supported") instance to the drop down list + if len(result) == 0: + # If not qualities are found we dynamically create a not supported container for this machine + material combination + not_supported_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0] + result.append(not_supported_container) + return result ## Re-computes the items in this model, and adds the layer height role. @@ -127,8 +122,7 @@ class ProfilesModel(InstanceContainersModel): extruder_stacks = new_extruder_stacks + extruder_stacks # Get a list of usable/available qualities for this machine and material - qualities = QualityManager.getInstance().findAllUsableQualitiesForMachineAndExtruders(global_container_stack, - extruder_stacks) + qualities = QualityManager.getInstance().findAllUsableQualitiesForMachineAndExtruders(global_container_stack, extruder_stacks) container_registry = ContainerRegistry.getInstance() machine_manager = Application.getInstance().getMachineManager() @@ -177,17 +171,23 @@ class ProfilesModel(InstanceContainersModel): if not profile: self._setItemLayerHeight(item, "", "") item["available"] = False - item["not_supported"] = True yield item continue profile = profile[0] + + # empty qualities should show in the list (they are "Not Supported" profiles) + if profile.getId() == "empty_quality": + self._setItemLayerHeight(item, "", "") + item["available"] = True + yield item + continue + item["available"] = profile in qualities # Easy case: This profile defines its own layer height. if profile.hasProperty("layer_height", "value"): self._setItemLayerHeight(item, profile.getProperty("layer_height", "value"), unit) - item["not_supported"] = False yield item continue @@ -199,14 +199,14 @@ class ProfilesModel(InstanceContainersModel): if quality_result["stack"] is global_container_stack: quality = quality_result["quality"] break - else: #No global container stack in the results: + else: + # No global container stack in the results: if quality_results: - quality = quality_results[0]["quality"] #Take any of the extruders. + quality = quality_results[0]["quality"] # Take any of the extruders. else: quality = None if quality and quality.hasProperty("layer_height", "value"): self._setItemLayerHeight(item, quality.getProperty("layer_height", "value"), unit) - item["not_supported"] = False yield item continue @@ -217,7 +217,6 @@ class ProfilesModel(InstanceContainersModel): if not skip_until_container or skip_until_container == ContainerRegistry.getInstance().getEmptyInstanceContainer(): #No variant in stack. skip_until_container = global_container_stack.getBottom() self._setItemLayerHeight(item, global_container_stack.getRawProperty("layer_height", "value", skip_until_container = skip_until_container.getId()), unit) # Fall through to the currently loaded material. - item["not_supported"] = False yield item def _setItemLayerHeight(self, item, value, unit): diff --git a/resources/definitions/builder_premium_small.def.json b/resources/definitions/builder_premium_small.def.json index 65103ce1af..b4756f955b 100644 --- a/resources/definitions/builder_premium_small.def.json +++ b/resources/definitions/builder_premium_small.def.json @@ -11,8 +11,11 @@ "file_formats": "text/x-gcode", "platform": "builder_premium_platform.stl", "platform_offset": [-126, -36, 117], + "has_machine_quality": true, - "preferred_quality": "*Normal*", + "preferred_material": "*pla*", + "preferred_quality": "*normal*", + "machine_extruder_trains": { "0": "builder_premium_small_rear", diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml index f3886d4743..fecea5ef99 100644 --- a/resources/qml/Menus/ProfileMenu.qml +++ b/resources/qml/Menus/ProfileMenu.qml @@ -17,12 +17,12 @@ Menu MenuItem { - text: model.name + (model.layer_height != "" ? (" - " + model.layer_height) : "") + text: (model.layer_height != "") ? model.name + " - " + model.layer_height : model.name checkable: true - checked: Cura.MachineManager.activeQualityChangesId == "" && Cura.MachineManager.activeQualityType == model.metadata.quality_type + checked: Cura.MachineManager.activeQualityId == model.id exclusiveGroup: group onTriggered: Cura.MachineManager.setActiveQuality(model.id) - visible: model.available || model.not_supported + visible: model.available } onObjectAdded: menu.insertItem(index, object); diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 1892b9d5a6..c116fa933a 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -63,10 +63,10 @@ Item menu: ProfileMenu { } function generateActiveQualityText () { - var result = "" + var result = catalog.i18nc("@", "No Profile Available") // default text - if (Cura.MachineManager.activeQualityName) { - result += Cura.MachineManager.activeQualityName + if (Cura.MachineManager.isActiveQualitySupported ) { + result = Cura.MachineManager.activeQualityName if (Cura.MachineManager.activeQualityLayerHeight > 0) { result += " " diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index f3887e2885..78e21f3a68 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -245,35 +245,29 @@ Column color: UM.Theme.getColor("text"); } - ToolButton { + ToolButton + { id: materialSelection + text: Cura.MachineManager.activeMaterialName tooltip: Cura.MachineManager.activeMaterialName visible: Cura.MachineManager.hasMaterials - property var valueError: - { - var data = Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeMaterialId, "compatible") - if(data == "False") - { - return true - } - else - { - return false - } - - } - property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported - enabled: !extrudersList.visible || base.currentExtruderIndex > -1 - height: UM.Theme.getSize("setting_control").height width: parent.width * 0.7 + UM.Theme.getSize("sidebar_margin").width anchors.right: parent.right style: UM.Theme.styles.sidebar_header_button activeFocusOnPress: true; + menu: MaterialMenu { + extruderIndex: base.currentExtruderIndex + } - menu: MaterialMenu { extruderIndex: base.currentExtruderIndex } + property var valueError: !isMaterialSupported() + property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported + + function isMaterialSupported () { + return Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeMaterialId, "compatible") == "True" + } } } diff --git a/resources/quality/ultimaker3/um3_aa0.4_PVA_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PVA_Fast_Print.inst.cfg deleted file mode 100644 index 121d9c92bb..0000000000 --- a/resources/quality/ultimaker3/um3_aa0.4_PVA_Fast_Print.inst.cfg +++ /dev/null @@ -1,14 +0,0 @@ -[general] -version = 2 -name = Not Supported -definition = ultimaker3 - -[metadata] -weight = 0 -type = quality -quality_type = normal -material = generic_pva_ultimaker3_AA_0.4 -supported = False -setting_version = 3 - -[values] diff --git a/resources/quality/ultimaker3/um3_aa0.8_PVA_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PVA_Fast_Print.inst.cfg deleted file mode 100644 index 071a72da0d..0000000000 --- a/resources/quality/ultimaker3/um3_aa0.8_PVA_Fast_Print.inst.cfg +++ /dev/null @@ -1,14 +0,0 @@ -[general] -version = 2 -name = Not Supported -definition = ultimaker3 - -[metadata] -weight = 0 -type = quality -quality_type = normal -material = generic_pva_ultimaker3_AA_0.8 -supported = False -setting_version = 3 - -[values] diff --git a/resources/quality/ultimaker3/um3_aa0.8_PVA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PVA_Superdraft_Print.inst.cfg deleted file mode 100644 index 485226fe3d..0000000000 --- a/resources/quality/ultimaker3/um3_aa0.8_PVA_Superdraft_Print.inst.cfg +++ /dev/null @@ -1,14 +0,0 @@ -[general] -version = 2 -name = Not Supported -definition = ultimaker3 - -[metadata] -weight = 0 -type = quality -quality_type = superdraft -material = generic_pva_ultimaker3_AA_0.8 -supported = False -setting_version = 3 - -[values]