Merge pull request #5 from Ultimaker/master

update
This commit is contained in:
MaukCC 2017-05-18 09:44:06 +02:00 committed by GitHub
commit 21cf47b352
201 changed files with 4940 additions and 586 deletions

View File

@ -433,7 +433,8 @@ class BuildVolume(SceneNode):
self._global_container_stack.getProperty("raft_interface_thickness", "value") + self._global_container_stack.getProperty("raft_interface_thickness", "value") +
self._global_container_stack.getProperty("raft_surface_layers", "value") * self._global_container_stack.getProperty("raft_surface_layers", "value") *
self._global_container_stack.getProperty("raft_surface_thickness", "value") + self._global_container_stack.getProperty("raft_surface_thickness", "value") +
self._global_container_stack.getProperty("raft_airgap", "value")) self._global_container_stack.getProperty("raft_airgap", "value") -
self._global_container_stack.getProperty("layer_0_z_overlap", "value"))
# Rounding errors do not matter, we check if raft_thickness has changed at all # Rounding errors do not matter, we check if raft_thickness has changed at all
if old_raft_thickness != self._raft_thickness: if old_raft_thickness != self._raft_thickness:
@ -562,7 +563,7 @@ class BuildVolume(SceneNode):
used_extruders = [self._global_container_stack] used_extruders = [self._global_container_stack]
result_areas = self._computeDisallowedAreasStatic(disallowed_border_size, used_extruders) #Normal machine disallowed areas can always be added. result_areas = self._computeDisallowedAreasStatic(disallowed_border_size, used_extruders) #Normal machine disallowed areas can always be added.
prime_areas = self._computeDisallowedAreasPrime(disallowed_border_size, used_extruders) prime_areas = self._computeDisallowedAreasPrimeBlob(disallowed_border_size, used_extruders)
prime_disallowed_areas = self._computeDisallowedAreasStatic(0, used_extruders) #Where the priming is not allowed to happen. This is not added to the result, just for collision checking. prime_disallowed_areas = self._computeDisallowedAreasStatic(0, used_extruders) #Where the priming is not allowed to happen. This is not added to the result, just for collision checking.
#Check if prime positions intersect with disallowed areas. #Check if prime positions intersect with disallowed areas.
@ -658,7 +659,7 @@ class BuildVolume(SceneNode):
return result return result
## Computes the disallowed areas for the prime locations. ## Computes the disallowed areas for the prime blobs.
# #
# These are special because they are not subject to things like brim or # These are special because they are not subject to things like brim or
# travel avoidance. They do get a dilute with the border size though # travel avoidance. They do get a dilute with the border size though
@ -669,17 +670,18 @@ class BuildVolume(SceneNode):
# \param used_extruders The extruder stacks to generate disallowed areas # \param used_extruders The extruder stacks to generate disallowed areas
# for. # for.
# \return A dictionary with for each used extruder ID the prime areas. # \return A dictionary with for each used extruder ID the prime areas.
def _computeDisallowedAreasPrime(self, border_size, used_extruders): def _computeDisallowedAreasPrimeBlob(self, border_size, used_extruders):
result = {} result = {}
machine_width = self._global_container_stack.getProperty("machine_width", "value") machine_width = self._global_container_stack.getProperty("machine_width", "value")
machine_depth = self._global_container_stack.getProperty("machine_depth", "value") machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
for extruder in used_extruders: for extruder in used_extruders:
prime_blob_enabled = extruder.getProperty("prime_blob_enable", "value")
prime_x = extruder.getProperty("extruder_prime_pos_x", "value") prime_x = extruder.getProperty("extruder_prime_pos_x", "value")
prime_y = - extruder.getProperty("extruder_prime_pos_y", "value") prime_y = - extruder.getProperty("extruder_prime_pos_y", "value")
#Ignore extruder prime position if it is not set #Ignore extruder prime position if it is not set or if blob is disabled
if prime_x == 0 and prime_y == 0: if (prime_x == 0 and prime_y == 0) or not prime_blob_enabled:
result[extruder.getId()] = [] result[extruder.getId()] = []
continue continue
@ -950,9 +952,9 @@ class BuildVolume(SceneNode):
return max(min(value, max_value), min_value) return max(min(value, max_value), min_value)
_skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist"] _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "brim_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist"]
_raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"] _raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap", "layer_0_z_overlap"]
_extra_z_settings = ["retraction_hop_enabled", "retraction_hop"] _extra_z_settings = ["retraction_hop_enabled", "retraction_hop"]
_prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z"] _prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z", "prime_blob_enable"]
_tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"] _tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"]
_ooze_shield_settings = ["ooze_shield_enabled", "ooze_shield_dist"] _ooze_shield_settings = ["ooze_shield_enabled", "ooze_shield_dist"]
_distance_settings = ["infill_wipe_dist", "travel_avoid_distance", "support_offset", "support_enable", "travel_avoid_other_parts"] _distance_settings = ["infill_wipe_dist", "travel_avoid_distance", "support_offset", "support_enable", "travel_avoid_other_parts"]

View File

@ -328,8 +328,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
return self.__isDescendant(root, node.getParent()) return self.__isDescendant(root, node.getParent())
_affected_settings = [ _affected_settings = [
"adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "adhesion_type", "raft_margin", "print_sequence",
"raft_surface_thickness", "raft_airgap", "raft_margin", "print_sequence",
"skirt_gap", "skirt_line_count", "skirt_brim_line_width", "skirt_distance", "brim_line_count"] "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "skirt_distance", "brim_line_count"]
## Settings that change the convex hull. ## Settings that change the convex hull.

View File

@ -99,6 +99,11 @@ if not MYPY:
class CuraApplication(QtApplication): class CuraApplication(QtApplication):
# SettingVersion represents the set of settings available in the machine/extruder definitions.
# You need to make sure that this version number needs to be increased if there is any non-backwards-compatible
# changes of the settings.
SettingVersion = 1
class ResourceTypes: class ResourceTypes:
QmlFiles = Resources.UserType + 1 QmlFiles = Resources.UserType + 1
Firmware = Resources.UserType + 2 Firmware = Resources.UserType + 2
@ -169,11 +174,11 @@ class CuraApplication(QtApplication):
UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions( UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions(
{ {
("quality", InstanceContainer.Version): (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"), ("quality_changes", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"),
("machine_stack", ContainerStack.Version): (self.ResourceTypes.MachineStack, "application/x-uranium-containerstack"), ("machine_stack", ContainerStack.Version): (self.ResourceTypes.MachineStack, "application/x-uranium-containerstack"),
("extruder_train", ContainerStack.Version): (self.ResourceTypes.ExtruderStack, "application/x-uranium-extruderstack"), ("extruder_train", ContainerStack.Version): (self.ResourceTypes.ExtruderStack, "application/x-uranium-extruderstack"),
("preferences", Preferences.Version): (Resources.Preferences, "application/x-uranium-preferences"), ("preferences", Preferences.Version): (Resources.Preferences, "application/x-uranium-preferences"),
("user", InstanceContainer.Version): (self.ResourceTypes.UserInstanceContainer, "application/x-uranium-instancecontainer") ("user", InstanceContainer.Version * 1000000 + self.SettingVersion): (self.ResourceTypes.UserInstanceContainer, "application/x-uranium-instancecontainer")
} }
) )
@ -620,7 +625,9 @@ class CuraApplication(QtApplication):
camera.lookAt(Vector(0, 0, 0)) camera.lookAt(Vector(0, 0, 0))
controller.getScene().setActiveCamera("3d") controller.getScene().setActiveCamera("3d")
self.getController().getTool("CameraTool").setOrigin(Vector(0, 100, 0)) camera_tool = self.getController().getTool("CameraTool")
camera_tool.setOrigin(Vector(0, 100, 0))
camera_tool.setZoomRange(0.1, 200000)
self._camera_animation = CameraAnimation.CameraAnimation() self._camera_animation = CameraAnimation.CameraAnimation()
self._camera_animation.setCameraTool(self.getController().getTool("CameraTool")) self._camera_animation.setCameraTool(self.getController().getTool("CameraTool"))

View File

@ -31,8 +31,8 @@ catalog = i18nCatalog("cura")
# - This triggers a new slice with the current settings - this is the "current settings pass". # - This triggers a new slice with the current settings - this is the "current settings pass".
# - When the slice is done, we update the current print time and material amount. # - When the slice is done, we update the current print time and material amount.
# - If the source of the slice was not a Setting change, we start the second slice pass, the "low quality settings pass". Otherwise we stop here. # - If the source of the slice was not a Setting change, we start the second slice pass, the "low quality settings pass". Otherwise we stop here.
# - When that is done, we update the minimum print time and start the final slice pass, the "high quality settings pass". # - When that is done, we update the minimum print time and start the final slice pass, the "Extra Fine settings pass".
# - When the high quality pass is done, we update the maximum print time. # - When the Extra Fine pass is done, we update the maximum print time.
# #
# This class also mangles the current machine name and the filename of the first loaded mesh into a job name. # This class also mangles the current machine name and the filename of the first loaded mesh into a job name.
# This job name is requested by the JobSpecs qml file. # This job name is requested by the JobSpecs qml file.

View File

@ -3,7 +3,7 @@
# This collects a lot of quality and quality changes related code which was split between ContainerManager # This collects a lot of quality and quality changes related code which was split between ContainerManager
# and the MachineManager and really needs to usable from both. # and the MachineManager and really needs to usable from both.
from typing import List from typing import List, Optional, Dict, TYPE_CHECKING
from UM.Application import Application from UM.Application import Application
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
@ -11,6 +11,10 @@ from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.InstanceContainer import InstanceContainer
from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderManager import ExtruderManager
if TYPE_CHECKING:
from cura.Settings.GlobalStack import GlobalStack
from cura.Settings.ExtruderStack import ExtruderStack
from UM.Settings.DefinitionContainer import DefinitionContainerInterface
class QualityManager: class QualityManager:
@ -27,12 +31,12 @@ class QualityManager:
## Find a quality by name for a specific machine definition and materials. ## Find a quality by name for a specific machine definition and materials.
# #
# \param quality_name # \param quality_name
# \param machine_definition (Optional) \type{ContainerInstance} If nothing is # \param machine_definition (Optional) \type{DefinitionContainerInterface} If nothing is
# specified then the currently selected machine definition is used. # specified then the currently selected machine definition is used.
# \param material_containers (Optional) \type{List[ContainerInstance]} If nothing is specified then # \param material_containers (Optional) \type{List[InstanceContainer]} If nothing is specified then
# the current set of selected materials is used. # the current set of selected materials is used.
# \return the matching quality container \type{ContainerInstance} # \return the matching quality container \type{InstanceContainer}
def findQualityByName(self, quality_name, machine_definition=None, material_containers=None): def findQualityByName(self, quality_name: str, machine_definition: Optional["DefinitionContainerInterface"] = None, material_containers: List[InstanceContainer] = None) -> Optional[InstanceContainer]:
criteria = {"type": "quality", "name": quality_name} criteria = {"type": "quality", "name": quality_name}
result = self._getFilteredContainersForStack(machine_definition, material_containers, **criteria) result = self._getFilteredContainersForStack(machine_definition, material_containers, **criteria)
@ -46,12 +50,10 @@ class QualityManager:
## Find a quality changes container by name. ## Find a quality changes container by name.
# #
# \param quality_changes_name \type{str} the name of the quality changes container. # \param quality_changes_name \type{str} the name of the quality changes container.
# \param machine_definition (Optional) \type{ContainerInstance} If nothing is # \param machine_definition (Optional) \type{DefinitionContainer} If nothing is
# specified then the currently selected machine definition is used. # specified then the currently selected machine definition is used..
# \param material_containers (Optional) \type{List[ContainerInstance]} If nothing is specified then # \return the matching quality changes containers \type{List[InstanceContainer]}
# the current set of selected materials is used. def findQualityChangesByName(self, quality_changes_name: str, machine_definition: Optional["DefinitionContainerInterface"] = None):
# \return the matching quality changes containers \type{List[ContainerInstance]}
def findQualityChangesByName(self, quality_changes_name, machine_definition=None):
criteria = {"type": "quality_changes", "name": quality_changes_name} criteria = {"type": "quality_changes", "name": quality_changes_name}
result = self._getFilteredContainersForStack(machine_definition, [], **criteria) result = self._getFilteredContainersForStack(machine_definition, [], **criteria)
@ -62,7 +64,7 @@ class QualityManager:
# \param machine_definition \type{DefinitionContainer} # \param machine_definition \type{DefinitionContainer}
# \param material_containers \type{List[InstanceContainer]} # \param material_containers \type{List[InstanceContainer]}
# \return \type{List[str]} # \return \type{List[str]}
def findAllQualityTypesForMachineAndMaterials(self, machine_definition, material_containers): def findAllQualityTypesForMachineAndMaterials(self, machine_definition: "DefinitionContainerInterface", material_containers: List[InstanceContainer]) -> List[str]:
# Determine the common set of quality types which can be # Determine the common set of quality types which can be
# applied to all of the materials for this machine. # applied to all of the materials for this machine.
quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_containers[0]) quality_type_dict = self.__fetchQualityTypeDictForMaterial(machine_definition, material_containers[0])
@ -76,9 +78,9 @@ class QualityManager:
## Fetches a dict of quality types names to quality profiles for a combination of machine and material. ## Fetches a dict of quality types names to quality profiles for a combination of machine and material.
# #
# \param machine_definition \type{DefinitionContainer} the machine definition. # \param machine_definition \type{DefinitionContainer} the machine definition.
# \param material \type{ContainerInstance} the material. # \param material \type{InstanceContainer} the material.
# \return \type{Dict[str, ContainerInstance]} the dict of suitable quality type names mapping to qualities. # \return \type{Dict[str, InstanceContainer]} the dict of suitable quality type names mapping to qualities.
def __fetchQualityTypeDictForMaterial(self, machine_definition, material): def __fetchQualityTypeDictForMaterial(self, machine_definition: "DefinitionContainerInterface", material: InstanceContainer) -> Dict[str, InstanceContainer]:
qualities = self.findAllQualitiesForMachineMaterial(machine_definition, material) qualities = self.findAllQualitiesForMachineMaterial(machine_definition, material)
quality_type_dict = {} quality_type_dict = {}
for quality in qualities: for quality in qualities:
@ -88,12 +90,12 @@ class QualityManager:
## Find a quality container by quality type. ## Find a quality container by quality type.
# #
# \param quality_type \type{str} the name of the quality type to search for. # \param quality_type \type{str} the name of the quality type to search for.
# \param machine_definition (Optional) \type{ContainerInstance} If nothing is # \param machine_definition (Optional) \type{InstanceContainer} If nothing is
# specified then the currently selected machine definition is used. # specified then the currently selected machine definition is used.
# \param material_containers (Optional) \type{List[ContainerInstance]} If nothing is specified then # \param material_containers (Optional) \type{List[InstanceContainer]} If nothing is specified then
# the current set of selected materials is used. # the current set of selected materials is used.
# \return the matching quality container \type{ContainerInstance} # \return the matching quality container \type{InstanceContainer}
def findQualityByQualityType(self, quality_type, machine_definition=None, material_containers=None, **kwargs): def findQualityByQualityType(self, quality_type: str, machine_definition: Optional["DefinitionContainerInterface"] = None, material_containers: List[InstanceContainer] = None, **kwargs) -> InstanceContainer:
criteria = kwargs criteria = kwargs
criteria["type"] = "quality" criteria["type"] = "quality"
if quality_type: if quality_type:
@ -110,9 +112,9 @@ class QualityManager:
## Find all suitable qualities for a combination of machine and material. ## Find all suitable qualities for a combination of machine and material.
# #
# \param machine_definition \type{DefinitionContainer} the machine definition. # \param machine_definition \type{DefinitionContainer} the machine definition.
# \param material_container \type{ContainerInstance} the material. # \param material_container \type{InstanceContainer} the material.
# \return \type{List[ContainerInstance]} the list of suitable qualities. # \return \type{List[InstanceContainer]} the list of suitable qualities.
def findAllQualitiesForMachineMaterial(self, machine_definition, material_container): def findAllQualitiesForMachineMaterial(self, machine_definition: "DefinitionContainerInterface", material_container: InstanceContainer) -> List[InstanceContainer]:
criteria = {"type": "quality" } criteria = {"type": "quality" }
result = self._getFilteredContainersForStack(machine_definition, [material_container], **criteria) result = self._getFilteredContainersForStack(machine_definition, [material_container], **criteria)
if not result: if not result:
@ -125,7 +127,7 @@ class QualityManager:
# #
# \param machine_definition \type{DefinitionContainer} the machine definition. # \param machine_definition \type{DefinitionContainer} the machine definition.
# \return \type{List[InstanceContainer]} the list of quality changes # \return \type{List[InstanceContainer]} the list of quality changes
def findAllQualityChangesForMachine(self, machine_definition: DefinitionContainer) -> List[InstanceContainer]: def findAllQualityChangesForMachine(self, machine_definition: "DefinitionContainerInterface") -> List[InstanceContainer]:
if machine_definition.getMetaDataEntry("has_machine_quality"): if machine_definition.getMetaDataEntry("has_machine_quality"):
definition_id = machine_definition.getId() definition_id = machine_definition.getId()
else: else:
@ -141,19 +143,19 @@ class QualityManager:
# Only one quality per quality type is returned. i.e. if there are 2 qualities with quality_type=normal # Only one quality per quality type is returned. i.e. if there are 2 qualities with quality_type=normal
# then only one of then is returned (at random). # then only one of then is returned (at random).
# #
# \param global_container_stack \type{ContainerStack} the global machine definition # \param global_container_stack \type{GlobalStack} the global machine definition
# \param extruder_stacks \type{List[ContainerStack]} the list of extruder stacks # \param extruder_stacks \type{List[ExtruderStack]} the list of extruder stacks
# \return \type{List[InstanceContainer]} the list of the matching qualities. The quality profiles # \return \type{List[InstanceContainer]} the list of the matching qualities. The quality profiles
# return come from the first extruder in the given list of extruders. # return come from the first extruder in the given list of extruders.
def findAllUsableQualitiesForMachineAndExtruders(self, global_container_stack, extruder_stacks): def findAllUsableQualitiesForMachineAndExtruders(self, global_container_stack: "GlobalStack", extruder_stacks: List["ExtruderStack"]) -> List[InstanceContainer]:
global_machine_definition = global_container_stack.getBottom() global_machine_definition = global_container_stack.getBottom()
if extruder_stacks: if extruder_stacks:
# Multi-extruder machine detected. # Multi-extruder machine detected.
materials = [stack.findContainer(type="material") for stack in extruder_stacks] materials = [stack.material for stack in extruder_stacks]
else: else:
# Machine with one extruder. # Machine with one extruder.
materials = [global_container_stack.findContainer(type="material")] materials = [global_container_stack.material]
quality_types = self.findAllQualityTypesForMachineAndMaterials(global_machine_definition, materials) quality_types = self.findAllQualityTypesForMachineAndMaterials(global_machine_definition, materials)
@ -170,7 +172,7 @@ class QualityManager:
# This tries to find a generic or basic version of the given material. # This tries to find a generic or basic version of the given material.
# \param material_container \type{InstanceContainer} the material # \param material_container \type{InstanceContainer} the material
# \return \type{List[InstanceContainer]} a list of the basic materials or an empty list if one could not be found. # \return \type{List[InstanceContainer]} a list of the basic materials or an empty list if one could not be found.
def _getBasicMaterials(self, material_container): def _getBasicMaterials(self, material_container: InstanceContainer):
base_material = material_container.getMetaDataEntry("material") base_material = material_container.getMetaDataEntry("material")
material_container_definition = material_container.getDefinition() material_container_definition = material_container.getDefinition()
if material_container_definition and material_container_definition.getMetaDataEntry("has_machine_quality"): if material_container_definition and material_container_definition.getMetaDataEntry("has_machine_quality"):
@ -192,7 +194,7 @@ class QualityManager:
def _getFilteredContainers(self, **kwargs): def _getFilteredContainers(self, **kwargs):
return self._getFilteredContainersForStack(None, None, **kwargs) return self._getFilteredContainersForStack(None, None, **kwargs)
def _getFilteredContainersForStack(self, machine_definition=None, material_containers=None, **kwargs): def _getFilteredContainersForStack(self, machine_definition: "DefinitionContainerInterface" = None, material_containers: List[InstanceContainer] = None, **kwargs):
# Fill in any default values. # Fill in any default values.
if machine_definition is None: if machine_definition is None:
machine_definition = Application.getInstance().getGlobalContainerStack().getBottom() machine_definition = Application.getInstance().getGlobalContainerStack().getBottom()
@ -202,7 +204,8 @@ class QualityManager:
if material_containers is None: if material_containers is None:
active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks()
material_containers = [stack.findContainer(type="material") for stack in active_stacks] if active_stacks:
material_containers = [stack.material for stack in active_stacks]
criteria = kwargs criteria = kwargs
filter_by_material = False filter_by_material = False
@ -216,12 +219,11 @@ class QualityManager:
filter_by_material = whole_machine_definition.getMetaDataEntry("has_materials") filter_by_material = whole_machine_definition.getMetaDataEntry("has_materials")
else: else:
criteria["definition"] = "fdmprinter" criteria["definition"] = "fdmprinter"
material_ids = set()
# Stick the material IDs in a set # Stick the material IDs in a set
if material_containers is None or len(material_containers) == 0: if material_containers is None or len(material_containers) == 0:
filter_by_material = False filter_by_material = False
else: else:
material_ids = set()
for material_instance in material_containers: for material_instance in material_containers:
if material_instance is not None: if material_instance is not None:
# Add the parent material too. # Add the parent material too.
@ -245,7 +247,7 @@ class QualityManager:
# an extruder definition. # an extruder definition.
# \return \type{DefinitionContainer} the parent machine definition. If the given machine # \return \type{DefinitionContainer} the parent machine definition. If the given machine
# definition doesn't have a parent then it is simply returned. # definition doesn't have a parent then it is simply returned.
def getParentMachineDefinition(self, machine_definition: DefinitionContainer) -> DefinitionContainer: def getParentMachineDefinition(self, machine_definition: "DefinitionContainerInterface") -> "DefinitionContainerInterface":
container_registry = ContainerRegistry.getInstance() container_registry = ContainerRegistry.getInstance()
machine_entry = machine_definition.getMetaDataEntry("machine") machine_entry = machine_definition.getMetaDataEntry("machine")
@ -274,8 +276,8 @@ class QualityManager:
# #
# \param machine_definition \type{DefinitionContainer} This may be a normal machine definition or # \param machine_definition \type{DefinitionContainer} This may be a normal machine definition or
# an extruder definition. # an extruder definition.
# \return \type{DefinitionContainer} # \return \type{DefinitionContainerInterface}
def getWholeMachineDefinition(self, machine_definition): def getWholeMachineDefinition(self, machine_definition: "DefinitionContainerInterface") -> "DefinitionContainerInterface":
machine_entry = machine_definition.getMetaDataEntry("machine") machine_entry = machine_definition.getMetaDataEntry("machine")
if machine_entry is None: if machine_entry is None:
# This already is a 'global' machine definition. # This already is a 'global' machine definition.

