CURA-4400 fixed merge conflicts

This commit is contained in:
Jack Ha 2018-03-05 16:47:40 +01:00
commit bad637eb14
121 changed files with 1157 additions and 936 deletions

View file

@ -1,7 +1,6 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import copy
import os.path
import urllib.parse
import uuid
@ -29,10 +28,10 @@ from UM.i18n import i18nCatalog
from cura.Settings.ExtruderManager import ExtruderManager
from cura.Settings.ExtruderStack import ExtruderStack
from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch
catalog = i18nCatalog("cura")
## Manager class that contains common actions to deal with containers in Cura.
#
# This is primarily intended as a class to be able to perform certain actions
@ -160,17 +159,6 @@ class ContainerManager(QObject):
return container.getProperty(setting_key, property_name)
## Set the name of the specified material.
@pyqtSlot("QVariant", str)
def setMaterialName(self, material_node, new_name):
root_material_id = material_node.metadata["base_file"]
if self._container_registry.isReadOnly(root_material_id):
Logger.log("w", "Cannot set name of read-only container %s.", root_material_id)
return
material_group = self._material_manager.getMaterialGroup(root_material_id)
material_group.root_material_node.getContainer().setName(new_name)
@pyqtSlot(str, result = str)
def makeUniqueName(self, original_name):
return self._container_registry.uniqueName(original_name)
@ -355,191 +343,6 @@ class ContainerManager(QObject):
for container in send_emits_containers:
container.sendPostponedEmits()
## Create quality changes containers from the user containers in the active stacks.
#
# This will go through the global and extruder stacks and create quality_changes containers from
# the user containers in each stack. These then replace the quality_changes containers in the
# stack and clear the user settings.
#
# \return \type{bool} True if the operation was successfully, False if not.
@pyqtSlot(str)
def createQualityChanges(self, base_name):
global_stack = Application.getInstance().getGlobalContainerStack()
if not global_stack:
return
active_quality_name = self._machine_manager.activeQualityOrQualityChangesName
if active_quality_name == "":
Logger.log("w", "No quality container found in stack %s, cannot create profile", global_stack.getId())
return
self._machine_manager.blurSettings.emit()
if base_name is None or base_name == "":
base_name = active_quality_name
unique_name = self._container_registry.uniqueName(base_name)
# Go through the active stacks and create quality_changes containers from the user containers.
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
user_container = stack.userChanges
quality_container = stack.quality
quality_changes_container = stack.qualityChanges
if not quality_container or not quality_changes_container:
Logger.log("w", "No quality or quality changes container found in stack %s, ignoring it", stack.getId())
continue
extruder_definition_id = None
if isinstance(stack, ExtruderStack):
extruder_definition_id = stack.definition.getId()
quality_type = quality_container.getMetaDataEntry("quality_type")
new_changes = self._createQualityChanges(quality_type, unique_name, global_stack, extruder_definition_id)
self._performMerge(new_changes, quality_changes_container, clear_settings = False)
self._performMerge(new_changes, user_container)
self._container_registry.addContainer(new_changes)
#
# Remove the given quality changes group
#
@pyqtSlot(QObject)
def removeQualityChangesGroup(self, quality_changes_group):
Logger.log("i", "Removing quality changes group [%s]", quality_changes_group.name)
for node in quality_changes_group.getAllNodes():
self._container_registry.removeContainer(node.metadata["id"])
#
# Rename a set of quality changes containers. Returns the new name.
#
@pyqtSlot(QObject, str, result = str)
def renameQualityChangesGroup(self, quality_changes_group, new_name) -> str:
Logger.log("i", "Renaming QualityChangesGroup[%s] to [%s]", quality_changes_group.name, new_name)
self._machine_manager.blurSettings.emit()
if new_name == quality_changes_group.name:
Logger.log("i", "QualityChangesGroup name [%s] unchanged.", quality_changes_group.name)
return new_name
new_name = self._container_registry.uniqueName(new_name)
for node in quality_changes_group.getAllNodes():
node.getContainer().setName(new_name)
self._machine_manager.activeQualityChanged.emit()
self._machine_manager.activeQualityGroupChanged.emit()
return new_name
@pyqtSlot(str, "QVariantMap")
def duplicateQualityChanges(self, quality_changes_name, quality_model_item):
global_stack = Application.getInstance().getGlobalContainerStack()
quality_group = quality_model_item["quality_group"]
quality_changes_group = quality_model_item["quality_changes_group"]
if quality_changes_group is None:
# create global quality changes only
new_quality_changes = self._createQualityChanges(quality_group.quality_type, quality_changes_name,
global_stack, extruder_id = None)
self._container_registry.addContainer(new_quality_changes)
else:
new_name = self._container_registry.uniqueName(quality_changes_name)
for node in quality_changes_group.getAllNodes():
container = node.getContainer()
new_id = self._container_registry.uniqueName(container.getId())
self._container_registry.addContainer(container.duplicate(new_id, new_name))
@pyqtSlot("QVariant")
def removeMaterial(self, material_node):
root_material_id = material_node.metadata["base_file"]
material_group = self._material_manager.getMaterialGroup(root_material_id)
if not material_group:
Logger.log("d", "Unable to remove the material with id %s, because it doesn't exist.", root_material_id)
return
nodes_to_remove = [material_group.root_material_node] + material_group.derived_material_node_list
for node in nodes_to_remove:
self._container_registry.removeContainer(node.metadata["id"])
## Create a duplicate of a material, which has the same GUID and base_file metadata
#
# \return \type{str} the id of the newly created container.
@pyqtSlot("QVariant", result = str)
def duplicateMaterial(self, material_node, new_base_id = None, new_metadata = None):
root_material_id = material_node.metadata["base_file"]
material_group = self._material_manager.getMaterialGroup(root_material_id)
if not material_group:
Logger.log("d", "Unable to duplicate the material with id %s, because it doesn't exist.", root_material_id)
return
base_container = material_group.root_material_node.getContainer()
containers_to_copy = []
for node in material_group.derived_material_node_list:
containers_to_copy.append(node.getContainer())
# Ensure all settings are saved.
Application.getInstance().saveSettings()
# Create a new ID & container to hold the data.
new_containers = []
if new_base_id is None:
new_base_id = self._container_registry.uniqueName(base_container.getId())
new_base_container = copy.deepcopy(base_container)
new_base_container.getMetaData()["id"] = new_base_id
new_base_container.getMetaData()["base_file"] = new_base_id
if new_metadata is not None:
for key, value in new_metadata.items():
new_base_container.getMetaData()[key] = value
new_containers.append(new_base_container)
# Clone all of them.
for container_to_copy in containers_to_copy:
# Create unique IDs for every clone.
new_id = new_base_id
if container_to_copy.getMetaDataEntry("definition") != "fdmprinter":
new_id += "_" + container_to_copy.getMetaDataEntry("definition")
if container_to_copy.getMetaDataEntry("variant_name"):
variant_name = container_to_copy.getMetaDataEntry("variant_name")
new_id += "_" + variant_name.replace(" ", "_")
new_container = copy.deepcopy(container_to_copy)
new_container.getMetaData()["id"] = new_id
new_container.getMetaData()["base_file"] = new_base_id
if new_metadata is not None:
for key, value in new_metadata.items():
new_container.getMetaData()[key] = value
new_containers.append(new_container)
for container_to_add in new_containers:
container_to_add.setDirty(True)
ContainerRegistry.getInstance().addContainer(container_to_add)
return new_base_id
## Create a new material by cloning Generic PLA for the current material diameter and setting the GUID to something unqiue
#
# \return \type{str} the id of the newly created container.
@pyqtSlot(result = str)
def createMaterial(self):
# Ensure all settings are saved.
Application.getInstance().saveSettings()
global_stack = Application.getInstance().getGlobalContainerStack()
approximate_diameter = str(round(global_stack.getProperty("material_diameter", "value")))
root_material_id = "generic_pla"
root_material_id = self._material_manager.getRootMaterialIDForDiameter(root_material_id, approximate_diameter)
material_group = self._material_manager.getMaterialGroup(root_material_id)
# Create a new ID & container to hold the data.
new_id = self._container_registry.uniqueName("custom_material")
new_metadata = {"name": catalog.i18nc("@label", "Custom Material"),
"brand": catalog.i18nc("@label", "Custom"),
"GUID": str(uuid.uuid4()),
}
self.duplicateMaterial(material_group.root_material_node,
new_base_id = new_id,
new_metadata = new_metadata)
return new_id
## Get a list of materials that have the same GUID as the reference material
#
# \param material_id \type{str} the id of the material for which to get the linked materials.
@ -643,51 +446,6 @@ class ContainerManager(QObject):
name_filter = "{0} ({1})".format(mime_type.comment, suffix_list)
self._container_name_filters[name_filter] = entry
## Creates a unique ID for a container by prefixing the name with the stack ID.
#
# This method creates a unique ID for a container by prefixing it with a specified stack ID.
# This is done to ensure we have an easily identified ID for quality changes, which have the
# same name across several stacks.
#
# \param stack_id The ID of the stack to prepend.
# \param container_name The name of the container that we are creating a unique ID for.
#
# \return Container name prefixed with stack ID, in lower case with spaces replaced by underscores.
def _createUniqueId(self, stack_id, container_name):
result = stack_id + "_" + container_name
result = result.lower()
result.replace(" ", "_")
return result
## Create a quality changes container for a specified quality container.
#
# \param quality_container The quality container to create a changes container for.
# \param new_name The name of the new quality_changes container.
# \param machine_definition The machine definition this quality changes container is specific to.
# \param extruder_id
#
# \return A new quality_changes container with the specified container as base.
def _createQualityChanges(self, quality_type, new_name, machine, extruder_id):
base_id = machine.definition.getId() if extruder_id is None else extruder_id
# Create a new quality_changes container for the quality.
quality_changes = InstanceContainer(self._createUniqueId(base_id, new_name))
quality_changes.setName(new_name)
quality_changes.addMetaDataEntry("type", "quality_changes")
quality_changes.addMetaDataEntry("quality_type", quality_type)
# If we are creating a container for an extruder, ensure we add that to the container
if extruder_id is not None:
quality_changes.addMetaDataEntry("extruder", extruder_id)
# If the machine specifies qualities should be filtered, ensure we match the current criteria.
machine_definition_id = getMachineDefinitionIDForQualitySearch(machine)
quality_changes.setDefinition(machine_definition_id)
from cura.CuraApplication import CuraApplication
quality_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
return quality_changes
## Import single profile, file_url does not have to end with curaprofile
@pyqtSlot(QUrl, result="QVariantMap")
def importProfile(self, file_url):

