diff --git a/cura/Machines/Models/IntentCategoryModel.py b/cura/Machines/Models/IntentCategoryModel.py index da8044addd..2733c87ca2 100644 --- a/cura/Machines/Models/IntentCategoryModel.py +++ b/cura/Machines/Models/IntentCategoryModel.py @@ -46,6 +46,8 @@ class IntentCategoryModel(ListModel): self.addRoleName(self.WeightRole, "weight") self.addRoleName(self.QualitiesRole, "qualities") + application = cura.CuraApplication.CuraApplication.getInstance() + ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChange) ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChange) machine_manager = cura.CuraApplication.CuraApplication.getInstance().getMachineManager() @@ -53,6 +55,9 @@ class IntentCategoryModel(ListModel): machine_manager.activeVariantChanged.connect(self.update) machine_manager.extruderChanged.connect(self.update) + extruder_manager = application.getExtruderManager() + extruder_manager.extrudersChanged.connect(self.update) + self.update() ## Updates the list of intents if an intent profile was added or removed. diff --git a/cura/Machines/Models/MaterialManagementModel.py b/cura/Machines/Models/MaterialManagementModel.py index b4f3bb9889..f00b81e987 100644 --- a/cura/Machines/Models/MaterialManagementModel.py +++ b/cura/Machines/Models/MaterialManagementModel.py @@ -8,6 +8,7 @@ import uuid # To generate new GUIDs for new materials. from UM.i18n import i18nCatalog from UM.Logger import Logger +from UM.Signal import postponeSignals, CompressTechnique import cura.CuraApplication # Imported like this to prevent circular imports. from cura.Machines.ContainerTree import ContainerTree @@ -73,8 +74,20 @@ class MaterialManagementModel(QObject): def removeMaterial(self, material_node: "MaterialNode") -> None: container_registry = CuraContainerRegistry.getInstance() materials_this_base_file = container_registry.findContainersMetadata(base_file = material_node.base_file) - for material_metadata in materials_this_base_file: - container_registry.removeContainer(material_metadata["id"]) + + # The material containers belonging to the same material file are supposed to work together. This postponeSignals() + # does two things: + # - optimizing the signal emitting. + # - making sure that the signals will only be emitted after all the material containers have been removed. + with postponeSignals(container_registry.containerRemoved, compress = CompressTechnique.CompressPerParameterValue): + # CURA-6886: Some containers may not have been loaded. If remove one material container, its material file + # will be removed. If later we remove a sub-material container which hasn't been loaded previously, it will + # crash because removeContainer() requires to load the container first, but the material file was already + # gone. + for material_metadata in materials_this_base_file: + container_registry.findInstanceContainers(id = material_metadata["id"]) + for material_metadata in materials_this_base_file: + container_registry.removeContainer(material_metadata["id"]) ## Creates a duplicate of a material with the same GUID and base_file # metadata. @@ -128,15 +141,33 @@ class MaterialManagementModel(QObject): new_container.getMetaData().update(new_metadata) new_containers.append(new_container) - for container_to_add in new_containers: - container_to_add.setDirty(True) - container_registry.addContainer(container_to_add) + # CURA-6863: Nodes in ContainerTree will be updated upon ContainerAdded signals, one at a time. It will use the + # best fit material container at the time it sees one. For example, if you duplicate and get generic_pva #2, + # if the node update function sees the containers in the following order: + # + # - generic_pva #2 + # - generic_pva #2_um3_aa04 + # + # It will first use "generic_pva #2" because that's the best fit it has ever seen, and later "generic_pva #2_um3_aa04" + # once it sees that. Because things run in the Qt event loop, they don't happen at the same time. This means if + # between those two events, the ContainerTree will have nodes that contain invalid data. + # + # This sort fixes the problem by emitting the most specific containers first. + new_containers = sorted(new_containers, key = lambda x: x.getId(), reverse = True) - # If the duplicated material was favorite then the new material should also be added to the favorites. - favorites_set = set(application.getPreferences().getValue("cura/favorite_materials").split(";")) - if base_file in favorites_set: - favorites_set.add(new_base_id) - application.getPreferences().setValue("cura/favorite_materials", ";".join(favorites_set)) + # Optimization. Serving the same purpose as the postponeSignals() in removeMaterial() + # postpone the signals emitted when duplicating materials. This is easier on the event loop; changes the + # behavior to be like a transaction. Prevents concurrency issues. + with postponeSignals(container_registry.containerAdded, compress=CompressTechnique.CompressPerParameterValue): + for container_to_add in new_containers: + container_to_add.setDirty(True) + container_registry.addContainer(container_to_add) + + # If the duplicated material was favorite then the new material should also be added to the favorites. + favorites_set = set(application.getPreferences().getValue("cura/favorite_materials").split(";")) + if base_file in favorites_set: + favorites_set.add(new_base_id) + application.getPreferences().setValue("cura/favorite_materials", ";".join(favorites_set)) return new_base_id diff --git a/cura/Machines/Models/QualityManagementModel.py b/cura/Machines/Models/QualityManagementModel.py index 1d30b1753e..3b503edcf0 100644 --- a/cura/Machines/Models/QualityManagementModel.py +++ b/cura/Machines/Models/QualityManagementModel.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import Any, cast, Dict, Optional, TYPE_CHECKING -from PyQt5.QtCore import pyqtSlot, QObject, Qt +from PyQt5.QtCore import pyqtSlot, QObject, Qt, QTimer from UM.Logger import Logger from UM.Qt.ListModel import ListModel @@ -51,14 +51,27 @@ class QualityManagementModel(ListModel): application = cura.CuraApplication.CuraApplication.getInstance() container_registry = application.getContainerRegistry() self._machine_manager = application.getMachineManager() - self._extruder_manager = application.getExtruderManager() + self._machine_manager.activeQualityGroupChanged.connect(self._onChange) + self._machine_manager.activeStackChanged.connect(self._onChange) + self._machine_manager.extruderChanged.connect(self._onChange) + self._machine_manager.globalContainerChanged.connect(self._onChange) + + self._extruder_manager = application.getExtruderManager() + self._extruder_manager.extrudersChanged.connect(self._onChange) - self._machine_manager.globalContainerChanged.connect(self._update) container_registry.containerAdded.connect(self._qualityChangesListChanged) container_registry.containerRemoved.connect(self._qualityChangesListChanged) container_registry.containerMetaDataChanged.connect(self._qualityChangesListChanged) - self._update() + self._update_timer = QTimer() + self._update_timer.setInterval(100) + self._update_timer.setSingleShot(True) + self._update_timer.timeout.connect(self._update) + + self._onChange() + + def _onChange(self) -> None: + self._update_timer.start() ## Deletes a custom profile. It will be gone forever. # \param quality_changes_group The quality changes group representing the diff --git a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py index 9e1dfc4949..3a79ceeaf1 100644 --- a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py +++ b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py @@ -44,6 +44,9 @@ class QualityProfilesDropDownMenuModel(ListModel): machine_manager.activeVariantChanged.connect(self._onChange) machine_manager.extruderChanged.connect(self._onChange) + extruder_manager = application.getExtruderManager() + extruder_manager.extrudersChanged.connect(self._onChange) + self._layer_height_unit = "" # This is cached self._update_timer = QTimer() # type: QTimer diff --git a/cura/Machines/VariantNode.py b/cura/Machines/VariantNode.py index 27e359afb1..1c5474422e 100644 --- a/cura/Machines/VariantNode.py +++ b/cura/Machines/VariantNode.py @@ -1,15 +1,18 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - from typing import Optional, TYPE_CHECKING from UM.Logger import Logger from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.Interfaces import ContainerInterface from UM.Signal import Signal + +from cura.Settings.cura_empty_instance_containers import empty_variant_container from cura.Machines.ContainerNode import ContainerNode from cura.Machines.MaterialNode import MaterialNode + import UM.FlameProfiler + if TYPE_CHECKING: from typing import Dict from cura.Machines.MachineNode import MachineNode @@ -101,6 +104,14 @@ class VariantNode(ContainerNode): def _materialAdded(self, container: ContainerInterface) -> None: if container.getMetaDataEntry("type") != "material": return # Not interested. + if not ContainerRegistry.getInstance().findContainersMetadata(id = container.getId()): + # CURA-6889 + # containerAdded and removed signals may be triggered in the next event cycle. If a container gets added + # and removed in the same event cycle, in the next cycle, the connections should just ignore the signals. + # The check here makes sure that the container in the signal still exists. + Logger.log("d", "Got container added signal for container [%s] but it no longer exists, do nothing.", + container.getId()) + return if not self.machine.has_materials: return # We won't add any materials. material_definition = container.getMetaDataEntry("definition") @@ -111,18 +122,18 @@ class VariantNode(ContainerNode): if base_file not in self.materials: # Completely new base file. Always better than not having a file as long as it matches our set-up. if material_definition != "fdmprinter" and material_definition != self.machine.container_id: return - material_variant = container.getMetaDataEntry("variant_name", "empty") - if material_variant != "empty" and material_variant != self.variant_name: + material_variant = container.getMetaDataEntry("variant_name", empty_variant_container.getName()) + if material_variant != self.variant_name: return else: # We already have this base profile. Replace the base profile if the new one is more specific. new_definition = container.getMetaDataEntry("definition") if new_definition == "fdmprinter": return # Just as unspecific or worse. - if new_definition != self.machine.container_id: + material_variant = container.getMetaDataEntry("variant_name") + if new_definition != self.machine.container_id or material_variant != self.variant_name: return # Doesn't match this set-up. original_metadata = ContainerRegistry.getInstance().findContainersMetadata(id = self.materials[base_file].container_id)[0] - original_variant = original_metadata.get("variant_name", "empty") - if original_variant != "empty" or container.getMetaDataEntry("variant_name", "empty") == "empty": + if "variant_name" in original_metadata or material_variant is None: return # Original was already specific or just as unspecific as the new one. if "empty_material" in self.materials: diff --git a/cura/PrinterOutput/Models/ExtruderConfigurationModel.py b/cura/PrinterOutput/Models/ExtruderConfigurationModel.py index 04a3c95afd..4a1cf4916f 100644 --- a/cura/PrinterOutput/Models/ExtruderConfigurationModel.py +++ b/cura/PrinterOutput/Models/ExtruderConfigurationModel.py @@ -25,9 +25,10 @@ class ExtruderConfigurationModel(QObject): return self._position def setMaterial(self, material: Optional[MaterialOutputModel]) -> None: - if self._material != material: - self._material = material - self.extruderConfigurationChanged.emit() + if material is None or self._material == material: + return + self._material = material + self.extruderConfigurationChanged.emit() @pyqtProperty(QObject, fset = setMaterial, notify = extruderConfigurationChanged) def activeMaterial(self) -> Optional[MaterialOutputModel]: diff --git a/cura/Settings/IntentManager.py b/cura/Settings/IntentManager.py index 9f9d174ffa..c1d59fb84a 100644 --- a/cura/Settings/IntentManager.py +++ b/cura/Settings/IntentManager.py @@ -78,6 +78,8 @@ class IntentManager(QObject): final_intent_ids = set() # type: Set[str] current_definition_id = global_stack.definition.getId() for extruder_stack in global_stack.extruderList: + if not extruder_stack.isEnabled: + continue nozzle_name = extruder_stack.variant.getMetaDataEntry("name") material_id = extruder_stack.material.getMetaDataEntry("base_file") final_intent_ids |= {metadata["id"] for metadata in self.intentMetadatas(current_definition_id, nozzle_name, material_id) if metadata.get("quality_type") in available_quality_types} @@ -104,6 +106,8 @@ class IntentManager(QObject): current_definition_id = global_stack.definition.getId() final_intent_categories = set() # type: Set[str] for extruder_stack in global_stack.extruderList: + if not extruder_stack.isEnabled: + continue nozzle_name = extruder_stack.variant.getMetaDataEntry("name") material_id = extruder_stack.material.getMetaDataEntry("base_file") final_intent_categories.update(self.intentCategories(current_definition_id, nozzle_name, material_id)) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 84e5495e7f..b3c5da0b07 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1322,7 +1322,8 @@ class MachineManager(QObject): # changed. # \param position The extruder stack to update. If provided with None, all # extruder stacks will be updated. - def updateMaterialWithVariant(self, position: Optional[str]) -> None: + @pyqtSlot() + def updateMaterialWithVariant(self, position: Optional[str] = None) -> None: if self._global_container_stack is None: return if position is None: @@ -1353,10 +1354,9 @@ class MachineManager(QObject): self._setMaterial(position_item, new_material) else: # The current material is not available, find the preferred one. - if position is not None: - approximate_material_diameter = int(self._global_container_stack.extruderList[int(position_item)].getApproximateMaterialDiameter()) - material_node = nozzle_node.preferredMaterial(approximate_material_diameter) - self._setMaterial(position_item, material_node) + approximate_material_diameter = int(self._global_container_stack.extruderList[int(position_item)].getApproximateMaterialDiameter()) + material_node = nozzle_node.preferredMaterial(approximate_material_diameter) + self._setMaterial(position_item, material_node) ## Given a printer definition name, select the right machine instance. In case it doesn't exist, create a new # instance with the same network key. diff --git a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py index 3a25b7781a..499214a0e9 100644 --- a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py +++ b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py @@ -162,6 +162,8 @@ class PauseAtHeight(Script): # use offset to calculate the current height: = - layer_0_z = 0 current_z = 0 + current_height = 0 + current_layer = 0 current_extrusion_f = 0 got_first_g_cmd_on_layer_0 = False current_t = 0 #Tracks the current extruder for tracking the target temperature. @@ -263,8 +265,8 @@ class PauseAtHeight(Script): # the nozzle) x, y = self.getNextXY(layer) prev_lines = prev_layer.split("\n") - for line in prev_lines: - new_e = self.getValue(line, 'E', current_e) + for lin in prev_lines: + new_e = self.getValue(lin, "E", current_e) if new_e != current_e: current_e = new_e break diff --git a/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml b/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml index 40b22c268d..f7edde1fff 100644 --- a/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml +++ b/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml @@ -30,7 +30,7 @@ UM.Dialog anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - text: licenseDialog.pluginName + catalog.i18nc("@label", "This plugin contains a license.\nYou need to accept this license to install this plugin.\nDo you agree with the terms below?") + text: licenseDialog.pluginName + ": " + catalog.i18nc("@label", "This plugin contains a license.\nYou need to accept this license to install this plugin.\nDo you agree with the terms below?") wrapMode: Text.Wrap renderType: Text.NativeRendering } diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index e32e4c8745..4930835f58 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -328,7 +328,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): def _setFirmwareName(self, name): new_name = re.findall(r"FIRMWARE_NAME:(.*);", str(name)) - if new_name: + if new_name: self._firmware_name = new_name[0] Logger.log("i", "USB output device Firmware name: %s", self._firmware_name) else: diff --git a/resources/intent/um_s5_aa0.4_ABS_Draft_Print_Quick.inst.cfg b/resources/intent/um_s5_aa0.4_ABS_Draft_Print_Quick.inst.cfg new file mode 100644 index 0000000000..f627fbf74b --- /dev/null +++ b/resources/intent/um_s5_aa0.4_ABS_Draft_Print_Quick.inst.cfg @@ -0,0 +1,34 @@ +[general] +version = 4 +name = Quick +definition = ultimaker_s5 + +[metadata] +setting_version = 10 +type = intent +intent_category = smooth +quality_type = draft +material = generic_abs +variant = AA 0.4 + +[values] +speed_infill = =speed_print +speed_topbottom = =speed_print +speed_wall = =speed_print +speed_wall_0 = =speed_wall +speed_wall_x = =speed_wall +speed_layer_0 = 20 +top_bottom_thickness = =wall_thickness +wall_thickness = =line_width * 2 +fill_perimeter_gaps = nowhere +infill_sparse_density = 15 +infill_line_width = =line_width +jerk_print = 30 +jerk_infill = =jerk_print +jerk_topbottom = =jerk_print +jerk_wall = =jerk_print +jerk_wall_0 = =jerk_wall +jerk_wall_x = =jerk_wall +jerk_layer_0 = 5 +line_width = =machine_nozzle_size +wall_line_width_x = =line_width diff --git a/resources/intent/um_s5_aa0.4_ABS_Fast_Print_Accurate.inst.cfg b/resources/intent/um_s5_aa0.4_ABS_Fast_Print_Accurate.inst.cfg new file mode 100644 index 0000000000..be622d6cfe --- /dev/null +++ b/resources/intent/um_s5_aa0.4_ABS_Fast_Print_Accurate.inst.cfg @@ -0,0 +1,34 @@ +[general] +version = 4 +name = Accurate +definition = ultimaker_s5 + +[metadata] +setting_version = 10 +type = intent +intent_category = engineering +quality_type = fast +material = generic_abs +variant = AA 0.4 + +[values] +infill_line_width = =line_width +jerk_print = 30 +jerk_infill = =jerk_print +jerk_topbottom = =jerk_print +jerk_wall = =jerk_print +jerk_wall_0 = =jerk_wall +jerk_wall_x = =jerk_wall +jerk_layer_0 = 5 +line_width = =machine_nozzle_size +speed_print = 30 +speed_infill = =speed_print +speed_layer_0 = 20 +speed_topbottom = =speed_print +speed_wall = =speed_print +speed_wall_0 = =speed_wall +speed_wall_x = =speed_wall +top_bottom_thickness = =wall_thickness +wall_line_width_x = =line_width +wall_thickness = =line_width * 3 +xy_offset = =- layer_height * 0.2 diff --git a/resources/intent/um_s5_aa0.4_ABS_Normal_Quality_Accurate.inst.cfg b/resources/intent/um_s5_aa0.4_ABS_Normal_Quality_Accurate.inst.cfg new file mode 100644 index 0000000000..352c26d312 --- /dev/null +++ b/resources/intent/um_s5_aa0.4_ABS_Normal_Quality_Accurate.inst.cfg @@ -0,0 +1,34 @@ +[general] +version = 4 +name = Accurate +definition = ultimaker_s5 + +[metadata] +setting_version = 10 +type = intent +intent_category = engineering +quality_type = normal +material = generic_abs +variant = AA 0.4 + +[values] +infill_line_width = =line_width +jerk_print = 30 +jerk_infill = =jerk_print +jerk_topbottom = =jerk_print +jerk_wall = =jerk_print +jerk_wall_0 = =jerk_wall +jerk_wall_x = =jerk_wall +jerk_layer_0 = 5 +line_width = =machine_nozzle_size +speed_print = 30 +speed_infill = =speed_print +speed_layer_0 = 20 +speed_topbottom = =speed_print +speed_wall = =speed_print +speed_wall_0 = =speed_wall +speed_wall_x = =speed_wall +top_bottom_thickness = =wall_thickness +wall_line_width_x = =line_width +wall_thickness = =line_width * 3 +xy_offset = =- layer_height * 0.2 diff --git a/resources/intent/um_s5_aa0.4_PLA_Draft_Print_Quick.inst.cfg b/resources/intent/um_s5_aa0.4_PLA_Draft_Print_Quick.inst.cfg new file mode 100644 index 0000000000..553a68201d --- /dev/null +++ b/resources/intent/um_s5_aa0.4_PLA_Draft_Print_Quick.inst.cfg @@ -0,0 +1,34 @@ +[general] +version = 4 +name = Quick +definition = ultimaker_s5 + +[metadata] +setting_version = 10 +type = intent +intent_category = smooth +quality_type = draft +material = generic_pla +variant = AA 0.4 + +[values] +speed_infill = =speed_print +speed_topbottom = =speed_print +speed_wall = =speed_print +speed_wall_0 = =speed_wall +speed_wall_x = =speed_wall +speed_layer_0 = 20 +top_bottom_thickness = =wall_thickness +wall_thickness = =line_width * 2 +fill_perimeter_gaps = nowhere +infill_sparse_density = 15 +infill_line_width = =line_width +jerk_print = 30 +jerk_infill = =jerk_print +jerk_topbottom = =jerk_print +jerk_wall = =jerk_print +jerk_wall_0 = =jerk_wall +jerk_wall_x = =jerk_wall +jerk_layer_0 = 5 +line_width = =machine_nozzle_size +wall_line_width_x = =line_width diff --git a/resources/intent/um_s5_aa0.4_PLA_Fast_Print_Accurate.inst.cfg b/resources/intent/um_s5_aa0.4_PLA_Fast_Print_Accurate.inst.cfg new file mode 100644 index 0000000000..0943bb2032 --- /dev/null +++ b/resources/intent/um_s5_aa0.4_PLA_Fast_Print_Accurate.inst.cfg @@ -0,0 +1,34 @@ +[general] +version = 4 +name = Accurate +definition = ultimaker_s5 + +[metadata] +setting_version = 10 +type = intent +intent_category = engineering +quality_type = fast +material = generic_pla +variant = AA 0.4 + +[values] +infill_line_width = =line_width +jerk_print = 30 +jerk_infill = =jerk_print +jerk_topbottom = =jerk_print +jerk_wall = =jerk_print +jerk_wall_0 = =jerk_wall +jerk_wall_x = =jerk_wall +jerk_layer_0 = 5 +line_width = =machine_nozzle_size +speed_print = 30 +speed_infill = =speed_print +speed_layer_0 = 20 +speed_topbottom = =speed_print +speed_wall = =speed_print +speed_wall_0 = =speed_wall +speed_wall_x = =speed_wall +top_bottom_thickness = =wall_thickness +wall_line_width_x = =line_width +wall_thickness = =line_width * 3 +xy_offset = =- layer_height * 0.2 diff --git a/resources/intent/um_s5_aa0.4_PLA_Normal_Quality_Accurate.inst.cfg b/resources/intent/um_s5_aa0.4_PLA_Normal_Quality_Accurate.inst.cfg new file mode 100644 index 0000000000..053b849aff --- /dev/null +++ b/resources/intent/um_s5_aa0.4_PLA_Normal_Quality_Accurate.inst.cfg @@ -0,0 +1,34 @@ +[general] +version = 4 +name = Accurate +definition = ultimaker_s5 + +[metadata] +setting_version = 10 +type = intent +intent_category = engineering +quality_type = normal +material = generic_pla +variant = AA 0.4 + +[values] +infill_line_width = =line_width +jerk_print = 30 +jerk_infill = =jerk_print +jerk_topbottom = =jerk_print +jerk_wall = =jerk_print +jerk_wall_0 = =jerk_wall +jerk_wall_x = =jerk_wall +jerk_layer_0 = 5 +line_width = =machine_nozzle_size +speed_print = 30 +speed_infill = =speed_print +speed_layer_0 = 20 +speed_topbottom = =speed_print +speed_wall = =speed_print +speed_wall_0 = =speed_wall +speed_wall_x = =speed_wall +top_bottom_thickness = =wall_thickness +wall_line_width_x = =line_width +wall_thickness = =line_width * 3 +xy_offset = =- layer_height * 0.2 diff --git a/resources/intent/um_s5_aa0.4_TPLA_Draft_Print_Quick.inst.cfg b/resources/intent/um_s5_aa0.4_TPLA_Draft_Print_Quick.inst.cfg new file mode 100644 index 0000000000..458b283dd8 --- /dev/null +++ b/resources/intent/um_s5_aa0.4_TPLA_Draft_Print_Quick.inst.cfg @@ -0,0 +1,34 @@ +[general] +version = 4 +name = Quick +definition = ultimaker_s5 + +[metadata] +setting_version = 10 +type = intent +intent_category = smooth +quality_type = draft +material = generic_tough_pla +variant = AA 0.4 + +[values] +speed_infill = =speed_print +speed_topbottom = =speed_print +speed_wall = =speed_print +speed_wall_0 = =speed_wall +speed_wall_x = =speed_wall +speed_layer_0 = 20 +top_bottom_thickness = =wall_thickness +wall_thickness = =line_width * 2 +fill_perimeter_gaps = nowhere +infill_sparse_density = 15 +infill_line_width = =line_width +jerk_print = 30 +jerk_infill = =jerk_print +jerk_topbottom = =jerk_print +jerk_wall = =jerk_print +jerk_wall_0 = =jerk_wall +jerk_wall_x = =jerk_wall +jerk_layer_0 = 5 +line_width = =machine_nozzle_size +wall_line_width_x = =line_width diff --git a/resources/intent/um_s5_aa0.4_TPLA_Fast_Print_Accurate.inst.cfg b/resources/intent/um_s5_aa0.4_TPLA_Fast_Print_Accurate.inst.cfg new file mode 100644 index 0000000000..d19ad53fae --- /dev/null +++ b/resources/intent/um_s5_aa0.4_TPLA_Fast_Print_Accurate.inst.cfg @@ -0,0 +1,34 @@ +[general] +version = 4 +name = Accurate +definition = ultimaker_s5 + +[metadata] +setting_version = 10 +type = intent +intent_category = engineering +quality_type = fast +material = generic_tough_pla +variant = AA 0.4 + +[values] +infill_line_width = =line_width +jerk_print = 30 +jerk_infill = =jerk_print +jerk_topbottom = =jerk_print +jerk_wall = =jerk_print +jerk_wall_0 = =jerk_wall +jerk_wall_x = =jerk_wall +jerk_layer_0 = 5 +line_width = =machine_nozzle_size +speed_print = 30 +speed_infill = =speed_print +speed_layer_0 = 20 +speed_topbottom = =speed_print +speed_wall = =speed_print +speed_wall_0 = =speed_wall +speed_wall_x = =speed_wall +top_bottom_thickness = =wall_thickness +wall_line_width_x = =line_width +wall_thickness = =line_width * 3 +xy_offset = =- layer_height * 0.2 diff --git a/resources/intent/um_s5_aa0.4_TPLA_Normal_Quality_Accurate.inst.cfg b/resources/intent/um_s5_aa0.4_TPLA_Normal_Quality_Accurate.inst.cfg new file mode 100644 index 0000000000..23d8b1e39b --- /dev/null +++ b/resources/intent/um_s5_aa0.4_TPLA_Normal_Quality_Accurate.inst.cfg @@ -0,0 +1,34 @@ +[general] +version = 4 +name = Accurate +definition = ultimaker_s5 + +[metadata] +setting_version = 10 +type = intent +intent_category = engineering +quality_type = normal +material = generic_tough_pla +variant = AA 0.4 + +[values] +infill_line_width = =line_width +jerk_print = 30 +jerk_infill = =jerk_print +jerk_topbottom = =jerk_print +jerk_wall = =jerk_print +jerk_wall_0 = =jerk_wall +jerk_wall_x = =jerk_wall +jerk_layer_0 = 5 +line_width = =machine_nozzle_size +speed_print = 30 +speed_infill = =speed_print +speed_layer_0 = 20 +speed_topbottom = =speed_print +speed_wall = =speed_print +speed_wall_0 = =speed_wall +speed_wall_x = =speed_wall +top_bottom_thickness = =wall_thickness +wall_line_width_x = =line_width +wall_thickness = =line_width * 3 +xy_offset = =- layer_height * 0.2 diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index 24b6dc7fe2..b9fe873b25 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -19,6 +19,7 @@ Item property alias color: label.color property alias text: label.text property alias font: label.font + property alias elide: label.elide property real margin: UM.Theme.getSize("narrow_margin").width // These properties can be used in combination with layouts. diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml index 7953128590..edc5ee1e0d 100644 --- a/resources/qml/Menus/MaterialMenu.qml +++ b/resources/qml/Menus/MaterialMenu.qml @@ -13,8 +13,12 @@ Menu title: catalog.i18nc("@label:category menu label", "Material") property int extruderIndex: 0 - property var currentRootMaterialId: Cura.MachineManager.currentRootMaterialId[extruderIndex] - property string activeMaterialId: Cura.MachineManager.activeMachine.extruderList[extruderIndex] ? Cura.MachineManager.activeMachine.extruderList[extruderIndex].material.id : "" + property string currentRootMaterialId: Cura.MachineManager.currentRootMaterialId[extruderIndex] + property string activeMaterialId: + { + var extruder = Cura.MachineManager.activeMachine.extruderList[extruderIndex] + return (extruder === undefined) ? "" : extruder.material.id + } property bool updateModels: true Cura.FavoriteMaterialsModel { @@ -73,7 +77,11 @@ Menu { text: model.name checkable: true - enabled: Cura.MachineManager.activeMachine.extruderList[extruderIndex].isEnabled + enabled: + { + var extruder = Cura.MachineManager.activeMachine.extruderList[extruderIndex] + return (extruder === undefined) ? false : extruder.isEnabled + } checked: model.root_material_id === menu.currentRootMaterialId exclusiveGroup: group onTriggered: Cura.MachineManager.setMaterial(extruderIndex, model.container_node) @@ -112,7 +120,11 @@ Menu { text: model.name checkable: true - enabled: Cura.MachineManager.activeMachine.extruderList[extruderIndex].isEnabled + enabled: + { + var extruder = Cura.MachineManager.activeMachine.extruderList[extruderIndex] + return (extruder === undefined) ? false : extruder.isEnabled + } checked: model.id === menu.activeMaterialId exclusiveGroup: group onTriggered: Cura.MachineManager.setMaterial(extruderIndex, model.container_node) diff --git a/resources/qml/Menus/NozzleMenu.qml b/resources/qml/Menus/NozzleMenu.qml index a291f125eb..2734e40489 100644 --- a/resources/qml/Menus/NozzleMenu.qml +++ b/resources/qml/Menus/NozzleMenu.qml @@ -29,10 +29,14 @@ Menu checkable: true checked: { var extruder = Cura.MachineManager.activeMachine.extruderList[extruderIndex] - return extruder.variant.name == model.hotend_name + return (extruder === undefined) ? false : (extruder.variant.name == model.hotend_name) } exclusiveGroup: group - enabled: Cura.MachineManager.activeMachine.extruderList[extruderIndex].isEnabled + enabled: + { + var extruder = Cura.MachineManager.activeMachine.extruderList[extruderIndex] + return (extruder === undefined) ? false : extruder.isEnabled + } onTriggered: { Cura.MachineManager.setVariant(menu.extruderIndex, model.container_node); } diff --git a/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml b/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml index b54af103fe..e821dfb955 100644 --- a/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml +++ b/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml @@ -64,8 +64,10 @@ Item height: childrenRect.height Label { + width: parent.width text: materialProperties.name font: UM.Theme.getFont("large_bold") + elide: Text.ElideRight } } diff --git a/resources/qml/Preferences/Materials/MaterialsList.qml b/resources/qml/Preferences/Materials/MaterialsList.qml index 96f6730029..8b82a87820 100644 --- a/resources/qml/Preferences/Materials/MaterialsList.qml +++ b/resources/qml/Preferences/Materials/MaterialsList.qml @@ -114,7 +114,7 @@ Item if (base.toActivateNewMaterial) { var position = Cura.ExtruderManager.activeExtruderIndex - Cura.MachineManager.setMaterial(position, base.currentItem.container_node) + Cura.MachineManager.setMaterialById(position, base.newRootMaterialIdToSwitchTo) } base.newRootMaterialIdToSwitchTo = "" base.toActivateNewMaterial = false diff --git a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml index 2c1e2128e1..3c36e12651 100644 --- a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml @@ -21,6 +21,7 @@ Item property var colorsModel: materialType != null ? materialType.colors: null height: childrenRect.height width: parent.width + anchors.left: parent.left Rectangle { id: material_type_header_background diff --git a/resources/qml/Preferences/Materials/MaterialsView.qml b/resources/qml/Preferences/Materials/MaterialsView.qml index 6d5a8119c5..8a5ec79d53 100644 --- a/resources/qml/Preferences/Materials/MaterialsView.qml +++ b/resources/qml/Preferences/Materials/MaterialsView.qml @@ -111,6 +111,8 @@ TabView { base.setMetaDataEntry("approximate_diameter", old_approximate_diameter_value, getApproximateDiameter(new_diameter_value).toString()); base.setMetaDataEntry("properties/diameter", properties.diameter, new_diameter_value); + // CURA-6868 Make sure to update the extruder to user a diameter-compatible material. + Cura.MachineManager.updateMaterialWithVariant() base.resetSelectedMaterial() } diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index 99fdafdb73..46297659ff 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -4,6 +4,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Controls 1.4 as OldControls +import QtQuick.Layouts 1.3 import UM 1.3 as UM import Cura 1.6 as Cura @@ -66,7 +67,6 @@ Item { id: intentSelection onClicked: menu.opened ? menu.close() : menu.open() - text: generateActiveQualityText() anchors.right: parent.right width: UM.Theme.getSize("print_setup_big_item").width @@ -75,18 +75,67 @@ Item baselineOffset: null // If we don't do this, there is a binding loop. WHich is a bit weird, since we override the contentItem anyway... - contentItem: Label + contentItem: RowLayout { - id: textLabel - text: intentSelection.text - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") + spacing: 0 anchors.left: parent.left + anchors.right: customisedSettings.left anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.verticalCenter: intentSelection.verticalCenter - height: contentHeight - verticalAlignment: Text.AlignVCenter - renderType: Text.NativeRendering + + Label + { + id: textLabel + text: Cura.MachineManager.activeQualityDisplayNameMap["main"] + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + Layout.margins: 0 + Layout.maximumWidth: Math.floor(parent.width * 0.7) // Always leave >= 30% for the rest of the row. + height: contentHeight + verticalAlignment: Text.AlignVCenter + renderType: Text.NativeRendering + elide: Text.ElideRight + } + + Label + { + text: activeQualityDetailText() + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text_detail") + Layout.margins: 0 + Layout.fillWidth: true + + height: contentHeight + verticalAlignment: Text.AlignVCenter + renderType: Text.NativeRendering + elide: Text.ElideRight + + function activeQualityDetailText() + { + var resultMap = Cura.MachineManager.activeQualityDisplayNameMap + var resultSuffix = resultMap["suffix"] + var result = "" + + if (Cura.MachineManager.isActiveQualityExperimental) + { + resultSuffix += " (Experimental)" + } + + if (Cura.MachineManager.isActiveQualitySupported) + { + if (Cura.MachineManager.activeQualityLayerHeight > 0) + { + if (resultSuffix) + { + result += " - " + resultSuffix + } + result += " - " + result += Cura.MachineManager.activeQualityLayerHeight + "mm" + } + } + + return result + } + } } background: Rectangle @@ -98,46 +147,6 @@ Item color: UM.Theme.getColor("main_background") } - function generateActiveQualityText() - { - var resultMap = Cura.MachineManager.activeQualityDisplayNameMap - var resultMain = resultMap["main"] - var resultSuffix = resultMap["suffix"] - var result = "" - - if (Cura.MachineManager.isActiveQualityExperimental) - { - resultSuffix += " (Experimental)" - } - - if (Cura.MachineManager.isActiveQualityCustom) - { - result = resultMain - } - - if (Cura.MachineManager.isActiveQualitySupported) - { - if (Cura.MachineManager.activeQualityLayerHeight > 0) - { - result = resultMain - if (resultSuffix) - { - result += " - " - } - result += "" - if (resultSuffix) - { - result += resultSuffix - } - result += " - " - result += Cura.MachineManager.activeQualityLayerHeight + "mm" - result += "" - } - } - - return result - } - UM.SimpleButton { id: customisedSettings @@ -169,7 +178,6 @@ Item { id: downArrow - source: UM.Theme.getIcon("arrow_bottom") width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml index a23b87fdbe..1a15980693 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml @@ -37,6 +37,7 @@ RowLayout return "" } font: UM.Theme.getFont("medium") + elide: Text.ElideMiddle UM.SettingPropertyProvider { diff --git a/resources/qml/ViewOrientationControls.qml b/resources/qml/ViewOrientationControls.qml index 5750e935f4..97f2bb9400 100644 --- a/resources/qml/ViewOrientationControls.qml +++ b/resources/qml/ViewOrientationControls.qml @@ -20,29 +20,64 @@ Row { iconSource: UM.Theme.getIcon("view_3d") onClicked: Cura.Actions.view3DCamera.trigger() + + UM.TooltipArea + { + anchors.fill: parent + text: catalog.i18nc("@info:tooltip", "3D View") + acceptedButtons: Qt.NoButton + } } ViewOrientationButton { iconSource: UM.Theme.getIcon("view_front") onClicked: Cura.Actions.viewFrontCamera.trigger() + + UM.TooltipArea + { + anchors.fill: parent + text: catalog.i18nc("@info:tooltip", "Front View") + acceptedButtons: Qt.NoButton + } } ViewOrientationButton { iconSource: UM.Theme.getIcon("view_top") onClicked: Cura.Actions.viewTopCamera.trigger() + + UM.TooltipArea + { + anchors.fill: parent + text: catalog.i18nc("@info:tooltip", "Top View") + acceptedButtons: Qt.NoButton + } } ViewOrientationButton { iconSource: UM.Theme.getIcon("view_left") onClicked: Cura.Actions.viewLeftSideCamera.trigger() + + UM.TooltipArea + { + anchors.fill: parent + text: catalog.i18nc("@info:tooltip", "Left View") + acceptedButtons: Qt.NoButton + } } ViewOrientationButton { iconSource: UM.Theme.getIcon("view_right") onClicked: Cura.Actions.viewRightSideCamera.trigger() + + UM.TooltipArea + { + anchors.fill: parent + text: catalog.i18nc("@info:tooltip", "Right View") + acceptedButtons: Qt.NoButton + } } } diff --git a/tests/Machines/TestVariantNode.py b/tests/Machines/TestVariantNode.py index 71257bd972..4e58069be3 100644 --- a/tests/Machines/TestVariantNode.py +++ b/tests/Machines/TestVariantNode.py @@ -104,10 +104,11 @@ def test_variantNodeInit_excludedMaterial(container_registry, machine_node): def test_materialAdded(container_registry, machine_node, metadata, material_result_list): variant_node = createVariantNode("machine_1", machine_node, container_registry) machine_node.exclude_materials = ["material_3"] - with patch("cura.Machines.VariantNode.MaterialNode"): # We're not testing the material node here, so patch it out. - with patch.dict(metadata_dict, metadata): - mocked_container = createMockedInstanceContainer() - variant_node._materialAdded(mocked_container) + with patch("UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", MagicMock(return_value = container_registry)): + with patch("cura.Machines.VariantNode.MaterialNode"): # We're not testing the material node here, so patch it out. + with patch.dict(metadata_dict, metadata): + mocked_container = createMockedInstanceContainer() + variant_node._materialAdded(mocked_container) assert len(material_result_list) == len(variant_node.materials) for name in material_result_list: