Merge branch 'master' of github.com:Ultimaker/Cura

This commit is contained in:
Jack Ha 2018-01-23 14:25:05 +01:00
commit e561217415
14 changed files with 266 additions and 143 deletions

4
Jenkinsfile vendored
View File

@ -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
stage('Prepare') {
// Ensure we start with a clean build directory.

View File

@ -1,9 +1,8 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.Settings.ExtruderManager import ExtruderManager
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.i18n import i18nCatalog
from UM.Scene.Platform import Platform
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
@ -195,29 +194,51 @@ class BuildVolume(SceneNode):
return True
## For every sliceable node, update outsideBuildArea
def updateNodeBoundaryCheck(self):
root = Application.getInstance().getController().getScene().getRoot()
nodes = list(BreadthFirstIterator(root))
group_nodes = []
## For every sliceable node, update node._outside_buildarea.
def updateAllBoundaryChecks(self):
self.updateNodeBoundaryCheck(Application.getInstance().getController().getScene().getRoot())
build_volume_bounding_box = self.getBoundingBox()
if build_volume_bounding_box:
# It's over 9000!
build_volume_bounding_box = build_volume_bounding_box.set(bottom=-9001)
else:
# No bounding box. This is triggered when running Cura from command line with a model for the first time
# In that situation there is a model, but no machine (and therefore no build volume.
## For a single node, update _outside_buildarea.
#
# If the node is a group node, the child nodes will also get updated.
# \param node The node to update the boundary checks of.
def updateNodeBoundaryCheck(self, node: SceneNode):
if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"):
for child in node.getChildren(): #Still update the children! For instance, the root is not sliceable.
self.updateNodeBoundaryCheck(child)
return #Don't compute for non-sliceable nodes.
#Mark the node as outside the build volume if the bounding box test fails.
build_volume = self.getBoundingBox()
if build_volume is None:
#No bounding box. This is triggered when running Cura from command line with a model for the first time.
#In that situation there is a model, but no machine (and therefore no build volume).
return
build_volume = build_volume.set(bottom = -999999) #Allow models to clip the build plate. This should allow printing but remove the bottom side of the model underneath the build plate.
bounding_box = node.getBoundingBox()
if build_volume.intersectsBox(bounding_box) != AxisAlignedBox.IntersectionResult.FullIntersection:
node._outside_buildarea = True
else:
for node in nodes:
# Need to check group nodes later
self.checkBoundsAndUpdate(node, bounds = build_volume_bounding_box)
#Check for collisions between disallowed areas and the object.
convex_hull = node.callDecoration("getConvexHull")
if not convex_hull or not convex_hull.isValid():
return
for area in self.getDisallowedAreas():
overlap = convex_hull.intersectsPolygon(area)
if overlap is not None:
node._outside_buildarea = True
break
else:
node._outside_buildarea = False
# Group nodes should override the _outside_buildarea property of their children.
for group_node in group_nodes:
for child_node in group_node.getAllChildren():
child_node._outside_buildarea = group_node._outside_buildarea
#Group nodes should override the _outside_buildarea property of their children.
if node.callDecoration("isGroup"):
for child in node.getAllChildren():
child._outside_buildarea = node._outside_buildarea
else:
for child in node.getChildren():
self.updateNodeBoundaryCheck(child)
## Update the outsideBuildArea of a single node, given bounds or current build volume
def checkBoundsAndUpdate(self, node: CuraSceneNode, bounds: Optional[AxisAlignedBox] = None):
@ -440,7 +461,7 @@ class BuildVolume(SceneNode):
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
self.updateNodeBoundaryCheck()
self.updateAllBoundaryChecks()
def getBoundingBox(self) -> AxisAlignedBox:
return self._volume_aabb

View File

@ -94,6 +94,10 @@ class CuraActions(QObject):
removed_group_nodes.append(group_node)
op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent()))
op.addOperation(RemoveSceneNodeOperation(group_node))
# Reset the print information
Application.getInstance().getController().getScene().sceneChanged.emit(node)
op.push()
## Set the extruder that should be used to print the selection.

View File

@ -1089,12 +1089,12 @@ class CuraApplication(QtApplication):
for node in nodes:
op.addOperation(RemoveSceneNodeOperation(node))
# Reset the print information
self.getController().getScene().sceneChanged.emit(node)
op.push()
Selection.clear()
# Reset the print information:
self.getController().getScene().sceneChanged.emit(node)
## Reset all translation on nodes with mesh data.
@pyqtSlot()
def resetAllTranslation(self):

View File

@ -1,4 +1,4 @@
# Copyright (c) 2015 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import QTimer
@ -56,14 +56,17 @@ class PlatformPhysics:
# By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve.
nodes = list(BreadthFirstIterator(root))
# Only check nodes inside build area.
nodes = [node for node in nodes if (hasattr(node, "_outside_buildarea") and not node._outside_buildarea)]
random.shuffle(nodes)
for node in nodes:
if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None:
continue
#Only check nodes inside the build area.
if not hasattr(node, "_outside_buildarea"):
self._build_volume.updateNodeBoundaryCheck(node)
if getattr(node, "_outside_buildarea", True):
continue
bbox = node.getBoundingBox()
# Move it downwards if bottom is above platform
@ -155,7 +158,7 @@ class PlatformPhysics:
# After moving, we have to evaluate the boundary checks for nodes
build_volume = Application.getInstance().getBuildVolume()
build_volume.updateNodeBoundaryCheck()
build_volume.updateAllBoundaryChecks()
def _onToolOperationStarted(self, tool):
self._enabled = False

View File

@ -8,7 +8,9 @@ from UM.Application import Application
from UM.Logger import Logger
from UM.Qt.Duration import Duration
from UM.Preferences import Preferences
from UM.Scene.SceneNode import SceneNode
from UM.Settings.ContainerRegistry import ContainerRegistry
from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.Settings.ExtruderManager import ExtruderManager
from typing import Dict
@ -65,7 +67,7 @@ class PrintInformation(QObject):
self._backend = Application.getInstance().getBackend()
if self._backend:
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._abbr_machine = ""
@ -395,12 +397,25 @@ class PrintInformation(QObject):
return result
# Simulate message with zero time duration
def setToZeroPrintInformation(self, build_plate_number):
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
def setToZeroPrintInformation(self, build_plate):
# 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]
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)

View File

@ -1,6 +1,7 @@
# 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 UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Scene.SceneNode import SceneNode
from copy import deepcopy

View File

@ -449,7 +449,13 @@ class CuraContainerRegistry(ContainerRegistry):
if not extruder_stacks:
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
extruder_definitions = self.findDefinitionContainers(id = new_extruder_id)
@ -545,8 +551,12 @@ class CuraContainerRegistry(ContainerRegistry):
quality_id = "empty_quality"
extruder_stack.setQualityById(quality_id)
if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"):
extruder_quality_changes_container = self.findInstanceContainers(name = machine.qualityChanges.getName(), extruder = extruder_id)
machine_quality_changes = machine.qualityChanges
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:
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
# 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.
extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName())
extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName())
if extruder_quality_changes_container:
quality_changes_id = extruder_quality_changes_container.getId()
extruder_stack.setQualityChangesById(quality_changes_id)
else:
# 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)
extruder_quality_changes_container = InstanceContainer(container_id)
extruder_quality_changes_container.setName(container_name)
extruder_quality_changes_container.addMetaDataEntry("type", "quality_changes")
extruder_quality_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
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.setDefinition(machine.qualityChanges.getDefinition().getId())
extruder_quality_changes_container.addMetaDataEntry("quality_type", machine_quality_changes.getMetaDataEntry("quality_type"))
extruder_quality_changes_container.setDefinition(machine_quality_changes.getDefinition().getId())
self.addContainer(extruder_quality_changes_container)
extruder_stack.qualityChanges = extruder_quality_changes_container
if not extruder_quality_changes_container:
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:
# 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")
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)
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.setDirty(True)
machine.qualityChanges.removeInstance(qc_setting_key, postpone_emit=True)
machine_quality_changes.removeInstance(qc_setting_key, postpone_emit=True)
else:
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
# per-extruder settings in the container for the machine instead of the extruder.
if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"):
quality_changes_machine_definition_id = machine.qualityChanges.getDefinition().getId()
if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"):
quality_changes_machine_definition_id = machine_quality_changes.getDefinition().getId()
else:
whole_machine_definition = machine.definition
machine_entry = machine.definition.getMetaDataEntry("machine")
@ -621,7 +631,7 @@ class CuraContainerRegistry(ContainerRegistry):
qc_groups[qc_name] = []
qc_groups[qc_name].append(qc)
# 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:
qc_groups[qc_name].append(quality_changes_container)

View File

@ -502,6 +502,89 @@ class ExtruderManager(QObject):
def getInstanceExtruderValues(self, 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.
#
# This is exposed to SettingFunction to use in value functions.

View File

@ -3,6 +3,7 @@
from typing import Any, TYPE_CHECKING, Optional
from UM.Application import Application
from UM.Decorators import override
from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase
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
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.
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
setting_value = stack.definitionChanges.getProperty(key, "value")
@ -80,6 +81,11 @@ class ExtruderStack(CuraContainerStack):
self.definitionChanges.addInstance(new_instance)
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
# 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

View File

@ -1,6 +1,7 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import time
#Type hinting.
from typing import Union, List, Dict
@ -407,21 +408,28 @@ class MachineManager(QObject):
Logger.log("w", "Failed creating a new machine!")
def _checkStacksHaveErrors(self) -> bool:
time_start = time.time()
if self._global_container_stack is None: #No active machine.
return False
if self._global_container_stack.hasErrors():
Logger.log("d", "Checking global stack for errors took %0.2f s and we found and error" % (time.time() - time_start))
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())
if len(extruder_stacks) > machine_extruder_count:
extruder_stacks = extruder_stacks[:machine_extruder_count] # we only have to check the used extruders
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
## Remove all instances from the top instanceContainer (effectively removing all user-changed settings)

View File

@ -31,10 +31,42 @@ import zipfile
import io
import configparser
import os
import threading
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.
class ThreeMFWorkspaceReader(WorkspaceReader):
def __init__(self):
@ -401,6 +433,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# containing global.cfg / extruder.cfg
#
# \param file_name
@call_on_qt_thread
def read(self, file_name):
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)]
user_instance_containers = []
quality_and_definition_changes_instance_containers = []
quality_changes_instance_containers = []
for instance_container_file in instance_container_files:
container_id = self._stripFileToId(instance_container_file)
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.
pass
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":
definition_changes_extruder_count = instance_container.getProperty("machine_extruder_count", "value")
@ -754,7 +790,19 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# 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
if not extruder_stacks:
stack = self._container_registry.addExtruderStackForSingleExtrusionMachine(global_stack, "fdmextruder")
# If we choose to override a machine but to create a new custom quality profile, the custom quality
# profile is not immediately applied to the global_stack, so this fix for single extrusion machines
# will use the current custom quality profile on the existing machine. The extra optional argument
# in that function is used in thia case to specify a new global stack quality_changes container so
# the fix can correctly create and copy over the custom quality settings to the newly created extruder.
new_global_quality_changes = None
if self._resolve_strategies["quality_changes"] == "new" and len(quality_changes_instance_containers) > 0:
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"):
stack.quality = empty_quality_container
if self._resolve_strategies["machine"] == "override":
@ -995,8 +1043,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
stack.setNextStack(global_stack)
stack.containersChanged.emit(stack.getTop())
else:
if quality_has_been_changed:
CuraApplication.getInstance().getMachineManager().activeQualityChanged.emit()
CuraApplication.getInstance().getMachineManager().activeQualityChanged.emit()
# Actually change the active machine.
Application.getInstance().setGlobalContainerStack(global_stack)

View File

@ -158,79 +158,4 @@ class MachineSettingsAction(MachineAction):
@pyqtSlot(int)
def updateMaterialForDiameter(self, extruder_position: int):
# Updates the material container to a material that matches the material diameter set for the printer
if not self._global_container_stack:
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]
Application.getInstance().getExtruderManager().updateMaterialForDiameter(extruder_position)

View File

@ -5339,7 +5339,7 @@
},
"infill_enable_travel_optimization":
{
"label": "Enable Travel Optimization",
"label": "Infill Travel Optimization",
"description": "When enabled, the order in which the infill lines are printed is optimized to reduce the distance travelled. The reduction in travel time achieved very much depends on the model being sliced, infill pattern, density, etc. Note that, for some models that have many small areas of infill, the time to slice the model may be greatly increased.",
"type": "bool",
"default_value": false,