View file

@ -41,29 +41,17 @@ class CuraStackBuilder:
# get variant container for the global stack
global_variant_container = application.empty_variant_container
if parseBool(machine_definition.getMetaDataEntry("has_variant_buildplates", False)):
global_variant_name = machine_definition.getMetaDataEntry("preferred_variant_buildplate_name")
if global_variant_name:
variant_node = variant_manager.getVariantNode(definition_id, global_variant_name,
variant_type = VariantType.BUILD_PLATE)
if variant_node is None:
raise RuntimeError("Cannot find buildplate variant with definition [%s] and variant name [%s]" %
(definition_id, global_variant_name))
global_variant_container = variant_node.getContainer()
global_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.BUILD_PLATE)
if global_variant_node:
global_variant_container = global_variant_node.getContainer()
# get variant container for extruders
extruder_variant_container = application.empty_variant_container
# Only look for the preferred variant if this machine has variants
extruder_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.NOZZLE)
extruder_variant_name = None
if parseBool(machine_definition.getMetaDataEntry("has_variants", False)):
extruder_variant_name = machine_definition.getMetaDataEntry("preferred_variant_name")
if extruder_variant_name:
variant_node = variant_manager.getVariantNode(definition_id, extruder_variant_name)
# Sanity check. If you see this error, the related definition files should be fixed.
if variant_node is None:
raise RuntimeError("Cannot find extruder variant with definition [%s] and variant name [%s]" %
(definition_id, extruder_variant_name))
extruder_variant_container = variant_node.getContainer()
if extruder_variant_node:
extruder_variant_container = extruder_variant_node.getContainer()
extruder_variant_name = extruder_variant_container.getName()
generated_name = registry.createUniqueName("machine", "", name, machine_definition.getName())
# Make sure the new name does not collide with any definition or (quality) profile
@ -83,19 +71,8 @@ class CuraStackBuilder:
# get material container for extruders
material_container = application.empty_material_container
# Only look for the preferred material if this machine has materials
if parseBool(machine_definition.getMetaDataEntry("has_materials", False)):
material_diameter = machine_definition.getProperty("material_diameter", "value")
if isinstance(material_diameter, SettingFunction):
material_diameter = material_diameter(new_global_stack)
approximate_material_diameter = str(round(material_diameter))
root_material_id = machine_definition.getMetaDataEntry("preferred_material")
root_material_id = material_manager.getRootMaterialIDForDiameter(root_material_id, approximate_material_diameter)
material_node = material_manager.getMaterialNode(definition_id, extruder_variant_name, material_diameter, root_material_id)
# Sanity check. If you see this error, the related definition files should be fixed.
if not material_node:
raise RuntimeError("Cannot find material with definition [%s], extruder_variant_name [%s], and root_material_id [%s]" %
(definition_id, extruder_variant_name, root_material_id))
material_node = material_manager.getDefaultMaterial(new_global_stack, extruder_variant_name)
if material_node:
material_container = material_node.getContainer()
# Create ExtruderStacks