View File

@ -1,13 +1,15 @@
# Copyright (c) 2016 Ultimaker B.V. # Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher. # Cura is released under the terms of the AGPLv3 or higher.
import os.path import os.path
import urllib import urllib
import uuid
from typing import Dict, Union from typing import Dict, Union
from PyQt5.QtCore import QObject, QUrl, QVariant from PyQt5.QtCore import QObject, QUrl, QVariant
from UM.FlameProfiler import pyqtSlot from UM.FlameProfiler import pyqtSlot
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
from UM.Util import parseBool
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
from UM.SaveFile import SaveFile from UM.SaveFile import SaveFile
@ -671,6 +673,9 @@ class ContainerManager(QObject):
return new_change_instances return new_change_instances
## 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(str, result = str) @pyqtSlot(str, result = str)
def duplicateMaterial(self, material_id: str) -> str: def duplicateMaterial(self, material_id: str) -> str:
containers = self._container_registry.findInstanceContainers(id=material_id) containers = self._container_registry.findInstanceContainers(id=material_id)
@ -693,6 +698,115 @@ class ContainerManager(QObject):
duplicated_container.deserialize(f.read()) duplicated_container.deserialize(f.read())
duplicated_container.setDirty(True) duplicated_container.setDirty(True)
self._container_registry.addContainer(duplicated_container) self._container_registry.addContainer(duplicated_container)
return self._getMaterialContainerIdForActiveMachine(new_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) -> str:
# Ensure all settings are saved.
Application.getInstance().saveSettings()
global_stack = Application.getInstance().getGlobalContainerStack()
if not global_stack:
return ""
approximate_diameter = round(global_stack.getProperty("material_diameter", "value"))
containers = self._container_registry.findInstanceContainers(id = "generic_pla*", approximate_diameter = approximate_diameter)
if not containers:
Logger.log("d", "Unable to create a new material by cloning Generic PLA, because it cannot be found for the material diameter for this machine.")
return ""
base_file = containers[0].getMetaDataEntry("base_file")
containers = self._container_registry.findInstanceContainers(id = base_file)
if not containers:
Logger.log("d", "Unable to create a new material by cloning Generic PLA, because the base file for Generic PLA for this machine can not be found.")
return ""
# Create a new ID & container to hold the data.
new_id = self._container_registry.uniqueName("custom_material")
container_type = type(containers[0]) # Always XMLMaterialProfile, since we specifically clone the base_file
duplicated_container = container_type(new_id)
# Instead of duplicating we load the data from the basefile again.
# This ensures that the inheritance goes well and all "cut up" subclasses of the xmlMaterial profile
# are also correctly created.
with open(containers[0].getPath(), encoding="utf-8") as f:
duplicated_container.deserialize(f.read())
duplicated_container.setMetaDataEntry("GUID", str(uuid.uuid4()))
duplicated_container.setMetaDataEntry("brand", catalog.i18nc("@label", "Custom"))
duplicated_container.setMetaDataEntry("material", catalog.i18nc("@label", "Custom"))
duplicated_container.setName(catalog.i18nc("@label", "Custom Material"))
self._container_registry.addContainer(duplicated_container)
return self._getMaterialContainerIdForActiveMachine(new_id)
## Find the id of a material container based on the new material
# Utilty function that is shared between duplicateMaterial and createMaterial
#
# \param base_file \type{str} the id of the created container.
def _getMaterialContainerIdForActiveMachine(self, base_file):
global_stack = Application.getInstance().getGlobalContainerStack()
if not global_stack:
return base_file
has_machine_materials = parseBool(global_stack.getMetaDataEntry("has_machine_materials", default = False))
has_variant_materials = parseBool(global_stack.getMetaDataEntry("has_variant_materials", default = False))
has_variants = parseBool(global_stack.getMetaDataEntry("has_variants", default = False))
if has_machine_materials or has_variant_materials:
if has_variants:
materials = self._container_registry.findInstanceContainers(type = "material", base_file = base_file, definition = global_stack.getBottom().getId(), variant = self._machine_manager.activeVariantId)
else:
materials = self._container_registry.findInstanceContainers(type = "material", base_file = base_file, definition = global_stack.getBottom().getId())
if materials:
return materials[0].getId()
Logger.log("w", "Unable to find a suitable container based on %s for the current machine .", base_file)
return "" # do not activate a new material if a container can not be found
return base_file
## 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.
# \return \type{list} a list of names of materials with the same GUID
@pyqtSlot(str, result = "QStringList")
def getLinkedMaterials(self, material_id: str):
containers = self._container_registry.findInstanceContainers(id=material_id)
if not containers:
Logger.log("d", "Unable to find materials linked to material with id %s, because it doesn't exist.", material_id)
return []
material_container = containers[0]
material_base_file = material_container.getMetaDataEntry("base_file", "")
material_guid = material_container.getMetaDataEntry("GUID", "")
if not material_guid:
Logger.log("d", "Unable to find materials linked to material with id %s, because it doesn't have a GUID.", material_id)
return []
containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_guid)
linked_material_names = []
for container in containers:
if container.getId() in [material_id, material_base_file] or container.getMetaDataEntry("base_file") != container.getId():
continue
linked_material_names.append(container.getName())
return linked_material_names
## Unlink a material from all other materials by creating a new GUID
# \param material_id \type{str} the id of the material to create a new GUID for.
@pyqtSlot(str)
def unlinkMaterial(self, material_id: str):
containers = self._container_registry.findInstanceContainers(id=material_id)
if not containers:
Logger.log("d", "Unable to make the material with id %s unique, because it doesn't exist.", material_id)
return ""
containers[0].setMetaDataEntry("GUID", str(uuid.uuid4()))
## Get the singleton instance for this class. ## Get the singleton instance for this class.
@classmethod @classmethod
@ -815,6 +929,8 @@ class ContainerManager(QObject):
quality_changes.setDefinition(self._container_registry.findContainers(id = "fdmprinter")[0]) quality_changes.setDefinition(self._container_registry.findContainers(id = "fdmprinter")[0])
else: else:
quality_changes.setDefinition(QualityManager.getInstance().getParentMachineDefinition(machine_definition)) quality_changes.setDefinition(QualityManager.getInstance().getParentMachineDefinition(machine_definition))
from cura.CuraApplication import CuraApplication
quality_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
return quality_changes return quality_changes

