mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-21 05:37:50 -06:00
Merge pull request #3137 from Ultimaker/feature_multiple_BP
Feature: Printers with different build plates
This commit is contained in:
commit
cb0ac9b0c8
12 changed files with 420 additions and 23 deletions
|
@ -135,7 +135,6 @@ class PrinterOutputDevice(QObject, OutputDevice):
|
||||||
def controlItem(self):
|
def controlItem(self):
|
||||||
if not self._control_component:
|
if not self._control_component:
|
||||||
self._createControlViewFromQML()
|
self._createControlViewFromQML()
|
||||||
|
|
||||||
return self._control_item
|
return self._control_item
|
||||||
|
|
||||||
def _createControlViewFromQML(self):
|
def _createControlViewFromQML(self):
|
||||||
|
|
|
@ -144,7 +144,7 @@ class CuraContainerStack(ContainerStack):
|
||||||
|
|
||||||
## Set the material container.
|
## Set the material container.
|
||||||
#
|
#
|
||||||
# \param new_quality_changes The new material container. It is expected to have a "type" metadata entry with the value "quality_changes".
|
# \param new_material The new material container. It is expected to have a "type" metadata entry with the value "material".
|
||||||
def setMaterial(self, new_material: InstanceContainer, postpone_emit = False) -> None:
|
def setMaterial(self, new_material: InstanceContainer, postpone_emit = False) -> None:
|
||||||
self.replaceContainer(_ContainerIndexes.Material, new_material, postpone_emit = postpone_emit)
|
self.replaceContainer(_ContainerIndexes.Material, new_material, postpone_emit = postpone_emit)
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ class CuraContainerStack(ContainerStack):
|
||||||
# to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultMaterial
|
# to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultMaterial
|
||||||
# for details.
|
# for details.
|
||||||
#
|
#
|
||||||
# \param new_quality_changes_id The ID of the new material container.
|
# \param new_material_id The ID of the new material container.
|
||||||
#
|
#
|
||||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||||
def setMaterialById(self, new_material_id: str) -> None:
|
def setMaterialById(self, new_material_id: str) -> None:
|
||||||
|
@ -182,7 +182,7 @@ class CuraContainerStack(ContainerStack):
|
||||||
|
|
||||||
## Set the variant container.
|
## Set the variant container.
|
||||||
#
|
#
|
||||||
# \param new_quality_changes The new variant container. It is expected to have a "type" metadata entry with the value "quality_changes".
|
# \param new_variant The new variant container. It is expected to have a "type" metadata entry with the value "variant".
|
||||||
def setVariant(self, new_variant: InstanceContainer) -> None:
|
def setVariant(self, new_variant: InstanceContainer) -> None:
|
||||||
self.replaceContainer(_ContainerIndexes.Variant, new_variant)
|
self.replaceContainer(_ContainerIndexes.Variant, new_variant)
|
||||||
|
|
||||||
|
@ -193,13 +193,13 @@ class CuraContainerStack(ContainerStack):
|
||||||
# to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultVariant
|
# to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultVariant
|
||||||
# for details.
|
# for details.
|
||||||
#
|
#
|
||||||
# \param new_quality_changes_id The ID of the new variant container.
|
# \param new_variant_id The ID of the new variant container.
|
||||||
#
|
#
|
||||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||||
def setVariantById(self, new_variant_id: str) -> None:
|
def setVariantById(self, new_variant_id: str) -> None:
|
||||||
variant = self._empty_variant
|
variant = self._empty_variant
|
||||||
if new_variant_id == "default":
|
if new_variant_id == "default":
|
||||||
new_variant = self.findDefaultVariant()
|
new_variant = self.findDefaultVariantBuildplate() if self.getMetaDataEntry("type") == "machine" else self.findDefaultVariant()
|
||||||
if new_variant:
|
if new_variant:
|
||||||
variant = new_variant
|
variant = new_variant
|
||||||
else:
|
else:
|
||||||
|
@ -220,13 +220,13 @@ class CuraContainerStack(ContainerStack):
|
||||||
|
|
||||||
## Set the definition changes container.
|
## Set the definition changes container.
|
||||||
#
|
#
|
||||||
# \param new_quality_changes The new definition changes container. It is expected to have a "type" metadata entry with the value "quality_changes".
|
# \param new_definition_changes The new definition changes container. It is expected to have a "type" metadata entry with the value "definition_changes".
|
||||||
def setDefinitionChanges(self, new_definition_changes: InstanceContainer) -> None:
|
def setDefinitionChanges(self, new_definition_changes: InstanceContainer) -> None:
|
||||||
self.replaceContainer(_ContainerIndexes.DefinitionChanges, new_definition_changes)
|
self.replaceContainer(_ContainerIndexes.DefinitionChanges, new_definition_changes)
|
||||||
|
|
||||||
## Set the definition changes container by an ID.
|
## Set the definition changes container by an ID.
|
||||||
#
|
#
|
||||||
# \param new_quality_changes_id The ID of the new definition changes container.
|
# \param new_definition_changes_id The ID of the new definition changes container.
|
||||||
#
|
#
|
||||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||||
def setDefinitionChangesById(self, new_definition_changes_id: str) -> None:
|
def setDefinitionChangesById(self, new_definition_changes_id: str) -> None:
|
||||||
|
@ -245,13 +245,13 @@ class CuraContainerStack(ContainerStack):
|
||||||
|
|
||||||
## Set the definition container.
|
## Set the definition container.
|
||||||
#
|
#
|
||||||
# \param new_quality_changes The new definition container. It is expected to have a "type" metadata entry with the value "quality_changes".
|
# \param new_definition The new definition container. It is expected to have a "type" metadata entry with the value "definition".
|
||||||
def setDefinition(self, new_definition: DefinitionContainerInterface) -> None:
|
def setDefinition(self, new_definition: DefinitionContainerInterface) -> None:
|
||||||
self.replaceContainer(_ContainerIndexes.Definition, new_definition)
|
self.replaceContainer(_ContainerIndexes.Definition, new_definition)
|
||||||
|
|
||||||
## Set the definition container by an ID.
|
## Set the definition container by an ID.
|
||||||
#
|
#
|
||||||
# \param new_quality_changes_id The ID of the new definition container.
|
# \param new_definition_id The ID of the new definition container.
|
||||||
#
|
#
|
||||||
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
|
||||||
def setDefinitionById(self, new_definition_id: str) -> None:
|
def setDefinitionById(self, new_definition_id: str) -> None:
|
||||||
|
@ -435,6 +435,51 @@ class CuraContainerStack(ContainerStack):
|
||||||
Logger.log("w", "Could not find a valid default variant for stack {stack}", stack = self.id)
|
Logger.log("w", "Could not find a valid default variant for stack {stack}", stack = self.id)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
## Find the global variant that should be used as "default". This is used for the buildplates.
|
||||||
|
#
|
||||||
|
# This will search for variants that match the current definition and pick the preferred one,
|
||||||
|
# if specified by the machine definition.
|
||||||
|
#
|
||||||
|
# The following criteria are used to find the default global variant:
|
||||||
|
# - If the machine definition does not have a metadata entry "has_variant_buildplates" set to True, return None
|
||||||
|
# - The definition of the variant should be the same as the machine definition for this stack.
|
||||||
|
# - The container should have a metadata entry "type" with value "variant" and "hardware_type" with value "buildplate".
|
||||||
|
# - If the machine definition has a metadata entry "preferred_variant_buildplate", filter the variant IDs based on that.
|
||||||
|
#
|
||||||
|
# \return The container that should be used as default, or None if nothing was found or the machine does not use variants.
|
||||||
|
#
|
||||||
|
# \note This method assumes the stack has a valid machine definition.
|
||||||
|
def findDefaultVariantBuildplate(self) -> Optional[ContainerInterface]:
|
||||||
|
definition = self._getMachineDefinition()
|
||||||
|
# has_variant_buildplates can be overridden in other containers and stacks.
|
||||||
|
# In the case of UM2, it is overridden in the GlobalStack
|
||||||
|
if not self.getMetaDataEntry("has_variant_buildplates"):
|
||||||
|
# If the machine does not use variants, we should never set a variant.
|
||||||
|
return None
|
||||||
|
|
||||||
|
# First add any variant. Later, overwrite with preference if the preference is valid.
|
||||||
|
variant = None
|
||||||
|
definition_id = self._findInstanceContainerDefinitionId(definition)
|
||||||
|
variants = ContainerRegistry.getInstance().findInstanceContainers(definition = definition_id, type = "variant", hardware_type = "buildplate")
|
||||||
|
if variants:
|
||||||
|
variant = variants[0]
|
||||||
|
|
||||||
|
preferred_variant_buildplate_id = definition.getMetaDataEntry("preferred_variant_buildplate")
|
||||||
|
if preferred_variant_buildplate_id:
|
||||||
|
preferred_variant_buildplates = ContainerRegistry.getInstance().findInstanceContainers(id = preferred_variant_buildplate_id, definition = definition_id, type = "variant")
|
||||||
|
if preferred_variant_buildplates:
|
||||||
|
variant = preferred_variant_buildplates[0]
|
||||||
|
else:
|
||||||
|
Logger.log("w", "The preferred variant buildplate \"{variant}\" of stack {stack} does not exist or is not a variant.",
|
||||||
|
variant = preferred_variant_buildplate_id, stack = self.id)
|
||||||
|
# And leave it at the default variant.
|
||||||
|
|
||||||
|
if variant:
|
||||||
|
return variant
|
||||||
|
|
||||||
|
Logger.log("w", "Could not find a valid default buildplate variant for stack {stack}", stack = self.id)
|
||||||
|
return None
|
||||||
|
|
||||||
## Find the material that should be used as "default" material.
|
## Find the material that should be used as "default" material.
|
||||||
#
|
#
|
||||||
# This will search for materials that match the current definition and pick the preferred one,
|
# This will search for materials that match the current definition and pick the preferred one,
|
||||||
|
|
|
@ -50,6 +50,7 @@ class MachineManager(QObject):
|
||||||
|
|
||||||
# Used to store the new containers until after confirming the dialog
|
# Used to store the new containers until after confirming the dialog
|
||||||
self._new_variant_container = None
|
self._new_variant_container = None
|
||||||
|
self._new_buildplate_container = None
|
||||||
self._new_material_container = None
|
self._new_material_container = None
|
||||||
self._new_quality_containers = []
|
self._new_quality_containers = []
|
||||||
|
|
||||||
|
@ -157,6 +158,10 @@ class MachineManager(QObject):
|
||||||
def newVariant(self):
|
def newVariant(self):
|
||||||
return self._new_variant_container
|
return self._new_variant_container
|
||||||
|
|
||||||
|
@property
|
||||||
|
def newBuildplate(self):
|
||||||
|
return self._new_buildplate_container
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def newMaterial(self):
|
def newMaterial(self):
|
||||||
return self._new_material_container
|
return self._new_material_container
|
||||||
|
@ -309,9 +314,10 @@ class MachineManager(QObject):
|
||||||
self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
|
self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
|
||||||
self._global_container_stack.propertyChanged.connect(self._onPropertyChanged)
|
self._global_container_stack.propertyChanged.connect(self._onPropertyChanged)
|
||||||
|
|
||||||
# set the global variant to empty as we now use the extruder stack at all times - CURA-4482
|
# Global stack can have only a variant if it is a buildplate
|
||||||
global_variant = self._global_container_stack.variant
|
global_variant = self._global_container_stack.variant
|
||||||
if global_variant != self._empty_variant_container:
|
if global_variant != self._empty_variant_container:
|
||||||
|
if global_variant.getMetaDataEntry("hardware_type") != "buildplate":
|
||||||
self._global_container_stack.setVariant(self._empty_variant_container)
|
self._global_container_stack.setVariant(self._empty_variant_container)
|
||||||
|
|
||||||
# set the global material to empty as we now use the extruder stack at all times - CURA-4482
|
# set the global material to empty as we now use the extruder stack at all times - CURA-4482
|
||||||
|
@ -675,6 +681,14 @@ class MachineManager(QObject):
|
||||||
return quality.getId()
|
return quality.getId()
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify=activeVariantChanged)
|
||||||
|
def globalVariantId(self) -> str:
|
||||||
|
if self._global_container_stack:
|
||||||
|
variant = self._global_container_stack.variant
|
||||||
|
if variant and not isinstance(variant, type(self._empty_variant_container)):
|
||||||
|
return variant.getId()
|
||||||
|
return ""
|
||||||
|
|
||||||
@pyqtProperty(str, notify = activeQualityChanged)
|
@pyqtProperty(str, notify = activeQualityChanged)
|
||||||
def activeQualityType(self) -> str:
|
def activeQualityType(self) -> str:
|
||||||
if self._active_container_stack:
|
if self._active_container_stack:
|
||||||
|
@ -855,6 +869,24 @@ class MachineManager(QObject):
|
||||||
else:
|
else:
|
||||||
Logger.log("w", "While trying to set the active variant, no variant was found to replace.")
|
Logger.log("w", "While trying to set the active variant, no variant was found to replace.")
|
||||||
|
|
||||||
|
@pyqtSlot(str)
|
||||||
|
def setActiveVariantBuildplate(self, variant_buildplate_id: str):
|
||||||
|
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
|
||||||
|
containers = ContainerRegistry.getInstance().findInstanceContainers(id = variant_buildplate_id)
|
||||||
|
if not containers or not self._global_container_stack:
|
||||||
|
return
|
||||||
|
Logger.log("d", "Attempting to change the active buildplate to %s", variant_buildplate_id)
|
||||||
|
old_buildplate = self._global_container_stack.variant
|
||||||
|
if old_buildplate:
|
||||||
|
self.blurSettings.emit()
|
||||||
|
self._new_buildplate_container = containers[0] # self._active_container_stack will be updated with a delay
|
||||||
|
Logger.log("d", "Active buildplate changed to {active_variant_buildplate_id}".format(active_variant_buildplate_id = containers[0].getId()))
|
||||||
|
|
||||||
|
# Force set the active quality as it is so the values are updated
|
||||||
|
self.setActiveMaterial(self._active_container_stack.material.getId())
|
||||||
|
else:
|
||||||
|
Logger.log("w", "While trying to set the active buildplate, no buildplate was found to replace.")
|
||||||
|
|
||||||
## set the active quality
|
## set the active quality
|
||||||
# \param quality_id The quality_id of either a quality or a quality_changes
|
# \param quality_id The quality_id of either a quality or a quality_changes
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
|
@ -932,6 +964,10 @@ class MachineManager(QObject):
|
||||||
self._active_container_stack.variant = self._new_variant_container
|
self._active_container_stack.variant = self._new_variant_container
|
||||||
self._new_variant_container = None
|
self._new_variant_container = None
|
||||||
|
|
||||||
|
if self._new_buildplate_container is not None:
|
||||||
|
self._global_container_stack.variant = self._new_buildplate_container
|
||||||
|
self._new_buildplate_container = None
|
||||||
|
|
||||||
if self._new_material_container is not None:
|
if self._new_material_container is not None:
|
||||||
self._active_container_stack.material = self._new_material_container
|
self._active_container_stack.material = self._new_material_container
|
||||||
self._new_material_container = None
|
self._new_material_container = None
|
||||||
|
@ -952,6 +988,7 @@ class MachineManager(QObject):
|
||||||
# Used for ignoring any changes when switching between printers (setActiveMachine)
|
# Used for ignoring any changes when switching between printers (setActiveMachine)
|
||||||
def _cancelDelayedActiveContainerStackChanges(self):
|
def _cancelDelayedActiveContainerStackChanges(self):
|
||||||
self._new_material_container = None
|
self._new_material_container = None
|
||||||
|
self._new_buildplate_container = None
|
||||||
self._new_variant_container = None
|
self._new_variant_container = None
|
||||||
|
|
||||||
## Determine the quality and quality changes settings for the current machine for a quality name.
|
## Determine the quality and quality changes settings for the current machine for a quality name.
|
||||||
|
@ -1116,6 +1153,15 @@ class MachineManager(QObject):
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@pyqtProperty(str, notify = activeVariantChanged)
|
||||||
|
def activeVariantBuildplateName(self) -> str:
|
||||||
|
if self._global_container_stack:
|
||||||
|
variant = self._global_container_stack.variant
|
||||||
|
if variant:
|
||||||
|
return variant.getName()
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
@pyqtProperty(str, notify = globalContainerChanged)
|
@pyqtProperty(str, notify = globalContainerChanged)
|
||||||
def activeDefinitionId(self) -> str:
|
def activeDefinitionId(self) -> str:
|
||||||
if self._global_container_stack:
|
if self._global_container_stack:
|
||||||
|
@ -1213,7 +1259,6 @@ class MachineManager(QObject):
|
||||||
def hasMaterials(self) -> bool:
|
def hasMaterials(self) -> bool:
|
||||||
if self._global_container_stack:
|
if self._global_container_stack:
|
||||||
return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False))
|
return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False))
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||||
|
@ -1222,6 +1267,53 @@ class MachineManager(QObject):
|
||||||
return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_variants", False))
|
return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_variants", False))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||||
|
def hasVariantBuildplates(self) -> bool:
|
||||||
|
if self._global_container_stack:
|
||||||
|
return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_variant_buildplates", False))
|
||||||
|
return False
|
||||||
|
|
||||||
|
## The selected buildplate is compatible if it is compatible with all the materials in all the extruders
|
||||||
|
@pyqtProperty(bool, notify = activeMaterialChanged)
|
||||||
|
def variantBuildplateCompatible(self) -> bool:
|
||||||
|
if not self._global_container_stack:
|
||||||
|
return True
|
||||||
|
|
||||||
|
buildplate_compatible = True # It is compatible by default
|
||||||
|
extruder_stacks = self._global_container_stack.extruders.values()
|
||||||
|
for stack in extruder_stacks:
|
||||||
|
material_container = stack.material
|
||||||
|
if material_container == self._empty_material_container:
|
||||||
|
continue
|
||||||
|
if material_container.getMetaDataEntry("buildplate_compatible"):
|
||||||
|
buildplate_compatible = buildplate_compatible and material_container.getMetaDataEntry("buildplate_compatible")[self.activeVariantBuildplateName]
|
||||||
|
|
||||||
|
return buildplate_compatible
|
||||||
|
|
||||||
|
## The selected buildplate is usable if it is usable for all materials OR it is compatible for one but not compatible
|
||||||
|
# for the other material but the buildplate is still usable
|
||||||
|
@pyqtProperty(bool, notify = activeMaterialChanged)
|
||||||
|
def variantBuildplateUsable(self) -> bool:
|
||||||
|
if not self._global_container_stack:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Here the next formula is being calculated:
|
||||||
|
# result = (not (material_left_compatible and material_right_compatible)) and
|
||||||
|
# (material_left_compatible or material_left_usable) and
|
||||||
|
# (material_right_compatible or material_right_usable)
|
||||||
|
result = not self.variantBuildplateCompatible
|
||||||
|
extruder_stacks = self._global_container_stack.extruders.values()
|
||||||
|
for stack in extruder_stacks:
|
||||||
|
material_container = stack.material
|
||||||
|
if material_container == self._empty_material_container:
|
||||||
|
continue
|
||||||
|
buildplate_compatible = material_container.getMetaDataEntry("buildplate_compatible")[self.activeVariantBuildplateName] if material_container.getMetaDataEntry("buildplate_compatible") else True
|
||||||
|
buildplate_usable = material_container.getMetaDataEntry("buildplate_recommended")[self.activeVariantBuildplateName] if material_container.getMetaDataEntry("buildplate_recommended") else True
|
||||||
|
|
||||||
|
result = result and (buildplate_compatible or buildplate_usable)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
## Property to indicate if a machine has "specialized" material profiles.
|
## Property to indicate if a machine has "specialized" material profiles.
|
||||||
# Some machines have their own material profiles that "override" the default catch all profiles.
|
# Some machines have their own material profiles that "override" the default catch all profiles.
|
||||||
@pyqtProperty(bool, notify = globalContainerChanged)
|
@pyqtProperty(bool, notify = globalContainerChanged)
|
||||||
|
|
|
@ -122,6 +122,12 @@ class StartSliceJob(Job):
|
||||||
self.setResult(StartJobResult.BuildPlateError)
|
self.setResult(StartJobResult.BuildPlateError)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Don't slice if the buildplate or the nozzle type is incompatible with the materials
|
||||||
|
if not Application.getInstance().getMachineManager().variantBuildplateCompatible and \
|
||||||
|
not Application.getInstance().getMachineManager().variantBuildplateUsable:
|
||||||
|
self.setResult(StartJobResult.MaterialIncompatible)
|
||||||
|
return
|
||||||
|
|
||||||
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
|
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
|
||||||
material = extruder_stack.findContainer({"type": "material"})
|
material = extruder_stack.findContainer({"type": "material"})
|
||||||
if material:
|
if material:
|
||||||
|
|
|
@ -576,6 +576,42 @@ class XmlMaterialProfile(InstanceContainer):
|
||||||
if is_new_material:
|
if is_new_material:
|
||||||
containers_to_add.append(new_material)
|
containers_to_add.append(new_material)
|
||||||
|
|
||||||
|
# Find the buildplates compatibility
|
||||||
|
buildplates = machine.iterfind("./um:buildplate", self.__namespaces)
|
||||||
|
buildplate_map = {}
|
||||||
|
buildplate_map["buildplate_compatible"] = {}
|
||||||
|
buildplate_map["buildplate_recommended"] = {}
|
||||||
|
for buildplate in buildplates:
|
||||||
|
buildplate_id = buildplate.get("id")
|
||||||
|
if buildplate_id is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(
|
||||||
|
id = buildplate_id)
|
||||||
|
if not variant_containers:
|
||||||
|
# It is not really properly defined what "ID" is so also search for variants by name.
|
||||||
|
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(
|
||||||
|
definition = machine_id, name = buildplate_id)
|
||||||
|
|
||||||
|
if not variant_containers:
|
||||||
|
continue
|
||||||
|
|
||||||
|
buildplate_compatibility = machine_compatibility
|
||||||
|
buildplate_recommended = machine_compatibility
|
||||||
|
settings = buildplate.iterfind("./um:setting", self.__namespaces)
|
||||||
|
for entry in settings:
|
||||||
|
key = entry.get("key")
|
||||||
|
if key in self.__unmapped_settings:
|
||||||
|
if key == "hardware compatible":
|
||||||
|
buildplate_compatibility = self._parseCompatibleValue(entry.text)
|
||||||
|
elif key == "hardware recommended":
|
||||||
|
buildplate_recommended = self._parseCompatibleValue(entry.text)
|
||||||
|
else:
|
||||||
|
Logger.log("d", "Unsupported material setting %s", key)
|
||||||
|
|
||||||
|
buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_compatibility
|
||||||
|
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_recommended
|
||||||
|
|
||||||
hotends = machine.iterfind("./um:hotend", self.__namespaces)
|
hotends = machine.iterfind("./um:hotend", self.__namespaces)
|
||||||
for hotend in hotends:
|
for hotend in hotends:
|
||||||
hotend_id = hotend.get("id")
|
hotend_id = hotend.get("id")
|
||||||
|
@ -605,8 +641,7 @@ class XmlMaterialProfile(InstanceContainer):
|
||||||
|
|
||||||
new_hotend_id = self.getId() + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
|
new_hotend_id = self.getId() + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
|
||||||
|
|
||||||
# Same as machine compatibility, keep the derived material containers consistent with the parent
|
# Same as machine compatibility, keep the derived material containers consistent with the parent material
|
||||||
# material
|
|
||||||
if ContainerRegistry.getInstance().isLoaded(new_hotend_id):
|
if ContainerRegistry.getInstance().isLoaded(new_hotend_id):
|
||||||
new_hotend_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_id)[0]
|
new_hotend_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_id)[0]
|
||||||
is_new_material = False
|
is_new_material = False
|
||||||
|
@ -623,6 +658,9 @@ class XmlMaterialProfile(InstanceContainer):
|
||||||
new_hotend_material.getMetaData()["compatible"] = hotend_compatibility
|
new_hotend_material.getMetaData()["compatible"] = hotend_compatibility
|
||||||
new_hotend_material.getMetaData()["machine_manufacturer"] = machine_manufacturer
|
new_hotend_material.getMetaData()["machine_manufacturer"] = machine_manufacturer
|
||||||
new_hotend_material.getMetaData()["definition"] = machine_id
|
new_hotend_material.getMetaData()["definition"] = machine_id
|
||||||
|
if buildplate_map["buildplate_compatible"]:
|
||||||
|
new_hotend_material.getMetaData()["buildplate_compatible"] = buildplate_map["buildplate_compatible"]
|
||||||
|
new_hotend_material.getMetaData()["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
|
||||||
|
|
||||||
cached_hotend_setting_properties = cached_machine_setting_properties.copy()
|
cached_hotend_setting_properties = cached_machine_setting_properties.copy()
|
||||||
cached_hotend_setting_properties.update(hotend_setting_values)
|
cached_hotend_setting_properties.update(hotend_setting_values)
|
||||||
|
@ -763,6 +801,34 @@ class XmlMaterialProfile(InstanceContainer):
|
||||||
if len(found_materials) == 0: #This is a new material.
|
if len(found_materials) == 0: #This is a new material.
|
||||||
result_metadata.append(new_material_metadata)
|
result_metadata.append(new_material_metadata)
|
||||||
|
|
||||||
|
buildplates = machine.iterfind("./um:buildplate", cls.__namespaces)
|
||||||
|
buildplate_map = {}
|
||||||
|
buildplate_map["buildplate_compatible"] = {}
|
||||||
|
buildplate_map["buildplate_recommended"] = {}
|
||||||
|
for buildplate in buildplates:
|
||||||
|
buildplate_id = buildplate.get("id")
|
||||||
|
if buildplate_id is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = buildplate_id)
|
||||||
|
if not variant_containers:
|
||||||
|
# It is not really properly defined what "ID" is so also search for variants by name.
|
||||||
|
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = buildplate_id)
|
||||||
|
|
||||||
|
if not variant_containers:
|
||||||
|
continue
|
||||||
|
|
||||||
|
settings = buildplate.iterfind("./um:setting", cls.__namespaces)
|
||||||
|
for entry in settings:
|
||||||
|
key = entry.get("key")
|
||||||
|
if key == "hardware compatible":
|
||||||
|
buildplate_compatibility = cls._parseCompatibleValue(entry.text)
|
||||||
|
elif key == "hardware recommended":
|
||||||
|
buildplate_recommended = cls._parseCompatibleValue(entry.text)
|
||||||
|
|
||||||
|
buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_map["buildplate_compatible"]
|
||||||
|
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_map["buildplate_recommended"]
|
||||||
|
|
||||||
for hotend in machine.iterfind("./um:hotend", cls.__namespaces):
|
for hotend in machine.iterfind("./um:hotend", cls.__namespaces):
|
||||||
hotend_id = hotend.get("id")
|
hotend_id = hotend.get("id")
|
||||||
if hotend_id is None:
|
if hotend_id is None:
|
||||||
|
@ -781,8 +847,7 @@ class XmlMaterialProfile(InstanceContainer):
|
||||||
|
|
||||||
new_hotend_id = container_id + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
|
new_hotend_id = container_id + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
|
||||||
|
|
||||||
# Same as machine compatibility, keep the derived material containers consistent with the parent
|
# Same as machine compatibility, keep the derived material containers consistent with the parent material
|
||||||
# material
|
|
||||||
found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_hotend_id)
|
found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_hotend_id)
|
||||||
if found_materials:
|
if found_materials:
|
||||||
new_hotend_material_metadata = found_materials[0]
|
new_hotend_material_metadata = found_materials[0]
|
||||||
|
@ -799,6 +864,9 @@ class XmlMaterialProfile(InstanceContainer):
|
||||||
new_hotend_material_metadata["machine_manufacturer"] = machine_manufacturer
|
new_hotend_material_metadata["machine_manufacturer"] = machine_manufacturer
|
||||||
new_hotend_material_metadata["id"] = new_hotend_id
|
new_hotend_material_metadata["id"] = new_hotend_id
|
||||||
new_hotend_material_metadata["definition"] = machine_id
|
new_hotend_material_metadata["definition"] = machine_id
|
||||||
|
if buildplate_map["buildplate_compatible"]:
|
||||||
|
new_hotend_material_metadata["buildplate_compatible"] = buildplate_map["buildplate_compatible"]
|
||||||
|
new_hotend_material_metadata["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
|
||||||
|
|
||||||
if len(found_materials) == 0:
|
if len(found_materials) == 0:
|
||||||
result_metadata.append(new_hotend_material_metadata)
|
result_metadata.append(new_hotend_material_metadata)
|
||||||
|
@ -874,7 +942,7 @@ class XmlMaterialProfile(InstanceContainer):
|
||||||
# Map XML file setting names to internal names
|
# Map XML file setting names to internal names
|
||||||
__material_settings_setting_map = {
|
__material_settings_setting_map = {
|
||||||
"print temperature": "default_material_print_temperature",
|
"print temperature": "default_material_print_temperature",
|
||||||
"heated bed temperature": "material_bed_temperature",
|
"heated bed temperature": "default_material_bed_temperature",
|
||||||
"standby temperature": "material_standby_temperature",
|
"standby temperature": "material_standby_temperature",
|
||||||
"processing temperature graph": "material_flow_temp_graph",
|
"processing temperature graph": "material_flow_temp_graph",
|
||||||
"print cooling": "cool_fan_speed",
|
"print cooling": "cool_fan_speed",
|
||||||
|
@ -884,7 +952,8 @@ class XmlMaterialProfile(InstanceContainer):
|
||||||
"surface energy": "material_surface_energy"
|
"surface energy": "material_surface_energy"
|
||||||
}
|
}
|
||||||
__unmapped_settings = [
|
__unmapped_settings = [
|
||||||
"hardware compatible"
|
"hardware compatible",
|
||||||
|
"hardware recommended"
|
||||||
]
|
]
|
||||||
__material_properties_setting_map = {
|
__material_properties_setting_map = {
|
||||||
"diameter": "material_diameter"
|
"diameter": "material_diameter"
|
||||||
|
|
|
@ -154,6 +154,21 @@
|
||||||
"settable_per_extruder": false,
|
"settable_per_extruder": false,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
},
|
},
|
||||||
|
"machine_buildplate_type":
|
||||||
|
{
|
||||||
|
"label": "Build Plate Material",
|
||||||
|
"description": "The material of the build plate installed on the printer.",
|
||||||
|
"default_value": "glass",
|
||||||
|
"type": "enum",
|
||||||
|
"options":
|
||||||
|
{
|
||||||
|
"glass": "Glass",
|
||||||
|
"aluminium": "Aluminium"
|
||||||
|
},
|
||||||
|
"settable_per_mesh": false,
|
||||||
|
"settable_per_extruder": false,
|
||||||
|
"settable_per_meshgroup": false
|
||||||
|
},
|
||||||
"machine_height":
|
"machine_height":
|
||||||
{
|
{
|
||||||
"label": "Machine Height",
|
"label": "Machine Height",
|
||||||
|
@ -1981,14 +1996,30 @@
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": true
|
"settable_per_extruder": true
|
||||||
},
|
},
|
||||||
|
"default_material_bed_temperature":
|
||||||
|
{
|
||||||
|
"label": "Default Build Plate Temperature",
|
||||||
|
"description": "The default temperature used for the heated build plate. This should be the \"base\" temperature of a build plate. All other print temperatures should use offsets based on this value",
|
||||||
|
"unit": "°C",
|
||||||
|
"type": "float",
|
||||||
|
"resolve": "max(extruderValues('default_material_bed_temperature'))",
|
||||||
|
"default_value": 60,
|
||||||
|
"minimum_value": "-273.15",
|
||||||
|
"minimum_value_warning": "0",
|
||||||
|
"maximum_value_warning": "130",
|
||||||
|
"enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\"",
|
||||||
|
"settable_per_mesh": false,
|
||||||
|
"settable_per_extruder": false,
|
||||||
|
"settable_per_meshgroup": false
|
||||||
|
},
|
||||||
"material_bed_temperature":
|
"material_bed_temperature":
|
||||||
{
|
{
|
||||||
"label": "Build Plate Temperature",
|
"label": "Build Plate Temperature",
|
||||||
"description": "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted.",
|
"description": "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted.",
|
||||||
"unit": "°C",
|
"unit": "°C",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"resolve": "max(extruderValues('material_bed_temperature'))",
|
|
||||||
"default_value": 60,
|
"default_value": 60,
|
||||||
|
"value": "default_material_bed_temperature",
|
||||||
"minimum_value": "-273.15",
|
"minimum_value": "-273.15",
|
||||||
"minimum_value_warning": "0",
|
"minimum_value_warning": "0",
|
||||||
"maximum_value_warning": "130",
|
"maximum_value_warning": "130",
|
||||||
|
|
|
@ -204,6 +204,7 @@ UM.MainWindow
|
||||||
onObjectRemoved: settingsMenu.removeItem(object)
|
onObjectRemoved: settingsMenu.removeItem(object)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BuildplateMenu { title: catalog.i18nc("@title:menu", "&Build plate"); visible: Cura.MachineManager.hasVariantBuildplates }
|
||||||
NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasVariants }
|
NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasVariants }
|
||||||
MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasMaterials }
|
MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasMaterials }
|
||||||
ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); visible: machineExtruderCount.properties.value <= 1 }
|
ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); visible: machineExtruderCount.properties.value <= 1 }
|
||||||
|
|
87
resources/qml/Menus/BuildplateMenu.qml
Normal file
87
resources/qml/Menus/BuildplateMenu.qml
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
// Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import QtQuick 2.2
|
||||||
|
import QtQuick.Controls 1.1
|
||||||
|
|
||||||
|
import UM 1.2 as UM
|
||||||
|
import Cura 1.0 as Cura
|
||||||
|
|
||||||
|
Menu
|
||||||
|
{
|
||||||
|
id: menu
|
||||||
|
title: "Build plate"
|
||||||
|
|
||||||
|
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
|
||||||
|
property bool isClusterPrinter:
|
||||||
|
{
|
||||||
|
if(Cura.MachineManager.printerOutputDevices.length == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var clusterSize = Cura.MachineManager.printerOutputDevices[0].clusterSize;
|
||||||
|
// This is not a cluster printer or the cluster it is just one printer
|
||||||
|
if(clusterSize == undefined || clusterSize == 1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuItem
|
||||||
|
{
|
||||||
|
id: automaticBuildplate
|
||||||
|
text:
|
||||||
|
{
|
||||||
|
if(printerConnected && Cura.MachineManager.printerOutputDevices[0].buildplateId != "" && !isClusterPrinter)
|
||||||
|
{
|
||||||
|
var buildplateName = Cura.MachineManager.printerOutputDevices[0].buildplateId
|
||||||
|
return catalog.i18nc("@title:menuitem %1 is the buildplate currently loaded in the printer", "Automatic: %1").arg(buildplateName)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
visible: printerConnected && Cura.MachineManager.printerOutputDevices[0].buildplateId != "" && !isClusterPrinter
|
||||||
|
onTriggered:
|
||||||
|
{
|
||||||
|
var buildplateId = Cura.MachineManager.printerOutputDevices[0].buildplateId
|
||||||
|
var itemIndex = buildplateInstantiator.model.find("name", buildplateId)
|
||||||
|
if(itemIndex > -1)
|
||||||
|
{
|
||||||
|
Cura.MachineManager.setActiveVariantBuildplate(buildplateInstantiator.model.getItem(itemIndex).id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuSeparator
|
||||||
|
{
|
||||||
|
visible: automaticBuildplate.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
Instantiator
|
||||||
|
{
|
||||||
|
id: buildplateInstantiator
|
||||||
|
model: UM.InstanceContainersModel
|
||||||
|
{
|
||||||
|
filter:
|
||||||
|
{
|
||||||
|
"type": "variant",
|
||||||
|
"hardware_type": "buildplate",
|
||||||
|
"definition": Cura.MachineManager.activeDefinitionId //Only show variants of this machine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: model.name
|
||||||
|
checkable: true
|
||||||
|
checked: model.id == Cura.MachineManager.globalVariantId
|
||||||
|
exclusiveGroup: group
|
||||||
|
onTriggered:
|
||||||
|
{
|
||||||
|
Cura.MachineManager.setActiveVariantBuildplate(model.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onObjectAdded: menu.insertItem(index, object)
|
||||||
|
onObjectRemoved: menu.removeItem(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
ExclusiveGroup { id: group }
|
||||||
|
}
|
|
@ -67,10 +67,19 @@ Menu
|
||||||
model: UM.InstanceContainersModel
|
model: UM.InstanceContainersModel
|
||||||
{
|
{
|
||||||
filter:
|
filter:
|
||||||
|
{
|
||||||
|
var filter_dict =
|
||||||
{
|
{
|
||||||
"type": "variant",
|
"type": "variant",
|
||||||
"definition": Cura.MachineManager.activeQualityDefinitionId //Only show variants of this machine
|
"definition": Cura.MachineManager.activeQualityDefinitionId //Only show variants of this machine
|
||||||
}
|
}
|
||||||
|
if (Cura.MachineManager.hasVariantBuildplates)
|
||||||
|
{
|
||||||
|
filter_dict["hardware_type"] = "nozzle"
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter_dict
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: model.name
|
text: model.name
|
||||||
|
|
|
@ -314,6 +314,62 @@ Column
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Buildplate row separator
|
||||||
|
Rectangle {
|
||||||
|
id: separator
|
||||||
|
|
||||||
|
anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width
|
||||||
|
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: buildplateRow.visible
|
||||||
|
width: parent.width - UM.Theme.getSize("sidebar_margin").width * 2
|
||||||
|
height: visible ? UM.Theme.getSize("sidebar_lining_thin").height / 2 : 0
|
||||||
|
color: UM.Theme.getColor("sidebar_lining_thin")
|
||||||
|
}
|
||||||
|
|
||||||
|
//Buildplate row
|
||||||
|
Item
|
||||||
|
{
|
||||||
|
id: buildplateRow
|
||||||
|
height: UM.Theme.getSize("sidebar_setup").height
|
||||||
|
visible: Cura.MachineManager.hasVariantBuildplates && !sidebar.monitoringPrint && !sidebar.hideSettings
|
||||||
|
|
||||||
|
anchors
|
||||||
|
{
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: UM.Theme.getSize("sidebar_margin").width
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: UM.Theme.getSize("sidebar_margin").width
|
||||||
|
}
|
||||||
|
|
||||||
|
Label
|
||||||
|
{
|
||||||
|
id: bulidplateLabel
|
||||||
|
text: catalog.i18nc("@label", "Build plate");
|
||||||
|
width: Math.floor(parent.width * 0.45 - UM.Theme.getSize("default_margin").width)
|
||||||
|
font: UM.Theme.getFont("default");
|
||||||
|
color: UM.Theme.getColor("text");
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolButton {
|
||||||
|
id: buildplateSelection
|
||||||
|
text: Cura.MachineManager.activeVariantBuildplateName
|
||||||
|
tooltip: Cura.MachineManager.activeVariantBuildplateName
|
||||||
|
visible: Cura.MachineManager.hasVariantBuildplates
|
||||||
|
|
||||||
|
height: UM.Theme.getSize("setting_control").height
|
||||||
|
width: Math.floor(parent.width * 0.7 + UM.Theme.getSize("sidebar_margin").width)
|
||||||
|
anchors.right: parent.right
|
||||||
|
style: UM.Theme.styles.sidebar_header_button
|
||||||
|
activeFocusOnPress: true;
|
||||||
|
|
||||||
|
menu: BuildplateMenu {}
|
||||||
|
|
||||||
|
property var valueError: !Cura.MachineManager.variantBuildplateCompatible && !Cura.MachineManager.variantBuildplateUsable
|
||||||
|
property var valueWarning: Cura.MachineManager.variantBuildplateUsable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Material info row
|
// Material info row
|
||||||
Item
|
Item
|
||||||
{
|
{
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
"sidebar_header_text_hover": [255, 255, 255, 255],
|
"sidebar_header_text_hover": [255, 255, 255, 255],
|
||||||
"sidebar_header_text_inactive": [255, 255, 255, 127],
|
"sidebar_header_text_inactive": [255, 255, 255, 127],
|
||||||
"sidebar_lining": [31, 36, 39, 255],
|
"sidebar_lining": [31, 36, 39, 255],
|
||||||
|
"sidebar_lining_thin": [255, 255, 255, 30],
|
||||||
|
|
||||||
"button": [39, 44, 48, 255],
|
"button": [39, 44, 48, 255],
|
||||||
"button_hover": [39, 44, 48, 255],
|
"button_hover": [39, 44, 48, 255],
|
||||||
|
|
|
@ -91,6 +91,7 @@
|
||||||
"sidebar_header_text_active": [255, 255, 255, 255],
|
"sidebar_header_text_active": [255, 255, 255, 255],
|
||||||
"sidebar_header_text_hover": [255, 255, 255, 255],
|
"sidebar_header_text_hover": [255, 255, 255, 255],
|
||||||
"sidebar_lining": [245, 245, 245, 255],
|
"sidebar_lining": [245, 245, 245, 255],
|
||||||
|
"sidebar_lining_thin": [127, 127, 127, 255],
|
||||||
|
|
||||||
"button": [31, 36, 39, 255],
|
"button": [31, 36, 39, 255],
|
||||||
"button_hover": [68, 72, 75, 255],
|
"button_hover": [68, 72, 75, 255],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue