mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-17 11:47:50 -06:00
Merge remote-tracking branch 'origin/3.2'
This commit is contained in:
commit
b14fdb1d0d
10 changed files with 228 additions and 131 deletions
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
|
@ -1,5 +1,5 @@
|
||||||
timeout(time: 2, unit: "HOURS") {
|
|
||||||
parallel_nodes(['linux && cura', 'windows && cura']) {
|
parallel_nodes(['linux && cura', 'windows && cura']) {
|
||||||
|
timeout(time: 2, unit: "HOURS") {
|
||||||
// Prepare building
|
// Prepare building
|
||||||
stage('Prepare') {
|
stage('Prepare') {
|
||||||
// Ensure we start with a clean build directory.
|
// Ensure we start with a clean build directory.
|
||||||
|
|
|
@ -94,6 +94,10 @@ class CuraActions(QObject):
|
||||||
removed_group_nodes.append(group_node)
|
removed_group_nodes.append(group_node)
|
||||||
op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent()))
|
op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent()))
|
||||||
op.addOperation(RemoveSceneNodeOperation(group_node))
|
op.addOperation(RemoveSceneNodeOperation(group_node))
|
||||||
|
|
||||||
|
# Reset the print information
|
||||||
|
Application.getInstance().getController().getScene().sceneChanged.emit(node)
|
||||||
|
|
||||||
op.push()
|
op.push()
|
||||||
|
|
||||||
## Set the extruder that should be used to print the selection.
|
## Set the extruder that should be used to print the selection.
|
||||||
|
|
|
@ -1078,7 +1078,7 @@ class CuraApplication(QtApplication):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
continue # Node that doesnt have a mesh and is not a group.
|
||||||
if only_selectable and not node.isSelectable():
|
if only_selectable and not node.isSelectable():
|
||||||
continue
|
continue
|
||||||
if not node.callDecoration("isSliceable") and not node.callDecoration("getLayerData"):
|
if not node.callDecoration("isSliceable") and not node.callDecoration("getLayerData") and not node.callDecoration("isGroup"):
|
||||||
continue # Only remove nodes that are selectable.
|
continue # Only remove nodes that are selectable.
|
||||||
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||||
|
@ -1089,12 +1089,12 @@ class CuraApplication(QtApplication):
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
op.addOperation(RemoveSceneNodeOperation(node))
|
op.addOperation(RemoveSceneNodeOperation(node))
|
||||||
|
|
||||||
|
# Reset the print information
|
||||||
|
self.getController().getScene().sceneChanged.emit(node)
|
||||||
|
|
||||||
op.push()
|
op.push()
|
||||||
Selection.clear()
|
Selection.clear()
|
||||||
|
|
||||||
# Reset the print information:
|
|
||||||
self.getController().getScene().sceneChanged.emit(node)
|
|
||||||
|
|
||||||
## Reset all translation on nodes with mesh data.
|
## Reset all translation on nodes with mesh data.
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def resetAllTranslation(self):
|
def resetAllTranslation(self):
|
||||||
|
|
|
@ -8,7 +8,9 @@ from UM.Application import Application
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Qt.Duration import Duration
|
from UM.Qt.Duration import Duration
|
||||||
from UM.Preferences import Preferences
|
from UM.Preferences import Preferences
|
||||||
|
from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
|
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
@ -65,7 +67,7 @@ class PrintInformation(QObject):
|
||||||
self._backend = Application.getInstance().getBackend()
|
self._backend = Application.getInstance().getBackend()
|
||||||
if self._backend:
|
if self._backend:
|
||||||
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
||||||
Application.getInstance().getController().getScene().sceneChanged.connect(self.setToZeroPrintInformation)
|
Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
||||||
|
|
||||||
self._base_name = ""
|
self._base_name = ""
|
||||||
self._abbr_machine = ""
|
self._abbr_machine = ""
|
||||||
|
@ -395,12 +397,25 @@ class PrintInformation(QObject):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Simulate message with zero time duration
|
# Simulate message with zero time duration
|
||||||
def setToZeroPrintInformation(self, build_plate_number):
|
def setToZeroPrintInformation(self, build_plate):
|
||||||
temp_message = {}
|
|
||||||
if build_plate_number not in self._print_time_message_values:
|
|
||||||
self._print_time_message_values[build_plate_number] = {}
|
|
||||||
for key in self._print_time_message_values[build_plate_number].keys():
|
|
||||||
temp_message[key] = 0
|
|
||||||
|
|
||||||
|
# Construct the 0-time message
|
||||||
|
temp_message = {}
|
||||||
|
if build_plate not in self._print_time_message_values:
|
||||||
|
self._print_time_message_values[build_plate] = {}
|
||||||
|
for key in self._print_time_message_values[build_plate].keys():
|
||||||
|
temp_message[key] = 0
|
||||||
temp_material_amounts = [0]
|
temp_material_amounts = [0]
|
||||||
self._onPrintDurationMessage(build_plate_number, temp_message, temp_material_amounts)
|
|
||||||
|
self._onPrintDurationMessage(build_plate, temp_message, temp_material_amounts)
|
||||||
|
|
||||||
|
## Listen to scene changes to check if we need to reset the print information
|
||||||
|
def _onSceneChanged(self, scene_node):
|
||||||
|
|
||||||
|
# Ignore any changes that are not related to sliceable objects
|
||||||
|
if not isinstance(scene_node, SceneNode)\
|
||||||
|
or not scene_node.callDecoration("isSliceable")\
|
||||||
|
or not scene_node.callDecoration("getBuildPlateNumber") == self._active_build_plate:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.setToZeroPrintInformation(self._active_build_plate)
|
||||||
|
|
|
@ -449,7 +449,13 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
if not extruder_stacks:
|
if not extruder_stacks:
|
||||||
self.addExtruderStackForSingleExtrusionMachine(container, "fdmextruder")
|
self.addExtruderStackForSingleExtrusionMachine(container, "fdmextruder")
|
||||||
|
|
||||||
def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id):
|
#
|
||||||
|
# new_global_quality_changes is optional. It is only used in project loading for a scenario like this:
|
||||||
|
# - override the current machine
|
||||||
|
# - create new for custom quality profile
|
||||||
|
# new_global_quality_changes is the new global quality changes container in this scenario.
|
||||||
|
#
|
||||||
|
def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id, new_global_quality_changes = None):
|
||||||
new_extruder_id = extruder_id
|
new_extruder_id = extruder_id
|
||||||
|
|
||||||
extruder_definitions = self.findDefinitionContainers(id = new_extruder_id)
|
extruder_definitions = self.findDefinitionContainers(id = new_extruder_id)
|
||||||
|
@ -545,8 +551,12 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
quality_id = "empty_quality"
|
quality_id = "empty_quality"
|
||||||
extruder_stack.setQualityById(quality_id)
|
extruder_stack.setQualityById(quality_id)
|
||||||
|
|
||||||
if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"):
|
machine_quality_changes = machine.qualityChanges
|
||||||
extruder_quality_changes_container = self.findInstanceContainers(name = machine.qualityChanges.getName(), extruder = extruder_id)
|
if new_global_quality_changes is not None:
|
||||||
|
machine_quality_changes = new_global_quality_changes
|
||||||
|
|
||||||
|
if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"):
|
||||||
|
extruder_quality_changes_container = self.findInstanceContainers(name = machine_quality_changes.getName(), extruder = extruder_id)
|
||||||
if extruder_quality_changes_container:
|
if extruder_quality_changes_container:
|
||||||
extruder_quality_changes_container = extruder_quality_changes_container[0]
|
extruder_quality_changes_container = extruder_quality_changes_container[0]
|
||||||
|
|
||||||
|
@ -556,34 +566,34 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
# Some extruder quality_changes containers can be created at runtime as files in the qualities
|
# Some extruder quality_changes containers can be created at runtime as files in the qualities
|
||||||
# folder. Those files won't be loaded in the registry immediately. So we also need to search
|
# folder. Those files won't be loaded in the registry immediately. So we also need to search
|
||||||
# the folder to see if the quality_changes exists.
|
# the folder to see if the quality_changes exists.
|
||||||
extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName())
|
extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName())
|
||||||
if extruder_quality_changes_container:
|
if extruder_quality_changes_container:
|
||||||
quality_changes_id = extruder_quality_changes_container.getId()
|
quality_changes_id = extruder_quality_changes_container.getId()
|
||||||
extruder_stack.setQualityChangesById(quality_changes_id)
|
extruder_stack.setQualityChangesById(quality_changes_id)
|
||||||
else:
|
else:
|
||||||
# if we still cannot find a quality changes container for the extruder, create a new one
|
# if we still cannot find a quality changes container for the extruder, create a new one
|
||||||
container_name = machine.qualityChanges.getName()
|
container_name = machine_quality_changes.getName()
|
||||||
container_id = self.uniqueName(extruder_stack.getId() + "_qc_" + container_name)
|
container_id = self.uniqueName(extruder_stack.getId() + "_qc_" + container_name)
|
||||||
extruder_quality_changes_container = InstanceContainer(container_id)
|
extruder_quality_changes_container = InstanceContainer(container_id)
|
||||||
extruder_quality_changes_container.setName(container_name)
|
extruder_quality_changes_container.setName(container_name)
|
||||||
extruder_quality_changes_container.addMetaDataEntry("type", "quality_changes")
|
extruder_quality_changes_container.addMetaDataEntry("type", "quality_changes")
|
||||||
extruder_quality_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
extruder_quality_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
|
||||||
extruder_quality_changes_container.addMetaDataEntry("extruder", extruder_stack.definition.getId())
|
extruder_quality_changes_container.addMetaDataEntry("extruder", extruder_stack.definition.getId())
|
||||||
extruder_quality_changes_container.addMetaDataEntry("quality_type", machine.qualityChanges.getMetaDataEntry("quality_type"))
|
extruder_quality_changes_container.addMetaDataEntry("quality_type", machine_quality_changes.getMetaDataEntry("quality_type"))
|
||||||
extruder_quality_changes_container.setDefinition(machine.qualityChanges.getDefinition().getId())
|
extruder_quality_changes_container.setDefinition(machine_quality_changes.getDefinition().getId())
|
||||||
|
|
||||||
self.addContainer(extruder_quality_changes_container)
|
self.addContainer(extruder_quality_changes_container)
|
||||||
extruder_stack.qualityChanges = extruder_quality_changes_container
|
extruder_stack.qualityChanges = extruder_quality_changes_container
|
||||||
|
|
||||||
if not extruder_quality_changes_container:
|
if not extruder_quality_changes_container:
|
||||||
Logger.log("w", "Could not find quality_changes named [%s] for extruder [%s]",
|
Logger.log("w", "Could not find quality_changes named [%s] for extruder [%s]",
|
||||||
machine.qualityChanges.getName(), extruder_stack.getId())
|
machine_quality_changes.getName(), extruder_stack.getId())
|
||||||
else:
|
else:
|
||||||
# move all per-extruder settings to the extruder's quality changes
|
# move all per-extruder settings to the extruder's quality changes
|
||||||
for qc_setting_key in machine.qualityChanges.getAllKeys():
|
for qc_setting_key in machine_quality_changes.getAllKeys():
|
||||||
settable_per_extruder = machine.getProperty(qc_setting_key, "settable_per_extruder")
|
settable_per_extruder = machine.getProperty(qc_setting_key, "settable_per_extruder")
|
||||||
if settable_per_extruder:
|
if settable_per_extruder:
|
||||||
setting_value = machine.qualityChanges.getProperty(qc_setting_key, "value")
|
setting_value = machine_quality_changes.getProperty(qc_setting_key, "value")
|
||||||
|
|
||||||
setting_definition = machine.getSettingDefinition(qc_setting_key)
|
setting_definition = machine.getSettingDefinition(qc_setting_key)
|
||||||
new_instance = SettingInstance(setting_definition, definition_changes)
|
new_instance = SettingInstance(setting_definition, definition_changes)
|
||||||
|
@ -592,7 +602,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
extruder_quality_changes_container.addInstance(new_instance)
|
extruder_quality_changes_container.addInstance(new_instance)
|
||||||
extruder_quality_changes_container.setDirty(True)
|
extruder_quality_changes_container.setDirty(True)
|
||||||
|
|
||||||
machine.qualityChanges.removeInstance(qc_setting_key, postpone_emit=True)
|
machine_quality_changes.removeInstance(qc_setting_key, postpone_emit=True)
|
||||||
else:
|
else:
|
||||||
extruder_stack.setQualityChangesById("empty_quality_changes")
|
extruder_stack.setQualityChangesById("empty_quality_changes")
|
||||||
|
|
||||||
|
@ -600,8 +610,8 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
|
|
||||||
# Also need to fix the other qualities that are suitable for this machine. Those quality changes may still have
|
# Also need to fix the other qualities that are suitable for this machine. Those quality changes may still have
|
||||||
# per-extruder settings in the container for the machine instead of the extruder.
|
# per-extruder settings in the container for the machine instead of the extruder.
|
||||||
if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"):
|
if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"):
|
||||||
quality_changes_machine_definition_id = machine.qualityChanges.getDefinition().getId()
|
quality_changes_machine_definition_id = machine_quality_changes.getDefinition().getId()
|
||||||
else:
|
else:
|
||||||
whole_machine_definition = machine.definition
|
whole_machine_definition = machine.definition
|
||||||
machine_entry = machine.definition.getMetaDataEntry("machine")
|
machine_entry = machine.definition.getMetaDataEntry("machine")
|
||||||
|
@ -621,7 +631,7 @@ class CuraContainerRegistry(ContainerRegistry):
|
||||||
qc_groups[qc_name] = []
|
qc_groups[qc_name] = []
|
||||||
qc_groups[qc_name].append(qc)
|
qc_groups[qc_name].append(qc)
|
||||||
# try to find from the quality changes cura directory too
|
# try to find from the quality changes cura directory too
|
||||||
quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName())
|
quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName())
|
||||||
if quality_changes_container:
|
if quality_changes_container:
|
||||||
qc_groups[qc_name].append(quality_changes_container)
|
qc_groups[qc_name].append(quality_changes_container)
|
||||||
|
|
||||||
|
|
|
@ -502,6 +502,89 @@ class ExtruderManager(QObject):
|
||||||
def getInstanceExtruderValues(self, key):
|
def getInstanceExtruderValues(self, key):
|
||||||
return ExtruderManager.getExtruderValues(key)
|
return ExtruderManager.getExtruderValues(key)
|
||||||
|
|
||||||
|
## Updates the material container to a material that matches the material diameter set for the printer
|
||||||
|
def updateMaterialForDiameter(self, extruder_position: int):
|
||||||
|
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||||
|
if not global_stack:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not global_stack.getMetaDataEntry("has_materials", False):
|
||||||
|
return
|
||||||
|
|
||||||
|
extruder_stack = global_stack.extruders[str(extruder_position)]
|
||||||
|
|
||||||
|
material_diameter = extruder_stack.material.getProperty("material_diameter", "value")
|
||||||
|
if not material_diameter:
|
||||||
|
# in case of "empty" material
|
||||||
|
material_diameter = 0
|
||||||
|
|
||||||
|
material_approximate_diameter = str(round(material_diameter))
|
||||||
|
machine_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value")
|
||||||
|
if not machine_diameter:
|
||||||
|
if extruder_stack.definition.hasProperty("material_diameter", "value"):
|
||||||
|
machine_diameter = extruder_stack.definition.getProperty("material_diameter", "value")
|
||||||
|
else:
|
||||||
|
machine_diameter = global_stack.definition.getProperty("material_diameter", "value")
|
||||||
|
machine_approximate_diameter = str(round(machine_diameter))
|
||||||
|
|
||||||
|
if material_approximate_diameter != machine_approximate_diameter:
|
||||||
|
Logger.log("i", "The the currently active material(s) do not match the diameter set for the printer. Finding alternatives.")
|
||||||
|
|
||||||
|
if global_stack.getMetaDataEntry("has_machine_materials", False):
|
||||||
|
materials_definition = global_stack.definition.getId()
|
||||||
|
has_material_variants = global_stack.getMetaDataEntry("has_variants", False)
|
||||||
|
else:
|
||||||
|
materials_definition = "fdmprinter"
|
||||||
|
has_material_variants = False
|
||||||
|
|
||||||
|
old_material = extruder_stack.material
|
||||||
|
search_criteria = {
|
||||||
|
"type": "material",
|
||||||
|
"approximate_diameter": machine_approximate_diameter,
|
||||||
|
"material": old_material.getMetaDataEntry("material", "value"),
|
||||||
|
"brand": old_material.getMetaDataEntry("brand", "value"),
|
||||||
|
"supplier": old_material.getMetaDataEntry("supplier", "value"),
|
||||||
|
"color_name": old_material.getMetaDataEntry("color_name", "value"),
|
||||||
|
"definition": materials_definition
|
||||||
|
}
|
||||||
|
if has_material_variants:
|
||||||
|
search_criteria["variant"] = extruder_stack.variant.getId()
|
||||||
|
|
||||||
|
container_registry = Application.getInstance().getContainerRegistry()
|
||||||
|
empty_material = container_registry.findInstanceContainers(id = "empty_material")[0]
|
||||||
|
|
||||||
|
if old_material == empty_material:
|
||||||
|
search_criteria.pop("material", None)
|
||||||
|
search_criteria.pop("supplier", None)
|
||||||
|
search_criteria.pop("brand", None)
|
||||||
|
search_criteria.pop("definition", None)
|
||||||
|
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
|
||||||
|
|
||||||
|
materials = container_registry.findInstanceContainers(**search_criteria)
|
||||||
|
if not materials:
|
||||||
|
# Same material with new diameter is not found, search for generic version of the same material type
|
||||||
|
search_criteria.pop("supplier", None)
|
||||||
|
search_criteria.pop("brand", None)
|
||||||
|
search_criteria["color_name"] = "Generic"
|
||||||
|
materials = container_registry.findInstanceContainers(**search_criteria)
|
||||||
|
if not materials:
|
||||||
|
# Generic material with new diameter is not found, search for preferred material
|
||||||
|
search_criteria.pop("color_name", None)
|
||||||
|
search_criteria.pop("material", None)
|
||||||
|
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
|
||||||
|
materials = container_registry.findInstanceContainers(**search_criteria)
|
||||||
|
if not materials:
|
||||||
|
# Preferred material with new diameter is not found, search for any material
|
||||||
|
search_criteria.pop("id", None)
|
||||||
|
materials = container_registry.findInstanceContainers(**search_criteria)
|
||||||
|
if not materials:
|
||||||
|
# Just use empty material as a final fallback
|
||||||
|
materials = [empty_material]
|
||||||
|
|
||||||
|
Logger.log("i", "Selecting new material: %s", materials[0].getId())
|
||||||
|
|
||||||
|
extruder_stack.material = materials[0]
|
||||||
|
|
||||||
## Get the value for a setting from a specific extruder.
|
## Get the value for a setting from a specific extruder.
|
||||||
#
|
#
|
||||||
# This is exposed to SettingFunction to use in value functions.
|
# This is exposed to SettingFunction to use in value functions.
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
from typing import Any, TYPE_CHECKING, Optional
|
from typing import Any, TYPE_CHECKING, Optional
|
||||||
|
|
||||||
|
from UM.Application import Application
|
||||||
from UM.Decorators import override
|
from UM.Decorators import override
|
||||||
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
|
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
|
||||||
from UM.Settings.ContainerStack import ContainerStack
|
from UM.Settings.ContainerStack import ContainerStack
|
||||||
|
@ -59,14 +60,14 @@ class ExtruderStack(CuraContainerStack):
|
||||||
keys_to_copy = ["material_diameter", "machine_nozzle_size"] # these will be copied over to all extruders
|
keys_to_copy = ["material_diameter", "machine_nozzle_size"] # these will be copied over to all extruders
|
||||||
|
|
||||||
for key in keys_to_copy:
|
for key in keys_to_copy:
|
||||||
|
# Since material_diameter is not on the extruder definition, we need to add it here
|
||||||
|
# WARNING: this might be very dangerous and should be refactored ASAP!
|
||||||
|
definition = stack.getSettingDefinition(key)
|
||||||
|
if definition:
|
||||||
|
self.definition.addDefinition(definition)
|
||||||
|
|
||||||
# Only copy the value when this extruder doesn't have the value.
|
# Only copy the value when this extruder doesn't have the value.
|
||||||
if self.definitionChanges.hasProperty(key, "value"):
|
if self.definitionChanges.hasProperty(key, "value"):
|
||||||
# If the first extruder has a value for this setting, we must copy it to the other extruders via the global stack.
|
|
||||||
# Note: this assumes the extruders are loaded in the same order as they are positioned on the machine.
|
|
||||||
if self.getMetaDataEntry("position") == "0":
|
|
||||||
setting_value = self.definitionChanges.getProperty(key, "value")
|
|
||||||
stack.definitionChanges.setProperty(key, "value", setting_value)
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
setting_value = stack.definitionChanges.getProperty(key, "value")
|
setting_value = stack.definitionChanges.getProperty(key, "value")
|
||||||
|
@ -80,6 +81,11 @@ class ExtruderStack(CuraContainerStack):
|
||||||
self.definitionChanges.addInstance(new_instance)
|
self.definitionChanges.addInstance(new_instance)
|
||||||
self.definitionChanges.setDirty(True)
|
self.definitionChanges.setDirty(True)
|
||||||
|
|
||||||
|
# Make sure the material diameter is up to date for the extruder stack.
|
||||||
|
if key == "material_diameter":
|
||||||
|
position = self.getMetaDataEntry("position", "0")
|
||||||
|
Application.getInstance().getExtruderManager().updateMaterialForDiameter(position)
|
||||||
|
|
||||||
# NOTE: We cannot remove the setting from the global stack's definition changes container because for
|
# NOTE: We cannot remove the setting from the global stack's definition changes container because for
|
||||||
# material diameter, it needs to be applied to all extruders, but here we don't know how many extruders
|
# material diameter, it needs to be applied to all extruders, but here we don't know how many extruders
|
||||||
# a machine actually has and how many extruders has already been loaded for that machine, so we have to
|
# a machine actually has and how many extruders has already been loaded for that machine, so we have to
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# Copyright (c) 2017 Ultimaker B.V.
|
# Copyright (c) 2017 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
import time
|
||||||
#Type hinting.
|
#Type hinting.
|
||||||
from typing import Union, List, Dict
|
from typing import Union, List, Dict
|
||||||
|
|
||||||
|
@ -407,15 +408,28 @@ class MachineManager(QObject):
|
||||||
Logger.log("w", "Failed creating a new machine!")
|
Logger.log("w", "Failed creating a new machine!")
|
||||||
|
|
||||||
def _checkStacksHaveErrors(self) -> bool:
|
def _checkStacksHaveErrors(self) -> bool:
|
||||||
|
time_start = time.time()
|
||||||
if self._global_container_stack is None: #No active machine.
|
if self._global_container_stack is None: #No active machine.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self._global_container_stack.hasErrors():
|
if self._global_container_stack.hasErrors():
|
||||||
return True
|
Logger.log("d", "Checking global stack for errors took %0.2f s and we found and error" % (time.time() - time_start))
|
||||||
for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()):
|
|
||||||
if stack.hasErrors():
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# Not a very pretty solution, but the extruder manager doesn't really know how many extruders there are
|
||||||
|
machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
|
||||||
|
extruder_stacks = ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())
|
||||||
|
count = 1 # we start with the global stack
|
||||||
|
for stack in extruder_stacks:
|
||||||
|
md = stack.getMetaData()
|
||||||
|
if "position" in md and int(md["position"]) >= machine_extruder_count:
|
||||||
|
continue
|
||||||
|
count += 1
|
||||||
|
if stack.hasErrors():
|
||||||
|
Logger.log("d", "Checking %s stacks for errors took %.2f s and we found an error in stack [%s]" % (count, time.time() - time_start, str(stack)))
|
||||||
|
return True
|
||||||
|
|
||||||
|
Logger.log("d", "Checking %s stacks for errors took %.2f s" % (count, time.time() - time_start))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
## Remove all instances from the top instanceContainer (effectively removing all user-changed settings)
|
## Remove all instances from the top instanceContainer (effectively removing all user-changed settings)
|
||||||
|
|
|
@ -31,10 +31,42 @@ import zipfile
|
||||||
import io
|
import io
|
||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
|
import threading
|
||||||
|
|
||||||
i18n_catalog = i18nCatalog("cura")
|
i18n_catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# HACK:
|
||||||
|
#
|
||||||
|
# In project loading, when override the existing machine is selected, the stacks and containers that are correctly
|
||||||
|
# active in the system will be overridden at runtime. Because the project loading is done in a different thread than
|
||||||
|
# the Qt thread, something else can kick in the middle of the process. One of them is the rendering. It will access
|
||||||
|
# the current stacks and container, which have not completely been updated yet, so Cura will crash in this case.
|
||||||
|
#
|
||||||
|
# This "@call_on_qt_thread" decorator makes sure that a function will always be called on the Qt thread (blocking).
|
||||||
|
# It is applied to the read() function of project loading so it can be guaranteed that only after the project loading
|
||||||
|
# process is completely done, everything else that needs to occupy the QT thread will be executed.
|
||||||
|
#
|
||||||
|
class InterCallObject:
|
||||||
|
def __init__(self):
|
||||||
|
self.finish_event = threading.Event()
|
||||||
|
self.result = None
|
||||||
|
|
||||||
|
|
||||||
|
def call_on_qt_thread(func):
|
||||||
|
def _call_on_qt_thread_wrapper(*args, **kwargs):
|
||||||
|
def _handle_call(ico, *args, **kwargs):
|
||||||
|
ico.result = func(*args, **kwargs)
|
||||||
|
ico.finish_event.set()
|
||||||
|
inter_call_object = InterCallObject()
|
||||||
|
new_args = tuple([inter_call_object] + list(args)[:])
|
||||||
|
CuraApplication.getInstance().callLater(_handle_call, *new_args, **kwargs)
|
||||||
|
inter_call_object.finish_event.wait()
|
||||||
|
return inter_call_object.result
|
||||||
|
return _call_on_qt_thread_wrapper
|
||||||
|
|
||||||
|
|
||||||
## Base implementation for reading 3MF workspace files.
|
## Base implementation for reading 3MF workspace files.
|
||||||
class ThreeMFWorkspaceReader(WorkspaceReader):
|
class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -401,6 +433,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
# containing global.cfg / extruder.cfg
|
# containing global.cfg / extruder.cfg
|
||||||
#
|
#
|
||||||
# \param file_name
|
# \param file_name
|
||||||
|
@call_on_qt_thread
|
||||||
def read(self, file_name):
|
def read(self, file_name):
|
||||||
archive = zipfile.ZipFile(file_name, "r")
|
archive = zipfile.ZipFile(file_name, "r")
|
||||||
|
|
||||||
|
@ -525,6 +558,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)]
|
instance_container_files = [name for name in cura_file_names if name.endswith(self._instance_container_suffix)]
|
||||||
user_instance_containers = []
|
user_instance_containers = []
|
||||||
quality_and_definition_changes_instance_containers = []
|
quality_and_definition_changes_instance_containers = []
|
||||||
|
quality_changes_instance_containers = []
|
||||||
for instance_container_file in instance_container_files:
|
for instance_container_file in instance_container_files:
|
||||||
container_id = self._stripFileToId(instance_container_file)
|
container_id = self._stripFileToId(instance_container_file)
|
||||||
serialized = archive.open(instance_container_file).read().decode("utf-8")
|
serialized = archive.open(instance_container_file).read().decode("utf-8")
|
||||||
|
@ -630,6 +664,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
# The ID already exists, but nothing in the values changed, so do nothing.
|
# The ID already exists, but nothing in the values changed, so do nothing.
|
||||||
pass
|
pass
|
||||||
quality_and_definition_changes_instance_containers.append(instance_container)
|
quality_and_definition_changes_instance_containers.append(instance_container)
|
||||||
|
if container_type == "quality_changes":
|
||||||
|
quality_changes_instance_containers.append(instance_container)
|
||||||
|
|
||||||
if container_type == "definition_changes":
|
if container_type == "definition_changes":
|
||||||
definition_changes_extruder_count = instance_container.getProperty("machine_extruder_count", "value")
|
definition_changes_extruder_count = instance_container.getProperty("machine_extruder_count", "value")
|
||||||
|
@ -754,14 +790,19 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
# If not extruder stacks were saved in the project file (pre 3.1) create one manually
|
# If not extruder stacks were saved in the project file (pre 3.1) create one manually
|
||||||
# We re-use the container registry's addExtruderStackForSingleExtrusionMachine method for this
|
# We re-use the container registry's addExtruderStackForSingleExtrusionMachine method for this
|
||||||
if not extruder_stacks:
|
if not extruder_stacks:
|
||||||
if self._resolve_strategies["machine"] == "new":
|
# If we choose to override a machine but to create a new custom quality profile, the custom quality
|
||||||
stack = self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder")
|
# profile is not immediately applied to the global_stack, so this fix for single extrusion machines
|
||||||
else:
|
# will use the current custom quality profile on the existing machine. The extra optional argument
|
||||||
stack = global_stack.extruders.get("0")
|
# in that function is used in thia case to specify a new global stack quality_changes container so
|
||||||
if not stack:
|
# the fix can correctly create and copy over the custom quality settings to the newly created extruder.
|
||||||
# this should not happen
|
new_global_quality_changes = None
|
||||||
Logger.log("e", "Cannot find any extruder in an existing global stack [%s].", global_stack.getId())
|
if self._resolve_strategies["quality_changes"] == "new" and len(quality_changes_instance_containers) > 0:
|
||||||
if stack:
|
new_global_quality_changes = quality_changes_instance_containers[0]
|
||||||
|
stack = self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder",
|
||||||
|
new_global_quality_changes)
|
||||||
|
if new_global_quality_changes is not None:
|
||||||
|
quality_changes_instance_containers.append(stack.qualityChanges)
|
||||||
|
quality_and_definition_changes_instance_containers.append(stack.qualityChanges)
|
||||||
if global_stack.quality.getId() in ("empty", "empty_quality"):
|
if global_stack.quality.getId() in ("empty", "empty_quality"):
|
||||||
stack.quality = empty_quality_container
|
stack.quality = empty_quality_container
|
||||||
if self._resolve_strategies["machine"] == "override":
|
if self._resolve_strategies["machine"] == "override":
|
||||||
|
@ -870,7 +911,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
# We will first find the correct quality profile for the extruder, then apply the same
|
# We will first find the correct quality profile for the extruder, then apply the same
|
||||||
# quality profile for the global stack.
|
# quality profile for the global stack.
|
||||||
#
|
#
|
||||||
if len(extruder_stacks) == 1:
|
if has_extruder_stack_files and len(extruder_stacks) == 1:
|
||||||
extruder_stack = extruder_stacks[0]
|
extruder_stack = extruder_stacks[0]
|
||||||
|
|
||||||
search_criteria = {"type": "quality", "quality_type": global_stack.quality.getMetaDataEntry("quality_type")}
|
search_criteria = {"type": "quality", "quality_type": global_stack.quality.getMetaDataEntry("quality_type")}
|
||||||
|
@ -1002,7 +1043,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
|
||||||
stack.setNextStack(global_stack)
|
stack.setNextStack(global_stack)
|
||||||
stack.containersChanged.emit(stack.getTop())
|
stack.containersChanged.emit(stack.getTop())
|
||||||
else:
|
else:
|
||||||
if quality_has_been_changed:
|
|
||||||
CuraApplication.getInstance().getMachineManager().activeQualityChanged.emit()
|
CuraApplication.getInstance().getMachineManager().activeQualityChanged.emit()
|
||||||
|
|
||||||
# Actually change the active machine.
|
# Actually change the active machine.
|
||||||
|
|
|
@ -158,79 +158,4 @@ class MachineSettingsAction(MachineAction):
|
||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
def updateMaterialForDiameter(self, extruder_position: int):
|
def updateMaterialForDiameter(self, extruder_position: int):
|
||||||
# Updates the material container to a material that matches the material diameter set for the printer
|
# Updates the material container to a material that matches the material diameter set for the printer
|
||||||
if not self._global_container_stack:
|
Application.getInstance().getExtruderManager().updateMaterialForDiameter(extruder_position)
|
||||||
return
|
|
||||||
|
|
||||||
if not self._global_container_stack.getMetaDataEntry("has_materials", False):
|
|
||||||
return
|
|
||||||
|
|
||||||
extruder_stack = self._global_container_stack.extruders[str(extruder_position)]
|
|
||||||
|
|
||||||
material_diameter = extruder_stack.material.getProperty("material_diameter", "value")
|
|
||||||
if not material_diameter:
|
|
||||||
# in case of "empty" material
|
|
||||||
material_diameter = 0
|
|
||||||
|
|
||||||
material_approximate_diameter = str(round(material_diameter))
|
|
||||||
machine_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value")
|
|
||||||
if not machine_diameter:
|
|
||||||
if extruder_stack.definition.hasProperty("material_diameter", "value"):
|
|
||||||
machine_diameter = extruder_stack.definition.getProperty("material_diameter", "value")
|
|
||||||
else:
|
|
||||||
machine_diameter = self._global_container_stack.definition.getProperty("material_diameter", "value")
|
|
||||||
machine_approximate_diameter = str(round(machine_diameter))
|
|
||||||
|
|
||||||
if material_approximate_diameter != machine_approximate_diameter:
|
|
||||||
Logger.log("i", "The the currently active material(s) do not match the diameter set for the printer. Finding alternatives.")
|
|
||||||
|
|
||||||
if self._global_container_stack.getMetaDataEntry("has_machine_materials", False):
|
|
||||||
materials_definition = self._global_container_stack.definition.getId()
|
|
||||||
has_material_variants = self._global_container_stack.getMetaDataEntry("has_variants", False)
|
|
||||||
else:
|
|
||||||
materials_definition = "fdmprinter"
|
|
||||||
has_material_variants = False
|
|
||||||
|
|
||||||
old_material = extruder_stack.material
|
|
||||||
search_criteria = {
|
|
||||||
"type": "material",
|
|
||||||
"approximate_diameter": machine_approximate_diameter,
|
|
||||||
"material": old_material.getMetaDataEntry("material", "value"),
|
|
||||||
"brand": old_material.getMetaDataEntry("brand", "value"),
|
|
||||||
"supplier": old_material.getMetaDataEntry("supplier", "value"),
|
|
||||||
"color_name": old_material.getMetaDataEntry("color_name", "value"),
|
|
||||||
"definition": materials_definition
|
|
||||||
}
|
|
||||||
if has_material_variants:
|
|
||||||
search_criteria["variant"] = extruder_stack.variant.getId()
|
|
||||||
|
|
||||||
if old_material == self._empty_container:
|
|
||||||
search_criteria.pop("material", None)
|
|
||||||
search_criteria.pop("supplier", None)
|
|
||||||
search_criteria.pop("brand", None)
|
|
||||||
search_criteria.pop("definition", None)
|
|
||||||
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
|
|
||||||
|
|
||||||
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
|
||||||
if not materials:
|
|
||||||
# Same material with new diameter is not found, search for generic version of the same material type
|
|
||||||
search_criteria.pop("supplier", None)
|
|
||||||
search_criteria.pop("brand", None)
|
|
||||||
search_criteria["color_name"] = "Generic"
|
|
||||||
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
|
||||||
if not materials:
|
|
||||||
# Generic material with new diameter is not found, search for preferred material
|
|
||||||
search_criteria.pop("color_name", None)
|
|
||||||
search_criteria.pop("material", None)
|
|
||||||
search_criteria["id"] = extruder_stack.getMetaDataEntry("preferred_material")
|
|
||||||
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
|
||||||
if not materials:
|
|
||||||
# Preferred material with new diameter is not found, search for any material
|
|
||||||
search_criteria.pop("id", None)
|
|
||||||
materials = self._container_registry.findInstanceContainers(**search_criteria)
|
|
||||||
if not materials:
|
|
||||||
# Just use empty material as a final fallback
|
|
||||||
materials = [self._empty_container]
|
|
||||||
|
|
||||||
Logger.log("i", "Selecting new material: %s", materials[0].getId())
|
|
||||||
|
|
||||||
extruder_stack.material = materials[0]
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue