diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py
index a91a85f406..ca4c8e7b34 100644
--- a/cura/BuildVolume.py
+++ b/cura/BuildVolume.py
@@ -280,7 +280,7 @@ class BuildVolume(SceneNode):
self._height = self._global_container_stack.getProperty("machine_height", "value")
rebuild_me = True
- if setting_key in self._skirt_settings or setting_key in self._prime_settings or setting_key in self._tower_settings:
+ if setting_key in self._skirt_settings or setting_key in self._prime_settings or setting_key in self._tower_settings or setting_key == "print_sequence":
self._updateDisallowedAreas()
rebuild_me = True
@@ -383,6 +383,10 @@ class BuildVolume(SceneNode):
def _getBedAdhesionSize(self, container_stack):
skirt_size = 0.0
+ # If we are printing one at a time, we need to add the bed adhesion size to the disallowed areas of the objects
+ if container_stack.getProperty("print_sequence", "value") == "one_at_a_time":
+ return 0.1 # Return a very small value, so we do draw disallowed area's near the edges.
+
adhesion_type = container_stack.getProperty("adhesion_type", "value")
if adhesion_type == "skirt":
skirt_distance = container_stack.getProperty("skirt_gap", "value")
diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py
index 5185579633..770ed42a9f 100644
--- a/cura/ConvexHullDecorator.py
+++ b/cura/ConvexHullDecorator.py
@@ -56,6 +56,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
if self._global_stack and self._node:
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
hull = hull.getMinkowskiHull(Polygon(numpy.array(self._global_stack.getProperty("machine_head_polygon", "value"), numpy.float32)))
+ hull = self._add2DAdhesionMargin(hull)
return hull
## Get the convex hull of the node with the full head size
@@ -229,17 +230,16 @@ class ConvexHullDecorator(SceneNodeDecorator):
machine_head_coords = numpy.array(
self._global_stack.getProperty("machine_head_with_fans_polygon", "value"),
numpy.float32)
- head_y_size = abs(machine_head_coords).min() # safe margin to take off in all directions
if adhesion_type == "raft":
- extra_margin = max(0, self._global_stack.getProperty("raft_margin", "value") - head_y_size)
+ extra_margin = max(0, self._global_stack.getProperty("raft_margin", "value"))
elif adhesion_type == "brim":
- extra_margin = max(0, self._global_stack.getProperty("brim_width", "value") - head_y_size)
+ extra_margin = max(0, self._global_stack.getProperty("brim_line_count", "value") * self._global_stack.getProperty("skirt_brim_line_width", "value"))
elif adhesion_type == "skirt":
extra_margin = max(
0, self._global_stack.getProperty("skirt_gap", "value") +
- self._global_stack.getProperty("skirt_line_count", "value") * self._global_stack.getProperty("skirt_brim_line_width", "value") -
- head_y_size)
+ self._global_stack.getProperty("skirt_line_count", "value") * self._global_stack.getProperty("skirt_brim_line_width", "value"))
+
# adjust head_and_fans with extra margin
if extra_margin > 0:
# In Cura 2.2+, there is a function to create this circle-like polygon.
@@ -288,4 +288,4 @@ class ConvexHullDecorator(SceneNodeDecorator):
_affected_settings = [
"adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers",
"raft_surface_thickness", "raft_airgap", "raft_margin", "print_sequence",
- "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "skirt_distance"]
+ "skirt_gap", "skirt_line_count", "skirt_brim_line_width", "skirt_distance", "brim_line_count"]
diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py
index 80bd4eb9a9..2091eff5ed 100644
--- a/cura/CuraApplication.py
+++ b/cura/CuraApplication.py
@@ -23,6 +23,7 @@ from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
from UM.Operations.GroupedOperation import GroupedOperation
from UM.Operations.SetTransformOperation import SetTransformOperation
+from UM.Operations.TranslateOperation import TranslateOperation
from cura.SetParentOperation import SetParentOperation
from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
@@ -36,7 +37,6 @@ from . import BuildVolume
from . import CameraAnimation
from . import PrintInformation
from . import CuraActions
-from . import MultiMaterialDecorator
from . import ZOffsetDecorator
from . import CuraSplashScreen
from . import CameraImageProvider
@@ -180,6 +180,8 @@ class CuraApplication(QtApplication):
ContainerRegistry.getInstance().addContainer(empty_material_container)
empty_quality_container = copy.deepcopy(empty_container)
empty_quality_container._id = "empty_quality"
+ empty_quality_container.setName("Not supported")
+ empty_quality_container.addMetaDataEntry("quality_type", "normal")
empty_quality_container.addMetaDataEntry("type", "quality")
ContainerRegistry.getInstance().addContainer(empty_quality_container)
empty_quality_changes_container = copy.deepcopy(empty_container)
@@ -700,8 +702,7 @@ class CuraApplication(QtApplication):
op = GroupedOperation()
for node in nodes:
node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
- op.addOperation(SetTransformOperation(node, Vector(0,0,0)))
-
+ op.addOperation(SetTransformOperation(node, Vector(0, node.getWorldPosition().y - node.getBoundingBox().bottom, 0)))
op.push()
## Reset all transformations on nodes with mesh data.
@@ -724,7 +725,8 @@ class CuraApplication(QtApplication):
for node in nodes:
# Ensure that the object is above the build platform
node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
- op.addOperation(SetTransformOperation(node, Vector(0,0,0), Quaternion(), Vector(1, 1, 1)))
+
+ op.addOperation(SetTransformOperation(node, Vector(0, node.getMeshData().getCenterPosition().y, 0), Quaternion(), Vector(1, 1, 1)))
op.push()
@@ -792,8 +794,6 @@ class CuraApplication(QtApplication):
except Exception as e:
Logger.log("d", "mergeSelected: Exception:", e)
return
- multi_material_decorator = MultiMaterialDecorator.MultiMaterialDecorator()
- group_node.addDecorator(multi_material_decorator)
# Compute the center of the objects when their origins are aligned.
object_centers = [node.getMeshData().getCenterPosition().scale(node.getScale()) for node in group_node.getChildren()]
diff --git a/cura/MultiMaterialDecorator.py b/cura/MultiMaterialDecorator.py
deleted file mode 100644
index 5fee777309..0000000000
--- a/cura/MultiMaterialDecorator.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
-
-class MultiMaterialDecorator(SceneNodeDecorator):
- def __init__(self):
- super().__init__()
-
- def isMultiMaterial(self):
- return True
-
- def __deepcopy__(self, memo):
- return MultiMaterialDecorator()
\ No newline at end of file
diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py
index 2a42765d3f..c9afb4ccbd 100644
--- a/cura/PlatformPhysics.py
+++ b/cura/PlatformPhysics.py
@@ -102,11 +102,11 @@ class PlatformPhysics:
# Get the overlap distance for both convex hulls. If this returns None, there is no intersection.
head_hull = node.callDecoration("getConvexHullHead")
if head_hull:
- overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHullHead"))
+ overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull"))
if not overlap:
other_head_hull = other_node.callDecoration("getConvexHullHead")
if other_head_hull:
- overlap = node.callDecoration("getConvexHullHead").intersectsPolygon(other_head_hull)
+ overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull)
else:
own_convex_hull = node.callDecoration("getConvexHull")
other_convex_hull = other_node.callDecoration("getConvexHull")
diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py
index e220a5f2f1..40e036eba5 100644
--- a/cura/Settings/ContainerManager.py
+++ b/cura/Settings/ContainerManager.py
@@ -423,8 +423,8 @@ class ContainerManager(QObject):
# stack and clear the user settings.
#
# \return \type{bool} True if the operation was successfully, False if not.
- @pyqtSlot(result = bool)
- def createQualityChanges(self):
+ @pyqtSlot(str, result = bool)
+ def createQualityChanges(self, base_name):
global_stack = UM.Application.getInstance().getGlobalContainerStack()
if not global_stack:
return False
@@ -435,8 +435,9 @@ class ContainerManager(QObject):
return False
self._machine_manager.blurSettings.emit()
-
- unique_name = self._container_registry.uniqueName(active_quality_name)
+ if base_name is None or base_name == "":
+ base_name = active_quality_name
+ unique_name = self._container_registry.uniqueName(base_name)
# Go through the active stacks and create quality_changes containers from the user containers.
for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
@@ -540,8 +541,8 @@ class ContainerManager(QObject):
# \param quality_name The name of the quality to duplicate.
#
# \return A string containing the name of the duplicated containers, or an empty string if it failed.
- @pyqtSlot(str, result = str)
- def duplicateQualityOrQualityChanges(self, quality_name):
+ @pyqtSlot(str, str, result = str)
+ def duplicateQualityOrQualityChanges(self, quality_name, base_name):
global_stack = UM.Application.getInstance().getGlobalContainerStack()
if not global_stack or not quality_name:
return ""
@@ -551,7 +552,10 @@ class ContainerManager(QObject):
UM.Logger.log("d", "Unable to duplicate the quality %s, because it doesn't exist.", quality_name)
return ""
- new_name = self._container_registry.uniqueName(quality_name)
+ if base_name is None:
+ base_name = quality_name
+
+ new_name = self._container_registry.uniqueName(base_name)
container_type = containers[0].getMetaDataEntry("type")
if container_type == "quality":
diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py
index 91cfd5e811..bf82a95f1a 100644
--- a/cura/Settings/MachineManager.py
+++ b/cura/Settings/MachineManager.py
@@ -7,7 +7,7 @@ from PyQt5.QtWidgets import QMessageBox
from UM.Application import Application
from UM.Preferences import Preferences
from UM.Logger import Logger
-
+from UM.Message import Message
from UM.Settings.SettingRelation import RelationType
import UM.Settings
@@ -547,6 +547,7 @@ class MachineManager(QObject):
preferred_material = None
if old_material:
preferred_material_name = old_material.getName()
+
self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material_name).id)
else:
Logger.log("w", "While trying to set the active variant, no variant was found to replace.")
@@ -614,9 +615,16 @@ class MachineManager(QObject):
stack_quality_changes = self._empty_quality_changes_container
old_quality = stack.findContainer(type = "quality")
- old_quality.nameChanged.disconnect(self._onQualityNameChanged)
+ if old_quality:
+ old_quality.nameChanged.disconnect(self._onQualityNameChanged)
+ else:
+ Logger.log("w", "Could not find old quality while changing active quality.")
+
old_changes = stack.findContainer(type = "quality_changes")
- old_changes.nameChanged.disconnect(self._onQualityNameChanged)
+ if old_changes:
+ old_changes.nameChanged.disconnect(self._onQualityNameChanged)
+ else:
+ Logger.log("w", "Could not find old quality_changes while changing active quality.")
stack.replaceContainer(stack.getContainerIndex(old_quality), stack_quality)
stack.replaceContainer(stack.getContainerIndex(old_changes), stack_quality_changes)
@@ -799,15 +807,15 @@ class MachineManager(QObject):
if containers:
return containers[0]
- if "name" in search_criteria or "id" in search_criteria:
+ containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
+ if "variant" in search_criteria or "id" in search_criteria:
# If a material by this name can not be found, try a wider set of search criteria
- search_criteria.pop("name", None)
+ search_criteria.pop("variant", None)
search_criteria.pop("id", None)
-
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
if containers:
return containers[0]
-
+ Logger.log("w", "Unable to find a material container with provided criteria, returning an empty one instead.")
return self._empty_material_container
def _updateQualityContainer(self, definition, variant_container, material_container = None, preferred_quality_name = None):
@@ -848,10 +856,9 @@ class MachineManager(QObject):
containers = container_registry.findInstanceContainers(**search_criteria)
if containers:
return containers[0]
-
# We still weren't able to find a quality for this specific material.
# Try to find qualities for a generic version of the material.
- material_search_criteria = { "type": "material", "material": material_container.getMetaDataEntry("material"), "color_name": "Generic" }
+ material_search_criteria = { "type": "material", "material": material_container.getMetaDataEntry("material"), "color_name": "Generic"}
if definition.getMetaDataEntry("has_machine_quality"):
if material_container:
material_search_criteria["definition"] = material_container.getDefinition().id
@@ -865,7 +872,6 @@ class MachineManager(QObject):
material_search_criteria["variant"] = variant_container.id
else:
material_search_criteria["definition"] = "fdmprinter"
-
material_containers = container_registry.findInstanceContainers(**material_search_criteria)
if material_containers:
search_criteria["material"] = material_containers[0].getId()
@@ -883,6 +889,9 @@ class MachineManager(QObject):
if containers:
return containers[0]
+ # Notify user that we were unable to find a matching quality
+ message = Message(catalog.i18nc("@info:status", "Unable to find a quality profile for this combination, using an empty one instead."))
+ message.show()
return self._empty_quality_container
## Finds a quality-changes container to use if any other container
diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt
index ad09470ccd..8041bbb171 100644
--- a/plugins/ChangeLogPlugin/ChangeLog.txt
+++ b/plugins/ChangeLogPlugin/ChangeLog.txt
@@ -1,7 +1,7 @@
[2.3.0]
*Speed improvements
-The first thing you will notice is the speed. STL loading is now 10 to 20 times faster, layer time is significantly faster and slicing speed is slightly improved.
+The first thing you will notice is the speed. STL loading is now 10 to 20 times faster, layerview is significantly faster and slicing speed is slightly improved.
*Multi Extrusion Support
Machines with multiple extruders are now supported. If you’ve got the Ultimaker Original with the dual extrusion upgrade kit, we’ve got you covered.
@@ -59,9 +59,6 @@ Set the travel speed of the initial layer(s) to reduce risk of extruder pulling
*Support Bottoms
This new feature duplicates the Support Roofs feature in the places where the support rests on the model.
-*Auto Temperature
-Automatically change the temperature of a layer based on the average flow of material in the layer.
-
*Bug fixes & minor changes
Deleting grouped objects works as intended again.
Duplicating groups works as intended again.
diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py
index c7cedc92ca..4d40da899d 100644
--- a/plugins/CuraEngineBackend/StartSliceJob.py
+++ b/plugins/CuraEngineBackend/StartSliceJob.py
@@ -206,7 +206,14 @@ class StartSliceJob(Job):
# Use resolvement value if available, or take the value
resolved_value = stack.getProperty(key, "resolve")
if resolved_value is not None:
- settings[key] = resolved_value
+ # There is a resolvement value. Check if we need to use it.
+ user_container = stack.findContainer({"type": "user"})
+ quality_changes_container = stack.findContainer({"type": "quality_changes"})
+ if user_container.hasProperty(key,"value") or quality_changes_container.hasProperty(key,"value"):
+ # Normal case
+ settings[key] = stack.getProperty(key, "value")
+ else:
+ settings[key] = resolved_value
else:
# Normal case
settings[key] = stack.getProperty(key, "value")
diff --git a/plugins/CuraProfileReader/CuraProfileReader.py b/plugins/CuraProfileReader/CuraProfileReader.py
index 8007a8e696..772b11890b 100644
--- a/plugins/CuraProfileReader/CuraProfileReader.py
+++ b/plugins/CuraProfileReader/CuraProfileReader.py
@@ -26,7 +26,11 @@ class CuraProfileReader(ProfileReader):
# not be read or didn't contain a valid profile, \code None \endcode is
# returned.
def read(self, file_name):
- archive = zipfile.ZipFile(file_name, "r")
+ try:
+ archive = zipfile.ZipFile(file_name, "r")
+ except Exception:
+ # zipfile doesn't give proper exceptions, so we can only catch broad ones
+ return []
results = []
for profile_id in archive.namelist():
# Create an empty profile.
diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py
index c2a932b68c..c15b079097 100644
--- a/plugins/GCodeWriter/GCodeWriter.py
+++ b/plugins/GCodeWriter/GCodeWriter.py
@@ -89,17 +89,17 @@ class GCodeWriter(MeshWriter):
prefix = ";SETTING_" + str(GCodeWriter.version) + " " # The prefix to put before each line.
prefix_length = len(prefix)
- container_with_profile = stack.findContainer({"type": "quality"})
+ container_with_profile = stack.findContainer({"type": "quality_changes"})
if not container_with_profile:
Logger.log("e", "No valid quality profile found, not writing settings to GCode!")
return ""
- flat_global_container = self._createFlattenedContainerInstance(stack.getTop(),container_with_profile)
+ flat_global_container = self._createFlattenedContainerInstance(stack.getTop(), container_with_profile)
serialized = flat_global_container.serialize()
data = {"global_quality": serialized}
for extruder in ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
- extruder_quality = extruder.findContainer({"type": "quality"})
+ extruder_quality = extruder.findContainer({"type": "quality_changes"})
if not extruder_quality:
Logger.log("w", "No extruder quality profile found, not writing quality for extruder %s to file!", extruder.getId())
continue
diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py
index 791ca0e153..801f4797dd 100644
--- a/plugins/LayerView/LayerView.py
+++ b/plugins/LayerView/LayerView.py
@@ -86,7 +86,7 @@ class LayerView(View):
if not self._ghost_shader:
self._ghost_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
- self._ghost_shader.setUniformValue("u_color", Color(0, 0, 0, 72))
+ self._ghost_shader.setUniformValue("u_color", Color(0, 0, 0, 64))
for node in DepthFirstIterator(scene.getRoot()):
# We do not want to render ConvexHullNode as it conflicts with the bottom layers.
@@ -98,10 +98,10 @@ class LayerView(View):
if node.getMeshData() and node.isVisible():
renderer.queueNode(node,
shader = self._ghost_shader,
- state_setup_callback = lambda gl: gl.glDepthMask(gl.GL_FALSE),
- state_teardown_callback = lambda gl: gl.glDepthMask(gl.GL_TRUE)
- )
+ type = RenderBatch.RenderType.Transparent )
+ for node in DepthFirstIterator(scene.getRoot()):
+ if type(node) is SceneNode:
if node.getMeshData() and node.isVisible():
layer_data = node.callDecoration("getLayerData")
if not layer_data:
diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py
index 3e3501a83f..3b56ac1881 100644
--- a/plugins/SolidView/SolidView.py
+++ b/plugins/SolidView/SolidView.py
@@ -47,7 +47,9 @@ class SolidView(View):
if global_container_stack:
if Preferences.getInstance().getValue("view/show_overhang"):
angle = global_container_stack.getProperty("support_angle", "value")
- if angle is not None and global_container_stack.getProperty("support_angle", "validationState") == ValidatorState.Valid:
+ # Make sure the overhang angle is valid before passing it to the shader
+ # Note: if the overhang angle is set to its default value, it does not need to get validated (validationState = None)
+ if angle is not None and global_container_stack.getProperty("support_angle", "validationState") in [None, ValidatorState.Valid]:
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(90 - angle)))
else:
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) #Overhang angle of 0 causes no area at all to be marked as overhang.
diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py
index 58a948c17b..cd10348f2f 100644
--- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py
+++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py
@@ -87,7 +87,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
super().setName(new_name)
- basefile = self.getMetaDataEntry("base_file", self._id) # if basefile is none, this is a basefile.
+ basefile = self.getMetaDataEntry("base_file", self._id) # if basefile is self.id, this is a basefile.
# Update the basefile as well, this is actually what we're trying to do
# Update all containers that share GUID and basefile
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile)
@@ -275,7 +275,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
# The XML material profile can have specific settings for machines.
# Some machines share profiles, so they are only created once.
# This function duplicates those elements so that each machine tag only has one identifier.
- def _flattenMachinesXML(self, element):
+ def _expandMachinesXML(self, element):
settings_element = element.find("./um:settings", self.__namespaces)
machines = settings_element.iterfind("./um:machine", self.__namespaces)
machines_to_add = []
@@ -309,7 +309,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
def _mergeXML(self, first, second):
result = copy.deepcopy(first)
- self._combineElement(self._flattenMachinesXML(result), self._flattenMachinesXML(second))
+ self._combineElement(self._expandMachinesXML(result), self._expandMachinesXML(second))
return result
def _createKey(self, element):
@@ -339,7 +339,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
key = self._createKey(element)
if len(element): # Check if element has children.
try:
- if "setting " in key:
+ if "setting" in element.tag and not "settings" in element.tag:
# Setting can have points in it. In that case, delete all values and override them.
for child in list(mapping[key]):
mapping[key].remove(child)
diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json
index 8b2805b3aa..29cf3adb30 100644
--- a/resources/definitions/fdmprinter.def.json
+++ b/resources/definitions/fdmprinter.def.json
@@ -3310,6 +3310,8 @@
"default_value": 200,
"minimum_value_warning": "-1000",
"maximum_value_warning": "1000",
+ "maximum_value": "machine_width - 0.5 * prime_tower_size",
+ "minimum_value": "0.5 * prime_tower_size",
"settable_per_mesh": false,
"settable_per_extruder": false
},
@@ -3323,6 +3325,8 @@
"default_value": 200,
"minimum_value_warning": "-1000",
"maximum_value_warning": "1000",
+ "maximum_value": "machine_depth - 0.5 * prime_tower_size",
+ "minimum_value": "0.5 * prime_tower_size",
"settable_per_mesh": false,
"settable_per_extruder": false
},
diff --git a/resources/materials/generic_cpe_plus.xml.fdm_material b/resources/materials/generic_cpe_plus.xml.fdm_material
index b688db30fb..f234d7cb45 100644
--- a/resources/materials/generic_cpe_plus.xml.fdm_material
+++ b/resources/materials/generic_cpe_plus.xml.fdm_material
@@ -28,7 +28,9 @@ Generic CPE+ profile. Serves as an example file, data in this file is not correc
yes
-
+
+ no
+
diff --git a/resources/materials/generic_tpu.xml.fdm_material b/resources/materials/generic_tpu.xml.fdm_material
index 1a099af9cf..455e7b89be 100644
--- a/resources/materials/generic_tpu.xml.fdm_material
+++ b/resources/materials/generic_tpu.xml.fdm_material
@@ -31,7 +31,9 @@ Generic TPU 95A profile. Serves as an example file, data in this file is not cor
-
+
+ no
+
diff --git a/resources/materials/ultimaker_pla_silver-metallic.xml.fdm_material b/resources/materials/ultimaker_pla_silver-metallic.xml.fdm_material
index 2f250e8e31..2b7fbaac46 100644
--- a/resources/materials/ultimaker_pla_silver-metallic.xml.fdm_material
+++ b/resources/materials/ultimaker_pla_silver-metallic.xml.fdm_material
@@ -3,7 +3,6 @@
Automatically generated PLA profile. Data in this file may not be not correct.
-->
- generic_pla
Ultimaker
@@ -38,14 +37,14 @@ Automatically generated PLA profile. Data in this file may not be not correct.
-
-
+
+
- 180
+ 150
diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml
index 5ba0c82f00..34dbcf1b67 100644
--- a/resources/qml/Cura.qml
+++ b/resources/qml/Cura.qml
@@ -463,7 +463,7 @@ UM.MainWindow
target: Cura.Actions.addProfile
onTriggered:
{
- Cura.ContainerManager.createQualityChanges();
+ Cura.ContainerManager.createQualityChanges(null);
preferences.setPage(4);
preferences.show();
diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml
index 4a0d87b812..56d8bd41a4 100644
--- a/resources/qml/Preferences/ProfilesPage.qml
+++ b/resources/qml/Preferences/ProfilesPage.qml
@@ -61,6 +61,10 @@ UM.ManagementPage
return -1;
}
+ function canCreateProfile() {
+ return base.currentItem && (base.currentItem.id == Cura.MachineManager.activeQualityId) && Cura.MachineManager.hasUserSettings;
+ }
+
buttons: [
Button
{
@@ -69,26 +73,39 @@ UM.ManagementPage
enabled: base.currentItem != null ? base.currentItem.id != Cura.MachineManager.activeQualityId : false;
onClicked: Cura.MachineManager.setActiveQuality(base.currentItem.id)
},
+
+ // Create button
Button
{
- text: base.currentItem && (base.currentItem.id == Cura.MachineManager.activeQualityId) && Cura.MachineManager.hasUserSettings ? catalog.i18nc("@label", "Create") : catalog.i18nc("@label", "Duplicate")
+ text: catalog.i18nc("@label", "Create")
+ enabled: base.canCreateProfile()
+ visible: base.canCreateProfile()
iconName: "list-add";
onClicked:
{
- var selectedContainer;
- if (base.currentItem.id == Cura.MachineManager.activeQualityId && Cura.MachineManager.hasUserSettings) {
- selectedContainer = Cura.ContainerManager.createQualityChanges();
- } else {
- selectedContainer = Cura.ContainerManager.duplicateQualityOrQualityChanges(base.currentItem.name);
- }
- base.selectContainer(selectedContainer);
-
- renameDialog.removeWhenRejected = true;
- renameDialog.open();
- renameDialog.selectText();
+ newNameDialog.object = base.currentItem != null ? base.currentItem.name : "";
+ newNameDialog.open();
+ newNameDialog.selectText();
}
},
+
+ // Duplicate button
+ Button
+ {
+ text: catalog.i18nc("@label", "Duplicate")
+ enabled: ! base.canCreateProfile()
+ visible: ! base.canCreateProfile()
+ iconName: "list-add";
+
+ onClicked:
+ {
+ newDuplicateNameDialog.object = base.currentItem.name;
+ newDuplicateNameDialog.open();
+ newDuplicateNameDialog.selectText();
+ }
+ },
+
Button
{
text: catalog.i18nc("@action:button", "Remove");
@@ -103,7 +120,6 @@ UM.ManagementPage
enabled: base.currentItem != null ? !base.currentItem.readOnly : false;
onClicked:
{
- renameDialog.removeWhenRejected = false;
renameDialog.open();
renameDialog.selectText();
}
@@ -128,7 +144,6 @@ UM.ManagementPage
signal showProfileNameDialog()
onShowProfileNameDialog:
{
- renameDialog.removeWhenRejected = true;
renameDialog.open();
renameDialog.selectText();
}
@@ -145,7 +160,7 @@ UM.ManagementPage
Label {
id: profileName
- text: base.currentItem ? base.currentItem.name : ""
+ text: base.currentItem ? base.currentItem.name: ""
font: UM.Theme.getFont("large")
width: parent.width
elide: Text.ElideRight
@@ -249,24 +264,44 @@ UM.ManagementPage
objectList.currentIndex = -1 //Reset selection.
}
}
+
UM.RenameDialog
{
id: renameDialog;
object: base.currentItem != null ? base.currentItem.name : ""
- property bool removeWhenRejected: false
onAccepted:
{
Cura.ContainerManager.renameQualityChanges(base.currentItem.name, newName)
objectList.currentIndex = -1 //Reset selection.
}
- onRejected:
+ }
+
+ // Dialog to request a name when creating a new profile
+ UM.RenameDialog
+ {
+ id: newNameDialog;
+ object: "";
+ onAccepted:
{
- if(removeWhenRejected)
- {
- Cura.ContainerManager.removeQualityChanges(base.currentItem.name)
- }
+ var selectedContainer = Cura.ContainerManager.createQualityChanges(newName);
+ base.selectContainer(selectedContainer);
+ objectList.currentIndex = -1 //Reset selection.
}
}
+
+ // Dialog to request a name when duplicating a new profile
+ UM.RenameDialog
+ {
+ id: newDuplicateNameDialog;
+ object: "";
+ onAccepted:
+ {
+ var selectedContainer = Cura.ContainerManager.duplicateQualityOrQualityChanges(base.currentItem.name, newName);
+ base.selectContainer(selectedContainer);
+ objectList.currentIndex = -1 //Reset selection.
+ }
+ }
+
MessageDialog
{
id: messageDialog
diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml
index 3350ebeb7b..c9c0e722eb 100644
--- a/resources/qml/SidebarHeader.qml
+++ b/resources/qml/SidebarHeader.qml
@@ -277,7 +277,7 @@ Column
height: UM.Theme.getSize("setting_control").height
tooltip: Cura.MachineManager.activeQualityName
style: UM.Theme.styles.sidebar_header_button
-
+ property var valueWarning: Cura.MachineManager.activeQualityId == "empty_quality"
menu: ProfileMenu { }
UM.SimpleButton
diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml
index 8d813bc2b5..3cfb4514ee 100644
--- a/resources/themes/cura/styles.qml
+++ b/resources/themes/cura/styles.qml
@@ -11,7 +11,22 @@ QtObject {
property Component sidebar_header_button: Component {
ButtonStyle {
background: Rectangle {
- color: control.enabled ? Theme.getColor("setting_control") : Theme.getColor("setting_control_disabled")
+ color:
+ {
+ if(control.enabled)
+ {
+ if(control.valueWarning)
+ {
+ return Theme.getColor("setting_validation_warning");
+ } else
+ {
+ return Theme.getColor("setting_control");
+ }
+ } else {
+ return Theme.getColor("setting_control_disabled");
+ }
+ }
+
border.width: Theme.getSize("default_lining").width
border.color: !control.enabled ? Theme.getColor("setting_control_disabled_border") :
control.hovered ? Theme.getColor("setting_control_border_highlight") : Theme.getColor("setting_control_border")