View File

@ -22,6 +22,8 @@ from . import GlobalStack
from .ContainerManager import ContainerManager from .ContainerManager import ContainerManager
from .ExtruderManager import ExtruderManager from .ExtruderManager import ExtruderManager
from cura.CuraApplication import CuraApplication
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
@ -43,7 +45,7 @@ class CuraContainerRegistry(ContainerRegistry):
if isinstance(container, InstanceContainer) and type(container) != type(self.getEmptyInstanceContainer()): if isinstance(container, InstanceContainer) and type(container) != type(self.getEmptyInstanceContainer()):
#Check against setting version of the definition. #Check against setting version of the definition.
required_setting_version = int(container.getDefinition().getMetaDataEntry("setting_version")) required_setting_version = CuraApplication.SettingVersion
actual_setting_version = int(container.getMetaDataEntry("setting_version", default = 0)) actual_setting_version = int(container.getMetaDataEntry("setting_version", default = 0))
if required_setting_version != actual_setting_version: if required_setting_version != actual_setting_version:
Logger.log("w", "Instance container {container_id} is outdated. Its setting version is {actual_setting_version} but it should be {required_setting_version}.".format(container_id = container.getId(), actual_setting_version = actual_setting_version, required_setting_version = required_setting_version)) Logger.log("w", "Instance container {container_id} is outdated. Its setting version is {actual_setting_version} but it should be {required_setting_version}.".format(container_id = container.getId(), actual_setting_version = actual_setting_version, required_setting_version = required_setting_version))

View File

@ -9,7 +9,6 @@ from UM.Settings.ContainerRegistry import ContainerRegistry
from .GlobalStack import GlobalStack from .GlobalStack import GlobalStack
from .ExtruderStack import ExtruderStack from .ExtruderStack import ExtruderStack
from .CuraContainerStack import CuraContainerStack
from typing import Optional from typing import Optional
@ -76,6 +75,8 @@ class CuraStackBuilder:
user_container = InstanceContainer(new_stack_id + "_user") user_container = InstanceContainer(new_stack_id + "_user")
user_container.addMetaDataEntry("type", "user") user_container.addMetaDataEntry("type", "user")
user_container.addMetaDataEntry("extruder", new_stack_id) user_container.addMetaDataEntry("extruder", new_stack_id)
from cura.CuraApplication import CuraApplication
user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
user_container.setDefinition(machine_definition) user_container.setDefinition(machine_definition)
stack.setUserChanges(user_container) stack.setUserChanges(user_container)
@ -124,6 +125,8 @@ class CuraStackBuilder:
user_container = InstanceContainer(new_stack_id + "_user") user_container = InstanceContainer(new_stack_id + "_user")
user_container.addMetaDataEntry("type", "user") user_container.addMetaDataEntry("type", "user")
user_container.addMetaDataEntry("machine", new_stack_id) user_container.addMetaDataEntry("machine", new_stack_id)
from cura.CuraApplication import CuraApplication
user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
user_container.setDefinition(definition) user_container.setDefinition(definition)
stack.setUserChanges(user_container) stack.setUserChanges(user_container)

View File

@ -1,4 +1,4 @@
# Copyright (c) 2016 Ultimaker B.V. # Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher. # Cura is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant #For communicating data and events to Qt. from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, QVariant #For communicating data and events to Qt.
@ -20,6 +20,7 @@ from typing import Optional, List, TYPE_CHECKING, Union
if TYPE_CHECKING: if TYPE_CHECKING:
from cura.Settings.ExtruderStack import ExtruderStack from cura.Settings.ExtruderStack import ExtruderStack
from cura.Settings.GlobalStack import GlobalStack
## Manages all existing extruder stacks. ## Manages all existing extruder stacks.
@ -76,8 +77,9 @@ class ExtruderManager(QObject):
@pyqtProperty("QVariantMap", notify=extrudersChanged) @pyqtProperty("QVariantMap", notify=extrudersChanged)
def extruderIds(self): def extruderIds(self):
map = {} map = {}
for position in self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()]: global_stack_id = Application.getInstance().getGlobalContainerStack().getId()
map[position] = self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()][position].getId() for position in self._extruder_trains[global_stack_id]:
map[position] = self._extruder_trains[global_stack_id][position].getId()
return map return map
@pyqtSlot(str, result = str) @pyqtSlot(str, result = str)
@ -85,7 +87,7 @@ class ExtruderManager(QObject):
for position in self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()]: for position in self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()]:
extruder = self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()][position] extruder = self._extruder_trains[Application.getInstance().getGlobalContainerStack().getId()][position]
if extruder.getId() == id: if extruder.getId() == id:
return extruder.findContainer(type = "quality_changes").getId() return extruder.qualityChanges.getId()
## The instance of the singleton pattern. ## The instance of the singleton pattern.
# #
@ -235,6 +237,13 @@ class ExtruderManager(QObject):
if machine_id not in self._extruder_trains: if machine_id not in self._extruder_trains:
self._extruder_trains[machine_id] = {} self._extruder_trains[machine_id] = {}
changed = True changed = True
# do not register if an extruder has already been registered at the position on this machine
if any(item.getId() == extruder_train.getId() for item in self._extruder_trains[machine_id].values()):
Logger.log("w", "Extruder [%s] has already been registered on machine [%s], not doing anything",
extruder_train.getId(), machine_id)
return
if extruder_train: if extruder_train:
self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train
changed = True changed = True
@ -362,6 +371,8 @@ class ExtruderManager(QObject):
user_profile = InstanceContainer(extruder_stack_id + "_current_settings") # Add an empty user profile. user_profile = InstanceContainer(extruder_stack_id + "_current_settings") # Add an empty user profile.
user_profile.addMetaDataEntry("type", "user") user_profile.addMetaDataEntry("type", "user")
user_profile.addMetaDataEntry("extruder", extruder_stack_id) user_profile.addMetaDataEntry("extruder", extruder_stack_id)
from cura.CuraApplication import CuraApplication
user_profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
user_profile.setDefinition(machine_definition) user_profile.setDefinition(machine_definition)
container_registry.addContainer(user_profile) container_registry.addContainer(user_profile)
container_stack.addContainer(user_profile) container_stack.addContainer(user_profile)
@ -457,10 +468,9 @@ class ExtruderManager(QObject):
# \param machine_id The machine to remove the extruders for. # \param machine_id The machine to remove the extruders for.
def removeMachineExtruders(self, machine_id: str): def removeMachineExtruders(self, machine_id: str):
for extruder in self.getMachineExtruders(machine_id): for extruder in self.getMachineExtruders(machine_id):
containers = ContainerRegistry.getInstance().findInstanceContainers(type = "user", extruder = extruder.getId()) ContainerRegistry.getInstance().removeContainer(extruder.userChanges.getId())
for container in containers:
ContainerRegistry.getInstance().removeContainer(container.getId())
ContainerRegistry.getInstance().removeContainer(extruder.getId()) ContainerRegistry.getInstance().removeContainer(extruder.getId())
del self._extruder_trains[machine_id]
## Returns extruders for a specific machine. ## Returns extruders for a specific machine.
# #

View File

@ -1,22 +1,21 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher. # Cura is released under the terms of the AGPLv3 or higher.
from typing import Any from typing import Any, TYPE_CHECKING, Optional
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot
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, InvalidContainerStackError from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Settings.Interfaces import ContainerInterface from UM.Settings.Interfaces import ContainerInterface
from . import Exceptions from . import Exceptions
from .CuraContainerStack import CuraContainerStack from .CuraContainerStack import CuraContainerStack
from .ExtruderManager import ExtruderManager from .ExtruderManager import ExtruderManager
if TYPE_CHECKING:
from cura.Settings.GlobalStack import GlobalStack
## Represents an Extruder and its related containers. ## Represents an Extruder and its related containers.
# #
# #
@ -38,6 +37,10 @@ class ExtruderStack(CuraContainerStack):
# For backward compatibility: Register the extruder with the Extruder Manager # For backward compatibility: Register the extruder with the Extruder Manager
ExtruderManager.getInstance().registerExtruder(self, stack.id) ExtruderManager.getInstance().registerExtruder(self, stack.id)
@override(ContainerStack)
def getNextStack(self) -> Optional["GlobalStack"]:
return super().getNextStack()
@classmethod @classmethod
def getLoadingPriority(cls) -> int: def getLoadingPriority(cls) -> int:
return 3 return 3
@ -59,6 +62,12 @@ class ExtruderStack(CuraContainerStack):
if not super().getProperty(key, "settable_per_extruder"): if not super().getProperty(key, "settable_per_extruder"):
return self.getNextStack().getProperty(key, property_name) return self.getNextStack().getProperty(key, property_name)
limit_to_extruder = super().getProperty(key, "limit_to_extruder")
if (limit_to_extruder is not None and limit_to_extruder != "-1") and self.getMetaDataEntry("position") != str(limit_to_extruder):
result = self.getNextStack().extruders[str(limit_to_extruder)].getProperty(key, property_name)
if result is not None:
return result
return super().getProperty(key, property_name) return super().getProperty(key, property_name)
@override(CuraContainerStack) @override(CuraContainerStack)

View File

@ -1,7 +1,7 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher. # Cura is released under the terms of the AGPLv3 or higher.
from typing import Any from typing import Any, Dict
from PyQt5.QtCore import pyqtProperty from PyQt5.QtCore import pyqtProperty
@ -24,7 +24,7 @@ class GlobalStack(CuraContainerStack):
self.addMetaDataEntry("type", "machine") # For backward compatibility self.addMetaDataEntry("type", "machine") # For backward compatibility
self._extruders = [] self._extruders = {}
# This property is used to track which settings we are calculating the "resolve" for # This property is used to track which settings we are calculating the "resolve" for
# and if so, to bypass the resolve to prevent an infinite recursion that would occur # and if so, to bypass the resolve to prevent an infinite recursion that would occur
@ -34,8 +34,8 @@ class GlobalStack(CuraContainerStack):
## Get the list of extruders of this stack. ## Get the list of extruders of this stack.
# #
# \return The extruders registered with this stack. # \return The extruders registered with this stack.
@pyqtProperty("QVariantList") @pyqtProperty("QVariantMap")
def extruders(self) -> list: def extruders(self) -> Dict[str, "ExtruderStack"]:
return self._extruders return self._extruders
@classmethod @classmethod
@ -52,8 +52,17 @@ class GlobalStack(CuraContainerStack):
extruder_count = self.getProperty("machine_extruder_count", "value") extruder_count = self.getProperty("machine_extruder_count", "value")
if extruder_count and len(self._extruders) + 1 > extruder_count: if extruder_count and len(self._extruders) + 1 > extruder_count:
Logger.log("w", "Adding extruder {meta} to {id} but its extruder count is {count}".format(id = self.id, count = extruder_count, meta = str(extruder.getMetaData()))) Logger.log("w", "Adding extruder {meta} to {id} but its extruder count is {count}".format(id = self.id, count = extruder_count, meta = str(extruder.getMetaData())))
return
self._extruders.append(extruder) position = extruder.getMetaDataEntry("position")
if position is None:
Logger.log("w", "No position defined for extruder {extruder}, cannot add it to stack {stack}", extruder = extruder.id, stack = self.id)
return
if any(item.getId() == extruder.id for item in self._extruders.values()):
Logger.log("w", "Extruder [%s] has already been added to this stack [%s]", extruder.id, self._id)
return
self._extruders[position] = extruder
## Overridden from ContainerStack ## Overridden from ContainerStack
# #
@ -71,6 +80,7 @@ class GlobalStack(CuraContainerStack):
if not self.definition.findDefinitions(key = key): if not self.definition.findDefinitions(key = key):
return None return None
# Handle the "resolve" property.
if self._shouldResolve(key, property_name): if self._shouldResolve(key, property_name):
self._resolving_settings.add(key) self._resolving_settings.add(key)
resolve = super().getProperty(key, "resolve") resolve = super().getProperty(key, "resolve")
@ -78,6 +88,16 @@ class GlobalStack(CuraContainerStack):
if resolve is not None: if resolve is not None:
return resolve return resolve
# Handle the "limit_to_extruder" property.
limit_to_extruder = super().getProperty(key, "limit_to_extruder")
if limit_to_extruder is not None and limit_to_extruder != "-1" and limit_to_extruder in self._extruders:
if super().getProperty(key, "settable_per_extruder"):
result = self._extruders[str(limit_to_extruder)].getProperty(key, property_name)
if result is not None:
return result
else:
Logger.log("e", "Setting {setting} has limit_to_extruder but is not settable per extruder!", setting = key)
return super().getProperty(key, property_name) return super().getProperty(key, property_name)
## Overridden from ContainerStack ## Overridden from ContainerStack

View File

@ -1061,19 +1061,19 @@ class MachineManager(QObject):
# If the machine that is being removed is the currently active machine, set another machine as the active machine. # If the machine that is being removed is the currently active machine, set another machine as the active machine.
activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id) activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id)
ExtruderManager.getInstance().removeMachineExtruders(machine_id) # activate a new machine before removing a machine because this is safer
if activate_new_machine:
machine_stacks = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
other_machine_stacks = [s for s in machine_stacks if s.getId() != machine_id]
if other_machine_stacks:
Application.getInstance().setGlobalContainerStack(other_machine_stacks[0])
ExtruderManager.getInstance().removeMachineExtruders(machine_id)
containers = ContainerRegistry.getInstance().findInstanceContainers(type = "user", machine = machine_id) containers = ContainerRegistry.getInstance().findInstanceContainers(type = "user", machine = machine_id)
for container in containers: for container in containers:
ContainerRegistry.getInstance().removeContainer(container.getId()) ContainerRegistry.getInstance().removeContainer(container.getId())
ContainerRegistry.getInstance().removeContainer(machine_id) ContainerRegistry.getInstance().removeContainer(machine_id)
if activate_new_machine:
stacks = ContainerRegistry.getInstance().findContainerStacks(type = "machine")
if stacks:
Application.getInstance().setGlobalContainerStack(stacks[0])
@pyqtProperty(bool, notify = globalContainerChanged) @pyqtProperty(bool, notify = globalContainerChanged)
def hasMaterials(self) -> bool: def hasMaterials(self) -> bool:
if self._global_container_stack: if self._global_container_stack:

View File

@ -1,3 +1,6 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from UM.Workspace.WorkspaceReader import WorkspaceReader from UM.Workspace.WorkspaceReader import WorkspaceReader
from UM.Application import Application from UM.Application import Application
@ -43,6 +46,12 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._extruder_stack_suffix = "." + ContainerRegistry.getMimeTypeForContainer(ExtruderStack).preferredSuffix self._extruder_stack_suffix = "." + ContainerRegistry.getMimeTypeForContainer(ExtruderStack).preferredSuffix
self._global_stack_suffix = "." + ContainerRegistry.getMimeTypeForContainer(GlobalStack).preferredSuffix self._global_stack_suffix = "." + ContainerRegistry.getMimeTypeForContainer(GlobalStack).preferredSuffix
# Certain instance container types are ignored because we make the assumption that only we make those types
# of containers. They are:
# - quality
# - variant
self._ignored_instance_container_types = {"quality", "variant"}
self._resolve_strategies = {} self._resolve_strategies = {}
self._id_mapping = {} self._id_mapping = {}
@ -180,6 +189,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
num_user_settings = 0 num_user_settings = 0
quality_changes_conflict = False quality_changes_conflict = False
definition_changes_conflict = False definition_changes_conflict = False
for each_instance_container_file in instance_container_files: for each_instance_container_file in instance_container_files:
container_id = self._stripFileToId(each_instance_container_file) container_id = self._stripFileToId(each_instance_container_file)
instance_container = InstanceContainer(container_id) instance_container = InstanceContainer(container_id)
@ -205,14 +215,12 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
if definition_changes: if definition_changes:
if definition_changes[0] != instance_container: if definition_changes[0] != instance_container:
definition_changes_conflict = True definition_changes_conflict = True
elif container_type == "quality":
# If the quality name is not set (either by quality or changes, set it now)
# Quality changes should always override this (as they are "on top")
if quality_name == "":
quality_name = instance_container.getName()
quality_type = instance_container.getName()
elif container_type == "user": elif container_type == "user":
num_user_settings += len(instance_container._instances) num_user_settings += len(instance_container._instances)
elif container_type in self._ignored_instance_container_types:
# Ignore certain instance container types
Logger.log("w", "Ignoring instance container [%s] with type [%s]", container_id, container_type)
continue
Job.yieldThread() Job.yieldThread()
@ -303,6 +311,19 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
return WorkspaceReader.PreReadResult.accepted return WorkspaceReader.PreReadResult.accepted
## Overrides an ExtruderStack in the given GlobalStack and returns the new ExtruderStack.
def _overrideExtruderStack(self, global_stack, extruder_index, extruder_file_content):
extruder_index_str = str(extruder_index)
extruder_stack = global_stack.extruders[extruder_index_str]
old_extruder_stack_id = extruder_stack.getId()
# override the given extruder stack
extruder_stack.deserialize(extruder_file_content)
# return the new ExtruderStack
return extruder_stack
## Read the project file ## Read the project file
# Add all the definitions / materials / quality changes that do not exist yet. Then it loads # Add all the definitions / materials / quality changes that do not exist yet. Then it loads
# all the stacks into the container registry. In some cases it will reuse the container for the global stack. # all the stacks into the container registry. In some cases it will reuse the container for the global stack.
@ -355,12 +376,23 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
global_stack_id_original = self._stripFileToId(global_stack_file) global_stack_id_original = self._stripFileToId(global_stack_file)
global_stack_id_new = global_stack_id_original global_stack_id_new = global_stack_id_original
global_stack_need_rename = False global_stack_need_rename = False
extruder_stack_id_map = {} # new and old ExtruderStack IDs map
if self._resolve_strategies["machine"] == "new": if self._resolve_strategies["machine"] == "new":
# We need a new id if the id already exists # We need a new id if the id already exists
if self._container_registry.findContainerStacks(id = global_stack_id_original): if self._container_registry.findContainerStacks(id = global_stack_id_original):
global_stack_id_new = self.getNewId(global_stack_id_original) global_stack_id_new = self.getNewId(global_stack_id_original)
global_stack_need_rename = True global_stack_need_rename = True
for each_extruder_stack_file in extruder_stack_files:
old_container_id = self._stripFileToId(each_extruder_stack_file)
new_container_id = old_container_id
if self._container_registry.findContainerStacks(id = old_container_id):
# get a new name for this extruder
new_container_id = self.getNewId(old_container_id)
extruder_stack_id_map[old_container_id] = new_container_id
# TODO: For the moment we use pretty naive existence checking. If the ID is the same, we assume in quite a few # TODO: For the moment we use pretty naive existence checking. If the ID is the same, we assume in quite a few
# TODO: cases that the container loaded is the same (most notable in materials & definitions). # TODO: cases that the container loaded is the same (most notable in materials & definitions).
# TODO: It might be possible that we need to add smarter checking in the future. # TODO: It might be possible that we need to add smarter checking in the future.
@ -414,13 +446,35 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
quality_and_definition_changes_instance_containers = [] quality_and_definition_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")
# HACK! we ignore the "metadata/type = quality" instance containers!
parser = configparser.ConfigParser()
parser.read_string(serialized)
if not parser.has_option("metadata", "type"):
Logger.log("w", "Cannot find metadata/type in %s, ignoring it", instance_container_file)
continue
if parser.get("metadata", "type") == "quality":
continue
instance_container = InstanceContainer(container_id) instance_container = InstanceContainer(container_id)
# Deserialize InstanceContainer by converting read data from bytes to string # Deserialize InstanceContainer by converting read data from bytes to string
instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8")) instance_container.deserialize(serialized)
container_type = instance_container.getMetaDataEntry("type") container_type = instance_container.getMetaDataEntry("type")
Job.yieldThread() Job.yieldThread()
if container_type == "user":
#
# IMPORTANT:
# If an instance container (or maybe other type of container) exists, and user chooses "Create New",
# we need to rename this container and all references to it, and changing those references are VERY
# HARD.
#
if container_type in self._ignored_instance_container_types:
# Ignore certain instance container types
Logger.log("w", "Ignoring instance container [%s] with type [%s]", container_id, container_type)
continue
elif container_type == "user":
# Check if quality changes already exists. # Check if quality changes already exists.
user_containers = self._container_registry.findInstanceContainers(id = container_id) user_containers = self._container_registry.findInstanceContainers(id = container_id)
if not user_containers: if not user_containers:
@ -432,9 +486,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
instance_container.setDirty(True) instance_container.setDirty(True)
elif self._resolve_strategies["machine"] == "new": elif self._resolve_strategies["machine"] == "new":
# The machine is going to get a spiffy new name, so ensure that the id's of user settings match. # The machine is going to get a spiffy new name, so ensure that the id's of user settings match.
extruder_id = instance_container.getMetaDataEntry("extruder", None) old_extruder_id = instance_container.getMetaDataEntry("extruder", None)
if extruder_id: if old_extruder_id:
new_extruder_id = self.getNewId(extruder_id) new_extruder_id = extruder_stack_id_map[old_extruder_id]
new_id = new_extruder_id + "_current_settings" new_id = new_extruder_id + "_current_settings"
instance_container._id = new_id instance_container._id = new_id
instance_container.setName(new_id) instance_container.setName(new_id)
@ -454,26 +508,30 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# Check if quality changes already exists. # Check if quality changes already exists.
changes_containers = self._container_registry.findInstanceContainers(id = container_id) changes_containers = self._container_registry.findInstanceContainers(id = container_id)
if not changes_containers: if not changes_containers:
# no existing containers with the same ID, so we can safely add the new one
containers_to_add.append(instance_container) containers_to_add.append(instance_container)
else: else:
# we have found existing container with the same ID, so we need to resolve according to the
# selected strategy.
if self._resolve_strategies[container_type] == "override": if self._resolve_strategies[container_type] == "override":
instance_container = changes_containers[0] instance_container = changes_containers[0]
instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8")) instance_container.deserialize(archive.open(instance_container_file).read().decode("utf-8"))
instance_container.setDirty(True) instance_container.setDirty(True)
elif self._resolve_strategies[container_type] == "new": elif self._resolve_strategies[container_type] == "new":
# TODO: how should we handle the case "new" for quality_changes and definition_changes? # TODO: how should we handle the case "new" for quality_changes and definition_changes?
instance_container.setName(self._container_registry.uniqueName(instance_container.getName()))
new_changes_container_id = self.getNewId(instance_container.getId()) new_changes_container_id = self.getNewId(instance_container.getId())
instance_container._id = new_changes_container_id instance_container._id = new_changes_container_id
instance_container.setName(new_changes_container_id)
# TODO: we don't know the following is correct or not, need to verify # TODO: we don't know the following is correct or not, need to verify
# AND REFACTOR!!! # AND REFACTOR!!!
if self._resolve_strategies["machine"] == "new": if self._resolve_strategies["machine"] == "new":
# The machine is going to get a spiffy new name, so ensure that the id's of user settings match. # The machine is going to get a spiffy new name, so ensure that the id's of user settings match.
extruder_id = instance_container.getMetaDataEntry("extruder", None) old_extruder_id = instance_container.getMetaDataEntry("extruder", None)
if extruder_id: if old_extruder_id:
new_extruder_id = self.getNewId(extruder_id) new_extruder_id = extruder_stack_id_map[old_extruder_id]
instance_container.setMetaDataEntry("extruder", new_extruder_id) instance_container.setMetaDataEntry("extruder", new_extruder_id)
machine_id = instance_container.getMetaDataEntry("machine", None) machine_id = instance_container.getMetaDataEntry("machine", None)
@ -563,18 +621,36 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
try: try:
for index, extruder_stack_file in enumerate(extruder_stack_files): for index, extruder_stack_file in enumerate(extruder_stack_files):
container_id = self._stripFileToId(extruder_stack_file) container_id = self._stripFileToId(extruder_stack_file)
extruder_file_content = archive.open(extruder_stack_file, "r").read().decode("utf-8")
container_stacks = self._container_registry.findContainerStacks(id = container_id) container_stacks = self._container_registry.findContainerStacks(id = container_id)
if container_stacks: if container_stacks:
# this container stack already exists, try to resolve # this container stack already exists, try to resolve
stack = container_stacks[0] stack = container_stacks[0]
if self._resolve_strategies["machine"] == "override": if self._resolve_strategies["machine"] == "override":
pass # do nothing # NOTE: This is the same code as those in the lower part
# deserialize new extruder stack over the current ones
stack = self._overrideExtruderStack(global_stack, index, extruder_file_content)
elif self._resolve_strategies["machine"] == "new": elif self._resolve_strategies["machine"] == "new":
# create a new extruder stack from this one # create a new extruder stack from this one
new_id = self.getNewId(container_id) new_id = extruder_stack_id_map[container_id]
stack = ExtruderStack(new_id) stack = ExtruderStack(new_id)
stack.deserialize(archive.open(extruder_stack_file).read().decode("utf-8"))
# HACK: the global stack can have a new name, so we need to make sure that this extruder stack
# references to the new name instead of the old one. Normally, this can be done after
# deserialize() by setting the metadata, but in the case of ExtruderStack, deserialize()
# also does addExtruder() to its machine stack, so we have to make sure that it's pointing
# to the right machine BEFORE deserialization.
extruder_config = configparser.ConfigParser()
extruder_config.read_string(extruder_file_content)
extruder_config.set("metadata", "machine", global_stack_id_new)
tmp_string_io = io.StringIO()
extruder_config.write(tmp_string_io)
extruder_file_content = tmp_string_io.getvalue()
stack.deserialize(extruder_file_content)
# Ensure a unique ID and name # Ensure a unique ID and name
stack._id = new_id stack._id = new_id
@ -583,37 +659,34 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
extruder_stacks_added.append(stack) extruder_stacks_added.append(stack)
containers_added.append(stack) containers_added.append(stack)
else: else:
# No extruder stack with the same ID can be found
if self._resolve_strategies["machine"] == "override": if self._resolve_strategies["machine"] == "override":
global_stacks = self._container_registry.findContainerStacks(id = global_stack_id_original)
# deserialize new extruder stack over the current ones # deserialize new extruder stack over the current ones
if global_stacks: stack = self._overrideExtruderStack(global_stack, index, extruder_file_content)
old_extruder_stack_id = global_stacks[0].extruders[index].getId()
# HACK delete file
self._container_registry._deleteFiles(global_stacks[0].extruders[index])
global_stacks[0].extruders[index].deserialize(archive.open(extruder_stack_file).read().decode("utf-8"))
# HACK
global_stacks[0]._extruders = global_stacks[0]._extruders[:2]
# HACK update cache
del self._container_registry._id_container_cache[old_extruder_stack_id]
new_extruder_stack_id = global_stacks[0].extruders[index].getId()
self._container_registry._id_container_cache[new_extruder_stack_id] = global_stacks[0].extruders[index]
stack = global_stacks[0].extruders[index]
else:
Logger.log("w", "Could not find global stack, while I expected it: %s" % global_stack_id_original)
elif self._resolve_strategies["machine"] == "new": elif self._resolve_strategies["machine"] == "new":
# container not found, create a new one # container not found, create a new one
stack = ExtruderStack(container_id) stack = ExtruderStack(container_id)
stack.deserialize(archive.open(extruder_stack_file).read().decode("utf-8"))
# HACK: the global stack can have a new name, so we need to make sure that this extruder stack
# references to the new name instead of the old one. Normally, this can be done after
# deserialize() by setting the metadata, but in the case of ExtruderStack, deserialize()
# also does addExtruder() to its machine stack, so we have to make sure that it's pointing
# to the right machine BEFORE deserialization.
extruder_config = configparser.ConfigParser()
extruder_config.read_string(extruder_file_content)
extruder_config.set("metadata", "machine", global_stack_id_new)
tmp_string_io = io.StringIO()
extruder_config.write(tmp_string_io)
extruder_file_content = tmp_string_io.getvalue()
stack.deserialize(extruder_file_content)
self._container_registry.addContainer(stack) self._container_registry.addContainer(stack)
extruder_stacks_added.append(stack) extruder_stacks_added.append(stack)
containers_added.append(stack) containers_added.append(stack)
else: else:
Logger.log("w", "Unknown resolve strategy: %s" % str(self._resolve_strategies["machine"])) Logger.log("w", "Unknown resolve strategy: %s" % str(self._resolve_strategies["machine"]))
if global_stack_need_rename:
if stack.getMetaDataEntry("machine"):
stack.setMetaDataEntry("machine", global_stack_id_new)
extruder_stacks.append(stack) extruder_stacks.append(stack)
except: except:
Logger.logException("w", "We failed to serialize the stack. Trying to clean up.") Logger.logException("w", "We failed to serialize the stack. Trying to clean up.")
@ -649,13 +722,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
if self._resolve_strategies[changes_container_type] == "new": if self._resolve_strategies[changes_container_type] == "new":
# Quality changes needs to get a new ID, added to registry and to the right stacks # Quality changes needs to get a new ID, added to registry and to the right stacks
for each_changes_container in quality_and_definition_changes_instance_containers: for each_changes_container in quality_and_definition_changes_instance_containers:
old_id = each_changes_container.getId() # NOTE: The renaming and giving new IDs are possibly redundant because they are done in the
each_changes_container.setName(self._container_registry.uniqueName(each_changes_container.getName())) # instance container loading part.
# We're not really supposed to change the ID in normal cases, but this is an exception. new_id = each_changes_container.getId()
each_changes_container._id = self.getNewId(each_changes_container.getId())
# The container was not added yet, as it didn't have an unique ID. It does now, so add it.
self._container_registry.addContainer(each_changes_container)
# Find the old (current) changes container in the global stack # Find the old (current) changes container in the global stack
if changes_container_type == "quality_changes": if changes_container_type == "quality_changes":
@ -672,7 +741,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# Replace the quality/definition changes container if it's in the GlobalStack # Replace the quality/definition changes container if it's in the GlobalStack
# NOTE: we can get an empty container here, but the IDs will not match, # NOTE: we can get an empty container here, but the IDs will not match,
# so this comparison is fine. # so this comparison is fine.
if old_container.getId() == old_id: if self._id_mapping.get(old_container.getId()) == new_id:
if changes_container_type == "quality_changes": if changes_container_type == "quality_changes":
global_stack.qualityChanges = each_changes_container global_stack.qualityChanges = each_changes_container
elif changes_container_type == "definition_changes": elif changes_container_type == "definition_changes":
@ -695,7 +764,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# NOTE: we can get an empty container here, but the IDs will not match, # NOTE: we can get an empty container here, but the IDs will not match,
# so this comparison is fine. # so this comparison is fine.
if changes_container.getId() == old_id: if self._id_mapping.get(changes_container.getId()) == new_id:
if changes_container_type == "quality_changes": if changes_container_type == "quality_changes":
each_extruder_stack.qualityChanges = each_changes_container each_extruder_stack.qualityChanges = each_changes_container
elif changes_container_type == "definition_changes": elif changes_container_type == "definition_changes":