View file

@ -414,6 +414,33 @@ class ExtruderManager(QObject):
self.extrudersChanged.emit(global_stack_id)
self.setActiveExtruderIndex(0)
#
# This function tries to fix the problem with per-extruder-settable nozzle size and material diameter problems
# in early versions (3.0 - 3.2.1).
#
# In earlier versions, "nozzle size" and "material diameter" are only applicable to the complete machine, so all
# extruders share the same values. In this case, "nozzle size" and "material diameter" are saved in the
# GlobalStack's DefinitionChanges container.
#
# Later, we could have different "nozzle size" for each extruder, but "material diameter" could only be set for
# the entire machine. In this case, "nozzle size" should be saved in each ExtruderStack's DefinitionChanges, but
# "material diameter" still remains in the GlobalStack's DefinitionChanges.
#
# Lateer, both "nozzle size" and "material diameter" are settable per-extruder, and both settings should be saved
# in the ExtruderStack's DefinitionChanges.
#
# There were some bugs in upgrade so the data weren't saved correct as described above. This function tries fix
# this.
#
# One more thing is about material diameter and single-extrusion machines. Most single-extrusion machines don't
# specifically define their extruder definition, so they reuse "fdmextruder", but for those machines, they may
# define "material diameter = 1.75" in their machine definition, but in "fdmextruder", it's still "2.85". This
# causes a problem with incorrect default values.
#
# This is also fixed here in this way: If no "material diameter" is specified, it will look for the default value
# in both the Extruder's definition and the Global's definition. If 2 values don't match, we will use the value
# from the Global definition by setting it in the Extruder's DefinitionChanges container.
#
def _fixMaterialDiameterAndNozzleSize(self, global_stack, extruder_stack_list):
keys_to_copy = ["material_diameter", "machine_nozzle_size"] # these will be copied over to all extruders
@ -424,22 +451,6 @@ class ExtruderManager(QObject):
if extruder_stack.definitionChanges.hasProperty(key, "value"):
continue
#
# We cannot add a setting definition of "material_diameter" into the extruder's definition at runtime
# because all other machines which uses "fdmextruder" as the extruder definition will be affected.
#
# The problem is that single extrusion machines have their default material diameter defined in the global
# definitions. Now we automatically create an extruder stack for those machines using "fdmextruder"
# definition, which doesn't have the specific "material_diameter" and "machine_nozzle_size" defined for
# each machine. This results in wrong values which can be found in the MachineSettings dialog.
#
# To solve this, we put "material_diameter" back into the "fdmextruder" definition because modifying it in
# the extruder definition will affect all machines which uses the "fdmextruder" definition. Moreover, now
# we also check the value defined in the machine definition. If present, the value defined in the global
# stack's definition changes container will be copied. Otherwise, we will check if the default values in the
# machine definition and the extruder definition are the same, and if not, the default value in the machine
# definition will be copied to the extruder stack's definition changes.
#
setting_value_in_global_def_changes = global_stack.definitionChanges.getProperty(key, "value")
setting_value_in_global_def = global_stack.definition.getProperty(key, "value")
setting_value = setting_value_in_global_def

View file

@ -13,7 +13,7 @@ from UM.Settings.Interfaces import ContainerInterface, PropertyEvaluationContext
from UM.Util import parseBool
from . import Exceptions
from .CuraContainerStack import CuraContainerStack
from .CuraContainerStack import CuraContainerStack, _ContainerIndexes
from .ExtruderManager import ExtruderManager
if TYPE_CHECKING:
@ -69,14 +69,10 @@ class ExtruderStack(CuraContainerStack):
# \return The filament diameter for the printer
@property
def materialDiameter(self) -> float:
containers_to_check = [self.variant, self.definitionChanges, self.definition]
context = PropertyEvaluationContext(self)
context.context["evaluate_from_container_index"] = _ContainerIndexes.Variant
for container in containers_to_check:
if container is not None:
material_diameter = container.getProperty("material_diameter", "value")
if material_diameter is not None:
return material_diameter
return -1
return self.getProperty("material_diameter", "value", context = context)
## Return the approximate filament diameter that the machine requires.
#

View file

@ -119,16 +119,13 @@ class MachineManager(QObject):
if containers:
containers[0].nameChanged.connect(self._onMaterialNameChanged)
# NEW
self._material_manager = self._application._material_manager
self._material_manager.materialsUpdated.connect(self._onMaterialsUpdated)
self._quality_manager = self._application.getQualityManager()
def _onMaterialsUpdated(self):
# When the materials lookup table gets updated, it can mean that a material has its name changed, which should
# be reflected on the GUI. This signal emission makes sure that it happens.
self.rootMaterialChanged.emit()
self._material_manager.materialsUpdated.connect(self.rootMaterialChanged)
### NEW
activeQualityGroupChanged = pyqtSignal()
activeQualityChangesGroupChanged = pyqtSignal()
@ -303,14 +300,12 @@ class MachineManager(QObject):
if containers:
global_stack = containers[0]
ExtruderManager.getInstance().setActiveExtruderIndex(0) # Switch to first extruder
Application.getInstance().setGlobalContainerStack(global_stack)
self._global_container_stack = global_stack
Application.getInstance().setGlobalContainerStack(global_stack)
ExtruderManager.getInstance()._globalContainerStackChanged()
self._initMachineState(containers[0])
self.updateDefaultExtruder()
self.updateNumberExtrudersEnabled()
self.globalContainerChanged.emit()
self._onGlobalContainerChanged()
self.__emitChangedSignals()
@ -835,11 +830,6 @@ class MachineManager(QObject):
container = extruder.userChanges
container.setProperty(setting_name, property_name, property_value)
#
# New
#
# We not fetch it from _current_root_material_id, but later we can get it from somewhere else
@pyqtProperty("QVariantList", notify = rootMaterialChanged)
def currentExtruderPositions(self):
return sorted(list(self._current_root_material_id.keys()))
@ -879,6 +869,10 @@ class MachineManager(QObject):
return result
#
# Sets all quality and quality_changes containers to empty_quality and empty_quality_changes containers
# for all stacks in the currently active machine.
#
def _setEmptyQuality(self):
self._current_quality_group = None
self._current_quality_changes_group = None
@ -911,11 +905,8 @@ class MachineManager(QObject):
self.activeQualityChangesGroupChanged.emit()
def _setQualityChangesGroup(self, quality_changes_group):
# TODO: quality_changes groups depend on a quality_type. Here it's fetching the quality_types every time.
# Can we do this better, like caching the quality group a quality_changes group depends on?
quality_type = quality_changes_group.quality_type
quality_manager = Application.getInstance()._quality_manager
quality_group_dict = quality_manager.getQualityGroups(self._global_container_stack)
quality_group_dict = self._quality_manager.getQualityGroups(self._global_container_stack)
quality_group = quality_group_dict[quality_type]
quality_changes_container = self._empty_quality_changes_container
@ -1031,13 +1022,6 @@ class MachineManager(QObject):
self._setMaterial(position, new_material)
continue
# # Find a fallback material
# preferred_material_query = self._global_container_stack.getMetaDataEntry("preferred_material")
# preferred_material_key = preferred_material_query.replace("*", "")
# if preferred_material_key in candidate_materials:
# self._setMaterial(position, candidate_materials[preferred_material_key])
# return
@pyqtSlot("QVariant")
def setGlobalVariant(self, container_node):
self.blurSettings.emit()

