diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 5d1e38f5c8..275fce6995 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -821,6 +821,7 @@ class BuildVolume(SceneNode): offset_y = extruder.getProperty("machine_nozzle_offset_y", "value") if offset_y is None: offset_y = 0 + offset_y = -offset_y #Y direction of g-code is the inverse of Y direction of Cura's scene space. result[extruder_id] = [] for polygon in machine_disallowed_polygons: diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index db0848bdf4..f624373b4e 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -57,10 +57,10 @@ from cura.Settings.MachineNameValidator import MachineNameValidator from cura.Machines.Models.BuildPlateModel import BuildPlateModel from cura.Machines.Models.NozzleModel import NozzleModel -from cura.Machines.Models.QualityProfilesModel import QualityProfilesModel -from cura.Machines.Models.CustomQualityProfilesModel import CustomQualityProfilesModel +from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfilesDropDownMenuModel +from cura.Machines.Models.CustomQualityProfilesDropDownMenuModel import CustomQualityProfilesDropDownMenuModel -from cura.Machines.Models.Other.MultiBuildPlateModel import MultiBuildPlateModel +from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel from cura.Machines.Models.MaterialManagementModel import MaterialManagementModel from cura.Machines.Models.GenericMaterialsModel import GenericMaterialsModel @@ -399,8 +399,8 @@ class CuraApplication(QtApplication): self.getCuraSceneController().setActiveBuildPlate(0) # Initialize - self._quality_profile_model = None - self._custom_quality_profile_model = None + self._quality_profile_drop_down_menu_model = None + self._custom_quality_profile_drop_down_menu_model = None CuraApplication.Created = True @@ -859,6 +859,7 @@ class CuraApplication(QtApplication): def getVariantManager(self, *args): return self._variant_manager + @pyqtSlot(result = QObject) def getMaterialManager(self, *args): return self._material_manager @@ -918,15 +919,15 @@ class CuraApplication(QtApplication): def getPrintInformation(self): return self._print_information - def getQualityProfileModel(self, *args, **kwargs): - if self._quality_profile_model is None: - self._quality_profile_model = QualityProfilesModel(self) - return self._quality_profile_model + def getQualityProfilesDropDownMenuModel(self, *args, **kwargs): + if self._quality_profile_drop_down_menu_model is None: + self._quality_profile_drop_down_menu_model = QualityProfilesDropDownMenuModel(self) + return self._quality_profile_drop_down_menu_model - def getCustomQualityProfilesModel(self, *args, **kwargs): - if self._custom_quality_profile_model is None: - self._custom_quality_profile_model = CustomQualityProfilesModel(self) - return self._custom_quality_profile_model + def getCustomQualityProfilesDropDownMenuModel(self, *args, **kwargs): + if self._custom_quality_profile_drop_down_menu_model is None: + self._custom_quality_profile_drop_down_menu_model = CustomQualityProfilesDropDownMenuModel(self) + return self._custom_quality_profile_drop_down_menu_model ## Registers objects for the QML engine to use. # @@ -960,8 +961,10 @@ class CuraApplication(QtApplication): qmlRegisterType(MaterialManagementModel, "Cura", 1, 0, "MaterialManagementModel") qmlRegisterType(QualityManagementModel, "Cura", 1, 0, "QualityManagementModel") - qmlRegisterSingletonType(QualityProfilesModel, "Cura", 1, 0, "QualityProfilesModel", self.getQualityProfileModel) - qmlRegisterSingletonType(CustomQualityProfilesModel, "Cura", 1, 0, "CustomQualityProfilesModel", self.getCustomQualityProfilesModel) + qmlRegisterSingletonType(QualityProfilesDropDownMenuModel, "Cura", 1, 0, + "QualityProfilesDropDownMenuModel", self.getQualityProfilesDropDownMenuModel) + qmlRegisterSingletonType(CustomQualityProfilesDropDownMenuModel, "Cura", 1, 0, + "CustomQualityProfilesDropDownMenuModel", self.getCustomQualityProfilesDropDownMenuModel) qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel") qmlRegisterType(MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler") diff --git a/cura/Machines/MaterialGroup.py b/cura/Machines/MaterialGroup.py index 9111cc3a80..009778943a 100644 --- a/cura/Machines/MaterialGroup.py +++ b/cura/Machines/MaterialGroup.py @@ -1,6 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. + # # A MaterialGroup represents a group of material InstanceContainers that are derived from a single material profile. # The main InstanceContainer which has the ID of the material profile file name is called the "root_material". For diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index ffb796a705..98e4f67f82 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -2,16 +2,24 @@ # Cura is released under the terms of the LGPLv3 or higher. from collections import defaultdict, OrderedDict -from typing import Optional +import copy +import uuid +from typing import Optional, TYPE_CHECKING -from PyQt5.Qt import QTimer, QObject, pyqtSignal +from PyQt5.Qt import QTimer, QObject, pyqtSignal, pyqtSlot +from UM.Application import Application from UM.Logger import Logger -from UM.Settings import ContainerRegistry +from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.Settings.SettingFunction import SettingFunction +from UM.Util import parseBool from .MaterialNode import MaterialNode from .MaterialGroup import MaterialGroup +if TYPE_CHECKING: + from cura.Settings.GlobalStack import GlobalStack + # # MaterialManager maintains a number of maps and trees for material lookup. @@ -29,6 +37,7 @@ class MaterialManager(QObject): def __init__(self, container_registry, parent = None): super().__init__(parent) + self._application = Application.getInstance() self._container_registry = container_registry # type: ContainerRegistry self._fallback_materials_map = dict() # material_type -> generic material metadata @@ -260,6 +269,20 @@ class MaterialManager(QObject): return material_id_metadata_dict + # + # A convenience function to get available materials for the given machine with the extruder position. + # + def getAvailableMaterialsForMachineExtruder(self, machine: "GlobalStack", + extruder_stack: "ExtruderStack") -> Optional[dict]: + machine_definition_id = machine.definition.getId() + variant_name = None + if extruder_stack.variant.getId() != "empty_variant": + variant_name = extruder_stack.variant.getName() + diameter = extruder_stack.approximateMaterialDiameter + + # Fetch the available materials (ContainerNode) for the current active machine and extruder setup. + return self.getAvailableMaterials(machine_definition_id, variant_name, diameter) + # # Gets MaterialNode for the given extruder and machine with the given material name. # Returns None if: @@ -315,7 +338,7 @@ class MaterialManager(QObject): # This function returns the generic root material ID for the given material type, where material types are "PLA", # "ABS", etc. # - def getFallbackMaterialIdByMaterialType(self, material_type: str) -> str: + def getFallbackMaterialIdByMaterialType(self, material_type: str) -> Optional[str]: # For safety if material_type not in self._fallback_materials_map: Logger.log("w", "The material type [%s] does not have a fallback material" % material_type) @@ -325,3 +348,132 @@ class MaterialManager(QObject): return self.getRootMaterialIDWithoutDiameter(fallback_material["id"]) else: return None + + def getDefaultMaterial(self, global_stack: "GlobalStack", extruder_variant_name: str) -> Optional["MaterialNode"]: + node = None + machine_definition = global_stack.definition + if parseBool(machine_definition.getMetaDataEntry("has_materials", False)): + material_diameter = machine_definition.getProperty("material_diameter", "value") + if isinstance(material_diameter, SettingFunction): + material_diameter = material_diameter(global_stack) + approximate_material_diameter = str(round(material_diameter)) + root_material_id = machine_definition.getMetaDataEntry("preferred_material") + root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_material_diameter) + node = self.getMaterialNode(machine_definition.getId(), extruder_variant_name, + material_diameter, root_material_id) + return node + + # + # Methods for GUI + # + + # + # Sets the new name for the given material. + # + @pyqtSlot("QVariant", str) + def setMaterialName(self, material_node: "MaterialNode", name: str): + root_material_id = material_node.metadata["base_file"] + if self._container_registry.isReadOnly(root_material_id): + Logger.log("w", "Cannot set name of read-only container %s.", root_material_id) + return + + material_group = self.getMaterialGroup(root_material_id) + material_group.root_material_node.getContainer().setName(name) + + # + # Removes the given material. + # + @pyqtSlot("QVariant") + def removeMaterial(self, material_node: "MaterialNode"): + root_material_id = material_node.metadata["base_file"] + material_group = self.getMaterialGroup(root_material_id) + if not material_group: + Logger.log("d", "Unable to remove the material with id %s, because it doesn't exist.", root_material_id) + return + + nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list + for node in nodes_to_remove: + self._container_registry.removeContainer(node.metadata["id"]) + + # + # Creates a duplicate of a material, which has the same GUID and base_file metadata. + # Returns the root material ID of the duplicated material if successful. + # + @pyqtSlot("QVariant", result = str) + def duplicateMaterial(self, material_node, new_base_id = None, new_metadata = None) -> Optional[str]: + root_material_id = material_node.metadata["base_file"] + + material_group = self.getMaterialGroup(root_material_id) + if not material_group: + Logger.log("i", "Unable to duplicate the material with id %s, because it doesn't exist.", root_material_id) + return None + + base_container = material_group.root_material_node.getContainer() + + # Ensure all settings are saved. + self._application.saveSettings() + + # Create a new ID & container to hold the data. + new_containers = [] + if new_base_id is None: + new_base_id = self._container_registry.uniqueName(base_container.getId()) + new_base_container = copy.deepcopy(base_container) + new_base_container.getMetaData()["id"] = new_base_id + new_base_container.getMetaData()["base_file"] = new_base_id + if new_metadata is not None: + for key, value in new_metadata.items(): + new_base_container.getMetaData()[key] = value + new_containers.append(new_base_container) + + # Clone all of them. + for node in material_group.derived_material_node_list: + container_to_copy = node.getContainer() + # Create unique IDs for every clone. + new_id = new_base_id + if container_to_copy.getMetaDataEntry("definition") != "fdmprinter": + new_id += "_" + container_to_copy.getMetaDataEntry("definition") + if container_to_copy.getMetaDataEntry("variant_name"): + variant_name = container_to_copy.getMetaDataEntry("variant_name") + new_id += "_" + variant_name.replace(" ", "_") + + new_container = copy.deepcopy(container_to_copy) + new_container.getMetaData()["id"] = new_id + new_container.getMetaData()["base_file"] = new_base_id + if new_metadata is not None: + for key, value in new_metadata.items(): + new_container.getMetaData()[key] = value + + new_containers.append(new_container) + + for container_to_add in new_containers: + container_to_add.setDirty(True) + self._container_registry.addContainer(container_to_add) + return new_base_id + + # + # Create a new material by cloning Generic PLA for the current material diameter and generate a new GUID. + # + @pyqtSlot(result = str) + def createMaterial(self) -> str: + from UM.i18n import i18nCatalog + catalog = i18nCatalog("cura") + # Ensure all settings are saved. + self._application.saveSettings() + + global_stack = self._application.getGlobalContainerStack() + approximate_diameter = str(round(global_stack.getProperty("material_diameter", "value"))) + root_material_id = "generic_pla" + root_material_id = self.getRootMaterialIDForDiameter(root_material_id, approximate_diameter) + material_group = self.getMaterialGroup(root_material_id) + + # Create a new ID & container to hold the data. + new_id = self._container_registry.uniqueName("custom_material") + new_metadata = {"name": catalog.i18nc("@label", "Custom Material"), + "brand": catalog.i18nc("@label", "Custom"), + "GUID": str(uuid.uuid4()), + } + + self.duplicateMaterial(material_group.root_material_node, + new_base_id = new_id, + new_metadata = new_metadata) + return new_id diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index 1b06fa5d1a..de0c68d60a 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -1,41 +1,17 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Optional from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty -from UM.Logger import Logger from UM.Qt.ListModel import ListModel -def getAvailableMaterials(extruder_position: Optional[int] = None): - from cura.CuraApplication import CuraApplication - machine_manager = CuraApplication.getInstance().getMachineManager() - extruder_manager = CuraApplication.getInstance().getExtruderManager() - material_manager = CuraApplication.getInstance().getMaterialManager() - - active_global_stack = machine_manager.activeMachine - extruder_stack = extruder_manager.getActiveExtruderStack() - if extruder_position is not None: - if active_global_stack is not None: - extruder_stack = active_global_stack.extruders.get(str(extruder_position)) - - if active_global_stack is None or extruder_stack is None: - Logger.log("d", "Active global stack [%s] or extruder stack [%s] is None, setting material list to empty.", - active_global_stack, extruder_stack) - return - - machine_definition_id = active_global_stack.definition.getId() - variant_name = None - if extruder_stack.variant.getId() != "empty_variant": - variant_name = extruder_stack.variant.getName() - diameter = extruder_stack.approximateMaterialDiameter - - # Fetch the available materials (ContainerNode) for the current active machine and extruder setup. - result_dict = material_manager.getAvailableMaterials(machine_definition_id, variant_name, diameter) - return result_dict - - +# +# This is the base model class for GenericMaterialsModel and BrandMaterialsModel +# Those 2 models are used by the material drop down menu to show generic materials and branded materials separately. +# The extruder position defined here is being used to bound a menu to the correct extruder. This is used in the top +# bar menu "Settings" -> "Extruder nr" -> "Material" -> this menu +# class BaseMaterialsModel(ListModel): RootMaterialIdRole = Qt.UserRole + 1 IdRole = Qt.UserRole + 2 diff --git a/cura/Machines/Models/BrandMaterialsModel.py b/cura/Machines/Models/BrandMaterialsModel.py index 1edef672d4..6628d924f1 100644 --- a/cura/Machines/Models/BrandMaterialsModel.py +++ b/cura/Machines/Models/BrandMaterialsModel.py @@ -5,9 +5,12 @@ from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty from UM.Qt.ListModel import ListModel -from .BaseMaterialsModel import BaseMaterialsModel, getAvailableMaterials +from .BaseMaterialsModel import BaseMaterialsModel +# +# This is an intermediate model to group materials with different colours for a same brand and type. +# class MaterialsModelGroupedByType(ListModel): NameRole = Qt.UserRole + 1 ColorsRole = Qt.UserRole + 2 @@ -19,7 +22,18 @@ class MaterialsModelGroupedByType(ListModel): self.addRoleName(self.ColorsRole, "colors") -## Brand --> Material Type -> list of materials +# +# This model is used to show branded materials in the material drop down menu. +# The structure of the menu looks like this: +# Brand -> Material Type -> list of materials +# +# To illustrate, a branded material menu may look like this: +# Ultimaker -> PLA -> Yellow PLA +# -> Black PLA +# -> ... +# -> ABS -> White ABS +# ... +# class BrandMaterialsModel(ListModel): NameRole = Qt.UserRole + 1 MaterialsRole = Qt.UserRole + 2 @@ -36,12 +50,12 @@ class BrandMaterialsModel(ListModel): from cura.CuraApplication import CuraApplication self._machine_manager = CuraApplication.getInstance().getMachineManager() - extruder_manager = CuraApplication.getInstance().getExtruderManager() - material_manager = CuraApplication.getInstance().getMaterialManager() + self._extruder_manager = CuraApplication.getInstance().getExtruderManager() + self._material_manager = CuraApplication.getInstance().getMaterialManager() self._machine_manager.globalContainerChanged.connect(self._update) - extruder_manager.activeExtruderChanged.connect(self._update) - material_manager.materialsUpdated.connect(self._update) + self._extruder_manager.activeExtruderChanged.connect(self._update) + self._material_manager.materialsUpdated.connect(self._update) self._update() @@ -59,15 +73,21 @@ class BrandMaterialsModel(ListModel): if global_stack is None: self.setItems([]) return + extruder_position = str(self._extruder_position) + if extruder_position not in global_stack.extruders: + self.setItems([]) + return + extruder_stack = global_stack.extruders[str(self._extruder_position)] - result_dict = getAvailableMaterials(self._extruder_position) - if result_dict is None: + available_material_dict = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, + extruder_stack) + if available_material_dict is None: self.setItems([]) return brand_item_list = [] brand_group_dict = {} - for root_material_id, container_node in result_dict.items(): + for root_material_id, container_node in available_material_dict.items(): metadata = container_node.metadata brand = metadata["brand"] # Only add results for generic materials @@ -109,4 +129,3 @@ class BrandMaterialsModel(ListModel): brand_item_list.append(brand_item) self.setItems(brand_item_list) - diff --git a/cura/Machines/Models/CustomQualityProfilesModel.py b/cura/Machines/Models/CustomQualityProfilesDropDownMenuModel.py similarity index 80% rename from cura/Machines/Models/CustomQualityProfilesModel.py rename to cura/Machines/Models/CustomQualityProfilesDropDownMenuModel.py index 4c88d83fdc..0d297379cd 100644 --- a/cura/Machines/Models/CustomQualityProfilesModel.py +++ b/cura/Machines/Models/CustomQualityProfilesDropDownMenuModel.py @@ -1,21 +1,20 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from UM.Application import Application from UM.Logger import Logger -from cura.Machines.Models.QualityProfilesModel import QualityProfilesModel +from cura.Machines.Models.QualityProfilesDropDownMenuModel import QualityProfilesDropDownMenuModel # # This model is used for the custom profile items in the profile drop down menu. # -class CustomQualityProfilesModel(QualityProfilesModel): +class CustomQualityProfilesDropDownMenuModel(QualityProfilesDropDownMenuModel): def _update(self): Logger.log("d", "Updating %s ...", self.__class__.__name__) - active_global_stack = Application.getInstance().getMachineManager().activeMachine + active_global_stack = self._machine_manager.activeMachine if active_global_stack is None: self.setItems([]) Logger.log("d", "No active GlobalStack, set %s as empty.", self.__class__.__name__) diff --git a/cura/Machines/Models/GenericMaterialsModel.py b/cura/Machines/Models/GenericMaterialsModel.py index c15f88d59a..d20fc05b6e 100644 --- a/cura/Machines/Models/GenericMaterialsModel.py +++ b/cura/Machines/Models/GenericMaterialsModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from .BaseMaterialsModel import BaseMaterialsModel, getAvailableMaterials +from .BaseMaterialsModel import BaseMaterialsModel class GenericMaterialsModel(BaseMaterialsModel): @@ -25,14 +25,20 @@ class GenericMaterialsModel(BaseMaterialsModel): if global_stack is None: self.setItems([]) return + extruder_position = str(self._extruder_position) + if extruder_position not in global_stack.extruders: + self.setItems([]) + return + extruder_stack = global_stack.extruders[extruder_position] - result_dict = getAvailableMaterials(self._extruder_position) - if result_dict is None: + available_material_dict = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, + extruder_stack) + if available_material_dict is None: self.setItems([]) return item_list = [] - for root_material_id, container_node in result_dict.items(): + for root_material_id, container_node in available_material_dict.items(): metadata = container_node.metadata # Only add results for generic materials if metadata["brand"].lower() != "generic": diff --git a/cura/Machines/Models/MaterialManagementModel.py b/cura/Machines/Models/MaterialManagementModel.py index cd1aa12427..b250232282 100644 --- a/cura/Machines/Models/MaterialManagementModel.py +++ b/cura/Machines/Models/MaterialManagementModel.py @@ -5,8 +5,6 @@ from PyQt5.QtCore import Qt, pyqtProperty from UM.Qt.ListModel import ListModel -from .BaseMaterialsModel import getAvailableMaterials - # # This model is for the Material management page. @@ -52,12 +50,12 @@ class MaterialManagementModel(ListModel): from cura.CuraApplication import CuraApplication self._container_registry = CuraApplication.getInstance().getContainerRegistry() self._machine_manager = CuraApplication.getInstance().getMachineManager() - extruder_manager = CuraApplication.getInstance().getExtruderManager() - material_manager = CuraApplication.getInstance().getMaterialManager() + self._extruder_manager = CuraApplication.getInstance().getExtruderManager() + self._material_manager = CuraApplication.getInstance().getMaterialManager() self._machine_manager.globalContainerChanged.connect(self._update) - extruder_manager.activeExtruderChanged.connect(self._update) - material_manager.materialsUpdated.connect(self._update) + self._extruder_manager.activeExtruderChanged.connect(self._update) + self._material_manager.materialsUpdated.connect(self._update) self._update() @@ -66,14 +64,16 @@ class MaterialManagementModel(ListModel): if global_stack is None: self.setItems([]) return + active_extruder_stack = self._machine_manager.activeStack - result_dict = getAvailableMaterials() - if result_dict is None: + available_material_dict = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, + active_extruder_stack) + if available_material_dict is None: self.setItems([]) return material_list = [] - for root_material_id, container_node in result_dict.items(): + for root_material_id, container_node in available_material_dict.items(): keys_to_fetch = ("name", "brand", "material", diff --git a/cura/Machines/Models/Other/MultiBuildPlateModel.py b/cura/Machines/Models/MultiBuildPlateModel.py similarity index 87% rename from cura/Machines/Models/Other/MultiBuildPlateModel.py rename to cura/Machines/Models/MultiBuildPlateModel.py index 78ad458a99..f0f4997014 100644 --- a/cura/Machines/Models/Other/MultiBuildPlateModel.py +++ b/cura/Machines/Models/MultiBuildPlateModel.py @@ -1,3 +1,6 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + from PyQt5.QtCore import pyqtSignal, pyqtProperty from UM.Application import Application @@ -5,6 +8,10 @@ from UM.Scene.Selection import Selection from UM.Qt.ListModel import ListModel +# +# This is the model for multi build plate feature. +# This has nothing to do with the build plate types you can choose on the sidebar for a machine. +# class MultiBuildPlateModel(ListModel): maxBuildPlateChanged = pyqtSignal() diff --git a/cura/Machines/Models/Other/__init__.py b/cura/Machines/Models/Other/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cura/Machines/Models/QualityManagementModel.py b/cura/Machines/Models/QualityManagementModel.py index 6e5b37bbfa..e089f92329 100644 --- a/cura/Machines/Models/QualityManagementModel.py +++ b/cura/Machines/Models/QualityManagementModel.py @@ -6,6 +6,9 @@ from PyQt5.QtCore import Qt, pyqtSlot from UM.Qt.ListModel import ListModel +# +# This the QML model for the quality management page. +# class QualityManagementModel(ListModel): NameRole = Qt.UserRole + 1 IsReadOnlyRole = Qt.UserRole + 2 @@ -24,7 +27,7 @@ class QualityManagementModel(ListModel): self._container_registry = CuraApplication.getInstance().getContainerRegistry() self._machine_manager = CuraApplication.getInstance().getMachineManager() self._extruder_manager = CuraApplication.getInstance().getExtruderManager() - self._quality_manager = CuraApplication.getInstance()._quality_manager + self._quality_manager = CuraApplication.getInstance().getQualityManager() self._machine_manager.globalContainerChanged.connect(self._update) self._quality_manager.qualitiesUpdated.connect(self._update) @@ -32,12 +35,13 @@ class QualityManagementModel(ListModel): self._update() def _update(self): - global_stack = self._machine_manager._global_container_stack + global_stack = self._machine_manager.activeMachine quality_group_dict = self._quality_manager.getQualityGroups(global_stack) quality_changes_group_dict = self._quality_manager.getQualityChangesGroups(global_stack) - available_quality_types = set(qt for qt, qg in quality_group_dict.items() if qg.is_available) + available_quality_types = set(quality_type for quality_type, quality_group in quality_group_dict.items() + if quality_group.is_available) if not available_quality_types and not quality_changes_group_dict: # Nothing to show self.setItems([]) diff --git a/cura/Machines/Models/QualityProfilesModel.py b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py similarity index 70% rename from cura/Machines/Models/QualityProfilesModel.py rename to cura/Machines/Models/QualityProfilesDropDownMenuModel.py index 519213e8e2..a604502af8 100644 --- a/cura/Machines/Models/QualityProfilesModel.py +++ b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py @@ -6,17 +6,18 @@ from PyQt5.QtCore import Qt from UM.Application import Application from UM.Logger import Logger from UM.Qt.ListModel import ListModel + from cura.Machines.QualityManager import QualityGroup # -# QML Model for all built-in quality profiles. +# QML Model for all built-in quality profiles. This model is used for the drop-down quality menu. # -class QualityProfilesModel(ListModel): +class QualityProfilesDropDownMenuModel(ListModel): NameRole = Qt.UserRole + 1 QualityTypeRole = Qt.UserRole + 2 LayerHeightRole = Qt.UserRole + 3 - LayerHeightWithoutUnitRole = Qt.UserRole + 4 + LayerHeightUnitRole = Qt.UserRole + 4 AvailableRole = Qt.UserRole + 5 QualityGroupRole = Qt.UserRole + 6 QualityChangesGroupRole = Qt.UserRole + 7 @@ -27,17 +28,18 @@ class QualityProfilesModel(ListModel): self.addRoleName(self.NameRole, "name") self.addRoleName(self.QualityTypeRole, "quality_type") self.addRoleName(self.LayerHeightRole, "layer_height") - self.addRoleName(self.LayerHeightWithoutUnitRole, "layer_height_without_unit") + self.addRoleName(self.LayerHeightUnitRole, "layer_height_unit") self.addRoleName(self.AvailableRole, "available") self.addRoleName(self.QualityGroupRole, "quality_group") self.addRoleName(self.QualityChangesGroupRole, "quality_changes_group") - # connect signals - Application.getInstance().globalContainerStackChanged.connect(self._update) - Application.getInstance().getMachineManager().activeQualityGroupChanged.connect(self._update) - Application.getInstance().getMachineManager().extruderChanged.connect(self._update) + self._application = Application.getInstance() + self._machine_manager = self._application.getMachineManager() + self._quality_manager = Application.getInstance().getQualityManager() - self._quality_manager = Application.getInstance()._quality_manager + self._application.globalContainerStackChanged.connect(self._update) + self._machine_manager.activeQualityGroupChanged.connect(self._update) + self._machine_manager.extruderChanged.connect(self._update) self._quality_manager.qualitiesUpdated.connect(self._update) self._layer_height_unit = "" # This is cached @@ -47,15 +49,15 @@ class QualityProfilesModel(ListModel): def _update(self): Logger.log("d", "Updating quality profile model ...") - machine_manager = Application.getInstance().getMachineManager() - global_stack = machine_manager._global_container_stack + global_stack = self._machine_manager.activeMachine 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 machine_manager.activeMaterialsCompatible(): + if not self._machine_manager.activeMaterialsCompatible(): + Logger.log("d", "No active material compatibility, set quality profile model as empty.") self.setItems([]) return @@ -69,20 +71,20 @@ class QualityProfilesModel(ListModel): item = {"name": quality_group.name, "quality_type": quality_group.quality_type, - "layer_height": layer_height + self._layer_height_unit, - "layer_height_without_unit": layer_height, + "layer_height": layer_height, + "layer_height_unit": self._layer_height_unit, "available": quality_group.is_available, "quality_group": quality_group} item_list.append(item) # Sort items based on layer_height - item_list = sorted(item_list, key = lambda x: float(x["layer_height_without_unit"])) + item_list = sorted(item_list, key = lambda x: x["layer_height"]) self.setItems(item_list) def _fetchLayerHeight(self, quality_group: "QualityGroup"): - global_stack = Application.getInstance().getMachineManager()._global_container_stack + global_stack = self._machine_manager.activeMachine if not self._layer_height_unit: unit = global_stack.definition.getProperty("layer_height", "unit") if not unit: @@ -96,10 +98,10 @@ class QualityProfilesModel(ListModel): layer_height = default_layer_height if container.hasProperty("layer_height", "value"): - layer_height = str(container.getProperty("layer_height", "value")) + layer_height = container.getProperty("layer_height", "value") else: # Look for layer_height in the GlobalStack from material -> definition container = global_stack.definition if container.hasProperty("layer_height", "value"): layer_height = container.getProperty("layer_height", "value") - return str(layer_height) + return float(layer_height) diff --git a/cura/Machines/Models/QualitySettingsModel.py b/cura/Machines/Models/QualitySettingsModel.py index a62654ad2c..38b7ec28e4 100644 --- a/cura/Machines/Models/QualitySettingsModel.py +++ b/cura/Machines/Models/QualitySettingsModel.py @@ -4,6 +4,7 @@ from PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt from UM.Application import Application +from UM.Logger import Logger from UM.Qt.ListModel import ListModel from UM.Settings.ContainerRegistry import ContainerRegistry @@ -33,9 +34,10 @@ class QualitySettingsModel(ListModel): self._container_registry = ContainerRegistry.getInstance() self._application = Application.getInstance() - self._quality_manager = self._application._quality_manager + self._quality_manager = self._application.getQualityManager() - self._extruder_position = "" + self._selected_position = "" # empty string means GlobalStack + # strings such as "0", "1", etc. mean extruder positions self._selected_quality_item = None # The selected quality in the quality management page self._i18n_catalog = None @@ -43,18 +45,18 @@ class QualitySettingsModel(ListModel): self._update() - extruderPositionChanged = pyqtSignal() + selectedPositionChanged = pyqtSignal() selectedQualityItemChanged = pyqtSignal() - def setExtruderPosition(self, extruder_position): - if extruder_position != self._extruder_position: - self._extruder_position = extruder_position - self.extruderPositionChanged.emit() + def setSelectedPosition(self, selected_position): + if selected_position != self._selected_position: + self._selected_position = selected_position + self.selectedPositionChanged.emit() self._update() - @pyqtProperty(str, fset = setExtruderPosition, notify = extruderPositionChanged) - def extruderPosition(self): - return self._extruder_position + @pyqtProperty(str, fset = setSelectedPosition, notify = selectedPositionChanged) + def selectedPosition(self): + return self._selected_position def setSelectedQualityItem(self, selected_quality_item): if selected_quality_item != self._selected_quality_item: @@ -73,32 +75,38 @@ class QualitySettingsModel(ListModel): items = [] - global_container_stack = Application.getInstance().getGlobalContainerStack() + global_container_stack = self._application.getGlobalContainerStack() definition_container = global_container_stack.definition quality_group = self._selected_quality_item["quality_group"] quality_changes_group = self._selected_quality_item["quality_changes_group"] - if self._extruder_position == "": + if self._selected_position == "": quality_node = quality_group.node_for_global else: - quality_node = quality_group.nodes_for_extruders.get(self._extruder_position) + quality_node = quality_group.nodes_for_extruders.get(self._selected_position) settings_keys = quality_group.getAllKeys() quality_containers = [quality_node.getContainer()] + # Here, if the user has selected a quality changes, then "quality_changes_group" will not be None, and we fetch + # the settings in that quality_changes_group. if quality_changes_group is not None: - if self._extruder_position == "": + if self._selected_position == "": quality_changes_node = quality_changes_group.node_for_global else: - quality_changes_node = quality_changes_group.nodes_for_extruders.get(self._extruder_position) + quality_changes_node = quality_changes_group.nodes_for_extruders.get(self._selected_position) if quality_changes_node is not None: # it can be None if number of extruders are changed during runtime try: quality_containers.insert(0, quality_changes_node.getContainer()) except: # FIXME: This is to prevent incomplete update of QualityManager + Logger.logException("d", "Failed to get container for quality changes node %s", quality_changes_node) return settings_keys.update(quality_changes_group.getAllKeys()) + # We iterate over all definitions instead of settings in a quality/qualtiy_changes group is because in the GUI, + # the settings are grouped together by categories, and we had to go over all the definitions to figure out + # which setting belongs in which category. current_category = "" for definition in definition_container.findDefinitions(): if definition.type == "category": @@ -117,7 +125,7 @@ class QualitySettingsModel(ListModel): profile_value = new_value # Global tab should use resolve (if there is one) - if self._extruder_position == "": + if self._selected_position == "": resolve_value = global_container_stack.getProperty(definition.key, "resolve") if resolve_value is not None and definition.key in settings_keys: profile_value = resolve_value @@ -125,10 +133,10 @@ class QualitySettingsModel(ListModel): if profile_value is not None: break - if not self._extruder_position: + if not self._selected_position: user_value = global_container_stack.userChanges.getProperty(definition.key, "value") else: - extruder_stack = global_container_stack.extruders[self._extruder_position] + extruder_stack = global_container_stack.extruders[self._selected_position] user_value = extruder_stack.userChanges.getProperty(definition.key, "value") if profile_value is None and user_value is None: diff --git a/cura/Machines/QualityChangesGroup.py b/cura/Machines/QualityChangesGroup.py new file mode 100644 index 0000000000..f8de3d2011 --- /dev/null +++ b/cura/Machines/QualityChangesGroup.py @@ -0,0 +1,53 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from UM.Application import Application + +from .QualityGroup import QualityGroup + + +class QualityChangesGroup(QualityGroup): + + def __init__(self, name: str, quality_type: str, parent = None): + super().__init__(name, quality_type, parent) + self._container_registry = Application.getInstance().getContainerRegistry() + + def addNode(self, node: "QualityNode"): + # TODO: in 3.2 and earlier, a quality_changes container may have a field called "extruder" which contains the + # extruder definition ID it belongs to. But, in fact, we only need to know the following things: + # 1. which machine a custom profile is suitable for, + # 2. if this profile is for the GlobalStack, + # 3. if this profile is for an ExtruderStack and which one (the position). + # + # So, it is preferred to have a field like this: + # extruder_position = 1 + # instead of this: + # extruder = custom_extruder_1 + # + # An upgrade needs to be done if we want to do it this way. Before that, we use the extruder's definition + # to figure out its position. + # + extruder_definition_id = node.metadata.get("extruder") + if extruder_definition_id: + metadata_list = self._container_registry.findDefinitionContainersMetadata(id = extruder_definition_id) + if not metadata_list: + raise RuntimeError("%s cannot get metadata for extruder definition [%s]" % + (self, extruder_definition_id)) + extruder_definition_metadata = metadata_list[0] + extruder_position = str(extruder_definition_metadata["position"]) + + if extruder_position in self.nodes_for_extruders: + raise RuntimeError("%s tries to overwrite the existing nodes_for_extruders position [%s] %s with %s" % + (self, extruder_position, self.node_for_global, node)) + + self.nodes_for_extruders[extruder_position] = node + + else: + # This is a quality_changes for the GlobalStack + if self.node_for_global is not None: + raise RuntimeError("%s tries to overwrite the existing node_for_global %s with %s" % + (self, self.node_for_global, node)) + self.node_for_global = node + + def __str__(self) -> str: + return "%s[<%s>, available = %s]" % (self.__class__.__name__, self.name, self.is_available) diff --git a/cura/Machines/QualityGroup.py b/cura/Machines/QualityGroup.py new file mode 100644 index 0000000000..6945162401 --- /dev/null +++ b/cura/Machines/QualityGroup.py @@ -0,0 +1,50 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from typing import Optional, List + +from PyQt5.QtCore import QObject, pyqtSlot + + +# +# A QualityGroup represents a group of containers that must be applied to each ContainerStack when it's used. +# Some concrete examples are Quality and QualityChanges: when we select quality type "normal", this quality type +# must be applied to all stacks in a machine, although each stack can have different containers. Use an Ultimaker 3 +# as an example, suppose we choose quality type "normal", the actual InstanceContainers on each stack may look +# as below: +# GlobalStack ExtruderStack 1 ExtruderStack 2 +# quality container: um3_global_normal um3_aa04_pla_normal um3_aa04_abs_normal +# +# This QualityGroup is mainly used in quality and quality_changes to group the containers that can be applied to +# a machine, so when a quality/custom quality is selected, the container can be directly applied to each stack instead +# of looking them up again. +# +class QualityGroup(QObject): + + def __init__(self, name: str, quality_type: str, parent = None): + super().__init__(parent) + self.name = name + self.node_for_global = None # type: Optional["QualityGroup"] + self.nodes_for_extruders = dict() # position str -> QualityGroup + self.quality_type = quality_type + self.is_available = False + + @pyqtSlot(result = str) + def getName(self) -> str: + return self.name + + def getAllKeys(self) -> set: + result = set() + for node in [self.node_for_global] + list(self.nodes_for_extruders.values()): + if node is None: + continue + result.update(node.getContainer().getAllKeys()) + return result + + def getAllNodes(self) -> List["QualityGroup"]: + result = [] + if self.node_for_global is not None: + result.append(self.node_for_global) + for extruder_node in self.nodes_for_extruders.values(): + result.append(extruder_node) + return result diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index d5a9b6ea8a..9df451732a 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -1,154 +1,27 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Optional, List +from typing import TYPE_CHECKING, Optional from PyQt5.QtCore import QObject, QTimer, pyqtSignal, pyqtSlot from UM.Application import Application from UM.Logger import Logger from UM.Util import parseBool +from UM.Settings.InstanceContainer import InstanceContainer -from cura.Machines.ContainerNode import ContainerNode +from cura.Settings.ExtruderStack import ExtruderStack + +from .QualityGroup import QualityGroup +from .QualityNode import QualityNode + +if TYPE_CHECKING: + from cura.Settings.GlobalStack import GlobalStack + from .QualityChangesGroup import QualityChangesGroup # -# Quality lookup tree structure: -# -# ------| -# | | -# -# | -# -# | -# -# | -# -# + -# - -# -# A QualityGroup represents a group of containers that must be applied to each ContainerStack when it's used. -# Some concrete examples are Quality and QualityChanges: when we select quality type "normal", this quality type -# must be applied to all stacks in a machine, although each stack can have different containers. Use an Ultimaker 3 -# as an example, suppose we choose quality type "normal", the actual InstanceContainers on each stack may look -# as below: -# GlobalStack ExtruderStack 1 ExtruderStack 2 -# quality container: um3_global_normal um3_aa04_pla_normal um3_aa04_abs_normal -# -# This QualityGroup is mainly used in quality and quality_changes to group the containers that can be applied to -# a machine, so when a quality/custom quality is selected, the container can be directly applied to each stack instead -# of looking them up again. -# -class QualityGroup(QObject): - - def __init__(self, name: str, quality_type: str, parent = None): - super().__init__(parent) - self.name = name - self.node_for_global = None # type: Optional["QualityGroup"] - self.nodes_for_extruders = dict() # position str -> QualityGroup - self.quality_type = quality_type - self.is_available = False - - @pyqtSlot(result = str) - def getName(self) -> str: - return self.name - - def getAllKeys(self) -> set: - result = set() - for node in [self.node_for_global] + list(self.nodes_for_extruders.values()): - if node is None: - continue - for key in node.getContainer().getAllKeys(): - result.add(key) - return result - - def getAllNodes(self) -> List["QualityGroup"]: - result = [] - if self.node_for_global is not None: - result.append(self.node_for_global) - for extruder_node in self.nodes_for_extruders.values(): - result.append(extruder_node) - return result - - -class QualityChangesGroup(QualityGroup): - - def __init__(self, name: str, quality_type: str, parent = None): - super().__init__(name, quality_type, parent) - - def addNode(self, node: "QualityNode"): - # TODO: in 3.2 and earlier, a quality_changes container may have a field called "extruder" which contains the - # extruder definition ID it belongs to. But, in fact, we only need to know the following things: - # 1. which machine a custom profile is suitable for, - # 2. if this profile is for the GlobalStack, - # 3. if this profile is for an ExtruderStack and which one (the position). - # - # So, it is preferred to have a field like this: - # extruder_position = 1 - # instead of this: - # extruder = custom_extruder_1 - # - # An upgrade needs to be done if we want to do it this way. Before that, we use the extruder's definition - # to figure out its position. - # - extruder_definition_id = node.metadata.get("extruder") - if extruder_definition_id: - container_registry = Application.getInstance().getContainerRegistry() - metadata_list = container_registry.findDefinitionContainersMetadata(id = extruder_definition_id) - if not metadata_list: - raise RuntimeError("%s cannot get metadata for extruder definition [%s]" % - (self, extruder_definition_id)) - extruder_definition_metadata = metadata_list[0] - extruder_position = str(extruder_definition_metadata["position"]) - - if extruder_position in self.nodes_for_extruders: - raise RuntimeError("%s tries to overwrite the existing nodes_for_extruders position [%s] %s with %s" % - (self, extruder_position, self.node_for_global, node)) - - self.nodes_for_extruders[extruder_position] = node - - else: - # This is a quality_changes for the GlobalStack - if self.node_for_global is not None: - raise RuntimeError("%s tries to overwrite the existing node_for_global %s with %s" % - (self, self.node_for_global, node)) - self.node_for_global = node - - def __str__(self) -> str: - return "%s[<%s>, available = %s]" % (self.__class__.__name__, self.name, self.is_available) - - -# -# QualityNode is used for BOTH quality and quality_changes containers. -# -class QualityNode(ContainerNode): - - def __init__(self, metadata: Optional[dict] = None): - super().__init__(metadata = metadata) - self.quality_type_map = {} # quality_type -> QualityNode for InstanceContainer - - def addQualityMetadata(self, quality_type: str, metadata: dict): - if quality_type not in self.quality_type_map: - self.quality_type_map[quality_type] = QualityNode(metadata) - - def getQualityNode(self, quality_type: str) -> Optional["QualityNode"]: - return self.quality_type_map.get(quality_type) - - def addQualityChangesMetadata(self, quality_type: str, metadata: dict): - if quality_type not in self.quality_type_map: - self.quality_type_map[quality_type] = QualityNode() - quality_type_node = self.quality_type_map[quality_type] - - name = metadata["name"] - if name not in quality_type_node.children_map: - quality_type_node.children_map[name] = QualityChangesGroup(name, quality_type) - quality_changes_group = quality_type_node.children_map[name] - quality_changes_group.addNode(QualityNode(metadata)) - - -# -# Similar to MaterialManager, QualityManager maintains a number of maps and trees for material lookup. +# Similar to MaterialManager, QualityManager maintains a number of maps and trees for quality profile lookup. # The models GUI and QML use are now only dependent on the QualityManager. That means as long as the data in # QualityManager gets updated correctly, the GUI models should be updated correctly too, and the same goes for GUI. # @@ -164,7 +37,7 @@ class QualityManager(QObject): def __init__(self, container_registry, parent = None): super().__init__(parent) self._application = Application.getInstance() - self._material_manager = self._application._material_manager + self._material_manager = self._application.getMaterialManager() self._container_registry = container_registry self._empty_quality_container = self._application.empty_quality_container @@ -305,7 +178,6 @@ class QualityManager(QObject): # Returns a dict of "custom profile name" -> QualityChangesGroup def getQualityChangesGroups(self, machine: "GlobalStack") -> dict: - # Get machine definition ID for quality search machine_definition_id = getMachineDefinitionIDForQualitySearch(machine) machine_node = self._machine_quality_type_to_quality_changes_dict.get(machine_definition_id) @@ -327,9 +199,15 @@ class QualityManager(QObject): return quality_changes_group_dict + # + # Gets all quality groups for the given machine. Both available and none available ones will be included. + # It returns a dictionary with "quality_type"s as keys and "QualityGroup"s as values. + # Whether a QualityGroup is available can be unknown via the field QualityGroup.is_available. + # For more details, see QualityGroup. + # def getQualityGroups(self, machine: "GlobalStack") -> dict: - # Get machine definition ID for quality search machine_definition_id = getMachineDefinitionIDForQualitySearch(machine) + # This determines if we should only get the global qualities for the global stack and skip the global qualities for the extruder stacks has_variant_materials = parseBool(machine.getMetaDataEntry("has_variant_materials", False)) @@ -380,6 +258,17 @@ class QualityManager(QObject): if fallback_root_material_id: root_material_id_list.append(fallback_root_material_id) + # Here we construct a list of nodes we want to look for qualities with the highest priority first. + # The use case is that, when we look for qualities for a machine, we first want to search in the following + # order: + # 1. machine-variant-and-material-specific qualities if exist + # 2. machine-variant-specific qualities if exist + # 3. machine-material-specific qualities if exist + # 4. machine-specific qualities if exist + # 5. generic qualities if exist + # Each points above can be represented as a node in the lookup tree, so here we simply put those nodes into + # the list with priorities as the order. Later, we just need to loop over each node in this list and fetch + # qualities from there. nodes_to_check = [] if variant_name: @@ -426,7 +315,6 @@ class QualityManager(QObject): return quality_group_dict def getQualityGroupsForMachineDefinition(self, machine: "GlobalStack") -> dict: - # Get machine definition ID for quality search machine_definition_id = getMachineDefinitionIDForQualitySearch(machine) # To find the quality container for the GlobalStack, check in the following fall-back manner: @@ -449,6 +337,135 @@ class QualityManager(QObject): return quality_group_dict + # + # Methods for GUI + # + + # + # Remove the given quality changes group. + # + @pyqtSlot(QObject) + def removeQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup"): + Logger.log("i", "Removing quality changes group [%s]", quality_changes_group.name) + for node in quality_changes_group.getAllNodes(): + self._container_registry.removeContainer(node.metadata["id"]) + + # + # Rename a set of quality changes containers. Returns the new name. + # + @pyqtSlot(QObject, str, result = str) + def renameQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup", new_name: str) -> str: + Logger.log("i", "Renaming QualityChangesGroup[%s] to [%s]", quality_changes_group.name, new_name) + if new_name == quality_changes_group.name: + Logger.log("i", "QualityChangesGroup name [%s] unchanged.", quality_changes_group.name) + return new_name + + new_name = self._container_registry.uniqueName(new_name) + for node in quality_changes_group.getAllNodes(): + node.getContainer().setName(new_name) + + quality_changes_group.name = new_name + + self._application.getMachineManager().activeQualityChanged.emit() + self._application.getMachineManager().activeQualityGroupChanged.emit() + + return new_name + + # + # Duplicates the given quality. + # + @pyqtSlot(str, "QVariantMap") + def duplicateQualityChanges(self, quality_changes_name, quality_model_item): + global_stack = self._application.getGlobalContainerStack() + if not global_stack: + Logger.log("i", "No active global stack, cannot duplicate quality changes.") + return + + quality_group = quality_model_item["quality_group"] + quality_changes_group = quality_model_item["quality_changes_group"] + if quality_changes_group is None: + # create global quality changes only + new_quality_changes = self._createQualityChanges(quality_group.quality_type, quality_changes_name, + global_stack, extruder_id = None) + self._container_registry.addContainer(new_quality_changes) + else: + new_name = self._container_registry.uniqueName(quality_changes_name) + for node in quality_changes_group.getAllNodes(): + container = node.getContainer() + new_id = self._container_registry.uniqueName(container.getId()) + self._container_registry.addContainer(container.duplicate(new_id, new_name)) + + ## Create quality changes containers from the user containers in the active stacks. + # + # This will go through the global and extruder stacks and create quality_changes containers from + # the user containers in each stack. These then replace the quality_changes containers in the + # stack and clear the user settings. + @pyqtSlot(str) + def createQualityChanges(self, base_name): + machine_manager = Application.getInstance().getMachineManager() + + global_stack = machine_manager.activeMachine + if not global_stack: + return + + active_quality_name = machine_manager.activeQualityOrQualityChangesName + if active_quality_name == "": + Logger.log("w", "No quality container found in stack %s, cannot create profile", global_stack.getId()) + return + + machine_manager.blurSettings.emit() + if base_name is None or base_name == "": + base_name = active_quality_name + unique_name = self._container_registry.uniqueName(base_name) + + # Go through the active stacks and create quality_changes containers from the user containers. + stack_list = [global_stack] + list(global_stack.extruders.values()) + for stack in stack_list: + user_container = stack.userChanges + quality_container = stack.quality + quality_changes_container = stack.qualityChanges + if not quality_container or not quality_changes_container: + Logger.log("w", "No quality or quality changes container found in stack %s, ignoring it", stack.getId()) + continue + + extruder_definition_id = None + if isinstance(stack, ExtruderStack): + extruder_definition_id = stack.definition.getId() + quality_type = quality_container.getMetaDataEntry("quality_type") + new_changes = self._createQualityChanges(quality_type, unique_name, global_stack, extruder_definition_id) + from cura.Settings.ContainerManager import ContainerManager + ContainerManager.getInstance()._performMerge(new_changes, quality_changes_container, clear_settings = False) + ContainerManager.getInstance()._performMerge(new_changes, user_container) + + self._container_registry.addContainer(new_changes) + + # + # Create a quality changes container with the given setup. + # + def _createQualityChanges(self, quality_type: str, new_name: str, machine: "GlobalStack", + extruder_id: Optional[str]) -> "InstanceContainer": + base_id = machine.definition.getId() if extruder_id is None else extruder_id + new_id = base_id + "_" + new_name + new_id = new_id.lower().replace(" ", "_") + new_id = self._container_registry.uniqueName(new_id) + + # Create a new quality_changes container for the quality. + quality_changes = InstanceContainer(new_id) + quality_changes.setName(new_name) + quality_changes.addMetaDataEntry("type", "quality_changes") + quality_changes.addMetaDataEntry("quality_type", quality_type) + + # If we are creating a container for an extruder, ensure we add that to the container + if extruder_id is not None: + quality_changes.addMetaDataEntry("extruder", extruder_id) + + # If the machine specifies qualities should be filtered, ensure we match the current criteria. + machine_definition_id = getMachineDefinitionIDForQualitySearch(machine) + quality_changes.setDefinition(machine_definition_id) + + quality_changes.addMetaDataEntry("setting_version", self._application.SettingVersion) + return quality_changes + # # Gets the machine definition ID that can be used to search for Quality containers that are suitable for the given diff --git a/cura/Machines/QualityNode.py b/cura/Machines/QualityNode.py new file mode 100644 index 0000000000..a30e219da3 --- /dev/null +++ b/cura/Machines/QualityNode.py @@ -0,0 +1,35 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from typing import Optional + +from .ContainerNode import ContainerNode +from .QualityChangesGroup import QualityChangesGroup + + +# +# QualityNode is used for BOTH quality and quality_changes containers. +# +class QualityNode(ContainerNode): + + def __init__(self, metadata: Optional[dict] = None): + super().__init__(metadata = metadata) + self.quality_type_map = {} # quality_type -> QualityNode for InstanceContainer + + def addQualityMetadata(self, quality_type: str, metadata: dict): + if quality_type not in self.quality_type_map: + self.quality_type_map[quality_type] = QualityNode(metadata) + + def getQualityNode(self, quality_type: str) -> Optional["QualityNode"]: + return self.quality_type_map.get(quality_type) + + def addQualityChangesMetadata(self, quality_type: str, metadata: dict): + if quality_type not in self.quality_type_map: + self.quality_type_map[quality_type] = QualityNode() + quality_type_node = self.quality_type_map[quality_type] + + name = metadata["name"] + if name not in quality_type_node.children_map: + quality_type_node.children_map[name] = QualityChangesGroup(name, quality_type) + quality_changes_group = quality_type_node.children_map[name] + quality_changes_group.addNode(QualityNode(metadata)) diff --git a/cura/Machines/VariantManager.py b/cura/Machines/VariantManager.py index 834b47dd0f..6cb0a3d7b2 100644 --- a/cura/Machines/VariantManager.py +++ b/cura/Machines/VariantManager.py @@ -1,17 +1,22 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from enum import Enum from collections import OrderedDict -from typing import Optional +from typing import Optional, TYPE_CHECKING from UM.Logger import Logger from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.Util import parseBool from cura.Machines.ContainerNode import ContainerNode from cura.Settings.GlobalStack import GlobalStack +if TYPE_CHECKING: + from UM.Settings.DefinitionContainer import DefinitionContainer -class VariantType: + +class VariantType(Enum): BUILD_PLATE = "buildplate" NOZZLE = "nozzle" @@ -64,6 +69,7 @@ class VariantManager: self._machine_to_variant_dict_map[variant_definition][variant_type] = dict() variant_type = variant_metadata["hardware_type"] + variant_type = VariantType(variant_type) variant_dict = self._machine_to_variant_dict_map[variant_definition][variant_type] if variant_name in variant_dict: # ERROR: duplicated variant name. @@ -77,10 +83,29 @@ class VariantManager: # Almost the same as getVariantMetadata() except that this returns an InstanceContainer if present. # def getVariantNode(self, machine_definition_id: str, variant_name: str, - variant_type: Optional[str] = VariantType.NOZZLE) -> Optional["ContainerNode"]: + variant_type: Optional["VariantType"] = VariantType.NOZZLE) -> Optional["ContainerNode"]: return self._machine_to_variant_dict_map[machine_definition_id].get(variant_type, {}).get(variant_name) def getVariantNodes(self, machine: "GlobalStack", - variant_type: Optional[str] = VariantType.NOZZLE) -> dict: + variant_type: Optional["VariantType"] = VariantType.NOZZLE) -> dict: machine_definition_id = machine.definition.getId() return self._machine_to_variant_dict_map.get(machine_definition_id, {}).get(variant_type, {}) + + # + # Gets the default variant for the given machine definition. + # + def getDefaultVariantNode(self, machine_definition: "DefinitionContainer", + variant_type: VariantType) -> Optional["ContainerNode"]: + machine_definition_id = machine_definition.getId() + preferred_variant_name = None + if variant_type == VariantType.BUILD_PLATE: + if parseBool(machine_definition.getMetaDataEntry("has_variant_buildplates", False)): + preferred_variant_name = machine_definition.getMetaDataEntry("preferred_variant_buildplate_name") + else: + if parseBool(machine_definition.getMetaDataEntry("has_variants", False)): + preferred_variant_name = machine_definition.getMetaDataEntry("preferred_variant_name") + + node = None + if preferred_variant_name: + node = self.getVariantNode(machine_definition_id, preferred_variant_name, variant_type) + return node diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 279e7e0c03..05b740637d 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -202,8 +202,6 @@ class PrintInformation(QObject): if global_stack is None: return - # Material amount is sent as an amount of mm^3, so calculate length from that - radius = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2 self._material_lengths[build_plate_number] = [] self._material_weights[build_plate_number] = [] self._material_costs[build_plate_number] = [] @@ -212,16 +210,16 @@ class PrintInformation(QObject): material_preference_values = json.loads(Preferences.getInstance().getValue("cura/material_settings")) extruder_stacks = global_stack.extruders - for extruder_key in global_stack.extruders.keys(): - index = int(extruder_key) - if index >= len(self._material_amounts): # Right now the _material_amounts is a list, where the index is the extruder number + for position, extruder_stack in extruder_stacks.items(): + index = int(position) + if index >= len(self._material_amounts): continue amount = self._material_amounts[index] ## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some # list comprehension filtering to solve this for us. - extruder_stack = extruder_stacks[str(index)] density = extruder_stack.getMetaDataEntry("properties", {}).get("density", 0) material = extruder_stack.findContainer({"type": "material"}) + radius = extruder_stack.getProperty("material_diameter", "value") / 2 weight = float(amount) * float(density) / 1000 cost = 0 @@ -240,6 +238,7 @@ class PrintInformation(QObject): else: cost = 0 + # Material amount is sent as an amount of mm^3, so calculate length from that if radius != 0: length = round((amount / (math.pi * radius ** 2)) / 1000, 2) else: diff --git a/cura/Scene/CuraSceneController.py b/cura/Scene/CuraSceneController.py index d60179ae08..749c5257a2 100644 --- a/cura/Scene/CuraSceneController.py +++ b/cura/Scene/CuraSceneController.py @@ -4,7 +4,7 @@ from PyQt5.QtCore import Qt, pyqtSlot, QObject from PyQt5.QtWidgets import QApplication from cura.ObjectsModel import ObjectsModel -from cura.Machines.Models.Other.MultiBuildPlateModel import MultiBuildPlateModel +from cura.Machines.Models.MultiBuildPlateModel import MultiBuildPlateModel from UM.Application import Application from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 20ce9638c1..37d1443f38 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -1,7 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import copy import os.path import urllib.parse import uuid @@ -29,10 +28,10 @@ from UM.i18n import i18nCatalog from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderStack import ExtruderStack -from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch catalog = i18nCatalog("cura") + ## Manager class that contains common actions to deal with containers in Cura. # # This is primarily intended as a class to be able to perform certain actions @@ -160,17 +159,6 @@ class ContainerManager(QObject): return container.getProperty(setting_key, property_name) - ## Set the name of the specified material. - @pyqtSlot("QVariant", str) - def setMaterialName(self, material_node, new_name): - root_material_id = material_node.metadata["base_file"] - if self._container_registry.isReadOnly(root_material_id): - Logger.log("w", "Cannot set name of read-only container %s.", root_material_id) - return - - material_group = self._material_manager.getMaterialGroup(root_material_id) - material_group.root_material_node.getContainer().setName(new_name) - @pyqtSlot(str, result = str) def makeUniqueName(self, original_name): return self._container_registry.uniqueName(original_name) @@ -355,191 +343,6 @@ class ContainerManager(QObject): for container in send_emits_containers: container.sendPostponedEmits() - ## Create quality changes containers from the user containers in the active stacks. - # - # This will go through the global and extruder stacks and create quality_changes containers from - # the user containers in each stack. These then replace the quality_changes containers in the - # stack and clear the user settings. - # - # \return \type{bool} True if the operation was successfully, False if not. - @pyqtSlot(str) - def createQualityChanges(self, base_name): - global_stack = Application.getInstance().getGlobalContainerStack() - if not global_stack: - return - - active_quality_name = self._machine_manager.activeQualityOrQualityChangesName - if active_quality_name == "": - Logger.log("w", "No quality container found in stack %s, cannot create profile", global_stack.getId()) - return - - self._machine_manager.blurSettings.emit() - if base_name is None or base_name == "": - base_name = active_quality_name - unique_name = self._container_registry.uniqueName(base_name) - - # Go through the active stacks and create quality_changes containers from the user containers. - for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks(): - user_container = stack.userChanges - quality_container = stack.quality - quality_changes_container = stack.qualityChanges - if not quality_container or not quality_changes_container: - Logger.log("w", "No quality or quality changes container found in stack %s, ignoring it", stack.getId()) - continue - - extruder_definition_id = None - if isinstance(stack, ExtruderStack): - extruder_definition_id = stack.definition.getId() - quality_type = quality_container.getMetaDataEntry("quality_type") - new_changes = self._createQualityChanges(quality_type, unique_name, global_stack, extruder_definition_id) - self._performMerge(new_changes, quality_changes_container, clear_settings = False) - self._performMerge(new_changes, user_container) - - self._container_registry.addContainer(new_changes) - - # - # Remove the given quality changes group - # - @pyqtSlot(QObject) - def removeQualityChangesGroup(self, quality_changes_group): - Logger.log("i", "Removing quality changes group [%s]", quality_changes_group.name) - for node in quality_changes_group.getAllNodes(): - self._container_registry.removeContainer(node.metadata["id"]) - - # - # Rename a set of quality changes containers. Returns the new name. - # - @pyqtSlot(QObject, str, result = str) - def renameQualityChangesGroup(self, quality_changes_group, new_name) -> str: - Logger.log("i", "Renaming QualityChangesGroup[%s] to [%s]", quality_changes_group.name, new_name) - self._machine_manager.blurSettings.emit() - - if new_name == quality_changes_group.name: - Logger.log("i", "QualityChangesGroup name [%s] unchanged.", quality_changes_group.name) - return new_name - - new_name = self._container_registry.uniqueName(new_name) - for node in quality_changes_group.getAllNodes(): - node.getContainer().setName(new_name) - - self._machine_manager.activeQualityChanged.emit() - self._machine_manager.activeQualityGroupChanged.emit() - - return new_name - - @pyqtSlot(str, "QVariantMap") - def duplicateQualityChanges(self, quality_changes_name, quality_model_item): - global_stack = Application.getInstance().getGlobalContainerStack() - - quality_group = quality_model_item["quality_group"] - quality_changes_group = quality_model_item["quality_changes_group"] - if quality_changes_group is None: - # create global quality changes only - new_quality_changes = self._createQualityChanges(quality_group.quality_type, quality_changes_name, - global_stack, extruder_id = None) - self._container_registry.addContainer(new_quality_changes) - else: - new_name = self._container_registry.uniqueName(quality_changes_name) - for node in quality_changes_group.getAllNodes(): - container = node.getContainer() - new_id = self._container_registry.uniqueName(container.getId()) - self._container_registry.addContainer(container.duplicate(new_id, new_name)) - - @pyqtSlot("QVariant") - def removeMaterial(self, material_node): - root_material_id = material_node.metadata["base_file"] - material_group = self._material_manager.getMaterialGroup(root_material_id) - if not material_group: - Logger.log("d", "Unable to remove the material with id %s, because it doesn't exist.", root_material_id) - return - - nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list - for node in nodes_to_remove: - self._container_registry.removeContainer(node.metadata["id"]) - - - ## Create a duplicate of a material, which has the same GUID and base_file metadata - # - # \return \type{str} the id of the newly created container. - @pyqtSlot("QVariant", result = str) - def duplicateMaterial(self, material_node, new_base_id = None, new_metadata = None): - root_material_id = material_node.metadata["base_file"] - - material_group = self._material_manager.getMaterialGroup(root_material_id) - if not material_group: - Logger.log("d", "Unable to duplicate the material with id %s, because it doesn't exist.", root_material_id) - return - - base_container = material_group.root_material_node.getContainer() - containers_to_copy = [] - for node in material_group.derived_material_node_list: - containers_to_copy.append(node.getContainer()) - - # Ensure all settings are saved. - Application.getInstance().saveSettings() - - # Create a new ID & container to hold the data. - new_containers = [] - if new_base_id is None: - new_base_id = self._container_registry.uniqueName(base_container.getId()) - new_base_container = copy.deepcopy(base_container) - new_base_container.getMetaData()["id"] = new_base_id - new_base_container.getMetaData()["base_file"] = new_base_id - if new_metadata is not None: - for key, value in new_metadata.items(): - new_base_container.getMetaData()[key] = value - new_containers.append(new_base_container) - - # Clone all of them. - for container_to_copy in containers_to_copy: - # Create unique IDs for every clone. - new_id = new_base_id - if container_to_copy.getMetaDataEntry("definition") != "fdmprinter": - new_id += "_" + container_to_copy.getMetaDataEntry("definition") - if container_to_copy.getMetaDataEntry("variant_name"): - variant_name = container_to_copy.getMetaDataEntry("variant_name") - new_id += "_" + variant_name.replace(" ", "_") - - new_container = copy.deepcopy(container_to_copy) - new_container.getMetaData()["id"] = new_id - new_container.getMetaData()["base_file"] = new_base_id - if new_metadata is not None: - for key, value in new_metadata.items(): - new_container.getMetaData()[key] = value - - new_containers.append(new_container) - - for container_to_add in new_containers: - container_to_add.setDirty(True) - ContainerRegistry.getInstance().addContainer(container_to_add) - return new_base_id - - ## Create a new material by cloning Generic PLA for the current material diameter and setting the GUID to something unqiue - # - # \return \type{str} the id of the newly created container. - @pyqtSlot(result = str) - def createMaterial(self): - # Ensure all settings are saved. - Application.getInstance().saveSettings() - - global_stack = Application.getInstance().getGlobalContainerStack() - approximate_diameter = str(round(global_stack.getProperty("material_diameter", "value"))) - root_material_id = "generic_pla" - root_material_id = self._material_manager.getRootMaterialIDForDiameter(root_material_id, approximate_diameter) - material_group = self._material_manager.getMaterialGroup(root_material_id) - - # Create a new ID & container to hold the data. - new_id = self._container_registry.uniqueName("custom_material") - new_metadata = {"name": catalog.i18nc("@label", "Custom Material"), - "brand": catalog.i18nc("@label", "Custom"), - "GUID": str(uuid.uuid4()), - } - - self.duplicateMaterial(material_group.root_material_node, - new_base_id = new_id, - new_metadata = new_metadata) - return new_id - ## Get a list of materials that have the same GUID as the reference material # # \param material_id \type{str} the id of the material for which to get the linked materials. @@ -643,51 +446,6 @@ class ContainerManager(QObject): name_filter = "{0} ({1})".format(mime_type.comment, suffix_list) self._container_name_filters[name_filter] = entry - ## Creates a unique ID for a container by prefixing the name with the stack ID. - # - # This method creates a unique ID for a container by prefixing it with a specified stack ID. - # This is done to ensure we have an easily identified ID for quality changes, which have the - # same name across several stacks. - # - # \param stack_id The ID of the stack to prepend. - # \param container_name The name of the container that we are creating a unique ID for. - # - # \return Container name prefixed with stack ID, in lower case with spaces replaced by underscores. - def _createUniqueId(self, stack_id, container_name): - result = stack_id + "_" + container_name - result = result.lower() - result.replace(" ", "_") - return result - - ## Create a quality changes container for a specified quality container. - # - # \param quality_container The quality container to create a changes container for. - # \param new_name The name of the new quality_changes container. - # \param machine_definition The machine definition this quality changes container is specific to. - # \param extruder_id - # - # \return A new quality_changes container with the specified container as base. - def _createQualityChanges(self, quality_type, new_name, machine, extruder_id): - base_id = machine.definition.getId() if extruder_id is None else extruder_id - - # Create a new quality_changes container for the quality. - quality_changes = InstanceContainer(self._createUniqueId(base_id, new_name)) - quality_changes.setName(new_name) - quality_changes.addMetaDataEntry("type", "quality_changes") - quality_changes.addMetaDataEntry("quality_type", quality_type) - - # If we are creating a container for an extruder, ensure we add that to the container - if extruder_id is not None: - quality_changes.addMetaDataEntry("extruder", extruder_id) - - # If the machine specifies qualities should be filtered, ensure we match the current criteria. - machine_definition_id = getMachineDefinitionIDForQualitySearch(machine) - quality_changes.setDefinition(machine_definition_id) - - from cura.CuraApplication import CuraApplication - quality_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) - return quality_changes - ## Import single profile, file_url does not have to end with curaprofile @pyqtSlot(QUrl, result="QVariantMap") def importProfile(self, file_url): diff --git a/cura/Settings/CuraStackBuilder.py b/cura/Settings/CuraStackBuilder.py index 4d103d2537..a8234b9de9 100644 --- a/cura/Settings/CuraStackBuilder.py +++ b/cura/Settings/CuraStackBuilder.py @@ -41,29 +41,17 @@ class CuraStackBuilder: # get variant container for the global stack global_variant_container = application.empty_variant_container - if parseBool(machine_definition.getMetaDataEntry("has_variant_buildplates", False)): - global_variant_name = machine_definition.getMetaDataEntry("preferred_variant_buildplate_name") - if global_variant_name: - variant_node = variant_manager.getVariantNode(definition_id, global_variant_name, - variant_type = VariantType.BUILD_PLATE) - if variant_node is None: - raise RuntimeError("Cannot find buildplate variant with definition [%s] and variant name [%s]" % - (definition_id, global_variant_name)) - global_variant_container = variant_node.getContainer() + global_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.BUILD_PLATE) + if global_variant_node: + global_variant_container = global_variant_node.getContainer() # get variant container for extruders extruder_variant_container = application.empty_variant_container - # Only look for the preferred variant if this machine has variants + extruder_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.NOZZLE) extruder_variant_name = None - if parseBool(machine_definition.getMetaDataEntry("has_variants", False)): - extruder_variant_name = machine_definition.getMetaDataEntry("preferred_variant_name") - if extruder_variant_name: - variant_node = variant_manager.getVariantNode(definition_id, extruder_variant_name) - # Sanity check. If you see this error, the related definition files should be fixed. - if variant_node is None: - raise RuntimeError("Cannot find extruder variant with definition [%s] and variant name [%s]" % - (definition_id, extruder_variant_name)) - extruder_variant_container = variant_node.getContainer() + if extruder_variant_node: + extruder_variant_container = extruder_variant_node.getContainer() + extruder_variant_name = extruder_variant_container.getName() generated_name = registry.createUniqueName("machine", "", name, machine_definition.getName()) # Make sure the new name does not collide with any definition or (quality) profile @@ -83,19 +71,8 @@ class CuraStackBuilder: # get material container for extruders material_container = application.empty_material_container - # Only look for the preferred material if this machine has materials - if parseBool(machine_definition.getMetaDataEntry("has_materials", False)): - material_diameter = machine_definition.getProperty("material_diameter", "value") - if isinstance(material_diameter, SettingFunction): - material_diameter = material_diameter(new_global_stack) - approximate_material_diameter = str(round(material_diameter)) - root_material_id = machine_definition.getMetaDataEntry("preferred_material") - root_material_id = material_manager.getRootMaterialIDForDiameter(root_material_id, approximate_material_diameter) - material_node = material_manager.getMaterialNode(definition_id, extruder_variant_name, material_diameter, root_material_id) - # Sanity check. If you see this error, the related definition files should be fixed. - if not material_node: - raise RuntimeError("Cannot find material with definition [%s], extruder_variant_name [%s], and root_material_id [%s]" % - (definition_id, extruder_variant_name, root_material_id)) + material_node = material_manager.getDefaultMaterial(new_global_stack, extruder_variant_name) + if material_node: material_container = material_node.getContainer() # Create ExtruderStacks diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index fcbe67e465..0ac3e4bd66 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -414,6 +414,33 @@ class ExtruderManager(QObject): self.extrudersChanged.emit(global_stack_id) self.setActiveExtruderIndex(0) + # + # This function tries to fix the problem with per-extruder-settable nozzle size and material diameter problems + # in early versions (3.0 - 3.2.1). + # + # In earlier versions, "nozzle size" and "material diameter" are only applicable to the complete machine, so all + # extruders share the same values. In this case, "nozzle size" and "material diameter" are saved in the + # GlobalStack's DefinitionChanges container. + # + # Later, we could have different "nozzle size" for each extruder, but "material diameter" could only be set for + # the entire machine. In this case, "nozzle size" should be saved in each ExtruderStack's DefinitionChanges, but + # "material diameter" still remains in the GlobalStack's DefinitionChanges. + # + # Lateer, both "nozzle size" and "material diameter" are settable per-extruder, and both settings should be saved + # in the ExtruderStack's DefinitionChanges. + # + # There were some bugs in upgrade so the data weren't saved correct as described above. This function tries fix + # this. + # + # One more thing is about material diameter and single-extrusion machines. Most single-extrusion machines don't + # specifically define their extruder definition, so they reuse "fdmextruder", but for those machines, they may + # define "material diameter = 1.75" in their machine definition, but in "fdmextruder", it's still "2.85". This + # causes a problem with incorrect default values. + # + # This is also fixed here in this way: If no "material diameter" is specified, it will look for the default value + # in both the Extruder's definition and the Global's definition. If 2 values don't match, we will use the value + # from the Global definition by setting it in the Extruder's DefinitionChanges container. + # def _fixMaterialDiameterAndNozzleSize(self, global_stack, extruder_stack_list): keys_to_copy = ["material_diameter", "machine_nozzle_size"] # these will be copied over to all extruders @@ -424,22 +451,6 @@ class ExtruderManager(QObject): if extruder_stack.definitionChanges.hasProperty(key, "value"): continue - # - # We cannot add a setting definition of "material_diameter" into the extruder's definition at runtime - # because all other machines which uses "fdmextruder" as the extruder definition will be affected. - # - # The problem is that single extrusion machines have their default material diameter defined in the global - # definitions. Now we automatically create an extruder stack for those machines using "fdmextruder" - # definition, which doesn't have the specific "material_diameter" and "machine_nozzle_size" defined for - # each machine. This results in wrong values which can be found in the MachineSettings dialog. - # - # To solve this, we put "material_diameter" back into the "fdmextruder" definition because modifying it in - # the extruder definition will affect all machines which uses the "fdmextruder" definition. Moreover, now - # we also check the value defined in the machine definition. If present, the value defined in the global - # stack's definition changes container will be copied. Otherwise, we will check if the default values in the - # machine definition and the extruder definition are the same, and if not, the default value in the machine - # definition will be copied to the extruder stack's definition changes. - # setting_value_in_global_def_changes = global_stack.definitionChanges.getProperty(key, "value") setting_value_in_global_def = global_stack.definition.getProperty(key, "value") setting_value = setting_value_in_global_def diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index fcdcdd9228..5cdcba68b0 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -13,7 +13,7 @@ from UM.Settings.Interfaces import ContainerInterface, PropertyEvaluationContext from UM.Util import parseBool from . import Exceptions -from .CuraContainerStack import CuraContainerStack +from .CuraContainerStack import CuraContainerStack, _ContainerIndexes from .ExtruderManager import ExtruderManager if TYPE_CHECKING: @@ -69,14 +69,10 @@ class ExtruderStack(CuraContainerStack): # \return The filament diameter for the printer @property def materialDiameter(self) -> float: - containers_to_check = [self.variant, self.definitionChanges, self.definition] + context = PropertyEvaluationContext(self) + context.context["evaluate_from_container_index"] = _ContainerIndexes.Variant - for container in containers_to_check: - if container is not None: - material_diameter = container.getProperty("material_diameter", "value") - if material_diameter is not None: - return material_diameter - return -1 + return self.getProperty("material_diameter", "value", context = context) ## Return the approximate filament diameter that the machine requires. # diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 56479f40a2..52887c1899 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -119,16 +119,13 @@ class MachineManager(QObject): if containers: containers[0].nameChanged.connect(self._onMaterialNameChanged) - # NEW self._material_manager = self._application._material_manager - self._material_manager.materialsUpdated.connect(self._onMaterialsUpdated) + self._quality_manager = self._application.getQualityManager() - def _onMaterialsUpdated(self): # When the materials lookup table gets updated, it can mean that a material has its name changed, which should # be reflected on the GUI. This signal emission makes sure that it happens. - self.rootMaterialChanged.emit() + self._material_manager.materialsUpdated.connect(self.rootMaterialChanged) - ### NEW activeQualityGroupChanged = pyqtSignal() activeQualityChangesGroupChanged = pyqtSignal() @@ -303,14 +300,12 @@ class MachineManager(QObject): if containers: global_stack = containers[0] ExtruderManager.getInstance().setActiveExtruderIndex(0) # Switch to first extruder - Application.getInstance().setGlobalContainerStack(global_stack) self._global_container_stack = global_stack Application.getInstance().setGlobalContainerStack(global_stack) ExtruderManager.getInstance()._globalContainerStackChanged() self._initMachineState(containers[0]) self.updateDefaultExtruder() self.updateNumberExtrudersEnabled() - self.globalContainerChanged.emit() self._onGlobalContainerChanged() self.__emitChangedSignals() @@ -835,11 +830,6 @@ class MachineManager(QObject): container = extruder.userChanges container.setProperty(setting_name, property_name, property_value) - # - # New - # - - # We not fetch it from _current_root_material_id, but later we can get it from somewhere else @pyqtProperty("QVariantList", notify = rootMaterialChanged) def currentExtruderPositions(self): return sorted(list(self._current_root_material_id.keys())) @@ -879,6 +869,10 @@ class MachineManager(QObject): return result + # + # Sets all quality and quality_changes containers to empty_quality and empty_quality_changes containers + # for all stacks in the currently active machine. + # def _setEmptyQuality(self): self._current_quality_group = None self._current_quality_changes_group = None @@ -911,11 +905,8 @@ class MachineManager(QObject): self.activeQualityChangesGroupChanged.emit() def _setQualityChangesGroup(self, quality_changes_group): - # TODO: quality_changes groups depend on a quality_type. Here it's fetching the quality_types every time. - # Can we do this better, like caching the quality group a quality_changes group depends on? quality_type = quality_changes_group.quality_type - quality_manager = Application.getInstance()._quality_manager - quality_group_dict = quality_manager.getQualityGroups(self._global_container_stack) + quality_group_dict = self._quality_manager.getQualityGroups(self._global_container_stack) quality_group = quality_group_dict[quality_type] quality_changes_container = self._empty_quality_changes_container @@ -1031,13 +1022,6 @@ class MachineManager(QObject): self._setMaterial(position, new_material) continue - # # Find a fallback material - # preferred_material_query = self._global_container_stack.getMetaDataEntry("preferred_material") - # preferred_material_key = preferred_material_query.replace("*", "") - # if preferred_material_key in candidate_materials: - # self._setMaterial(position, candidate_materials[preferred_material_key]) - # return - @pyqtSlot("QVariant") def setGlobalVariant(self, container_node): self.blurSettings.emit() diff --git a/cura/Settings/SettingOverrideDecorator.py b/cura/Settings/SettingOverrideDecorator.py index 686c546836..3aea6b5d54 100644 --- a/cura/Settings/SettingOverrideDecorator.py +++ b/cura/Settings/SettingOverrideDecorator.py @@ -3,12 +3,14 @@ import copy +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.SceneNodeDecorator import SceneNodeDecorator from UM.Signal import Signal, signalemitter from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Logger import Logger - +from UM.Settings.Validator import ValidatorState +from PyQt5.QtCore import QTimer from UM.Application import Application from cura.Settings.PerObjectContainerStack import PerObjectContainerStack @@ -38,6 +40,10 @@ class SettingOverrideDecorator(SceneNodeDecorator): self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId() self._is_non_printing_mesh = False + self._error_check_timer = QTimer() + self._error_check_timer.setInterval(250) + self._error_check_timer.setSingleShot(True) + self._error_check_timer.timeout.connect(self._checkStackForErrors) self._stack.propertyChanged.connect(self._onSettingChanged) @@ -97,8 +103,21 @@ class SettingOverrideDecorator(SceneNodeDecorator): if property_name == "value": self._is_non_printing_mesh = self.evaluateIsNonPrintingMesh() - Application.getInstance().getBackend().needsSlicing() - Application.getInstance().getBackend().tickle() + if not self._is_non_printing_mesh: + # self._error_check_timer.start() + self._checkStackForErrors() + Application.getInstance().getBackend().needsSlicing() + Application.getInstance().getBackend().tickle() + + def _checkStackForErrors(self): + hasErrors = False; + for key in self._stack.getAllKeys(): + validation_state = self._stack.getProperty(key, "validationState") + if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError): + Logger.log("w", "Setting Per Object %s is not valid.", key) + hasErrors = True + break + Application.getInstance().getObjectsModel().setStacksHaveErrors(hasErrors) ## Makes sure that the stack upon which the container stack is placed is # kept up to date. diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 6dd20b66e0..cbe882f253 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -1082,6 +1082,12 @@ class ThreeMFWorkspaceReader(WorkspaceReader): CuraApplication.getInstance().getMachineManager().activeQualityChanged.emit() # Actually change the active machine. + # + # This is scheduled for later is because it depends on the Variant/Material/Qualitiy Managers to have the latest + # data, but those managers will only update upon a container/container metadata changed signal. Because this + # function is running on the main thread (Qt thread), although those "changed" signals have been emitted, but + # they won't take effect until this function is done. + # To solve this, we schedule _updateActiveMachine() for later so it will have the latest data. CuraApplication.getInstance().callLater(self._updateActiveMachine, global_stack) # Load all the nodes / meshdata of the workspace diff --git a/plugins/PerObjectSettingsTool/PerObjectItem.qml b/plugins/PerObjectSettingsTool/PerObjectItem.qml index 89679b57a2..1317c00b19 100644 --- a/plugins/PerObjectSettingsTool/PerObjectItem.qml +++ b/plugins/PerObjectSettingsTool/PerObjectItem.qml @@ -32,11 +32,11 @@ UM.TooltipArea if(checked) { addedSettingsModel.setVisible(model.key, checked); - UM.ActiveTool.triggerAction("subscribeForSettingValidation", model.key) + UM.ActiveTool.triggerActionWithData("subscribeForSettingValidation", model.key) } else { - UM.ActiveTool.triggerAction("unsubscribeForSettingValidation", model.key) + UM.ActiveTool.triggerActionWithData("unsubscribeForSettingValidation", model.key) addedSettingsModel.setVisible(model.key, checked); } UM.ActiveTool.forceUpdate(); diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 4b5d2a5708..e72e1224df 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -242,7 +242,7 @@ Item { onClicked: { addedSettingsModel.setVisible(model.key, false) - UM.ActiveTool.triggerAction("unsubscribeForSettingValidation", model.key) + UM.ActiveTool.triggerActionWithData("unsubscribeForSettingValidation", model.key) } style: ButtonStyle diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py index aa8bb92045..b671db48fb 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py @@ -44,7 +44,6 @@ class PerObjectSettingsTool(Tool): self._error_check_timer.setSingleShot(True) self._error_check_timer.timeout.connect(self._updateStacksHaveErrors) - def event(self, event): super().event(event) if event.type == Event.MousePressEvent and self._controller.getToolsEnabled(): @@ -156,10 +155,12 @@ class PerObjectSettingsTool(Tool): def _onPropertyChanged(self, key: str, property_name: str) -> None: if property_name == "validationState": - self._error_check_timer.start() + # self._error_check_timer.start() + return def _updateStacksHaveErrors(self) -> None: - self._checkStacksHaveErrors() + return + # self._checkStacksHaveErrors() def _checkStacksHaveErrors(self): @@ -180,6 +181,7 @@ class PerObjectSettingsTool(Tool): def _checkStackForErrors(self, stack): + print("checking for errors") if stack is None: return False diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 80e3e3b721..edc782cc9e 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -48,6 +48,12 @@ class XmlMaterialProfile(InstanceContainer): ## Overridden from InstanceContainer # set the meta data for all machine / variant combinations + # + # The "apply_to_all" flag indicates whether this piece of metadata should be applied to all material containers + # or just this specific container. + # For example, when you change the material name, you want to apply it to all its derived containers, but for + # some specific settings, they should only be applied to a machine/variant-specific container. + # def setMetaDataEntry(self, key, value, apply_to_all = True): registry = ContainerRegistry.getInstance() if registry.isReadOnly(self.getId()): @@ -59,7 +65,7 @@ class XmlMaterialProfile(InstanceContainer): return # Get the MaterialGroup - material_manager = CuraApplication.getInstance()._material_manager + material_manager = CuraApplication.getInstance().getMaterialManager() root_material_id = self.getMetaDataEntry("base_file") #if basefile is self.getId, this is a basefile. material_group = material_manager.getMaterialGroup(root_material_id) @@ -606,8 +612,6 @@ class XmlMaterialProfile(InstanceContainer): if not variant_node: continue - # TODO: check if build plate variant exists - buildplate_compatibility = machine_compatibility buildplate_recommended = machine_compatibility settings = buildplate.iterfind("./um:setting", self.__namespaces) diff --git a/resources/definitions/abax_pri3.def.json b/resources/definitions/abax_pri3.def.json index f91a501a6f..2fa648096f 100644 --- a/resources/definitions/abax_pri3.def.json +++ b/resources/definitions/abax_pri3.def.json @@ -6,8 +6,7 @@ "visible": true, "author": "ABAX 3d Technologies", "manufacturer": "ABAX 3d Technologies", - "file_formats": "text/x-gcode", - "has_machine_quality": true + "file_formats": "text/x-gcode" }, "overrides": { "machine_start_gcode": { diff --git a/resources/definitions/dagoma_discoeasy200.def.json b/resources/definitions/dagoma_discoeasy200.def.json index 1674801b5e..4f0fddc41d 100644 --- a/resources/definitions/dagoma_discoeasy200.def.json +++ b/resources/definitions/dagoma_discoeasy200.def.json @@ -50,7 +50,7 @@ "default_value": 60 }, "speed_travel": { - "default_value": 100 + "value": "100" }, "retraction_amount": { "default_value": 3.5 diff --git a/resources/definitions/dagoma_neva.def.json b/resources/definitions/dagoma_neva.def.json index 430d2f6ab1..21a557ac22 100644 --- a/resources/definitions/dagoma_neva.def.json +++ b/resources/definitions/dagoma_neva.def.json @@ -42,7 +42,7 @@ "default_value": "elliptic" }, "machine_gcode_flavor": { - "default_value": "RepRap" + "default_value": "RepRap (RepRap)" }, "machine_start_gcode": { "default_value": ";Gcode by Cura\nG90\nG28\nM109 S100\nG29\nM104 S{material_print_temperature_layer_0}\nG0 X0 Y-85\nG0 Z0.26\nM109 S{material_print_temperature_layer_0}\nM82\nG92 E0\nG1 F200 E6\nG92 E0\nG1 F200 E-3.5\nG0 Z0.15\nG0 X10\nG0 Z3\nG1 F6000\n" diff --git a/resources/definitions/fabtotum.def.json b/resources/definitions/fabtotum.def.json index a53e9d9efb..d66de07c4a 100644 --- a/resources/definitions/fabtotum.def.json +++ b/resources/definitions/fabtotum.def.json @@ -21,10 +21,10 @@ "overrides": { "machine_name": { "default_value": "FABtotum Personal Fabricator" }, "machine_start_gcode": { - "default_value": ";Layer height: {layer_height}\n;Walls: {wall_thickness}\n;Fill: {infill_sparse_density}\n;Top\\Bottom Thickness: {top_bottom_thickness}\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG4 S1 ;1 millisecond pause to buffer the bep bep \nM728 ;FAB bep bep (start the print, go check the oozing and skirt lines adesion) \nG4 S1 ;1 second pause to reach the printer (run fast)\nG92 E0 ;zero the extruded length \nG1 F200 E35 ;slowly extrude 35mm of filament to clean the nozzle and build up extrusion pressure \nG92 E0 ;zero the extruded length again \nG1 F{speed_travel} ;Set travel speed \n;print" + "default_value": ";Layer height: {layer_height}\n;Walls: {wall_thickness}\n;Fill: {infill_sparse_density}\n;Top\\Bottom Thickness: {top_bottom_thickness}\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nG4 S1 ;1 millisecond pause to buffer the bep bep \nM300 S2 ;FAB bep bep (start the print, go check the oozing and skirt lines adesion) \nG4 S1 ;1 second pause to reach the printer (run fast)\nG92 E0 ;zero the extruded length \nG1 F200 E35 ;slowly extrude 35mm of filament to clean the nozzle and build up extrusion pressure \nG92 E0 ;zero the extruded length again \n;print" }, "machine_end_gcode": { - "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-3 X+5 Y+5 F5000 ;move Z up a bit and retract filament even more\n;end of the print\nM84 ;steppers off\nG90 ;absolute positioning\nM728 ;FAB bep bep (end print)" + "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-3 X+5 Y+5 F5000 ;move Z up a bit and retract filament even more\n;end of the print\nM84 ;steppers off\nG90 ;absolute positioning\nM300 S2 ;FAB bep bep (end print)" }, "gantry_height": { "default_value": 55 }, "machine_width": { "default_value": 214 }, diff --git a/resources/definitions/ultimaker2.def.json b/resources/definitions/ultimaker2.def.json index 038071574f..33b48116be 100644 --- a/resources/definitions/ultimaker2.def.json +++ b/resources/definitions/ultimaker2.def.json @@ -20,12 +20,10 @@ "overrides": { "machine_name": { "default_value": "Ultimaker 2" }, "machine_start_gcode" : { - "default_value": "", "value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"G21 ;metric values\\nG90 ;absolute positioning\\nM82 ;set extruder to absolute mode\\nM107 ;start with the fan off\\nG28 Z0 ;move Z to bottom endstops\\nG28 X0 Y0 ;move X/Y to endstops\\nG1 X15 Y0 F4000 ;move X/Y to front of printer\\nG1 Z15.0 F9000 ;move the platform to 15mm\\nG92 E0 ;zero the extruded length\\nG1 F200 E10 ;extrude 10 mm of feed stock\\nG92 E0 ;zero the extruded length again\\nG1 F9000\\n;Put printing message on LCD screen\\nM117 Printing...\"" }, "machine_end_gcode" : { - "default_value": "", - "value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"M104 S0 ;extruder heater off\\nM140 S0 ;heated bed heater off (if you have it)\\nG91 ;relative positioning\\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\\nM84 ;steppers off\\nG90 ;absolute positioning\"" + "value": "\";Version _2.6 of the firmware can abort the print too early if the file ends\\n;too soon. However if the file hasn't ended yet because there are comments at\\n;the end of the file, it won't abort yet. Therefore we have to put at least 512\\n;bytes at the end of the g-code so that the file is not yet finished by the\\n;time that the motion planner gets flushed. With firmware version _3.3 this\\n;should be fixed, so this comment wouldn't be necessary any more. Now we have\\n;to pad this text to make precisely 512 bytes.\" if machine_gcode_flavor == \"UltiGCode\" else \"M104 S0 ;extruder heater off\\nM140 S0 ;heated bed heater off (if you have it)\\nG91 ;relative positioning\\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\\nM84 ;steppers off\\nG90 ;absolute positioning\\n;Version _2.6 of the firmware can abort the print too early if the file ends\\n;too soon. However if the file hasn't ended yet because there are comments at\\n;the end of the file, it won't abort yet. Therefore we have to put at least 512\\n;bytes at the end of the g-code so that the file is not yet finished by the\\n;time that the motion planner gets flushed. With firmware version _3.3 this\\n;should be fixed, so this comment wouldn't be necessary any more. Now we have\\n;to pad this text to make precisely 512 bytes.\"" }, "machine_width": { "default_value": 223 diff --git a/resources/definitions/ultimaker3.def.json b/resources/definitions/ultimaker3.def.json index 8fda63f970..dcf6b167c0 100644 --- a/resources/definitions/ultimaker3.def.json +++ b/resources/definitions/ultimaker3.def.json @@ -123,7 +123,6 @@ "raft_margin": { "value": "10" }, "raft_surface_layers": { "value": "1" }, "retraction_amount": { "value": "2" }, - "retraction_combing": { "default_value": "noskin" }, "retraction_count_max": { "value": "10" }, "retraction_extrusion_window": { "value": "1" }, "retraction_hop": { "value": "2" }, @@ -154,6 +153,7 @@ "travel_avoid_distance": { "value": "3" }, "wall_0_inset": { "value": "0" }, "wall_line_width_x": { "value": "round(wall_line_width * 0.3 / 0.35, 2)" }, - "wall_thickness": { "value": "1" } + "wall_thickness": { "value": "1" }, + "zig_zaggify_infill": { "value": "True" } } } diff --git a/resources/preset_setting_visibility_groups/advanced.cfg b/resources/preset_setting_visibility_groups/advanced.cfg index 3bbe0ce065..e68ee787f5 100644 --- a/resources/preset_setting_visibility_groups/advanced.cfg +++ b/resources/preset_setting_visibility_groups/advanced.cfg @@ -8,120 +8,71 @@ weight = 2 layer_height layer_height_0 line_width +wall_line_width +wall_line_width_0 +wall_line_width_x +skin_line_width +infill_line_width +initial_layer_line_width_factor [shell] wall_extruder_nr +wall_0_extruder_nr +wall_x_extruder_nr wall_thickness -wall_0_wipe_dist -roofing_extruder_nr -roofing_layer_count +wall_line_count top_bottom_extruder_nr top_bottom_thickness -top_bottom_pattern -top_bottom_pattern_0 -skin_angles -wall_0_inset +top_thickness +top_layers +bottom_thickness +bottom_layers optimize_wall_printing_order -outer_inset_first -alternate_extra_perimeter -travel_compensate_overlapping_walls_enabled fill_perimeter_gaps -filter_out_tiny_gaps -fill_outline_gaps xy_offset -xy_offset_layer_0 -z_seam_type -z_seam_x -z_seam_y -z_seam_corner -z_seam_relative -skin_no_small_gaps_heuristic -skin_outline_count ironing_enabled -ironing_only_highest_layer -ironing_pattern -ironing_line_spacing -ironing_flow -ironing_inset -speed_ironing -acceleration_ironing -jerk_ironing [infill] infill_extruder_nr infill_sparse_density +infill_line_distance infill_pattern -zig_zaggify_infill -infill_angles -infill_offset_x -infill_offset_y -sub_div_rad_add infill_overlap -skin_overlap -infill_wipe_dist infill_sparse_thickness gradual_infill_steps -gradual_infill_step_height -infill_before_walls -min_infill_area -skin_preshrink -expand_skins_expand_distance -max_skin_angle_for_expansion [material] -default_material_print_temperature material_print_temperature material_print_temperature_layer_0 material_initial_print_temperature material_final_print_temperature -material_extrusion_cool_down_speed -default_material_bed_temperature material_bed_temperature material_bed_temperature_layer_0 -material_diameter -material_adhesion_tendency -material_surface_energy -material_flow retraction_enable retract_at_layer_change retraction_amount retraction_speed -retraction_extra_prime_amount -retraction_min_travel -retraction_count_max -retraction_extrusion_window material_standby_temperature -switch_extruder_retraction_amount -switch_extruder_retraction_speeds [speed] speed_print +speed_infill +speed_wall +speed_wall_0 +speed_wall_x +speed_topbottom +speed_support +speed_prime_tower speed_travel speed_layer_0 skirt_brim_speed -max_feedrate_z_override -speed_slowdown_layers -speed_equalize_flow_enabled -speed_equalize_flow_max acceleration_enabled -acceleration_print -acceleration_travel -acceleration_layer_0 -acceleration_skirt_brim jerk_enabled -jerk_print -jerk_travel -jerk_layer_0 -jerk_skirt_brim [travel] retraction_combing -travel_retract_before_outer_wall travel_avoid_other_parts travel_avoid_distance -start_layers_at_same_position -layer_start_x -layer_start_y retraction_hop_enabled retraction_hop_only_when_collides retraction_hop @@ -130,9 +81,12 @@ retraction_hop_after_extruder_switch [cooling] cool_fan_enabled cool_fan_speed +cool_fan_speed_min +cool_fan_speed_max cool_min_layer_time_fan_speed_max cool_fan_speed_0 cool_fan_full_at_height +cool_fan_full_layer cool_min_layer_time cool_min_speed cool_lift_head @@ -140,115 +94,45 @@ cool_lift_head [support] support_enable support_extruder_nr +support_infill_extruder_nr +support_extruder_nr_layer_0 +support_interface_extruder_nr support_type support_angle support_pattern -support_connect_zigzags support_infill_rate -support_z_distance -support_xy_distance -support_xy_overrides_z -support_xy_distance_overhang -support_bottom_stair_step_height -support_bottom_stair_step_width -support_join_distance support_offset support_infill_sparse_thickness gradual_support_infill_steps gradual_support_infill_step_height support_interface_enable -support_interface_height -support_interface_skip_height -support_interface_density -support_interface_pattern -support_use_towers -support_tower_diameter -support_minimal_diameter -support_tower_roof_angle -support_mesh_drop_down +support_roof_enable +support_bottom_enable [platform_adhesion] prime_blob_enable -extruder_prime_pos_x -extruder_prime_pos_y adhesion_type adhesion_extruder_nr skirt_line_count -skirt_gap -skirt_brim_minimal_length brim_width +brim_line_count brim_outside_only -raft_margin -raft_smoothing -raft_airgap -layer_0_z_overlap -raft_surface_layers -raft_surface_thickness -raft_surface_line_width -raft_surface_line_spacing -raft_interface_thickness -raft_interface_line_width -raft_interface_line_spacing -raft_base_thickness -raft_base_line_width -raft_base_line_spacing -raft_speed -raft_acceleration -raft_jerk -raft_fan_speed [dual] prime_tower_enable -prime_tower_size -prime_tower_min_volume prime_tower_position_x prime_tower_position_y -prime_tower_flow -prime_tower_wipe_enabled -dual_pre_wipe prime_tower_purge_volume -ooze_shield_enabled -ooze_shield_angle -ooze_shield_dist [meshfix] -meshfix_union_all -meshfix_union_all_remove_holes -meshfix_extensive_stitching -meshfix_keep_open_polygons -multiple_mesh_overlap -carve_multiple_volumes -alternate_carve_order -remove_empty_first_layers [blackmagic] print_sequence -infill_mesh -infill_mesh_order -cutting_mesh -mold_enabled -mold_width -mold_roof_height -mold_angle -support_mesh -anti_overhang_mesh magic_mesh_surface_mode magic_spiralize smooth_spiralized_contours -relative_extrusion [experimental] -infill_enable_travel_optimization -material_flow_dependent_temperature -material_flow_temp_graph -meshfix_maximum_resolution -roofing_angles -roofing_pattern -slicing_tolerance -support_tree_angle -support_tree_branch_diameter -support_tree_branch_diameter_angle -support_tree_branch_distance -support_tree_collision_resolution -support_tree_enable -support_tree_wall_thickness +conical_overhang_enabled +support_conical_enabled +adaptive_layer_height_enabled diff --git a/resources/preset_setting_visibility_groups/basic.cfg b/resources/preset_setting_visibility_groups/basic.cfg index 062feb9189..4196a3a9e7 100644 --- a/resources/preset_setting_visibility_groups/basic.cfg +++ b/resources/preset_setting_visibility_groups/basic.cfg @@ -9,30 +9,28 @@ layer_height [shell] wall_thickness +wall_line_count top_bottom_thickness -z_seam_x -z_seam_y +top_thickness +top_layers +bottom_thickness +bottom_layers +xy_offset [infill] infill_sparse_density -gradual_infill_steps +infill_pattern [material] material_print_temperature material_bed_temperature -material_diameter -material_flow retraction_enable [speed] speed_print -speed_travel -acceleration_print -acceleration_travel -jerk_print -jerk_travel [travel] +retraction_hop_enabled [cooling] cool_fan_enabled @@ -41,26 +39,20 @@ cool_fan_enabled support_enable support_extruder_nr support_type +support_angle [platform_adhesion] +prime_blob_enable adhesion_type adhesion_extruder_nr -brim_width -raft_airgap -layer_0_z_overlap -raft_surface_layers [dual] prime_tower_enable -prime_tower_size prime_tower_position_x prime_tower_position_y [meshfix] [blackmagic] -print_sequence -infill_mesh -cutting_mesh -[experimental] \ No newline at end of file +[experimental] diff --git a/resources/preset_setting_visibility_groups/expert.cfg b/resources/preset_setting_visibility_groups/expert.cfg index 1be5ca7804..e180b831d8 100644 --- a/resources/preset_setting_visibility_groups/expert.cfg +++ b/resources/preset_setting_visibility_groups/expert.cfg @@ -8,15 +8,33 @@ weight = 3 layer_height layer_height_0 line_width +wall_line_width +wall_line_width_0 +wall_line_width_x +skin_line_width +infill_line_width +skirt_brim_line_width +support_line_width +support_interface_line_width +support_roof_line_width +support_bottom_line_width +prime_tower_line_width +initial_layer_line_width_factor [shell] wall_extruder_nr +wall_0_extruder_nr +wall_x_extruder_nr wall_thickness +wall_line_count wall_0_wipe_dist -roofing_extruder_nr roofing_layer_count top_bottom_extruder_nr top_bottom_thickness +top_thickness +top_layers +bottom_thickness +bottom_layers top_bottom_pattern top_bottom_pattern_0 skin_angles @@ -25,6 +43,8 @@ optimize_wall_printing_order outer_inset_first alternate_extra_perimeter travel_compensate_overlapping_walls_enabled +travel_compensate_overlapping_walls_0_enabled +travel_compensate_overlapping_walls_x_enabled fill_perimeter_gaps filter_out_tiny_gaps fill_outline_gaps @@ -50,6 +70,7 @@ jerk_ironing [infill] infill_extruder_nr infill_sparse_density +infill_line_distance infill_pattern zig_zaggify_infill infill_angles @@ -57,7 +78,9 @@ infill_offset_x infill_offset_y sub_div_rad_add infill_overlap +infill_overlap_mm skin_overlap +skin_overlap_mm infill_wipe_dist infill_sparse_thickness gradual_infill_steps @@ -65,8 +88,13 @@ gradual_infill_step_height infill_before_walls min_infill_area skin_preshrink +top_skin_preshrink +bottom_skin_preshrink expand_skins_expand_distance +top_skin_expand_distance +bottom_skin_expand_distance max_skin_angle_for_expansion +min_skin_width_for_expansion [material] default_material_print_temperature @@ -75,17 +103,19 @@ material_print_temperature_layer_0 material_initial_print_temperature material_final_print_temperature material_extrusion_cool_down_speed -default_material_bed_temperature material_bed_temperature material_bed_temperature_layer_0 material_diameter material_adhesion_tendency material_surface_energy material_flow +material_flow_layer_0 retraction_enable retract_at_layer_change retraction_amount retraction_speed +retraction_retract_speed +retraction_prime_speed retraction_extra_prime_amount retraction_min_travel retraction_count_max @@ -93,11 +123,25 @@ retraction_extrusion_window material_standby_temperature switch_extruder_retraction_amount switch_extruder_retraction_speeds +switch_extruder_retraction_speed +switch_extruder_prime_speed [speed] speed_print +speed_infill +speed_wall +speed_wall_0 +speed_wall_x +speed_roofing +speed_topbottom +speed_support +speed_support_infill +speed_support_interface +speed_prime_tower speed_travel speed_layer_0 +speed_print_layer_0 +speed_travel_layer_0 skirt_brim_speed max_feedrate_z_override speed_slowdown_layers @@ -105,13 +149,37 @@ speed_equalize_flow_enabled speed_equalize_flow_max acceleration_enabled acceleration_print +acceleration_infill +acceleration_wall +acceleration_wall_0 +acceleration_wall_x +acceleration_roofing +acceleration_topbottom +acceleration_support +acceleration_support_infill +acceleration_support_interface +acceleration_prime_tower acceleration_travel acceleration_layer_0 +acceleration_print_layer_0 +acceleration_travel_layer_0 acceleration_skirt_brim jerk_enabled jerk_print +jerk_infill +jerk_wall +jerk_wall_0 +jerk_wall_x +jerk_roofing +jerk_topbottom +jerk_support +jerk_support_infill +jerk_support_interface +jerk_prime_tower jerk_travel jerk_layer_0 +jerk_print_layer_0 +jerk_travel_layer_0 jerk_skirt_brim [travel] @@ -130,9 +198,12 @@ retraction_hop_after_extruder_switch [cooling] cool_fan_enabled cool_fan_speed +cool_fan_speed_min +cool_fan_speed_max cool_min_layer_time_fan_speed_max cool_fan_speed_0 cool_fan_full_at_height +cool_fan_full_layer cool_min_layer_time cool_min_speed cool_lift_head @@ -140,12 +211,21 @@ cool_lift_head [support] support_enable support_extruder_nr +support_infill_extruder_nr +support_extruder_nr_layer_0 +support_interface_extruder_nr +support_roof_extruder_nr +support_bottom_extruder_nr support_type support_angle support_pattern +zig_zaggify_support support_connect_zigzags support_infill_rate +support_line_distance support_z_distance +support_top_distance +support_bottom_distance support_xy_distance support_xy_overrides_z support_xy_distance_overhang @@ -157,9 +237,15 @@ support_infill_sparse_thickness gradual_support_infill_steps gradual_support_infill_step_height support_interface_enable +support_roof_enable +support_bottom_enable support_interface_height +support_roof_height +support_bottom_height support_interface_skip_height support_interface_density +support_roof_density +support_bottom_density support_interface_pattern support_use_towers support_tower_diameter @@ -169,19 +255,17 @@ support_mesh_drop_down [platform_adhesion] prime_blob_enable -extruder_prime_pos_x -extruder_prime_pos_y adhesion_type adhesion_extruder_nr skirt_line_count skirt_gap skirt_brim_minimal_length brim_width +brim_line_count brim_outside_only raft_margin raft_smoothing raft_airgap -layer_0_z_overlap raft_surface_layers raft_surface_thickness raft_surface_line_width @@ -199,8 +283,10 @@ raft_fan_speed [dual] prime_tower_enable +prime_tower_circular prime_tower_size prime_tower_min_volume +prime_tower_wall_thickness prime_tower_position_x prime_tower_position_y prime_tower_flow @@ -238,8 +324,25 @@ smooth_spiralized_contours relative_extrusion [experimental] +support_tree_enable +support_tree_angle +support_tree_branch_distance +support_tree_branch_diameter +support_tree_branch_diameter_angle +support_tree_collision_resolution +support_tree_wall_thickness +support_tree_wall_count +slicing_tolerance +roofing_line_width +roofing_pattern +roofing_angles +infill_enable_travel_optimization +material_flow_dependent_temperature +material_flow_temp_graph +meshfix_maximum_resolution support_skip_some_zags support_skip_zag_per_mm +support_zag_skip_count draft_shield_enabled draft_shield_dist draft_shield_height_limitation @@ -267,19 +370,34 @@ infill_hollow magic_fuzzy_skin_enabled magic_fuzzy_skin_thickness magic_fuzzy_skin_point_density +magic_fuzzy_skin_point_dist flow_rate_max_extrusion_offset flow_rate_extrusion_offset_factor -infill_enable_travel_optimization -material_flow_dependent_temperature -material_flow_temp_graph -meshfix_maximum_resolution -roofing_angles -roofing_pattern -slicing_tolerance -support_tree_angle -support_tree_branch_diameter -support_tree_branch_diameter_angle -support_tree_branch_distance -support_tree_collision_resolution -support_tree_enable -support_tree_wall_thickness +wireframe_enabled +wireframe_height +wireframe_roof_inset +wireframe_printspeed +wireframe_printspeed_bottom +wireframe_printspeed_up +wireframe_printspeed_down +wireframe_printspeed_flat +wireframe_flow +wireframe_flow_connection +wireframe_flow_flat +wireframe_top_delay +wireframe_bottom_delay +wireframe_flat_delay +wireframe_up_half_speed +wireframe_top_jump +wireframe_fall_down +wireframe_drag_along +wireframe_strategy +wireframe_straight_before_down +wireframe_roof_fall_down +wireframe_roof_drag_along +wireframe_roof_outer_delay +wireframe_nozzle_clearance +adaptive_layer_height_enabled +adaptive_layer_height_variation +adaptive_layer_height_variation_step +adaptive_layer_height_threshold diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index f2f33800d0..7002711614 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -236,6 +236,16 @@ Item onTriggered: CuraActions.deleteSelection(); } + Action //Also add backspace as the same function as delete because on Macintosh keyboards the button called "delete" is actually a backspace, and the user expects it to function as a delete. + { + id: backspaceSelectionAction + text: catalog.i18ncp("@action:inmenu menubar:edit", "Delete &Selected Model", "Delete &Selected Models", UM.Selection.selectionCount) + enabled: UM.Controller.toolsEnabled && UM.Selection.hasSelection + iconName: "edit-delete" + shortcut: StandardKey.Backspace + onTriggered: CuraActions.deleteSelection() + } + Action { id: centerSelectionAction; diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 4007d43c37..185e3563a5 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -490,6 +490,14 @@ UM.MainWindow collapseSidebarAnimation.start(); } } + + MouseArea + { + visible: UM.Controller.activeStage.sidebarComponent != "" + anchors.fill: parent + acceptedButtons: Qt.AllButtons + onWheel: wheel.accepted = true + } } UM.MessageStack diff --git a/resources/qml/Menus/BuildplateMenu.qml b/resources/qml/Menus/BuildplateMenu.qml index 0b67b37cc1..b924aa0879 100644 --- a/resources/qml/Menus/BuildplateMenu.qml +++ b/resources/qml/Menus/BuildplateMenu.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.8 +import QtQuick 2.7 import QtQuick.Controls 1.4 import UM 1.2 as UM @@ -12,7 +12,7 @@ Menu id: menu title: "Build plate" - property Cura.BuildPlateModel buildPlateModel: CuraApplication.getBuildPlateModel() + property var buildPlateModel: CuraApplication.getBuildPlateModel() Instantiator { diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index 656e94b336..83302f9463 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -15,7 +15,7 @@ Menu property bool shouldShowExtruders: machineExtruderCount.properties.value > 1; - property Cura.MultiBuildPlateModel multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel() + property var multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel() // Selection-related actions. MenuItem { action: Cura.Actions.centerSelection; } diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml index 25fa221e9a..d81e0c86c3 100644 --- a/resources/qml/Menus/MaterialMenu.qml +++ b/resources/qml/Menus/MaterialMenu.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.8 +import QtQuick 2.7 import QtQuick.Controls 1.4 import UM 1.2 as UM diff --git a/resources/qml/Menus/NozzleMenu.qml b/resources/qml/Menus/NozzleMenu.qml index 0b961a5a11..43f3b79dd4 100644 --- a/resources/qml/Menus/NozzleMenu.qml +++ b/resources/qml/Menus/NozzleMenu.qml @@ -1,7 +1,7 @@ // Copyright (c) 2017 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.8 +import QtQuick 2.7 import QtQuick.Controls 1.4 import UM 1.2 as UM diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml index 5924ad5322..72cda13ca9 100644 --- a/resources/qml/Menus/ProfileMenu.qml +++ b/resources/qml/Menus/ProfileMenu.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.8 +import QtQuick 2.7 import QtQuick.Controls 1.4 import UM 1.2 as UM @@ -13,11 +13,11 @@ Menu Instantiator { - model: Cura.QualityProfilesModel + model: Cura.QualityProfilesDropDownMenuModel MenuItem { - text: (model.layer_height != "") ? model.name + " - " + model.layer_height : model.name + text: (model.layer_height != "") ? model.name + " - " + model.layer_height + model.layer_height_unit : model.name checkable: true checked: Cura.MachineManager.activeQualityOrQualityChangesName == model.name exclusiveGroup: group @@ -34,18 +34,18 @@ Menu MenuSeparator { id: customSeparator - visible: Cura.CustomQualityProfilesModel.rowCount > 0 + visible: Cura.CustomQualityProfilesDropDownMenuModel.rowCount > 0 } Instantiator { id: customProfileInstantiator - model: Cura.CustomQualityProfilesModel + model: Cura.CustomQualityProfilesDropDownMenuModel Connections { - target: Cura.CustomQualityProfilesModel - onModelReset: customSeparator.visible = Cura.CustomQualityProfilesModel.rowCount() > 0 + target: Cura.CustomQualityProfilesDropDownMenuModel + onModelReset: customSeparator.visible = Cura.CustomQualityProfilesDropDownMenuModel.rowCount() > 0 } MenuItem diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index fc0339e0c6..6bbb0b1e2e 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -13,7 +13,7 @@ Menu id: base enabled: !PrintInformation.preSliced - property Cura.MultiBuildPlateModel multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel() + property var multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel() // main views Instantiator diff --git a/resources/qml/ObjectsList.qml b/resources/qml/ObjectsList.qml index e7dd63ea05..8c8eaa16ae 100644 --- a/resources/qml/ObjectsList.qml +++ b/resources/qml/ObjectsList.qml @@ -31,9 +31,9 @@ Rectangle border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") - property bool collapsed: true; + property bool collapsed: true - property Cura.MultiBuildPlateModel multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel() + property var multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel() SystemPalette { id: palette } diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index 49a8d286a8..62e5ef98b4 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -1,7 +1,7 @@ // Copyright (c) 2016 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.8 +import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Window 2.1 diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 021fcc02e1..c987880305 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -1,7 +1,7 @@ // Copyright (c) 2017 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.8 +import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 @@ -12,6 +12,8 @@ TabView { id: base + property QtObject materialManager: CuraApplication.getMaterialManager() + property QtObject properties property var currentMaterialNode: null @@ -214,7 +216,8 @@ TabView confirmDiameterChangeDialog.old_approximate_diameter_value = old_approximate_diameter; confirmDiameterChangeDialog.open() - } else { + } + else { Cura.ContainerManager.setContainerProperty(base.containerId, "material_diameter", "value", value); base.setMetaDataEntry("approximate_diameter", old_approximate_diameter, getApproximateDiameter(value).toString()); base.setMetaDataEntry("properties/diameter", properties.diameter, value); @@ -396,13 +399,15 @@ TabView onEditingFinished: materialPropertyProvider.setPropertyValue("value", value) } - UM.ContainerPropertyProvider { + UM.ContainerPropertyProvider + { id: materialPropertyProvider containerId: base.containerId watchedProperties: [ "value" ] key: model.key } - UM.ContainerPropertyProvider { + UM.ContainerPropertyProvider + { id: machinePropertyProvider containerId: Cura.MachineManager.activeDefinitionId watchedProperties: [ "value" ] @@ -497,7 +502,7 @@ TabView } // update the values - Cura.ContainerManager.setMaterialName(base.currentMaterialNode, new_name) + base.materialManager.setMaterialName(base.currentMaterialNode, new_name) materialProperties.name = new_name } diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index 1177f20334..4a6d07df81 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Uranium is released under the terms of the LGPLv3 or higher. -import QtQuick 2.8 +import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 import QtQuick.Dialogs 1.2 @@ -13,6 +13,8 @@ import Cura 1.0 as Cura Item { id: base + + property QtObject materialManager: CuraApplication.getMaterialManager() property var resetEnabled: false // Keep PreferencesDialog happy UM.I18nCatalog { id: catalog; name: "cura"; } @@ -37,12 +39,14 @@ Item property var hasCurrentItem: materialListView.currentItem != null - property var currentItem: { // is soon to be overwritten + property var currentItem: + { // is soon to be overwritten var current_index = materialListView.currentIndex; return materialsModel.getItem(current_index); } - property var isCurrentItemActivated: { + property var isCurrentItemActivated: + { const extruder_position = Cura.ExtruderManager.activeExtruderIndex; const root_material_id = Cura.MachineManager.currentRootMaterialId[extruder_position]; return base.currentItem.root_material_id == root_material_id; @@ -51,7 +55,8 @@ Item Row // Button Row { id: buttonRow - anchors { + anchors + { left: parent.left right: parent.right top: titleLabel.bottom @@ -59,11 +64,13 @@ Item height: childrenRect.height // Activate button - Button { + Button + { text: catalog.i18nc("@action:button", "Activate") iconName: "list-activate" enabled: !isCurrentItemActivated - onClicked: { + onClicked: + { forceActiveFocus() const extruder_position = Cura.ExtruderManager.activeExtruderIndex; @@ -72,44 +79,52 @@ Item } // Create button - Button { + Button + { text: catalog.i18nc("@action:button", "Create") iconName: "list-add" - onClicked: { + onClicked: + { forceActiveFocus(); - base.newRootMaterialIdToSwitchTo = Cura.ContainerManager.createMaterial(); + base.newRootMaterialIdToSwitchTo = base.materialManager.createMaterial(); base.toActivateNewMaterial = true; } } // Duplicate button - Button { + Button + { text: catalog.i18nc("@action:button", "Duplicate"); iconName: "list-add" enabled: base.hasCurrentItem - onClicked: { + onClicked: + { forceActiveFocus(); - base.newRootMaterialIdToSwitchTo = Cura.ContainerManager.duplicateMaterial(base.currentItem.container_node); + base.newRootMaterialIdToSwitchTo = base.materialManager.duplicateMaterial(base.currentItem.container_node); base.toActivateNewMaterial = true; } } // Remove button - Button { + Button + { text: catalog.i18nc("@action:button", "Remove") iconName: "list-remove" enabled: base.hasCurrentItem && !base.currentItem.is_read_only && !base.isCurrentItemActivated - onClicked: { + onClicked: + { forceActiveFocus(); confirmRemoveMaterialDialog.open(); } } // Import button - Button { + Button + { text: catalog.i18nc("@action:button", "Import") iconName: "document-import" - onClicked: { + onClicked: + { forceActiveFocus(); importMaterialDialog.open(); } @@ -117,10 +132,12 @@ Item } // Export button - Button { + Button + { text: catalog.i18nc("@action:button", "Export") iconName: "document-export" - onClicked: { + onClicked: + { forceActiveFocus(); exportMaterialDialog.open(); } @@ -181,7 +198,7 @@ Item onYes: { - Cura.ContainerManager.removeMaterial(base.currentItem.container_node); + base.materialManager.removeMaterial(base.currentItem.container_node); } } @@ -281,7 +298,6 @@ Item } visible: text != "" text: { - // OLD STUFF var caption = catalog.i18nc("@action:label", "Printer") + ": " + Cura.MachineManager.activeMachineName; if (Cura.MachineManager.hasVariants) { @@ -343,10 +359,19 @@ Item Row { + id: materialRow spacing: (UM.Theme.getSize("default_margin").width / 2) | 0 anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.right: parent.right + + property bool isItemActivated: + { + const extruder_position = Cura.ExtruderManager.activeExtruderIndex; + const root_material_id = Cura.MachineManager.currentRootMaterialId[extruder_position]; + return model.root_material_id == root_material_id; + } + Rectangle { width: Math.floor(parent.height * 0.8) @@ -360,22 +385,14 @@ Item width: Math.floor((parent.width * 0.3)) text: model.material elide: Text.ElideRight - font.italic: { // TODO: make it easier - const extruder_position = Cura.ExtruderManager.activeExtruderIndex; - const root_material_id = Cura.MachineManager.currentRootMaterialId[extruder_position]; - return model.root_material_id == root_material_id - } + font.italic: materialRow.isItemActivated color: parent.ListView.isCurrentItem ? palette.highlightedText : palette.text; } Label { text: (model.name != model.material) ? model.name : "" elide: Text.ElideRight - font.italic: { // TODO: make it easier - const extruder_position = Cura.ExtruderManager.activeExtruderIndex; - const root_material_id = Cura.MachineManager.currentRootMaterialId[extruder_position]; - return model.root_material_id == root_material_id; - } + font.italic: materialRow.isItemActivated color: parent.ListView.isCurrentItem ? palette.highlightedText : palette.text; } } diff --git a/resources/qml/Preferences/ProfileTab.qml b/resources/qml/Preferences/ProfileTab.qml index 82671f306f..40f725e092 100644 --- a/resources/qml/Preferences/ProfileTab.qml +++ b/resources/qml/Preferences/ProfileTab.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.8 +import QtQuick 2.7 import QtQuick.Controls 1.4 import UM 1.2 as UM @@ -85,7 +85,7 @@ Tab model: Cura.QualitySettingsModel { id: qualitySettings - extruderPosition: base.extruderPosition + selectedPosition: base.extruderPosition selectedQualityItem: base.qualityItem } diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index e5c7e3662a..3e10aca000 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Uranium is released under the terms of the LGPLv3 or higher. -import QtQuick 2.8 +import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.3 import QtQuick.Dialogs 1.2 @@ -13,6 +13,8 @@ import Cura 1.0 as Cura Item { id: base + + property QtObject qualityManager: CuraApplication.getQualityManager() property var resetEnabled: false // Keep PreferencesDialog happy property var extrudersModel: Cura.ExtrudersModel {} @@ -173,7 +175,7 @@ Item { base.newQualityNameToSelect = newName; // We want to switch to the new profile once it's created base.toActivateNewQuality = true; - Cura.ContainerManager.createQualityChanges(newName); + base.qualityManager.createQualityChanges(newName); } } @@ -222,7 +224,7 @@ Item object: "" onAccepted: { - Cura.ContainerManager.duplicateQualityChanges(newName, base.currentItem); + base.qualityManager.duplicateQualityChanges(newName, base.currentItem); } } @@ -239,9 +241,9 @@ Item onYes: { - Cura.ContainerManager.removeQualityChangesGroup(base.currentItem.quality_changes_group); + base.qualityManager.removeQualityChangesGroup(base.currentItem.quality_changes_group); // reset current item to the first if available - qualityListView.currentIndex = -1; // TODO: Reset selection. + qualityListView.currentIndex = -1; // Reset selection. } } @@ -253,7 +255,7 @@ Item object: "" onAccepted: { - var actualNewName = Cura.ContainerManager.renameQualityChangesGroup(base.currentItem.quality_changes_group, newName); + var actualNewName = base.qualityManager.renameQualityChangesGroup(base.currentItem.quality_changes_group, newName); base.newQualityNameToSelect = actualNewName; // Select the new name after the model gets updated } } @@ -264,7 +266,7 @@ Item id: importDialog title: catalog.i18nc("@title:window", "Import Profile") selectExisting: true - nameFilters: qualitiesModel.getFileNameFilters("profile_reader") // TODO: make this easier + nameFilters: qualitiesModel.getFileNameFilters("profile_reader") folder: CuraApplication.getDefaultPath("dialog_profile_path") onAccepted: { @@ -290,11 +292,10 @@ Item id: exportDialog title: catalog.i18nc("@title:window", "Export Profile") selectExisting: false - nameFilters: qualitiesModel.getFileNameFilters("profile_writer") // TODO: make this easier + nameFilters: qualitiesModel.getFileNameFilters("profile_writer") folder: CuraApplication.getDefaultPath("dialog_profile_path") onAccepted: { - // TODO: make this easier var result = Cura.ContainerManager.exportQualityChangesGroup(base.currentItem.quality_changes_group, fileUrl, selectedNameFilter); diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index cef7ce9077..615e66277b 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -142,7 +142,7 @@ Item TextField { id: filter; - + height: parent.height anchors.left: parent.left anchors.right: clearFilterButton.left anchors.rightMargin: Math.round(UM.Theme.getSize("sidebar_margin").width) diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 57a8e8beaa..e04d8607da 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -515,15 +515,12 @@ Rectangle weights = ["0"]; costs = ["0.00"]; } + var result = lengths.join(" + ") + "m / ~ " + weights.join(" + ") + "g"; if(someCostsKnown) { - return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + ")) - .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); - } - else - { - return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + ")); + result += " / ~ " + costs.join(" + ") + " " + UM.Preferences.getValue("cura/currency"); } + return result; } MouseArea { diff --git a/resources/qml/SidebarAdvanced.qml b/resources/qml/SidebarAdvanced.qml index ae77bc8d1b..ff5f545c80 100644 --- a/resources/qml/SidebarAdvanced.qml +++ b/resources/qml/SidebarAdvanced.qml @@ -1,7 +1,7 @@ // Copyright (c) 2015 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.8 +import QtQuick 2.7 import QtQuick.Controls 2.0 import "Settings" diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 222a92465f..699365238a 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.8 +import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 @@ -58,7 +58,7 @@ Item running: false repeat: false onTriggered: { - var item = Cura.QualityProfilesModel.getItem(qualitySlider.value); + var item = Cura.QualityProfilesDropDownMenuModel.getItem(qualitySlider.value); Cura.MachineManager.activeQualityGroup = item.quality_group; } } @@ -103,8 +103,8 @@ Item var availableMin = -1 var availableMax = -1 - for (var i = 0; i < Cura.QualityProfilesModel.rowCount(); i++) { - var qualityItem = Cura.QualityProfilesModel.getItem(i) + for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++) { + var qualityItem = Cura.QualityProfilesDropDownMenuModel.getItem(i) // Add each quality item to the UI quality model qualityModel.append(qualityItem) @@ -166,7 +166,7 @@ Item qualityModel.existingQualityProfile = 0 // check, the ticks count cannot be less than zero - qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesModel.rowCount() - 1) + qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.rowCount() - 1) } } @@ -192,13 +192,13 @@ Item anchors.verticalCenter: parent.verticalCenter anchors.top: parent.top anchors.topMargin: Math.round(UM.Theme.getSize("sidebar_margin").height / 2) - color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") text: { var result = "" if(Cura.MachineManager.activeMachine != null) { - result = Cura.QualityProfilesModel.getItem(index).layer_height_without_unit + result = Cura.QualityProfilesDropDownMenuModel.getItem(index).layer_height if(result == undefined) { @@ -263,7 +263,7 @@ Item Rectangle { anchors.verticalCenter: parent.verticalCenter - color: Cura.QualityProfilesModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + color: Cura.QualityProfilesDropDownMenuModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") width: 1 * screenScaleFactor height: 6 * screenScaleFactor y: 0 @@ -401,9 +401,9 @@ Item // if the current profile is user-created, switch to a built-in quality if (Cura.SimpleModeSettingsManager.isProfileUserCreated) { - if (Cura.QualityProfilesModel.rowCount() > 0) + if (Cura.QualityProfilesDropDownMenuModel.rowCount() > 0) { - var item = Cura.QualityProfilesModel.getItem(0); + var item = Cura.QualityProfilesDropDownMenuModel.getItem(0); Cura.MachineManager.activeQualityGroup = item.quality_group; } } diff --git a/resources/quality/abax_pri3/apri3_fast.inst.cfg b/resources/quality/abax_pri3/apri3_fast.inst.cfg deleted file mode 100644 index 7db569ec72..0000000000 --- a/resources/quality/abax_pri3/apri3_fast.inst.cfg +++ /dev/null @@ -1,14 +0,0 @@ -[general] -version = 2 -name = Fast -definition = abax_pri3 - -[metadata] -setting_version = 4 -type = quality -quality_type = fast -weight = -1 -global_quality = True - -[values] -layer_height = 0.2 diff --git a/resources/quality/abax_pri3/apri3_high.inst.cfg b/resources/quality/abax_pri3/apri3_high.inst.cfg deleted file mode 100644 index 8a95b71a88..0000000000 --- a/resources/quality/abax_pri3/apri3_high.inst.cfg +++ /dev/null @@ -1,14 +0,0 @@ -[general] -version = 2 -name = Extra Fine -definition = abax_pri3 - -[metadata] -setting_version = 4 -type = quality -quality_type = high -weight = 1 -global_quality = True - -[values] -layer_height = 0.1 diff --git a/resources/quality/abax_pri3/apri3_normal.inst.cfg b/resources/quality/abax_pri3/apri3_normal.inst.cfg deleted file mode 100644 index 29b25d889f..0000000000 --- a/resources/quality/abax_pri3/apri3_normal.inst.cfg +++ /dev/null @@ -1,14 +0,0 @@ -[general] -version = 2 -name = Fine -definition = abax_pri3 - -[metadata] -setting_version = 4 -type = quality -quality_type = normal -weight = 0 -global_quality = True - -[values] -layer_height = 0.2 diff --git a/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg b/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg index f24f198b2e..7d1c1bf588 100644 --- a/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg +++ b/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg @@ -1,16 +1,17 @@ [general] version = 2 -name = Fast +name = Fine definition = abax_pri3 [metadata] setting_version = 4 type = quality -quality_type = fast +quality_type = normal weight = -1 material = generic_pla [values] +layer_height = 0.2 wall_thickness = 1.05 top_bottom_thickness = 0.8 infill_sparse_density = 20 diff --git a/resources/quality/abax_pri3/apri3_pla_high.inst.cfg b/resources/quality/abax_pri3/apri3_pla_high.inst.cfg index 5b4c470842..46a4178dd9 100644 --- a/resources/quality/abax_pri3/apri3_pla_high.inst.cfg +++ b/resources/quality/abax_pri3/apri3_pla_high.inst.cfg @@ -11,6 +11,7 @@ weight = 1 material = generic_pla [values] +layer_height = 0.1 wall_thickness = 1.05 top_bottom_thickness = 0.8 infill_sparse_density = 20 diff --git a/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg b/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg index 2df629af38..3f6f36cfe6 100644 --- a/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg +++ b/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg @@ -11,6 +11,7 @@ weight = 0 material = generic_pla [values] +layer_height = 0.2 wall_thickness = 1.05 top_bottom_thickness = 0.8 infill_sparse_density = 20 diff --git a/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg index 8a48962553..54ad42537e 100644 --- a/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg @@ -12,9 +12,10 @@ material = fabtotum_abs [values] adhesion_type = raft +speed_print = 80 layer_height = 0.2 layer_height_0 = 0.2 -cool_fan_enabled = True +cool_fan_enabled = False cool_fan_full_at_height = 0.4 cool_fan_speed = 50 cool_fan_speed_max = 50 diff --git a/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg b/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg index c8a970e180..a7a4e71709 100644 --- a/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg @@ -12,9 +12,10 @@ material = fabtotum_abs [values] adhesion_type = raft +speed_print = 45 layer_height = 0.1 layer_height_0 = 0.1 -cool_fan_enabled = True +cool_fan_enabled = False cool_fan_full_at_height = 0.2 cool_fan_speed = 50 cool_fan_speed_max = 50 diff --git a/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg index 03b580a9c3..19d6062dfa 100644 --- a/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg @@ -12,9 +12,10 @@ material = fabtotum_abs [values] adhesion_type = raft +speed_print = 60 layer_height = 0.15 layer_height_0 = 0.15 -cool_fan_enabled = True +cool_fan_enabled = False cool_fan_full_at_height = 0.3 cool_fan_speed = 50 cool_fan_speed_max = 50 diff --git a/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg index 7c5769c489..1983191e90 100644 --- a/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg @@ -44,7 +44,7 @@ skirt_gap = 1.5 skirt_line_count = 5 speed_infill = =speed_print speed_layer_0 = 25 -speed_print = 50 +speed_print = 30 speed_topbottom = 40 speed_travel = 200 speed_wall_0 = 40 diff --git a/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg b/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg index a74a6d5138..5f81c12f22 100644 --- a/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg @@ -44,7 +44,7 @@ skirt_gap = 1.5 skirt_line_count = 5 speed_infill = =speed_print speed_layer_0 = 25 -speed_print = 50 +speed_print = 30 speed_topbottom = 40 speed_travel = 200 speed_wall_0 = 40 diff --git a/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg index c3de34beb3..834d9b8006 100644 --- a/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg @@ -44,7 +44,7 @@ skirt_gap = 1.5 skirt_line_count = 5 speed_infill = =speed_print speed_layer_0 = 25 -speed_print = 50 +speed_print = 30 speed_topbottom = 40 speed_travel = 200 speed_wall_0 = 40 diff --git a/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg index 3c71af3727..2f123bc05b 100644 --- a/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg @@ -12,6 +12,7 @@ material = fabtotum_pla [values] adhesion_type = skirt +speed_print = 80 layer_height = 0.2 layer_height_0 = 0.2 cool_fan_enabled = True diff --git a/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg b/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg index 0832f84d0c..4b3aff15d9 100644 --- a/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg @@ -12,6 +12,7 @@ material = fabtotum_pla [values] adhesion_type = skirt +speed_print = 45 layer_height = 0.1 layer_height_0 = 0.1 cool_fan_enabled = True diff --git a/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg index 16afc56cf1..44ddbcb085 100644 --- a/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg @@ -12,6 +12,7 @@ material = fabtotum_pla [values] adhesion_type = skirt +speed_print = 60 layer_height = 0.15 layer_height_0 = 0.15 cool_fan_enabled = True diff --git a/resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg new file mode 100644 index 0000000000..d8ee095d61 --- /dev/null +++ b/resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg @@ -0,0 +1,25 @@ +[general] +version = 2 +definition = fabtotum +name = Fast Quality + +[metadata] +type = quality +setting_version = 4 +material = fabtotum_tpu +quality_type = fast +weight = -1 + +[values] +adhesion_type = skirt +speed_print = 80 +layer_height = 0.2 +layer_height_0 = 0.2 +cool_fan_enabled = True +cool_fan_full_at_height = 0.4 +cool_fan_speed = 100 +cool_fan_speed_max = 100 +cool_fan_speed_min = 100 +cool_min_layer_time = 5 +cool_min_speed = 20 + diff --git a/resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg b/resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg new file mode 100644 index 0000000000..315a56015d --- /dev/null +++ b/resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg @@ -0,0 +1,24 @@ +[general] +version = 2 +definition = fabtotum +name = High Quality + +[metadata] +type = quality +setting_version = 4 +material = fabtotum_tpu +quality_type = high +weight = 1 + +[values] +adhesion_type = skirt +layer_height = 0.1 +layer_height_0 = 0.1 +cool_fan_enabled = True +cool_fan_full_at_height = 0.2 +cool_fan_speed = 100 +cool_fan_speed_max = 100 +cool_fan_speed_min = 100 +cool_min_layer_time = 5 +cool_min_speed = 20 + diff --git a/resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg new file mode 100644 index 0000000000..3ce1592e70 --- /dev/null +++ b/resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg @@ -0,0 +1,25 @@ +[general] +version = 2 +definition = fabtotum +name = Normal Quality + +[metadata] +type = quality +setting_version = 4 +material = fabtotum_TPU +quality_type = normal +weight = 0 + +[values] +adhesion_type = skirt +speed_print = 80 +layer_height = 0.15 +layer_height_0 = 0.15 +cool_fan_enabled = True +cool_fan_full_at_height = 0.3 +cool_fan_speed = 100 +cool_fan_speed_max = 100 +cool_fan_speed_min = 100 +cool_min_layer_time = 5 +cool_min_speed = 20 + diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg index f300f5d95b..4d0493ae8b 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg @@ -12,6 +12,7 @@ material = generic_petg variant = 0.4 mm [values] +adhesion_type = skirt bottom_thickness = 0.6 coasting_enable = True coasting_speed = 95 diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg index 804807184b..9251ae43b4 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg @@ -12,6 +12,7 @@ material = generic_petg variant = 0.4 mm 2-fans [values] +adhesion_type = skirt bottom_thickness = 0.6 coasting_enable = True coasting_speed = 95 diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg index c39c8a7a98..a10a3bcf0a 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg @@ -12,6 +12,7 @@ material = generic_petg variant = 0.4 mm [values] +adhesion_type = skirt bottom_thickness = 0.6 coasting_enable = True coasting_speed = 95 diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg index 17cc1493e1..b851c2e17c 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg @@ -12,6 +12,7 @@ material = generic_petg variant = 0.4 mm 2-fans [values] +adhesion_type = skirt bottom_thickness = 0.6 coasting_enable = True coasting_speed = 95 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg index 738eb627fe..bc86119eb8 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg @@ -12,6 +12,7 @@ material = generic_pla variant = 0.4 mm [values] +adhesion_type = skirt bottom_thickness = 0.6 coasting_enable = True coasting_speed = 95 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg index 4f44559fbc..c05927fad5 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg @@ -12,6 +12,7 @@ material = generic_pla variant = 0.4 mm 2-fans [values] +adhesion_type = skirt bottom_thickness = 0.6 coasting_enable = True coasting_speed = 95 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg index f9f32237b0..1bec96f05c 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg @@ -12,6 +12,7 @@ material = generic_pla variant = 0.4 mm [values] +adhesion_type = skirt bottom_thickness = 0.6 coasting_enable = True coasting_speed = 95 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg index 82f5a4fa32..609662a149 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg @@ -12,6 +12,7 @@ material = generic_pla variant = 0.4 mm 2-fans [values] +adhesion_type = skirt bottom_thickness = 0.6 coasting_enable = True coasting_speed = 95 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg index 5ee2111c3a..5249f2dc2b 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg @@ -12,6 +12,7 @@ material = generic_pla variant = 0.4 mm [values] +adhesion_type = skirt bottom_thickness = 0.6 coasting_enable = True coasting_speed = 95 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg index 5d34259ccd..1534d3a6fb 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg @@ -12,6 +12,7 @@ material = generic_pla variant = 0.4 mm 2-fans [values] +adhesion_type = skirt bottom_thickness = 0.6 coasting_enable = True coasting_speed = 95 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg index 1826e69513..1166bd70c9 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg @@ -12,6 +12,7 @@ material = generic_pla variant = 0.4 mm [values] +adhesion_type = skirt bottom_thickness = 0.6 coasting_enable = True coasting_speed = 95 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg index c34415462c..d3654cb3e0 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg @@ -45,7 +45,7 @@ retraction_extrusion_window = 1 retraction_hop = 1.5 retraction_hop_enabled = True retraction_hop_only_when_collides = True -retraction_min_travel = 0.8 +retraction_min_travel = =line_width * 2 retraction_prime_speed = 15 skin_overlap = 5 speed_equalize_flow_enabled = True diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg index 70a638ee17..3686fefdff 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg @@ -46,7 +46,7 @@ retraction_extrusion_window = 1 retraction_hop = 1.5 retraction_hop_enabled = True retraction_hop_only_when_collides = True -retraction_min_travel = 0.8 +retraction_min_travel = =line_width * 2 retraction_prime_speed = 15 skin_overlap = 5 speed_equalize_flow_enabled = True diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg index a92c9c7391..18d7a5275a 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg @@ -43,7 +43,7 @@ retraction_extrusion_window = 1 retraction_hop = 1.5 retraction_hop_enabled = True retraction_hop_only_when_collides = True -retraction_min_travel = 0.8 +retraction_min_travel = =line_width * 2 retraction_prime_speed = 15 skin_overlap = 5 speed_equalize_flow_enabled = True diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg index c2dd5f696d..be3ea7e06d 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg @@ -41,7 +41,7 @@ retraction_count_max = 12 retraction_extra_prime_amount = 0.5 retraction_hop = 1.5 retraction_hop_only_when_collides = False -retraction_min_travel = 0.8 +retraction_min_travel = =line_width * 2 retraction_prime_speed = 15 skin_line_width = =round(line_width * 0.78 / 0.8, 2) speed_print = 30 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg index b4efb6158d..cc0e963a35 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg @@ -42,7 +42,7 @@ retraction_count_max = 12 retraction_extra_prime_amount = 0.5 retraction_hop = 1.5 retraction_hop_only_when_collides = False -retraction_min_travel = 0.8 +retraction_min_travel = =line_width * 2 retraction_prime_speed = 15 skin_line_width = =round(line_width * 0.78 / 0.8, 2) speed_print = 30 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg index 4d2a18cd71..3310c09ba7 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg @@ -41,7 +41,7 @@ retraction_count_max = 12 retraction_extra_prime_amount = 0.5 retraction_hop = 1.5 retraction_hop_only_when_collides = False -retraction_min_travel = 0.8 +retraction_min_travel = =line_width * 2 retraction_prime_speed = 15 skin_line_width = =round(line_width * 0.78 / 0.8, 2) speed_print = 30 diff --git a/resources/variants/cartesio_0.25.inst.cfg b/resources/variants/cartesio_0.25.inst.cfg index 2272aebde5..014069451c 100644 --- a/resources/variants/cartesio_0.25.inst.cfg +++ b/resources/variants/cartesio_0.25.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = cartesio [metadata] +author = Cartesio setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/cartesio_0.4.inst.cfg b/resources/variants/cartesio_0.4.inst.cfg index bd3302da03..7b5dccd980 100644 --- a/resources/variants/cartesio_0.4.inst.cfg +++ b/resources/variants/cartesio_0.4.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = cartesio [metadata] +author = Cartesio setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/cartesio_0.8.inst.cfg b/resources/variants/cartesio_0.8.inst.cfg index eda8248584..70271dbf75 100644 --- a/resources/variants/cartesio_0.8.inst.cfg +++ b/resources/variants/cartesio_0.8.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = cartesio [metadata] +author = Cartesio setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_hyb35.inst.cfg b/resources/variants/fabtotum_hyb35.inst.cfg index 6d699f6df8..e036b2e474 100644 --- a/resources/variants/fabtotum_hyb35.inst.cfg +++ b/resources/variants/fabtotum_hyb35.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = fabtotum [metadata] +author = FABtotum setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_lite04.inst.cfg b/resources/variants/fabtotum_lite04.inst.cfg index 5d376de4a1..defa6692a8 100644 --- a/resources/variants/fabtotum_lite04.inst.cfg +++ b/resources/variants/fabtotum_lite04.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = fabtotum [metadata] +author = FABtotum setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_lite06.inst.cfg b/resources/variants/fabtotum_lite06.inst.cfg index 0181461613..d269092b42 100644 --- a/resources/variants/fabtotum_lite06.inst.cfg +++ b/resources/variants/fabtotum_lite06.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = fabtotum [metadata] +author = FABtotum setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro02.inst.cfg b/resources/variants/fabtotum_pro02.inst.cfg index c63ed6d982..b705929c35 100644 --- a/resources/variants/fabtotum_pro02.inst.cfg +++ b/resources/variants/fabtotum_pro02.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = fabtotum [metadata] +author = FABtotum setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro04.inst.cfg b/resources/variants/fabtotum_pro04.inst.cfg index 6fc2d7c4f7..b835312324 100644 --- a/resources/variants/fabtotum_pro04.inst.cfg +++ b/resources/variants/fabtotum_pro04.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = fabtotum [metadata] +author = FABtotum setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro06.inst.cfg b/resources/variants/fabtotum_pro06.inst.cfg index bf48b91965..140b6618cf 100644 --- a/resources/variants/fabtotum_pro06.inst.cfg +++ b/resources/variants/fabtotum_pro06.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = fabtotum [metadata] +author = FABtotum setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro08.inst.cfg b/resources/variants/fabtotum_pro08.inst.cfg index 83f13ef8fc..421cd0b662 100644 --- a/resources/variants/fabtotum_pro08.inst.cfg +++ b/resources/variants/fabtotum_pro08.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = fabtotum [metadata] +author = FABtotum setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_025_e3d.inst.cfg b/resources/variants/gmax15plus_025_e3d.inst.cfg index 88730e6722..3c7b2e4949 100644 --- a/resources/variants/gmax15plus_025_e3d.inst.cfg +++ b/resources/variants/gmax15plus_025_e3d.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_04_e3d.inst.cfg b/resources/variants/gmax15plus_04_e3d.inst.cfg index f107d2ab16..e9c0cf1b18 100644 --- a/resources/variants/gmax15plus_04_e3d.inst.cfg +++ b/resources/variants/gmax15plus_04_e3d.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_05_e3d.inst.cfg b/resources/variants/gmax15plus_05_e3d.inst.cfg index 72081a4b3d..8575f3c539 100644 --- a/resources/variants/gmax15plus_05_e3d.inst.cfg +++ b/resources/variants/gmax15plus_05_e3d.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_05_jhead.inst.cfg b/resources/variants/gmax15plus_05_jhead.inst.cfg index ca086fca96..14d6ab5e9d 100644 --- a/resources/variants/gmax15plus_05_jhead.inst.cfg +++ b/resources/variants/gmax15plus_05_jhead.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_06_e3d.inst.cfg b/resources/variants/gmax15plus_06_e3d.inst.cfg index bbb5d18df1..5d3f5c63a9 100644 --- a/resources/variants/gmax15plus_06_e3d.inst.cfg +++ b/resources/variants/gmax15plus_06_e3d.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_08_e3d.inst.cfg b/resources/variants/gmax15plus_08_e3d.inst.cfg index 944420e064..af08cd2c7c 100644 --- a/resources/variants/gmax15plus_08_e3d.inst.cfg +++ b/resources/variants/gmax15plus_08_e3d.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_10_jhead.inst.cfg b/resources/variants/gmax15plus_10_jhead.inst.cfg index 832861a98a..df61d9729f 100644 --- a/resources/variants/gmax15plus_10_jhead.inst.cfg +++ b/resources/variants/gmax15plus_10_jhead.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_025_e3d.inst.cfg b/resources/variants/gmax15plus_dual_025_e3d.inst.cfg index b80074479e..07bdfca9b7 100644 --- a/resources/variants/gmax15plus_dual_025_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_025_e3d.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus_dual [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_04_e3d.inst.cfg b/resources/variants/gmax15plus_dual_04_e3d.inst.cfg index 3f25835e3a..966fe9f224 100644 --- a/resources/variants/gmax15plus_dual_04_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_04_e3d.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus_dual [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_05_e3d.inst.cfg b/resources/variants/gmax15plus_dual_05_e3d.inst.cfg index c7dccfd7c7..85464bd733 100644 --- a/resources/variants/gmax15plus_dual_05_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_05_e3d.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus_dual [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_05_jhead.inst.cfg b/resources/variants/gmax15plus_dual_05_jhead.inst.cfg index 0cffafee3b..689c76541a 100644 --- a/resources/variants/gmax15plus_dual_05_jhead.inst.cfg +++ b/resources/variants/gmax15plus_dual_05_jhead.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus_dual [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_06_e3d.inst.cfg b/resources/variants/gmax15plus_dual_06_e3d.inst.cfg index a0dc450ec0..57641a4244 100644 --- a/resources/variants/gmax15plus_dual_06_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_06_e3d.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus_dual [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_08_e3d.inst.cfg b/resources/variants/gmax15plus_dual_08_e3d.inst.cfg index 547cee823d..11523ccd67 100644 --- a/resources/variants/gmax15plus_dual_08_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_08_e3d.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus_dual [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_10_jhead.inst.cfg b/resources/variants/gmax15plus_dual_10_jhead.inst.cfg index a565c073af..26721d0169 100644 --- a/resources/variants/gmax15plus_dual_10_jhead.inst.cfg +++ b/resources/variants/gmax15plus_dual_10_jhead.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = gmax15plus_dual [metadata] +author = gcreate setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/imade3d_jellybox_0.4.inst.cfg b/resources/variants/imade3d_jellybox_0.4.inst.cfg index cb20dadbf7..5baa8123f2 100644 --- a/resources/variants/imade3d_jellybox_0.4.inst.cfg +++ b/resources/variants/imade3d_jellybox_0.4.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = imade3d_jellybox [metadata] +author = IMADE3D setting_version = 4 type = variant hardware_type = nozzle diff --git a/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg b/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg index 95b696f432..5d1a01c366 100644 --- a/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg +++ b/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg @@ -4,6 +4,7 @@ version = 2 definition = imade3d_jellybox [metadata] +author = IMADE3D setting_version = 4 type = variant hardware_type = nozzle diff --git a/tools/check_preset_settings.py b/tools/check_preset_settings.py old mode 100644 new mode 100755