View File

@ -7,6 +7,7 @@ from cura.Settings.ExtruderManager import ExtruderManager
import zipfile import zipfile
from io import StringIO from io import StringIO
import copy import copy
import configparser
class ThreeMFWorkspaceWriter(WorkspaceWriter): class ThreeMFWorkspaceWriter(WorkspaceWriter):
@ -48,6 +49,16 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter):
Preferences.getInstance().writeToFile(preferences_string) Preferences.getInstance().writeToFile(preferences_string)
archive.writestr(preferences_file, preferences_string.getvalue()) archive.writestr(preferences_file, preferences_string.getvalue())
# Save Cura version
version_file = zipfile.ZipInfo("Cura/version.ini")
version_config_parser = configparser.ConfigParser()
version_config_parser.add_section("versions")
version_config_parser.set("versions", "cura_version", Application.getStaticVersion())
version_file_string = StringIO()
version_config_parser.write(version_file_string)
archive.writestr(version_file, version_file_string.getvalue())
# Close the archive & reset states. # Close the archive & reset states.
archive.close() archive.close()
mesh_writer.setStoreArchive(False) mesh_writer.setStoreArchive(False)

View File

@ -79,7 +79,7 @@ The initial and final printing temperatures reduce the amount of oozing during P
Initial and final printing temperature settings have been tuned for higher quality results. For all materials the initial print temperature is 5 degrees above the default value. Initial and final printing temperature settings have been tuned for higher quality results. For all materials the initial print temperature is 5 degrees above the default value.
*Printing temperature of the materials *Printing temperature of the materials
The printing temperature of the materials in the material profiles is now the same as the printing temperature for the Normal Quality profile. The printing temperature of the materials in the material profiles is now the same as the printing temperature for the Fine profile.
*Improved PLA-PVA layer adhesion *Improved PLA-PVA layer adhesion
The PVA jerk and acceleration have been optimized to improve the layer adhesion between PVA and PLA. The PVA jerk and acceleration have been optimized to improve the layer adhesion between PVA and PLA.

View File

@ -14,7 +14,7 @@ from UM.Settings.DefinitionContainer import DefinitionContainer
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Logger import Logger from UM.Logger import Logger
from cura.Settings.CuraContainerRegistry import CuraContainerRegistry from cura.CuraApplication import CuraApplication
from cura.Settings.ExtruderManager import ExtruderManager from cura.Settings.ExtruderManager import ExtruderManager
import UM.i18n import UM.i18n
@ -99,6 +99,7 @@ class MachineSettingsAction(MachineAction):
definition = container_stack.getBottom() definition = container_stack.getBottom()
definition_changes_container.setDefinition(definition) definition_changes_container.setDefinition(definition)
definition_changes_container.addMetaDataEntry("type", "definition_changes") definition_changes_container.addMetaDataEntry("type", "definition_changes")
definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
self._container_registry.addContainer(definition_changes_container) self._container_registry.addContainer(definition_changes_container)
container_stack.definitionChanges = definition_changes_container container_stack.definitionChanges = definition_changes_container

View File

