Merge remote-tracking branch 'origin/3.2'

This commit is contained in:
Lipu Fei 2018-01-23 11:20:49 +01:00
commit b14fdb1d0d
10 changed files with 228 additions and 131 deletions

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,25 +790,30 @@ 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:
if self._resolve_strategies["machine"] == "new":
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":
# in case the extruder is newly created (for a single-extrusion machine), we need to override
# the existing extruder stack.
existing_extruder_stack = global_stack.extruders[stack.getMetaDataEntry("position")]
for idx in range(len(_ContainerIndexes.IndexTypeMap)):
existing_extruder_stack.replaceContainer(idx, stack._containers[idx], postpone_emit = True)
extruder_stacks.append(existing_extruder_stack)
else:
stack = global_stack.extruders.get("0")
if not stack:
# this should not happen
Logger.log("e", "Cannot find any extruder in an existing global stack [%s].", global_stack.getId())
if stack:
if global_stack.quality.getId() in ("empty", "empty_quality"):
stack.quality = empty_quality_container
if self._resolve_strategies["machine"] == "override":
# in case the extruder is newly created (for a single-extrusion machine), we need to override
# the existing extruder stack.
existing_extruder_stack = global_stack.extruders[stack.getMetaDataEntry("position")]
for idx in range(len(_ContainerIndexes.IndexTypeMap)):
existing_extruder_stack.replaceContainer(idx, stack._containers[idx], postpone_emit = True)
extruder_stacks.append(existing_extruder_stack)
else:
extruder_stacks.append(stack)
extruder_stacks.append(stack)
except:
Logger.logException("w", "We failed to serialize the stack. Trying to clean up.")
@ -870,7 +911,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# We will first find the correct quality profile for the extruder, then apply the same
# 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]
search_criteria = {"type": "quality", "quality_type": global_stack.quality.getMetaDataEntry("quality_type")}
@ -1002,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)