View file

@ -3,12 +3,14 @@
import copy
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
from UM.Signal import Signal, signalemitter
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Logger import Logger
from UM.Settings.Validator import ValidatorState
from PyQt5.QtCore import QTimer
from UM.Application import Application
from cura.Settings.PerObjectContainerStack import PerObjectContainerStack
@ -38,6 +40,10 @@ class SettingOverrideDecorator(SceneNodeDecorator):
self._extruder_stack = ExtruderManager.getInstance().getExtruderStack(0).getId()
self._is_non_printing_mesh = False
self._error_check_timer = QTimer()
self._error_check_timer.setInterval(250)
self._error_check_timer.setSingleShot(True)
self._error_check_timer.timeout.connect(self._checkStackForErrors)
self._stack.propertyChanged.connect(self._onSettingChanged)
@ -97,8 +103,21 @@ class SettingOverrideDecorator(SceneNodeDecorator):
if property_name == "value":
self._is_non_printing_mesh = self.evaluateIsNonPrintingMesh()
Application.getInstance().getBackend().needsSlicing()
Application.getInstance().getBackend().tickle()
if not self._is_non_printing_mesh:
# self._error_check_timer.start()
self._checkStackForErrors()
Application.getInstance().getBackend().needsSlicing()
Application.getInstance().getBackend().tickle()
def _checkStackForErrors(self):
hasErrors = False;
for key in self._stack.getAllKeys():
validation_state = self._stack.getProperty(key, "validationState")
if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
Logger.log("w", "Setting Per Object %s is not valid.", key)
hasErrors = True
break
Application.getInstance().getObjectsModel().setStacksHaveErrors(hasErrors)
## Makes sure that the stack upon which the container stack is placed is
# kept up to date.