@ -341,7 +341,6 @@ Cura.MachineAction
sourceComponent: numericTextFieldWithUnit sourceComponent: numericTextFieldWithUnit
property var propertyProvider: gantryHeightProvider property var propertyProvider: gantryHeightProvider
property string unit: catalog.i18nc("@label", "mm") property string unit: catalog.i18nc("@label", "mm")
property bool forceUpdateOnChange: false
} }
Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height } Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height }
@ -385,7 +384,6 @@ Cura.MachineAction
sourceComponent: numericTextFieldWithUnit sourceComponent: numericTextFieldWithUnit
property var propertyProvider: materialDiameterProvider property var propertyProvider: materialDiameterProvider
property string unit: catalog.i18nc("@label", "mm") property string unit: catalog.i18nc("@label", "mm")
property bool forceUpdateOnChange: false
} }
Label Label
{ {
@ -399,7 +397,6 @@ Cura.MachineAction
sourceComponent: numericTextFieldWithUnit sourceComponent: numericTextFieldWithUnit
property var propertyProvider: machineNozzleSizeProvider property var propertyProvider: machineNozzleSizeProvider
property string unit: catalog.i18nc("@label", "mm") property string unit: catalog.i18nc("@label", "mm")
property bool forceUpdateOnChange: false
} }
} }
} }
@ -550,7 +547,6 @@ Cura.MachineAction
sourceComponent: numericTextFieldWithUnit sourceComponent: numericTextFieldWithUnit
property var propertyProvider: extruderNozzleSizeProvider property var propertyProvider: extruderNozzleSizeProvider
property string unit: catalog.i18nc("@label", "mm") property string unit: catalog.i18nc("@label", "mm")
property bool forceUpdateOnChange: false
} }
Label Label
@ -564,6 +560,7 @@ Cura.MachineAction
property var propertyProvider: extruderOffsetXProvider property var propertyProvider: extruderOffsetXProvider
property string unit: catalog.i18nc("@label", "mm") property string unit: catalog.i18nc("@label", "mm")
property bool forceUpdateOnChange: true property bool forceUpdateOnChange: true
property bool allowNegative: true
} }
Label Label
{ {
@ -576,6 +573,7 @@ Cura.MachineAction
property var propertyProvider: extruderOffsetYProvider property var propertyProvider: extruderOffsetYProvider
property string unit: catalog.i18nc("@label", "mm") property string unit: catalog.i18nc("@label", "mm")
property bool forceUpdateOnChange: true property bool forceUpdateOnChange: true
property bool allowNegative: true
} }
} }
@ -655,17 +653,21 @@ Cura.MachineAction
Item { Item {
height: textField.height height: textField.height
width: textField.width width: textField.width
property bool _allowNegative: (typeof(allowNegative) === 'undefined') ? false : allowNegative
property bool _forceUpdateOnChange: (typeof(forceUpdateOnChange) === 'undefined') ? false: forceUpdateOnChange
TextField TextField
{ {
id: textField id: textField
text: (propertyProvider.properties.value) ? propertyProvider.properties.value : "" text: (propertyProvider.properties.value) ? propertyProvider.properties.value : ""
validator: RegExpValidator { regExp: /[0-9\.]{0,6}/ } validator: RegExpValidator { regExp: _allowNegative ? /-?[0-9\.]{0,6}/ : /[0-9\.]{0,6}/ }
onEditingFinished: onEditingFinished:
{ {
if (propertyProvider && text != propertyProvider.properties.value) if (propertyProvider && text != propertyProvider.properties.value)
{ {
propertyProvider.setPropertyValue("value", text); propertyProvider.setPropertyValue("value", text);
if(forceUpdateOnChange) if(_forceUpdateOnChange)
{ {
var extruderIndex = ExtruderManager.activeExtruderIndex; var extruderIndex = ExtruderManager.activeExtruderIndex;
manager.forceUpdate(); manager.forceUpdate();

View File

@ -11,7 +11,7 @@ from UM.Application import Application
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
import UM.Settings.InstanceContainer import UM.Settings.InstanceContainer
from cura.CuraApplication import CuraApplication
## The Ultimaker Original can have a few revisions & upgrades. This action helps with selecting them, so they are added ## The Ultimaker Original can have a few revisions & upgrades. This action helps with selecting them, so they are added
# as a variant. # as a variant.
@ -49,6 +49,7 @@ class UMOUpgradeSelection(MachineAction):
definition = global_container_stack.getBottom() definition = global_container_stack.getBottom()
definition_changes_container.setDefinition(definition) definition_changes_container.setDefinition(definition)
definition_changes_container.addMetaDataEntry("type", "definition_changes") definition_changes_container.addMetaDataEntry("type", "definition_changes")
definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion)
UM.Settings.ContainerRegistry.ContainerRegistry.getInstance().addContainer(definition_changes_container) UM.Settings.ContainerRegistry.ContainerRegistry.getInstance().addContainer(definition_changes_container)
# Insert definition_changes between the definition and the variant # Insert definition_changes between the definition and the variant

View File

@ -142,6 +142,16 @@ class VersionUpgrade22to24(VersionUpgrade):
config.write(output) config.write(output)
return [filename], [output.getvalue()] return [filename], [output.getvalue()]
def upgradeQuality(self, serialised, filename):
config = configparser.ConfigParser(interpolation = None)
config.read_string(serialised) # Read the input string as config file.
config.set("metadata", "type", "quality_changes") # Update metadata/type to quality_changes
config.set("general", "version", "2") # Just bump the version number. That is all we need for now.
output = io.StringIO()
config.write(output)
return [filename], [output.getvalue()]
def getCfgVersion(self, serialised): def getCfgVersion(self, serialised):
parser = configparser.ConfigParser(interpolation = None) parser = configparser.ConfigParser(interpolation = None)
parser.read_string(serialised) parser.read_string(serialised)

View File

@ -21,9 +21,9 @@ def getMetaData():
# From To Upgrade function # From To Upgrade function
("machine_instance", 2000000): ("machine_stack", 3000000, upgrade.upgradeMachineInstance), ("machine_instance", 2000000): ("machine_stack", 3000000, upgrade.upgradeMachineInstance),
("extruder_train", 2000000): ("extruder_train", 3000000, upgrade.upgradeExtruderTrain), ("extruder_train", 2000000): ("extruder_train", 3000000, upgrade.upgradeExtruderTrain),
("preferences", 3000000): ("preferences", 4000000, upgrade.upgradePreferences) ("preferences", 3000000): ("preferences", 4000000, upgrade.upgradePreferences),
("quality", 2000000): ("quality_changes", 2000000, upgrade.upgradeQuality),
}, },
"sources": { "sources": {
"machine_stack": { "machine_stack": {
"get_version": upgrade.getCfgVersion, "get_version": upgrade.getCfgVersion,

View File

@ -5,6 +5,7 @@ import configparser #To parse the files we need to upgrade and write the new fil
import io #To serialise configparser output to a string. import io #To serialise configparser output to a string.
from UM.VersionUpgrade import VersionUpgrade from UM.VersionUpgrade import VersionUpgrade
from cura.CuraApplication import CuraApplication
_removed_settings = { #Settings that were removed in 2.5. _removed_settings = { #Settings that were removed in 2.5.
"start_layers_at_same_position", "start_layers_at_same_position",
@ -86,11 +87,17 @@ class VersionUpgrade25to26(VersionUpgrade):
parser["values"][replacement] = parser["values"][replaced_setting] #Copy to replacement before removing the original! parser["values"][replacement] = parser["values"][replaced_setting] #Copy to replacement before removing the original!
del replaced_setting del replaced_setting
#Change the version number in the file. for each_section in ("general", "metadata"):
if parser.has_section("general"): if not parser.has_section(each_section):
parser["general"]["setting_version"] = "1" parser.add_section(each_section)
# Change the version number in the file.
parser["metadata"]["setting_version"] = str(CuraApplication.SettingVersion)
# Update version
parser["general"]["version"] = "2"
#Re-serialise the file. #Re-serialise the file.
output = io.StringIO() output = io.StringIO()
parser.write(output) parser.write(output)
return [filename], [output.getvalue()] return [filename], [output.getvalue()]

View File

@ -18,14 +18,16 @@ def getMetaData():
"api": 3 "api": 3
}, },
"version_upgrade": { "version_upgrade": {
# From To Upgrade function # From To Upgrade function
("preferences", 4000000): ("preferences", 4000001, upgrade.upgradePreferences), ("preferences", 4000000): ("preferences", 4000001, upgrade.upgradePreferences),
("quality", 2000000): ("quality", 2000001, upgrade.upgradeInstanceContainer), # NOTE: All the instance containers share the same general/version, so we have to update all of them
("variant", 2000000): ("variant", 2000001, upgrade.upgradeInstanceContainer), #We can re-use upgradeContainerStack since there is nothing specific to quality, variant or user profiles being changed. # if any is updated.
("user", 2000000): ("user", 2000001, upgrade.upgradeInstanceContainer) ("quality_changes", 2000000): ("quality_changes", 2000001, upgrade.upgradeInstanceContainer),
("user", 2000000): ("user", 2000001, upgrade.upgradeInstanceContainer),
("quality", 2000000): ("quality", 2000001, upgrade.upgradeInstanceContainer),
}, },
"sources": { "sources": {
"quality": { "quality_changes": {
"get_version": upgrade.getCfgVersion, "get_version": upgrade.getCfgVersion,
"location": {"./quality"} "location": {"./quality"}
}, },
@ -36,7 +38,7 @@ def getMetaData():
"user": { "user": {
"get_version": upgrade.getCfgVersion, "get_version": upgrade.getCfgVersion,
"location": {"./user"} "location": {"./user"}
} },
} }
} }

View File

@ -159,10 +159,10 @@ class XmlMaterialProfile(InstanceContainer):
for key, value in metadata.items(): for key, value in metadata.items():
builder.start(key) builder.start(key)
# Normally value is a string. if value is not None: #Nones get handled well by the builder.
# Nones get handled well. #Otherwise the builder always expects a string.
if isinstance(value, bool): #Deserialize expects the stringified version.
value = str(value) # parseBool in deserialize expects 'True'. value = str(value)
builder.data(value) builder.data(value)
builder.end(key) builder.end(key)

View File

@ -19,7 +19,7 @@
"variants_name": "Nozzle size", "variants_name": "Nozzle size",
"preferred_variant": "*0.8*", "preferred_variant": "*0.8*",
"preferred_material": "*pla*", "preferred_material": "*pla*",
"preferred_quality": "*high*", "preferred_quality": "*normal*",
"machine_extruder_trains": "machine_extruder_trains":
{ {

View File

@ -1323,7 +1323,7 @@
"default_value": 0, "default_value": 0,
"type": "int", "type": "int",
"minimum_value": "0", "minimum_value": "0",
"maximum_value_warning": "4", "maximum_value_warning": "5",
"maximum_value": "0 if spaghetti_infill_enabled else (999999 if infill_line_distance == 0 else (20 - math.log(infill_line_distance) / math.log(2)))", "maximum_value": "0 if spaghetti_infill_enabled else (999999 if infill_line_distance == 0 else (20 - math.log(infill_line_distance) / math.log(2)))",
"enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv' and not spaghetti_infill_enabled", "enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv' and not spaghetti_infill_enabled",
"settable_per_mesh": true "settable_per_mesh": true
@ -1953,7 +1953,7 @@
"minimum_value": "0.1", "minimum_value": "0.1",
"maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)", "maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
"maximum_value_warning": "150", "maximum_value_warning": "150",
"enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", "enabled": "support_interface_enable and support_enable",
"limit_to_extruder": "support_interface_extruder_nr", "limit_to_extruder": "support_interface_extruder_nr",
"value": "speed_support / 1.5", "value": "speed_support / 1.5",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -1970,7 +1970,7 @@
"minimum_value": "0.1", "minimum_value": "0.1",
"maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)", "maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
"maximum_value_warning": "150", "maximum_value_warning": "150",
"enabled": "extruderValue(support_roof_extruder_nr, 'support_roof_enable') and support_enable", "enabled": "support_roof_enable and support_enable",
"limit_to_extruder": "support_roof_extruder_nr", "limit_to_extruder": "support_roof_extruder_nr",
"value": "speed_support_interface", "value": "speed_support_interface",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -1986,7 +1986,7 @@
"minimum_value": "0.1", "minimum_value": "0.1",
"maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)", "maximum_value": "math.sqrt(machine_max_feedrate_x ** 2 + machine_max_feedrate_y ** 2)",
"maximum_value_warning": "150", "maximum_value_warning": "150",
"enabled": "extruderValue(support_bottom_extruder_nr, 'support_bottom_enable') and support_enable", "enabled": "support_bottom_enable and support_enable",
"limit_to_extruder": "support_bottom_extruder_nr", "limit_to_extruder": "support_bottom_extruder_nr",
"value": "speed_support_interface", "value": "speed_support_interface",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -2275,7 +2275,7 @@
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "100", "minimum_value_warning": "100",
"maximum_value_warning": "10000", "maximum_value_warning": "10000",
"enabled": "resolveOrValue('acceleration_enabled') and extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", "enabled": "resolveOrValue('acceleration_enabled') and support_interface_enable and support_enable",
"limit_to_extruder": "support_interface_extruder_nr", "limit_to_extruder": "support_interface_extruder_nr",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -2292,7 +2292,7 @@
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "100", "minimum_value_warning": "100",
"maximum_value_warning": "10000", "maximum_value_warning": "10000",
"enabled": "resolveOrValue('acceleration_enabled') and extruderValue(support_roof_extruder_nr, 'support_roof_enable') and support_enable", "enabled": "resolveOrValue('acceleration_enabled') and support_roof_enable and support_enable",
"limit_to_extruder": "support_roof_extruder_nr", "limit_to_extruder": "support_roof_extruder_nr",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
@ -2308,7 +2308,7 @@
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "100", "minimum_value_warning": "100",
"maximum_value_warning": "10000", "maximum_value_warning": "10000",
"enabled": "resolveOrValue('acceleration_enabled') and extruderValue(support_bottom_extruder_nr, 'support_bottom_enable') and support_enable", "enabled": "resolveOrValue('acceleration_enabled') and support_bottom_enable and support_enable",
"limit_to_extruder": "support_bottom_extruder_nr", "limit_to_extruder": "support_bottom_extruder_nr",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
@ -2540,7 +2540,7 @@
"value": "jerk_support", "value": "jerk_support",
"minimum_value": "0.1", "minimum_value": "0.1",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled') and extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", "enabled": "resolveOrValue('jerk_enabled') and support_interface_enable and support_enable",
"limit_to_extruder": "support_interface_extruder_nr", "limit_to_extruder": "support_interface_extruder_nr",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -2555,9 +2555,8 @@
"default_value": 20, "default_value": 20,
"value": "jerk_support_interface", "value": "jerk_support_interface",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled') and extruderValue(support_roof_extruder_nr, 'support_roof_enable') and support_enable", "enabled": "resolveOrValue('jerk_enabled') and support_roof_enable and support_enable",
"limit_to_extruder": "support_roof_extruder_nr", "limit_to_extruder": "support_roof_extruder_nr",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
@ -2571,9 +2570,8 @@
"default_value": 20, "default_value": 20,
"value": "jerk_support_interface", "value": "jerk_support_interface",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled') and extruderValue(support_bottom_extruder_nr, 'support_bottom_enable') and support_enable", "enabled": "resolveOrValue('jerk_enabled') and support_bottom_enable and support_enable",
"limit_to_extruder": "support_bottom_extruder_nr", "limit_to_extruder": "support_bottom_extruder_nr",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
@ -3158,7 +3156,7 @@
"default_value": 0.1, "default_value": 0.1,
"type": "float", "type": "float",
"enabled": "support_enable", "enabled": "support_enable",
"value": "extruderValue(support_extruder_nr, 'support_z_distance')", "value": "support_z_distance",
"limit_to_extruder": "support_roof_extruder_nr if support_roof_enable else support_infill_extruder_nr", "limit_to_extruder": "support_roof_extruder_nr if support_roof_enable else support_infill_extruder_nr",
"settable_per_mesh": true "settable_per_mesh": true
}, },
@ -3170,7 +3168,7 @@
"minimum_value": "0", "minimum_value": "0",
"maximum_value_warning": "machine_nozzle_size", "maximum_value_warning": "machine_nozzle_size",
"default_value": 0.1, "default_value": 0.1,
"value": "extruderValue(support_extruder_nr, 'support_z_distance') if resolveOrValue('support_type') == 'everywhere' else 0", "value": "support_z_distance if support_type == 'everywhere' else 0",
"limit_to_extruder": "support_bottom_extruder_nr if support_bottom_enable else support_infill_extruder_nr", "limit_to_extruder": "support_bottom_extruder_nr if support_bottom_enable else support_infill_extruder_nr",
"type": "float", "type": "float",
"enabled": "support_enable and resolveOrValue('support_type') == 'everywhere'", "enabled": "support_enable and resolveOrValue('support_type') == 'everywhere'",
@ -3213,11 +3211,11 @@
"unit": "mm", "unit": "mm",
"type": "float", "type": "float",
"minimum_value": "0", "minimum_value": "0",
"maximum_value_warning": "extruderValue(support_infill_extruder_nr, 'support_xy_distance')", "maximum_value_warning": "support_xy_distance",
"default_value": 0.2, "default_value": 0.2,
"value": "machine_nozzle_size / 2", "value": "machine_nozzle_size / 2",
"limit_to_extruder": "support_infill_extruder_nr", "limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_enable and extruderValue(support_infill_extruder_nr, 'support_xy_overrides_z') == 'z_overrides_xy'", "enabled": "support_enable and support_xy_overrides_z == 'z_overrides_xy'",
"settable_per_mesh": true "settable_per_mesh": true
}, },
"support_bottom_stair_step_height": "support_bottom_stair_step_height":
@ -3315,10 +3313,10 @@
"type": "float", "type": "float",
"default_value": 1, "default_value": 1,
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "0.2 + resolveOrValue('layer_height')", "minimum_value_warning": "0.2 + layer_height",
"maximum_value_warning": "10", "maximum_value_warning": "10",
"limit_to_extruder": "support_interface_extruder_nr", "limit_to_extruder": "support_interface_extruder_nr",
"enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", "enabled": "support_interface_enable and support_enable",
"settable_per_mesh": true, "settable_per_mesh": true,
"children": "children":
{ {
@ -3330,11 +3328,11 @@
"type": "float", "type": "float",
"default_value": 1, "default_value": 1,
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "0.2 + resolveOrValue('layer_height')", "minimum_value_warning": "0.2 + layer_height",
"maximum_value_warning": "10", "maximum_value_warning": "10",
"value": "extruderValue(support_roof_extruder_nr, 'support_interface_height')", "value": "support_interface_height",
"limit_to_extruder": "support_roof_extruder_nr", "limit_to_extruder": "support_roof_extruder_nr",
"enabled": "extruderValue(support_roof_extruder_nr, 'support_roof_enable') and support_enable", "enabled": "support_roof_enable and support_enable",
"settable_per_mesh": true "settable_per_mesh": true
}, },
"support_bottom_height": "support_bottom_height":
@ -3344,12 +3342,12 @@
"unit": "mm", "unit": "mm",
"type": "float", "type": "float",
"default_value": 1, "default_value": 1,
"value": "extruderValue(support_bottom_extruder_nr, 'support_interface_height')", "value": "support_interface_height",
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "min(0.2 + resolveOrValue('layer_height'), extruderValue(support_bottom_extruder_nr, 'support_bottom_stair_step_height'))", "minimum_value_warning": "min(0.2 + layer_height, support_bottom_stair_step_height)",
"maximum_value_warning": "10", "maximum_value_warning": "10",
"limit_to_extruder": "support_bottom_extruder_nr", "limit_to_extruder": "support_bottom_extruder_nr",
"enabled": "extruderValue(support_bottom_extruder_nr, 'support_bottom_enable') and support_enable", "enabled": "support_bottom_enable and support_enable",
"settable_per_mesh": true "settable_per_mesh": true
} }
} }
@ -3363,7 +3361,7 @@
"minimum_value": "0", "minimum_value": "0",
"maximum_value_warning": "support_interface_height", "maximum_value_warning": "support_interface_height",
"limit_to_extruder": "support_interface_extruder_nr", "limit_to_extruder": "support_interface_extruder_nr",
"enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", "enabled": "support_interface_enable and support_enable",
"settable_per_mesh": true "settable_per_mesh": true
}, },
"support_interface_density": "support_interface_density":
@ -3376,7 +3374,7 @@
"minimum_value": "0", "minimum_value": "0",
"maximum_value_warning": "100", "maximum_value_warning": "100",
"limit_to_extruder": "support_interface_extruder_nr", "limit_to_extruder": "support_interface_extruder_nr",
"enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", "enabled": "support_interface_enable and support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
"children": "children":
@ -3391,7 +3389,7 @@
"minimum_value": "0", "minimum_value": "0",
"maximum_value": "100", "maximum_value": "100",
"limit_to_extruder": "support_roof_extruder_nr", "limit_to_extruder": "support_roof_extruder_nr",
"enabled": "extruderValue(support_roof_extruder_nr, 'support_roof_enable') and support_enable", "enabled": "support_roof_enable and support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
"children": "children":
@ -3407,7 +3405,7 @@
"minimum_value_warning": "support_roof_line_width - 0.0001", "minimum_value_warning": "support_roof_line_width - 0.0001",
"value": "0 if support_roof_density == 0 else (support_roof_line_width * 100) / support_roof_density * (2 if support_roof_pattern == 'grid' else (3 if support_roof_pattern == 'triangles' else 1))", "value": "0 if support_roof_density == 0 else (support_roof_line_width * 100) / support_roof_density * (2 if support_roof_pattern == 'grid' else (3 if support_roof_pattern == 'triangles' else 1))",
"limit_to_extruder": "support_roof_extruder_nr", "limit_to_extruder": "support_roof_extruder_nr",
"enabled": "extruderValue(support_roof_extruder_nr, 'support_roof_enable') and support_enable", "enabled": "support_roof_enable and support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
} }
@ -3423,7 +3421,7 @@
"minimum_value": "0", "minimum_value": "0",
"maximum_value": "100", "maximum_value": "100",
"limit_to_extruder": "support_bottom_extruder_nr", "limit_to_extruder": "support_bottom_extruder_nr",
"enabled": "extruderValue(support_bottom_extruder_nr, 'support_bottom_enable') and support_enable", "enabled": "support_bottom_enable and support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
"children": "children":
@ -3439,7 +3437,7 @@
"minimum_value_warning": "support_bottom_line_width - 0.0001", "minimum_value_warning": "support_bottom_line_width - 0.0001",
"value": "0 if support_bottom_density == 0 else (support_bottom_line_width * 100) / support_bottom_density * (2 if support_bottom_pattern == 'grid' else (3 if support_bottom_pattern == 'triangles' else 1))", "value": "0 if support_bottom_density == 0 else (support_bottom_line_width * 100) / support_bottom_density * (2 if support_bottom_pattern == 'grid' else (3 if support_bottom_pattern == 'triangles' else 1))",
"limit_to_extruder": "support_bottom_extruder_nr", "limit_to_extruder": "support_bottom_extruder_nr",
"enabled": "extruderValue(support_bottom_extruder_nr, 'support_bottom_enable') and support_enable", "enabled": "support_bottom_enable and support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
} }
@ -3463,7 +3461,7 @@
}, },
"default_value": "concentric", "default_value": "concentric",
"limit_to_extruder": "support_interface_extruder_nr", "limit_to_extruder": "support_interface_extruder_nr",
"enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", "enabled": "support_interface_enable and support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
"children": "children":
@ -3485,7 +3483,7 @@
"default_value": "concentric", "default_value": "concentric",
"value": "support_interface_pattern", "value": "support_interface_pattern",
"limit_to_extruder": "support_roof_extruder_nr", "limit_to_extruder": "support_roof_extruder_nr",
"enabled": "extruderValue(support_roof_extruder_nr, 'support_roof_enable') and support_enable", "enabled": "support_roof_enable and support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
}, },
@ -3506,7 +3504,7 @@
"default_value": "concentric", "default_value": "concentric",
"value": "support_interface_pattern", "value": "support_interface_pattern",
"limit_to_extruder": "support_bottom_extruder_nr", "limit_to_extruder": "support_bottom_extruder_nr",
"enabled": "extruderValue(support_bottom_extruder_nr, 'support_bottom_enable') and support_enable", "enabled": "support_bottom_enable and support_enable",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
} }
@ -3533,7 +3531,7 @@
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "2 * machine_nozzle_size", "minimum_value_warning": "2 * machine_nozzle_size",
"maximum_value_warning": "20", "maximum_value_warning": "20",
"enabled": "support_enable and extruderValue(support_infill_extruder_nr, 'support_use_towers')", "enabled": "support_enable and support_use_towers",
"settable_per_mesh": true "settable_per_mesh": true
}, },
"support_minimal_diameter": "support_minimal_diameter":
@ -3547,8 +3545,8 @@
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "2 * machine_nozzle_size", "minimum_value_warning": "2 * machine_nozzle_size",
"maximum_value_warning": "20", "maximum_value_warning": "20",
"maximum_value": "extruderValue(support_infill_extruder_nr, 'support_tower_diameter')", "maximum_value": "support_tower_diameter",
"enabled": "support_enable and extruderValue(support_infill_extruder_nr, 'support_use_towers')", "enabled": "support_enable and support_use_towers",
"settable_per_mesh": true "settable_per_mesh": true
}, },
"support_tower_roof_angle": "support_tower_roof_angle":
@ -3561,7 +3559,7 @@
"maximum_value": "90", "maximum_value": "90",
"default_value": 65, "default_value": 65,
"limit_to_extruder": "support_infill_extruder_nr", "limit_to_extruder": "support_infill_extruder_nr",
"enabled": "support_enable and extruderValue(support_infill_extruder_nr, 'support_use_towers')", "enabled": "support_enable and support_use_towers",
"settable_per_mesh": true "settable_per_mesh": true
} }
} }
@ -3624,7 +3622,7 @@
"none": "None" "none": "None"
}, },
"default_value": "brim", "default_value": "brim",
"limit_to_extruder": "adhesion_extruder_nr", "resolve": "extruderValue(adhesion_extruder_nr, 'adhesion_type')",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": false "settable_per_extruder": false
}, },
@ -3787,7 +3785,7 @@
"value": "resolveOrValue('layer_height')", "value": "resolveOrValue('layer_height')",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.04", "minimum_value_warning": "0.04",
"maximum_value_warning": "0.75 * extruderValue(adhesion_extruder_nr, 'machine_nozzle_size')", "maximum_value_warning": "0.75 * machine_nozzle_size",
"enabled": "resolveOrValue('adhesion_type') == 'raft'", "enabled": "resolveOrValue('adhesion_type') == 'raft'",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -3802,8 +3800,8 @@
"default_value": 0.4, "default_value": 0.4,
"value": "line_width", "value": "line_width",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "extruderValue(adhesion_extruder_nr, 'machine_nozzle_size') * 0.1", "minimum_value_warning": "machine_nozzle_size * 0.1",
"maximum_value_warning": "extruderValue(adhesion_extruder_nr, 'machine_nozzle_size') * 2", "maximum_value_warning": "machine_nozzle_size * 2",
"enabled": "resolveOrValue('adhesion_type') == 'raft'", "enabled": "resolveOrValue('adhesion_type') == 'raft'",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -3817,8 +3815,8 @@
"type": "float", "type": "float",
"default_value": 0.4, "default_value": 0.4,
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "extruderValue(adhesion_extruder_nr, 'raft_surface_line_width')", "minimum_value_warning": "raft_surface_line_width",
"maximum_value_warning": "extruderValue(adhesion_extruder_nr, 'raft_surface_line_width') * 3", "maximum_value_warning": "raft_surface_line_width * 3",
"enabled": "resolveOrValue('adhesion_type') == 'raft'", "enabled": "resolveOrValue('adhesion_type') == 'raft'",
"value": "raft_surface_line_width", "value": "raft_surface_line_width",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -3835,7 +3833,7 @@
"value": "resolveOrValue('layer_height') * 1.5", "value": "resolveOrValue('layer_height') * 1.5",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.04", "minimum_value_warning": "0.04",
"maximum_value_warning": "0.75 * extruderValue(adhesion_extruder_nr, 'machine_nozzle_size')", "maximum_value_warning": "0.75 * machine_nozzle_size",
"enabled": "resolveOrValue('adhesion_type') == 'raft'", "enabled": "resolveOrValue('adhesion_type') == 'raft'",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -3850,8 +3848,8 @@
"default_value": 0.7, "default_value": 0.7,
"value": "line_width * 2", "value": "line_width * 2",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "extruderValue(adhesion_extruder_nr, 'machine_nozzle_size') * 0.5", "minimum_value_warning": "machine_nozzle_size * 0.5",
"maximum_value_warning": "extruderValue(adhesion_extruder_nr, 'machine_nozzle_size') * 3", "maximum_value_warning": "machine_nozzle_size * 3",
"enabled": "resolveOrValue('adhesion_type') == 'raft'", "enabled": "resolveOrValue('adhesion_type') == 'raft'",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -3866,7 +3864,7 @@
"default_value": 0.9, "default_value": 0.9,
"value": "raft_interface_line_width + 0.2", "value": "raft_interface_line_width + 0.2",
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "extruderValue(adhesion_extruder_nr, 'raft_interface_line_width')", "minimum_value_warning": "raft_interface_line_width",
"maximum_value_warning": "15.0", "maximum_value_warning": "15.0",
"enabled": "resolveOrValue('adhesion_type') == 'raft'", "enabled": "resolveOrValue('adhesion_type') == 'raft'",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -3883,7 +3881,7 @@
"value": "resolveOrValue('layer_height_0') * 1.2", "value": "resolveOrValue('layer_height_0') * 1.2",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.04", "minimum_value_warning": "0.04",
"maximum_value_warning": "0.75 * extruderValue(adhesion_extruder_nr, 'raft_base_line_width')", "maximum_value_warning": "0.75 * raft_base_line_width",
"enabled": "resolveOrValue('adhesion_type') == 'raft'", "enabled": "resolveOrValue('adhesion_type') == 'raft'",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -3897,9 +3895,9 @@
"type": "float", "type": "float",
"default_value": 0.8, "default_value": 0.8,
"minimum_value": "0.001", "minimum_value": "0.001",
"value": "extruderValue(adhesion_extruder_nr, 'machine_nozzle_size') * 2", "value": "machine_nozzle_size * 2",
"minimum_value_warning": "extruderValue(adhesion_extruder_nr, 'machine_nozzle_size') * 0.5", "minimum_value_warning": "machine_nozzle_size * 0.5",
"maximum_value_warning": "extruderValue(adhesion_extruder_nr, 'machine_nozzle_size') * 3", "maximum_value_warning": "machine_nozzle_size * 3",
"enabled": "resolveOrValue('adhesion_type') == 'raft'", "enabled": "resolveOrValue('adhesion_type') == 'raft'",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -3914,7 +3912,7 @@
"default_value": 1.6, "default_value": 1.6,
"value": "raft_base_line_width * 2", "value": "raft_base_line_width * 2",
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "extruderValue(adhesion_extruder_nr, 'raft_base_line_width')", "minimum_value_warning": "raft_base_line_width",
"maximum_value_warning": "100", "maximum_value_warning": "100",
"enabled": "resolveOrValue('adhesion_type') == 'raft'", "enabled": "resolveOrValue('adhesion_type') == 'raft'",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -4571,7 +4569,7 @@
"magic_spiralize": "magic_spiralize":
{ {
"label": "Spiralize Outer Contour", "label": "Spiralize Outer Contour",
"description": "Spiralize smooths out the Z move of the outer edge. This will create a steady Z increase over the whole print. This feature turns a solid model into a single walled print with a solid bottom. This feature used to be called Joris in older versions.", "description": "Spiralize smooths out the Z move of the outer edge. This will create a steady Z increase over the whole print. This feature turns a solid model into a single walled print with a solid bottom. This feature should only be enabled when each layer only contains a single part.",
"type": "bool", "type": "bool",
"default_value": false, "default_value": false,
"settable_per_mesh": false, "settable_per_mesh": false,

File diff suppressed because it is too large Load Diff

View File

@ -152,6 +152,7 @@ UM.PreferencesPage
append({ text: "Suomi", code: "fi" }) append({ text: "Suomi", code: "fi" })
append({ text: "Français", code: "fr" }) append({ text: "Français", code: "fr" })
append({ text: "Italiano", code: "it" }) append({ text: "Italiano", code: "it" })
append({ text: "日本語", code: "jp" })
append({ text: "Nederlands", code: "nl" }) append({ text: "Nederlands", code: "nl" })
append({ text: "Português do Brasil", code: "ptbr" }) append({ text: "Português do Brasil", code: "ptbr" })
append({ text: "Русский", code: "ru" }) append({ text: "Русский", code: "ru" })

View File

@ -24,66 +24,89 @@ TabView
property double spoolLength: calculateSpoolLength() property double spoolLength: calculateSpoolLength()
property real costPerMeter: calculateCostPerMeter() property real costPerMeter: calculateCostPerMeter()
property bool reevaluateLinkedMaterials: false
property string linkedMaterialNames:
{
if (reevaluateLinkedMaterials)
{
reevaluateLinkedMaterials = false;
}
if(!base.containerId || !base.editingEnabled)
{
return ""
}
var linkedMaterials = Cura.ContainerManager.getLinkedMaterials(base.containerId);
return linkedMaterials.join(", ");
}
Tab Tab
{ {
title: catalog.i18nc("@title","Information") title: catalog.i18nc("@title","Information")
anchors anchors.margins: UM.Theme.getSize("default_margin").width
{
leftMargin: UM.Theme.getSize("default_margin").width
topMargin: UM.Theme.getSize("default_margin").height
bottomMargin: UM.Theme.getSize("default_margin").height
rightMargin: 0
}
ScrollView ScrollView
{ {
id: scrollView
anchors.fill: parent anchors.fill: parent
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
flickableItem.flickableDirection: Flickable.VerticalFlick flickableItem.flickableDirection: Flickable.VerticalFlick
frameVisible: true
property real columnWidth: Math.floor(viewport.width * 0.5) - UM.Theme.getSize("default_margin").width
Flow Flow
{ {
id: containerGrid id: containerGrid
width: base.width; x: UM.Theme.getSize("default_margin").width
y: UM.Theme.getSize("default_lining").height
property real rowHeight: textField.height; width: base.width
property real rowHeight: textField.height + UM.Theme.getSize("default_lining").height
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Display Name") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Display Name") }
ReadOnlyTextField ReadOnlyTextField
{ {
id: displayNameTextField; id: displayNameTextField;
width: base.secondColumnWidth; width: scrollView.columnWidth;
text: properties.name; text: properties.name;
readOnly: !base.editingEnabled; readOnly: !base.editingEnabled;
onEditingFinished: base.setName(properties.name, text) onEditingFinished: base.setName(properties.name, text)
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") }
ReadOnlyTextField ReadOnlyTextField
{ {
id: textField; id: textField;
width: base.secondColumnWidth; width: scrollView.columnWidth;
text: properties.supplier; text: properties.supplier;
readOnly: !base.editingEnabled; readOnly: !base.editingEnabled;
onEditingFinished: base.setMetaDataEntry("brand", properties.supplier, text) onEditingFinished:
{
base.setMetaDataEntry("brand", properties.supplier, text);
pane.objectList.currentIndex = pane.getIndexById(base.containerId);
}
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") }
ReadOnlyTextField ReadOnlyTextField
{ {
width: base.secondColumnWidth; width: scrollView.columnWidth;
text: properties.material_type; text: properties.material_type;
readOnly: !base.editingEnabled; readOnly: !base.editingEnabled;
onEditingFinished: base.setMetaDataEntry("material", properties.material_type, text) onEditingFinished:
{
base.setMetaDataEntry("material", properties.material_type, text);
pane.objectList.currentIndex = pane.getIndexById(base.containerId)
}
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") }
Row Row
{ {
width: base.secondColumnWidth; width: scrollView.columnWidth;
height: parent.rowHeight; height: parent.rowHeight;
spacing: UM.Theme.getSize("default_margin").width/2 spacing: UM.Theme.getSize("default_margin").width/2
@ -115,11 +138,11 @@ TabView
Label { width: parent.width; height: parent.rowHeight; font.bold: true; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Properties") } Label { width: parent.width; height: parent.rowHeight; font.bold: true; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Properties") }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") }
ReadOnlySpinBox ReadOnlySpinBox
{ {
id: densitySpinBox id: densitySpinBox
width: base.secondColumnWidth width: scrollView.columnWidth
value: properties.density value: properties.density
decimals: 2 decimals: 2
suffix: " g/cm³" suffix: " g/cm³"
@ -130,11 +153,11 @@ TabView
onValueChanged: updateCostPerMeter() onValueChanged: updateCostPerMeter()
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") }
ReadOnlySpinBox ReadOnlySpinBox
{ {
id: diameterSpinBox id: diameterSpinBox
width: base.secondColumnWidth width: scrollView.columnWidth
value: properties.diameter value: properties.diameter
decimals: 2 decimals: 2
suffix: " mm" suffix: " mm"
@ -145,11 +168,11 @@ TabView
onValueChanged: updateCostPerMeter() onValueChanged: updateCostPerMeter()
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") }
SpinBox SpinBox
{ {
id: spoolCostSpinBox id: spoolCostSpinBox
width: base.secondColumnWidth width: scrollView.columnWidth
value: base.getMaterialPreferenceValue(properties.guid, "spool_cost") value: base.getMaterialPreferenceValue(properties.guid, "spool_cost")
prefix: base.currency + " " prefix: base.currency + " "
decimals: 2 decimals: 2
@ -161,11 +184,11 @@ TabView
} }
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") }
SpinBox SpinBox
{ {
id: spoolWeightSpinBox id: spoolWeightSpinBox
width: base.secondColumnWidth width: scrollView.columnWidth
value: base.getMaterialPreferenceValue(properties.guid, "spool_weight") value: base.getMaterialPreferenceValue(properties.guid, "spool_weight")
suffix: " g" suffix: " g"
stepSize: 100 stepSize: 100
@ -178,24 +201,45 @@ TabView
} }
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") }
Label Label
{ {
width: base.secondColumnWidth width: scrollView.columnWidth
text: "~ %1 m".arg(Math.round(base.spoolLength)) text: "~ %1 m".arg(Math.round(base.spoolLength))
verticalAlignment: Qt.AlignVCenter verticalAlignment: Qt.AlignVCenter
height: parent.rowHeight height: parent.rowHeight
} }
Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter") } Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter") }
Label Label
{ {
width: base.secondColumnWidth width: scrollView.columnWidth
text: "~ %1 %2/m".arg(base.costPerMeter.toFixed(2)).arg(base.currency) text: "~ %1 %2/m".arg(base.costPerMeter.toFixed(2)).arg(base.currency)
verticalAlignment: Qt.AlignVCenter verticalAlignment: Qt.AlignVCenter
height: parent.rowHeight height: parent.rowHeight
} }
Item { width: parent.width; height: UM.Theme.getSize("default_margin").height; visible: unlinkMaterialButton.visible }
Label
{
width: 2 * scrollView.columnWidth
verticalAlignment: Qt.AlignVCenter
text: catalog.i18nc("@label", "This material is linked to %1 and shares some of its properties.").arg(base.linkedMaterialNames)
wrapMode: Text.WordWrap
visible: unlinkMaterialButton.visible
}
Button
{
id: unlinkMaterialButton
text: catalog.i18nc("@label", "Unlink Material")
visible: base.linkedMaterialNames != ""
onClicked:
{
Cura.ContainerManager.unlinkMaterial(base.containerId)
base.reevaluateLinkedMaterials = true
}
}
Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Description") } Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Description") }
@ -203,7 +247,7 @@ TabView
ReadOnlyTextArea ReadOnlyTextArea
{ {
text: properties.description; text: properties.description;
width: base.firstColumnWidth + base.secondColumnWidth width: 2 * scrollView.columnWidth
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
readOnly: !base.editingEnabled; readOnly: !base.editingEnabled;
@ -216,13 +260,15 @@ TabView
ReadOnlyTextArea ReadOnlyTextArea
{ {
text: properties.adhesion_info; text: properties.adhesion_info;
width: base.firstColumnWidth + base.secondColumnWidth width: 2 * scrollView.columnWidth
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
readOnly: !base.editingEnabled; readOnly: !base.editingEnabled;
onEditingFinished: base.setMetaDataEntry("adhesion_info", properties.adhesion_info, text) onEditingFinished: base.setMetaDataEntry("adhesion_info", properties.adhesion_info, text)
} }
Item { width: parent.width; height: UM.Theme.getSize("default_margin").height }
} }
function updateCostPerMeter() function updateCostPerMeter()
@ -266,8 +312,10 @@ TabView
{ {
id: label id: label
width: base.firstColumnWidth; width: base.firstColumnWidth;
height: spinBox.height height: spinBox.height + UM.Theme.getSize("default_lining").height
text: model.label text: model.label
elide: Text.ElideRight
verticalAlignment: Qt.AlignVCenter
} }
ReadOnlySpinBox ReadOnlySpinBox
{ {
@ -380,6 +428,7 @@ TabView
Cura.ContainerManager.setContainerName(base.containerId, new_value); Cura.ContainerManager.setContainerName(base.containerId, new_value);
// update material name label. not so pretty, but it works // update material name label. not so pretty, but it works
materialProperties.name = new_value; materialProperties.name = new_value;
pane.objectList.currentIndex = pane.getIndexById(base.containerId)
} }
} }
} }

View File

@ -14,6 +14,12 @@ UM.ManagementPage
title: catalog.i18nc("@title:tab", "Materials"); title: catalog.i18nc("@title:tab", "Materials");
Component.onCompleted:
{
// Workaround to make sure all of the items are visible
objectList.positionViewAtBeginning();
}
model: UM.InstanceContainersModel model: UM.InstanceContainersModel
{ {
filter: filter:
@ -81,6 +87,7 @@ UM.ManagementPage
anchors.fill: parent; anchors.fill: parent;
onClicked: onClicked:
{ {
forceActiveFocus();
if(!parent.ListView.isCurrentItem) if(!parent.ListView.isCurrentItem)
{ {
parent.ListView.view.currentIndex = index; parent.ListView.view.currentIndex = index;
@ -91,9 +98,11 @@ UM.ManagementPage
} }
activeId: Cura.MachineManager.activeMaterialId activeId: Cura.MachineManager.activeMaterialId
activeIndex: { activeIndex: getIndexById(activeId)
function getIndexById(material_id)
{
for(var i = 0; i < model.rowCount(); i++) { for(var i = 0; i < model.rowCount(); i++) {
if (model.getItem(i).id == Cura.MachineManager.activeMaterialId) { if (model.getItem(i).id == material_id) {
return i; return i;
} }
} }
@ -135,6 +144,24 @@ UM.ManagementPage
} }
}, },
Button Button
{
text: catalog.i18nc("@action:button", "Create")
iconName: "list-add"
onClicked:
{
var material_id = Cura.ContainerManager.createMaterial()
if(material_id == "")
{
return
}
if(Cura.MachineManager.hasMaterials)
{
Cura.MachineManager.setActiveMaterial(material_id)
}
base.objectList.currentIndex = base.getIndexById(material_id);
}
},
Button
{ {
text: catalog.i18nc("@action:button", "Duplicate"); text: catalog.i18nc("@action:button", "Duplicate");
iconName: "list-add"; iconName: "list-add";
@ -152,6 +179,7 @@ UM.ManagementPage
{ {
Cura.MachineManager.setActiveMaterial(material_id) Cura.MachineManager.setActiveMaterial(material_id)
} }
base.objectList.currentIndex = base.getIndexById(material_id);
} }
}, },
Button Button
@ -206,6 +234,8 @@ UM.ManagementPage
properties: materialProperties properties: materialProperties
containerId: base.currentItem != null ? base.currentItem.id : "" containerId: base.currentItem != null ? base.currentItem.id : ""
property alias pane: base
} }
QtObject QtObject
@ -251,6 +281,10 @@ UM.ManagementPage
{ {
Cura.ContainerManager.removeContainer(containers[i]) Cura.ContainerManager.removeContainer(containers[i])
} }
if(base.objectList.currentIndex > 0)
{
base.objectList.currentIndex--;
}
currentItem = base.model.getItem(base.objectList.currentIndex) // Refresh the current item. currentItem = base.model.getItem(base.objectList.currentIndex) // Refresh the current item.
} }
} }

View File

@ -45,17 +45,4 @@ Item
} }
} }
} }
Label
{
visible: base.readOnly
text: textArea.text
anchors.fill: parent
anchors.margins: textArea.__style ? textArea.__style.textMargin : 4
color: palette.buttonText
}
SystemPalette { id: palette }
} }

View File

@ -15,22 +15,19 @@ SettingItem
contents: ComboBox contents: ComboBox
{ {
id: control id: control
anchors.fill: parent
model: Cura.ExtrudersModel model: Cura.ExtrudersModel { onModelChanged: control.color = getItem(control.currentIndex).color }
{
id: extruders_model
onModelChanged: control.color = extruders_model.getItem(control.currentIndex).color
}
property string color:
{
var model_color = extruders_model.getItem(control.currentIndex).color;
return (model_color) ? model_color : "";
}
textRole: "name" textRole: "name"
anchors.fill: parent onActivated:
onCurrentIndexChanged: updateCurrentColor(); {
forceActiveFocus();
propertyProvider.setPropertyValue("value", model.getItem(index).index);
}
currentIndex: propertyProvider.properties.value
MouseArea MouseArea
{ {
@ -39,6 +36,17 @@ SettingItem
onWheel: wheel.accepted = true; onWheel: wheel.accepted = true;
} }
property string color: "#fff"
Binding
{
// We override the color property's value when the ExtruderModel changes. So we need to use an
// explicit binding here otherwise we do not handle value changes after the model changes.
target: control
property: "color"
value: control.currentText != "" ? control.model.getItem(control.currentIndex).color : ""
}
style: ComboBoxStyle style: ComboBoxStyle
{ {
background: Rectangle background: Rectangle
@ -59,7 +67,19 @@ SettingItem
} }
} }
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : control.hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border") border.color:
{
if(!enabled)
{
return UM.Theme.getColor("setting_control_disabled_border");
}
if(control.hovered || base.activeFocus)
{
UM.Theme.getColor("setting_control_border_highlight")
}
return UM.Theme.getColor("setting_control_border")
}
} }
label: Item label: Item
{ {
@ -68,35 +88,36 @@ SettingItem
id: swatch id: swatch
height: UM.Theme.getSize("setting_control").height / 2 height: UM.Theme.getSize("setting_control").height / 2
width: height width: height
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_lining").width
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
color: control.color
border.width: UM.Theme.getSize("default_lining").width border.width: UM.Theme.getSize("default_lining").width
border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : UM.Theme.getColor("setting_control_border") border.color: enabled ? UM.Theme.getColor("setting_control_border") : UM.Theme.getColor("setting_control_disabled_border")
color: control.color
} }
Label Label
{ {
anchors.left: swatch.right anchors
anchors.leftMargin: UM.Theme.getSize("default_lining").width {
anchors.right: downArrow.left left: swatch.right;
anchors.rightMargin: UM.Theme.getSize("default_lining").width right: arrow.left;
anchors.verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
margins: UM.Theme.getSize("default_lining").width
}
width: parent.width - swatch.width;
text: control.currentText text: control.currentText
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text") color: enabled ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
elide: Text.ElideRight elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
} }
UM.RecolorImage UM.RecolorImage
{ {
id: downArrow id: arrow
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
source: UM.Theme.getIcon("arrow_bottom") source: UM.Theme.getIcon("arrow_bottom")
@ -109,57 +130,5 @@ SettingItem
} }
} }
} }
onActivated:
{
forceActiveFocus();
propertyProvider.setPropertyValue("value", extruders_model.getItem(index).index);
control.color = extruders_model.getItem(index).color;
}
onModelChanged: updateCurrentIndex();
Binding
{
target: control
property: "currentIndex"
value:
{
for(var i = 0; i < extruders_model.rowCount(); ++i)
{
if(extruders_model.getItem(i).index == propertyProvider.properties.value)
{
return i;
}
}
return -1;
}
}
// In some cases we want to update the current color without updating the currentIndex, so it's a seperate function.
function updateCurrentColor()
{
for(var i = 0; i < extruders_model.rowCount(); ++i)
{
if(extruders_model.getItem(i).index == currentIndex)
{
control.color = extruders_model.getItem(i).color;
return;
}
}
}
function updateCurrentIndex()
{
for(var i = 0; i < extruders_model.rowCount(); ++i)
{
if(extruders_model.getItem(i).index == propertyProvider.properties.value)
{
control.currentIndex = i;
return;
}
}
currentIndex = -1;
}
} }
} }

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = abax_pri3 definition = abax_pri3
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = abax_pri3 definition = abax_pri3
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = abax_pri3 definition = abax_pri3
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = abax_pri5 definition = abax_pri5
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = abax_pri5 definition = abax_pri5
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = abax_pri5 definition = abax_pri5
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = abax_titan definition = abax_titan
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = abax_titan definition = abax_titan
[metadata] [metadata]
type = quality type = quality

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = abax_titan definition = abax_titan
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = cartesio definition = cartesio
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = fdmprinter definition = fdmprinter
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = fdmprinter definition = fdmprinter
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = peopoly_moai definition = peopoly_moai
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = peopoly_moai definition = peopoly_moai
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = ultimaker2_plus definition = ultimaker2_plus
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Fast Print name = Normal
definition = ultimaker2_plus definition = ultimaker2_plus
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = ultimaker2_plus definition = ultimaker2_plus
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = ultimaker2_plus definition = ultimaker2_plus
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = ultimaker2_plus definition = ultimaker2_plus
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Fast Print name = Normal
definition = ultimaker2_plus definition = ultimaker2_plus
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = ultimaker2_plus definition = ultimaker2_plus
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Fast Print name = Normal
definition = ultimaker2_plus definition = ultimaker2_plus
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = High Quality name = Extra Fine
definition = ultimaker2_plus definition = ultimaker2_plus
[metadata] [metadata]

View File

@ -1,6 +1,6 @@
[general] [general]
version = 2 version = 2
name = Normal Quality name = Fine
definition = ultimaker2_plus definition = ultimaker2_plus
[metadata] [metadata]

Some files were not shown because too many files have changed in this diff Show More