Merge remote-tracking branch 'upstream/master' into master-CURA-1696

This commit is contained in:
Thomas Karl Pietrowski 2016-09-05 22:15:18 +02:00
commit aa2ba26a82
207 changed files with 13417 additions and 5423 deletions

View file

@ -1,5 +1,5 @@
project(cura)
project(cura NONE)
cmake_minimum_required(VERSION 2.8.12)
include(UraniumTranslationTools)

View file

@ -74,10 +74,14 @@ class BuildVolume(SceneNode):
self._adhesion_type = None
self._platform = Platform(self)
self._active_container_stack = None
self._global_container_stack = None
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
self._onGlobalContainerStackChanged()
self._active_extruder_stack = None
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
self._onActiveExtruderStackChanged()
def setWidth(self, width):
if width: self._width = width
@ -208,22 +212,22 @@ class BuildVolume(SceneNode):
"@info:status",
"The build volume height has been reduced due to the value of the"
" \"Print Sequence\" setting to prevent the gantry from colliding"
" with printed objects."), lifetime=10).show()
" with printed models.")).show()
def getRaftThickness(self):
return self._raft_thickness
def _updateRaftThickness(self):
old_raft_thickness = self._raft_thickness
self._adhesion_type = self._active_container_stack.getProperty("adhesion_type", "value")
self._adhesion_type = self._global_container_stack.getProperty("adhesion_type", "value")
self._raft_thickness = 0.0
if self._adhesion_type == "raft":
self._raft_thickness = (
self._active_container_stack.getProperty("raft_base_thickness", "value") +
self._active_container_stack.getProperty("raft_interface_thickness", "value") +
self._active_container_stack.getProperty("raft_surface_layers", "value") *
self._active_container_stack.getProperty("raft_surface_thickness", "value") +
self._active_container_stack.getProperty("raft_airgap", "value"))
self._global_container_stack.getProperty("raft_base_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_thickness", "value") +
self._global_container_stack.getProperty("raft_airgap", "value"))
# Rounding errors do not matter, we check if raft_thickness has changed at all
if old_raft_thickness != self._raft_thickness:
@ -231,41 +235,52 @@ class BuildVolume(SceneNode):
self.raftThicknessChanged.emit()
def _onGlobalContainerStackChanged(self):
if self._active_container_stack:
self._active_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
self._active_container_stack = Application.getInstance().getGlobalContainerStack()
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
if self._active_container_stack:
self._active_container_stack.propertyChanged.connect(self._onSettingPropertyChanged)
if self._global_container_stack:
self._global_container_stack.propertyChanged.connect(self._onSettingPropertyChanged)
self._width = self._active_container_stack.getProperty("machine_width", "value")
if self._active_container_stack.getProperty("print_sequence", "value") == "one_at_a_time":
self._height = self._active_container_stack.getProperty("gantry_height", "value")
self._buildVolumeMessage()
self._width = self._global_container_stack.getProperty("machine_width", "value")
machine_height = self._global_container_stack.getProperty("machine_height", "value")
if self._global_container_stack.getProperty("print_sequence", "value") == "one_at_a_time":
self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height)
if self._height < machine_height:
self._buildVolumeMessage()
else:
self._height = self._active_container_stack.getProperty("machine_height", "value")
self._depth = self._active_container_stack.getProperty("machine_depth", "value")
self._height = self._global_container_stack.getProperty("machine_height", "value")
self._depth = self._global_container_stack.getProperty("machine_depth", "value")
self._updateDisallowedAreas()
self._updateRaftThickness()
self.rebuild()
def _onActiveExtruderStackChanged(self):
if self._active_extruder_stack:
self._active_extruder_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
self._active_extruder_stack = ExtruderManager.getInstance().getActiveExtruderStack()
if self._active_extruder_stack:
self._active_extruder_stack.propertyChanged.connect(self._onSettingPropertyChanged)
def _onSettingPropertyChanged(self, setting_key, property_name):
if property_name != "value":
return
rebuild_me = False
if setting_key == "print_sequence":
machine_height = self._global_container_stack.getProperty("machine_height", "value")
if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time":
self._height = self._active_container_stack.getProperty("gantry_height", "value")
self._buildVolumeMessage()
self._height = min(self._global_container_stack.getProperty("gantry_height", "value"), machine_height)
if self._height < machine_height:
self._buildVolumeMessage()
else:
self._height = self._active_container_stack.getProperty("machine_height", "value")
self._height = self._global_container_stack.getProperty("machine_height", "value")
rebuild_me = True
if setting_key in self._skirt_settings:
if setting_key in self._skirt_settings or setting_key in self._prime_settings or setting_key in self._tower_settings:
self._updateDisallowedAreas()
rebuild_me = True
@ -277,19 +292,33 @@ class BuildVolume(SceneNode):
self.rebuild()
def _updateDisallowedAreas(self):
if not self._active_container_stack:
if not self._global_container_stack:
return
disallowed_areas = copy.deepcopy(
self._active_container_stack.getProperty("machine_disallowed_areas", "value"))
self._global_container_stack.getProperty("machine_disallowed_areas", "value"))
areas = []
machine_width = self._global_container_stack.getProperty("machine_width", "value")
machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
# Add prime tower location as disallowed area.
if self._global_container_stack.getProperty("prime_tower_enable", "value") == True:
prime_tower_size = self._global_container_stack.getProperty("prime_tower_size", "value")
prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value") - machine_width / 2
prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value") + machine_depth / 2
disallowed_areas.append([
[prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
[prime_tower_x, prime_tower_y - prime_tower_size],
[prime_tower_x, prime_tower_y],
[prime_tower_x - prime_tower_size, prime_tower_y],
])
# Add extruder prime locations as disallowed areas.
# Probably needs some rework after coordinate system change.
extruder_manager = ExtruderManager.getInstance()
extruders = extruder_manager.getMachineExtruders(self._active_container_stack.getId())
machine_width = self._active_container_stack.getProperty("machine_width", "value")
machine_depth = self._active_container_stack.getProperty("machine_depth", "value")
extruders = extruder_manager.getMachineExtruders(self._global_container_stack.getId())
for single_extruder in extruders:
extruder_prime_pos_x = single_extruder.getProperty("extruder_prime_pos_x", "value")
extruder_prime_pos_y = single_extruder.getProperty("extruder_prime_pos_y", "value")
@ -305,7 +334,7 @@ class BuildVolume(SceneNode):
[prime_x - PRIME_CLEARANCE, prime_y + PRIME_CLEARANCE],
])
bed_adhesion_size = self._getBedAdhesionSize(self._active_container_stack)
bed_adhesion_size = self._getBedAdhesionSize(self._global_container_stack)
if disallowed_areas:
# Extend every area already in the disallowed_areas with the skirt size.
@ -317,8 +346,8 @@ class BuildVolume(SceneNode):
# Add the skirt areas around the borders of the build plate.
if bed_adhesion_size > 0:
half_machine_width = self._active_container_stack.getProperty("machine_width", "value") / 2
half_machine_depth = self._active_container_stack.getProperty("machine_depth", "value") / 2
half_machine_width = self._global_container_stack.getProperty("machine_width", "value") / 2
half_machine_depth = self._global_container_stack.getProperty("machine_depth", "value") / 2
areas.append(Polygon(numpy.array([
[-half_machine_width, -half_machine_depth],
@ -377,3 +406,5 @@ class BuildVolume(SceneNode):
_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", "xy_offset"]
_raft_settings = ["adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers", "raft_surface_thickness", "raft_airgap"]
_prime_settings = ["extruder_prime_pos_x", "extruder_prime_pos_y", "extruder_prime_pos_z"]
_tower_settings = ["prime_tower_enable", "prime_tower_size", "prime_tower_position_x", "prime_tower_position_y"]

View file

@ -13,7 +13,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Mesh.ReadMeshJob import ReadMeshJob
from UM.Logger import Logger
from UM.Preferences import Preferences
from UM.Platform import Platform
from UM.JobQueue import JobQueue
from UM.SaveFile import SaveFile
from UM.Scene.Selection import Selection
@ -50,12 +49,12 @@ from PyQt5.QtGui import QColor, QIcon
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
import platform
import sys
import os.path
import numpy
import copy
import urllib
numpy.seterr(all="ignore")
try:
@ -85,11 +84,12 @@ class CuraApplication(QtApplication):
self._open_file_queue = [] # Files to open when plug-ins are loaded.
# Need to do this before ContainerRegistry tries to load the machines
SettingDefinition.addSupportedProperty("settable_per_mesh", DefinitionPropertyType.Any, default = True)
SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True)
SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True)
SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True)
SettingDefinition.addSupportedProperty("settable_per_mesh", DefinitionPropertyType.Any, default = True, read_only = True)
SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True, read_only = True)
SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True, read_only = True)
SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True, read_only = True)
SettingDefinition.addSupportedProperty("global_inherits_stack", DefinitionPropertyType.Function, default = "-1")
SettingDefinition.addSupportedProperty("resolve", DefinitionPropertyType.Function, default = None)
SettingDefinition.addSettingType("extruder", None, str, Validator)
SettingFunction.registerOperator("extruderValues", cura.Settings.ExtruderManager.getExtruderValues)
@ -182,6 +182,10 @@ class CuraApplication(QtApplication):
empty_quality_container._id = "empty_quality"
empty_quality_container.addMetaDataEntry("type", "quality")
ContainerRegistry.getInstance().addContainer(empty_quality_container)
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container._id = "empty_quality_changes"
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
ContainerRegistry.getInstance().load()
@ -237,11 +241,17 @@ class CuraApplication(QtApplication):
raft_airgap
layer_0_z_overlap
raft_surface_layers
dual
adhesion_extruder_nr
support_extruder_nr
prime_tower_enable
prime_tower_size
prime_tower_position_x
prime_tower_position_y
meshfix
blackmagic
print_sequence
infill_mesh
dual
experimental
""".replace("\n", ";").replace(" ", ""))
@ -302,7 +312,7 @@ class CuraApplication(QtApplication):
path = None
if instance_type == "material":
path = Resources.getStoragePath(self.ResourceTypes.MaterialInstanceContainer, file_name)
elif instance_type == "quality":
elif instance_type == "quality" or instance_type == "quality_changes":
path = Resources.getStoragePath(self.ResourceTypes.QualityInstanceContainer, file_name)
elif instance_type == "user":
path = Resources.getStoragePath(self.ResourceTypes.UserInstanceContainer, file_name)
@ -470,6 +480,7 @@ class CuraApplication(QtApplication):
qmlRegisterType(cura.Settings.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
qmlRegisterType(cura.Settings.MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler")
qmlRegisterType(cura.Settings.QualitySettingsModel, "Cura", 1, 0, "QualitySettingsModel")
qmlRegisterSingletonType(cura.Settings.ContainerManager, "Cura", 1, 0, "ContainerManager", cura.Settings.ContainerManager.createContainerManager)
@ -783,12 +794,19 @@ class CuraApplication(QtApplication):
return
multi_material_decorator = MultiMaterialDecorator.MultiMaterialDecorator()
group_node.addDecorator(multi_material_decorator)
# Reset the position of each node
for node in group_node.getChildren():
new_position = node.getMeshData().getCenterPosition()
new_position = new_position.scale(node.getScale())
node.setPosition(new_position)
# 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()]
middle_x = sum([v.x for v in object_centers]) / len(object_centers)
middle_y = sum([v.y for v in object_centers]) / len(object_centers)
middle_z = sum([v.z for v in object_centers]) / len(object_centers)
offset = Vector(middle_x, middle_y, middle_z)
# Move each node to the same position.
for center, node in zip(object_centers, group_node.getChildren()):
# Align the object and also apply the offset to center it inside the group.
node.setPosition(center - offset)
# Use the previously found center of the group bounding box as the new location of the group
group_node.setPosition(group_node.getBoundingBox().center)
@ -914,3 +932,7 @@ class CuraApplication(QtApplication):
self._additional_components[area_id].append(component)
self.additionalComponentsChanged.emit(area_id)
@pyqtSlot(str)
def log(self, msg):
Logger.log("d", msg)

View file

@ -31,6 +31,7 @@ class PlatformPhysics:
self._change_timer.timeout.connect(self._onChangeTimerFinished)
Preferences.getInstance().addPreference("physics/automatic_push_free", True)
Preferences.getInstance().addPreference("physics/automatic_drop_down", True)
def _onSceneChanged(self, source):
self._change_timer.start()
@ -41,6 +42,10 @@ class PlatformPhysics:
root = self._controller.getScene().getRoot()
# Keep a list of nodes that are moving. We use this so that we don't move two intersecting objects in the
# same direction.
transformed_nodes = []
for node in BreadthFirstIterator(root):
if node is root or type(node) is not SceneNode or node.getBoundingBox() is None:
continue
@ -64,7 +69,7 @@ class PlatformPhysics:
# Move it downwards if bottom is above platform
move_vector = Vector()
if not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down
if Preferences.getInstance().getValue("physics/automatic_drop_down") and not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
move_vector = move_vector.set(y=-bbox.bottom + z_offset)
@ -91,6 +96,9 @@ class PlatformPhysics:
if not other_node.callDecoration("getConvexHull") or not other_node.getBoundingBox():
continue
if other_node in transformed_nodes:
continue # Other node is already moving, wait for next pass.
# Get the overlap distance for both convex hulls. If this returns None, there is no intersection.
head_hull = node.callDecoration("getConvexHullHead")
if head_hull:
@ -125,6 +133,7 @@ class PlatformPhysics:
node._outside_buildarea = True
if not Vector.Null.equals(move_vector, epsilon=1e-5):
transformed_nodes.append(node)
op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
op.push()

View file

@ -1,10 +1,15 @@
from UM.i18n import i18nCatalog
from UM.OutputDevice.OutputDevice import OutputDevice
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject
from PyQt5.QtWidgets import QMessageBox
from enum import IntEnum # For the connection state tracking.
from UM.Logger import Logger
from UM.Application import Application
from UM.Signal import signalemitter
i18n_catalog = i18nCatalog("cura")
## Printer output device adds extra interface options on top of output device.
#
# The assumption is made the printer is a FDM printer.
@ -276,6 +281,11 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._hotend_ids[index] = hotend_id
self.hotendIdChanged.emit(index, hotend_id)
## Let the user decide if the hotends and/or material should be synced with the printer
# NB: the UX needs to be implemented by the plugin
def materialHotendChangedMessage(self, callback):
Logger.log("w", "materialHotendChangedMessage needs to be implemented, returning 'Yes'")
callback(QMessageBox.Yes)
## Attempt to establish connection
def connect(self):
@ -329,7 +339,7 @@ class PrinterOutputDevice(QObject, OutputDevice):
return self._head_z
## Update the saved position of the head
# This function should be called when a new position for the head is recieved.
# This function should be called when a new position for the head is received.
def _updateHeadPosition(self, x, y ,z):
position_changed = False
if self._head_x != x:

View file

@ -18,8 +18,8 @@ class ProfileWriter(PluginObject):
# The profile writer may write its own file format to the specified file.
#
# \param path \type{string} The file to output to.
# \param profile \type{Profile} The profile to write to the file.
# \param profiles \type{Profile} or \type{List} The profile(s) to write to the file.
# \return \code True \endcode if the writing was successful, or \code
# False \endcode if it wasn't.
def write(self, path, node):
def write(self, path, profiles):
raise NotImplementedError("Profile writer plugin was not correctly implemented. No write was specified.")

View file

@ -14,6 +14,8 @@ import UM.Platform
import UM.MimeTypeDatabase
import UM.Logger
import cura.Settings
from UM.MimeTypeDatabase import MimeTypeNotFoundError
from UM.i18n import i18nCatalog
@ -28,7 +30,9 @@ class ContainerManager(QObject):
def __init__(self, parent = None):
super().__init__(parent)
self._registry = UM.Settings.ContainerRegistry.getInstance()
self._container_registry = UM.Settings.ContainerRegistry.getInstance()
self._machine_manager = UM.Application.getInstance().getMachineManager()
self._container_name_filters = {}
## Create a duplicate of the specified container
@ -41,7 +45,7 @@ class ContainerManager(QObject):
# \return The ID of the new container, or an empty string if duplication failed.
@pyqtSlot(str, result = str)
def duplicateContainer(self, container_id):
containers = self._registry.findContainers(None, id = container_id)
containers = self._container_registry.findContainers(None, id = container_id)
if not containers:
UM.Logger.log("w", "Could duplicate container %s because it was not found.", container_id)
return ""
@ -49,7 +53,7 @@ class ContainerManager(QObject):
container = containers[0]
new_container = None
new_name = self._registry.uniqueName(container.getName())
new_name = self._container_registry.uniqueName(container.getName())
# Only InstanceContainer has a duplicate method at the moment.
# So fall back to serialize/deserialize when no duplicate method exists.
if hasattr(container, "duplicate"):
@ -60,7 +64,7 @@ class ContainerManager(QObject):
new_container.setName(new_name)
if new_container:
self._registry.addContainer(new_container)
self._container_registry.addContainer(new_container)
return new_container.getId()
@ -73,24 +77,24 @@ class ContainerManager(QObject):
# \return True if successful, False if not.
@pyqtSlot(str, str, str, result = bool)
def renameContainer(self, container_id, new_id, new_name):
containers = self._registry.findContainers(None, id = container_id)
containers = self._container_registry.findContainers(None, id = container_id)
if not containers:
UM.Logger.log("w", "Could rename container %s because it was not found.", container_id)
return False
container = containers[0]
# First, remove the container from the registry. This will clean up any files related to the container.
self._registry.removeContainer(container)
self._container_registry.removeContainer(container)
# Ensure we have a unique name for the container
new_name = self._registry.uniqueName(new_name)
new_name = self._container_registry.uniqueName(new_name)
# Then, update the name and ID of the container
container.setName(new_name)
container._id = new_id # TODO: Find a nicer way to set a new, unique ID
# Finally, re-add the container so it will be properly serialized again.
self._registry.addContainer(container)
self._container_registry.addContainer(container)
return True
@ -101,12 +105,12 @@ class ContainerManager(QObject):
# \return True if the container was successfully removed, False if not.
@pyqtSlot(str, result = bool)
def removeContainer(self, container_id):
containers = self._registry.findContainers(None, id = container_id)
containers = self._container_registry.findContainers(None, id = container_id)
if not containers:
UM.Logger.log("w", "Could remove container %s because it was not found.", container_id)
return False
self._registry.removeContainer(containers[0].getId())
self._container_registry.removeContainer(containers[0].getId())
return True
@ -121,26 +125,25 @@ class ContainerManager(QObject):
# \return True if successfully merged, False if not.
@pyqtSlot(str, result = bool)
def mergeContainers(self, merge_into_id, merge_id):
containers = self._registry.findContainers(None, id = merge_into_id)
containers = self._container_registry.findContainers(None, id = merge_into_id)
if not containers:
UM.Logger.log("w", "Could merge into container %s because it was not found.", merge_into_id)
return False
merge_into = containers[0]
containers = self._registry.findContainers(None, id = merge_id)
containers = self._container_registry.findContainers(None, id = merge_id)
if not containers:
UM.Logger.log("w", "Could not merge container %s because it was not found", merge_id)
return False
merge = containers[0]
if type(merge) != type(merge_into):
if not isinstance(merge, type(merge_into)):
UM.Logger.log("w", "Cannot merge two containers of different types")
return False
for key in merge.getAllKeys():
merge_into.setProperty(key, "value", merge.getProperty(key, "value"))
self._performMerge(merge_into, merge)
return True
@ -151,7 +154,7 @@ class ContainerManager(QObject):
# \return True if successful, False if not.
@pyqtSlot(str, result = bool)
def clearContainer(self, container_id):
containers = self._registry.findContainers(None, id = container_id)
containers = self._container_registry.findContainers(None, id = container_id)
if not containers:
UM.Logger.log("w", "Could clear container %s because it was not found.", container_id)
return False
@ -178,9 +181,9 @@ class ContainerManager(QObject):
# \return True if successful, False if not.
@pyqtSlot(str, str, str, result = bool)
def setContainerMetaDataEntry(self, container_id, entry_name, entry_value):
containers = UM.Settings.ContainerRegistry.getInstance().findContainers(None, id = container_id)
containers = self._container_registry.findContainers(None, id = container_id)
if not containers:
UM.Logger.log("w", "Could set metadata of container %s because it was not found.", container_id)
UM.Logger.log("w", "Could not set metadata of container %s because it was not found.", container_id)
return False
container = containers[0]
@ -209,6 +212,24 @@ class ContainerManager(QObject):
return True
## Set the name of the specified container.
@pyqtSlot(str, str, result = bool)
def setContainerName(self, container_id, new_name):
containers = self._container_registry.findContainers(None, id = container_id)
if not containers:
UM.Logger.log("w", "Could not set name of container %s because it was not found.", container_id)
return False
container = containers[0]
if container.isReadOnly():
UM.Logger.log("w", "Cannot set name of read-only container %s.", container_id)
return False
container.setName(new_name)
return True
## Find instance containers matching certain criteria.
#
# This effectively forwards to ContainerRegistry::findInstanceContainers.
@ -219,11 +240,20 @@ class ContainerManager(QObject):
@pyqtSlot("QVariantMap", result = "QVariantList")
def findInstanceContainers(self, criteria):
result = []
for entry in self._registry.findInstanceContainers(**criteria):
for entry in self._container_registry.findInstanceContainers(**criteria):
result.append(entry.getId())
return result
@pyqtSlot(str, result = bool)
def isContainerUsed(self, container_id):
UM.Logger.log("d", "Checking if container %s is currently used in the active stacks", container_id)
for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
if container_id in [child.getId() for child in stack.getContainers()]:
UM.Logger.log("d", "The container is in use by %s", stack.getId())
return True
return False
## Get a list of string that can be used as name filters for a Qt File Dialog
#
# This will go through the list of available container types and generate a list of strings
@ -276,7 +306,7 @@ class ContainerManager(QObject):
else:
mime_type = self._container_name_filters[file_type]["mime"]
containers = UM.Settings.ContainerRegistry.getInstance().findContainers(None, id = container_id)
containers = self._container_registry.findContainers(None, id = container_id)
if not containers:
return { "status": "error", "message": "Container not found"}
container = containers[0]
@ -334,7 +364,7 @@ class ContainerManager(QObject):
return { "status": "error", "message": "Could not find a container to handle the specified file."}
container_id = urllib.parse.unquote_plus(mime_type.stripExtension(os.path.basename(file_url)))
container_id = UM.Settings.ContainerRegistry.getInstance().uniqueName(container_id)
container_id = self._container_registry.uniqueName(container_id)
container = container_type(container_id)
@ -346,10 +376,220 @@ class ContainerManager(QObject):
container.setName(container_id)
UM.Settings.ContainerRegistry.getInstance().addContainer(container)
self._container_registry.addContainer(container)
return { "status": "success", "message": "Successfully imported container {0}".format(container.getName()) }
## Update the current active quality changes container with the settings from the user container.
#
# This will go through the active global stack and all active extruder stacks and merge the changes from the user
# container into the quality_changes container. After that, the user container is cleared.
#
# \return \type{bool} True if successful, False if not.
@pyqtSlot(result = bool)
def updateQualityChanges(self):
global_stack = UM.Application.getInstance().getGlobalContainerStack()
if not global_stack:
return False
self._machine_manager.blurSettings.emit()
for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
# Find the quality_changes container for this stack and merge the contents of the top container into it.
quality_changes = stack.findContainer(type = "quality_changes")
if not quality_changes or quality_changes.isReadOnly():
UM.Logger.log("e", "Could not update quality of a nonexistant or read only quality profile in stack %s", stack.getId())
continue
self._performMerge(quality_changes, stack.getTop())
self._machine_manager.activeQualityChanged.emit()
return True
## Clear the top-most (user) containers of the active stacks.
@pyqtSlot()
def clearUserContainers(self):
self._machine_manager.blurSettings.emit()
# Go through global and extruder stacks and clear their topmost container (the user settings).
for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
stack.getTop().clear()
## Create quality changes containers from the user containers in the active stacks.
#
# This will go through the global and extruder stacks and create quality_changes containers from
# the user containers in each stack. These then replace the quality_changes containers in the
# stack and clear the user settings.
#
# \return \type{bool} True if the operation was successfully, False if not.
@pyqtSlot(str, result = bool)
def createQualityChanges(self, base_name):
global_stack = UM.Application.getInstance().getGlobalContainerStack()
if not global_stack:
return False
active_quality_name = self._machine_manager.activeQualityName
if active_quality_name == "":
UM.Logger.log("w", "No quality container found in stack %s, cannot create profile", global_stack.getId())
return False
self._machine_manager.blurSettings.emit()
if base_name is None:
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():
user_container = stack.getTop()
quality_container = stack.findContainer(type = "quality")
quality_changes_container = stack.findContainer(type = "quality_changes")
if not quality_container or not quality_changes_container:
UM.Logger.log("w", "No quality or quality changes container found in stack %s, ignoring it", stack.getId())
continue
new_changes = self._createQualityChanges(quality_container, unique_name, stack.getId())
self._performMerge(new_changes, user_container)
self._container_registry.addContainer(new_changes)
stack.replaceContainer(stack.getContainerIndex(quality_changes_container), new_changes)
self._machine_manager.activeQualityChanged.emit()
return True
## Remove all quality changes containers matching a specified name.
#
# This will search for quality_changes containers matching the supplied name and remove them.
# Note that if the machine specifies that qualities should be filtered by machine and/or material
# only the containers related to the active machine/material are removed.
#
# \param quality_name The name of the quality changes to remove.
#
# \return \type{bool} True if successful, False if not.
@pyqtSlot(str, result = bool)
def removeQualityChanges(self, quality_name):
UM.Logger.log("d", "Attempting to remove the quality change containers with name %s", quality_name)
containers_found = False
if not quality_name:
return containers_found # Without a name we will never find a container to remove.
# If the container that is being removed is the currently active quality, set another quality as the active quality
activate_quality = quality_name == self._machine_manager.activeQualityName
activate_quality_type = None
for container in self._getFilteredContainers(name = quality_name, type = "quality_changes"):
containers_found = True
if activate_quality and not activate_quality_type:
activate_quality_type = container.getMetaDataEntry("quality")
self._container_registry.removeContainer(container.getId())
if not containers_found:
UM.Logger.log("d", "Unable to remove quality containers, as we did not find any by the name of %s", quality_name)
elif activate_quality:
definition_id = "fdmprinter" if not self._machine_manager.filterQualityByMachine else self._machine_manager.activeDefinitionId
containers = self._container_registry.findInstanceContainers(type = "quality", definition = definition_id, quality_type = activate_quality_type)
if containers:
self._machine_manager.setActiveQuality(containers[0].getId())
self._machine_manager.activeQualityChanged.emit()
return containers_found
## Rename a set of quality changes containers.
#
# This will search for quality_changes containers matching the supplied name and rename them.
# Note that if the machine specifies that qualities should be filtered by machine and/or material
# only the containers related to the active machine/material are renamed.
#
# \param quality_name The name of the quality changes containers to rename.
# \param new_name The new name of the quality changes.
#
# \return True if successful, False if not.
@pyqtSlot(str, str, result = bool)
def renameQualityChanges(self, quality_name, new_name):
UM.Logger.log("d", "User requested QualityChanges container rename of %s to %s", quality_name, new_name)
if not quality_name or not new_name:
return False
if quality_name == new_name:
UM.Logger.log("w", "Unable to rename %s to %s, because they are the same.", quality_name, new_name)
return True
global_stack = UM.Application.getInstance().getGlobalContainerStack()
if not global_stack:
return False
self._machine_manager.blurSettings.emit()
new_name = self._container_registry.uniqueName(new_name)
container_registry = self._container_registry
for container in self._getFilteredContainers(name = quality_name, type = "quality_changes"):
stack_id = container.getMetaDataEntry("extruder", global_stack.getId())
container_registry.renameContainer(container.getId(), new_name, self._createUniqueId(stack_id, new_name))
self._machine_manager.activeQualityChanged.emit()
return True
## Duplicate a specified set of quality or quality_changes containers.
#
# This will search for containers matching the specified name. If the container is a "quality" type container, a new
# quality_changes container will be created with the specified quality as base. If the container is a "quality_changes"
# container, it is simply duplicated and renamed.
#
# \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, 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 ""
UM.Logger.log("d", "Attempting to duplicate the quality %s", quality_name)
containers = self._container_registry.findInstanceContainers(name = quality_name)
if not containers:
UM.Logger.log("d", "Unable to duplicate the quality %s, because it doesn't exist.", quality_name)
return ""
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":
for container in self._getFilteredContainers(name = quality_name, type = "quality"):
for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
new_changes = self._createQualityChanges(container, new_name, stack.getId())
self._container_registry.addContainer(new_changes)
elif container_type == "quality_changes":
for container in self._getFilteredContainers(name = quality_name, type = "quality_changes"):
stack_id = container.getMetaDataEntry("extruder", global_stack.getId())
new_container = container.duplicate(self._createUniqueId(stack_id, new_name), new_name)
self._container_registry.addContainer(new_container)
else:
return ""
return new_name
# Factory function, used by QML
@staticmethod
def createContainerManager(engine, js_engine):
return ContainerManager()
def _performMerge(self, merge_into, merge):
assert isinstance(merge, type(merge_into))
if merge == merge_into:
return
for key in merge.getAllKeys():
merge_into.setProperty(key, "value", merge.getProperty(key, "value"))
merge.clear()
def _updateContainerNameFilters(self):
self._container_name_filters = {}
for plugin_id, container_type in UM.Settings.ContainerRegistry.getContainerTypes():
@ -394,7 +634,83 @@ class ContainerManager(QObject):
name_filter = "{0} ({1})".format(mime_type.comment, suffix_list)
self._container_name_filters[name_filter] = entry
# Factory function, used by QML
@staticmethod
def createContainerManager(engine, js_engine):
return ContainerManager()
## Return a generator that iterates over a set of containers that are filtered by machine and material when needed.
#
# \param kwargs Initial search criteria that the containers need to match.
#
# \return A generator that iterates over the list of containers matching the search criteria.
def _getFilteredContainers(self, **kwargs):
global_stack = UM.Application.getInstance().getGlobalContainerStack()
if not global_stack:
return False
criteria = kwargs
filter_by_material = False
if global_stack.getMetaDataEntry("has_machine_quality"):
criteria["definition"] = global_stack.getBottom().getId()
filter_by_material = global_stack.getMetaDataEntry("has_materials")
material_ids = []
if filter_by_material:
for stack in cura.Settings.ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
material_ids.append(stack.findContainer(type = "material").getId())
containers = self._container_registry.findInstanceContainers(**criteria)
for container in containers:
# If the machine specifies we should filter by material, exclude containers that do not match any active material.
if filter_by_material and container.getMetaDataEntry("material") not in material_ids:
continue
yield container
## Creates a unique ID for a container by prefixing the name with the stack ID.
#
# This method creates a unique ID for a container by prefixing it with a specified stack ID.
# This is done to ensure we have an easily identified ID for quality changes, which have the
# same name across several stacks.
#
# \param stack_id The ID of the stack to prepend.
# \param container_name The name of the container that we are creating a unique ID for.
#
# \return Container name prefixed with stack ID, in lower case with spaces replaced by underscores.
def _createUniqueId(self, stack_id, container_name):
result = stack_id + "_" + container_name
result = result.lower()
result.replace(" ", "_")
return result
## Create a quality changes container for a specified quality container.
#
# \param quality_container The quality container to create a changes container for.
# \param new_name The name of the new quality_changes container.
# \param stack_id The ID of the container stack the new container "belongs to". It is used primarily to ensure a unique ID.
#
# \return A new quality_changes container with the specified container as base.
def _createQualityChanges(self, quality_container, new_name, stack_id):
global_stack = UM.Application.getInstance().getGlobalContainerStack()
assert global_stack is not None
# Create a new quality_changes container for the quality.
quality_changes = UM.Settings.InstanceContainer(self._createUniqueId(stack_id, new_name))
quality_changes.setName(new_name)
quality_changes.addMetaDataEntry("type", "quality_changes")
quality_changes.addMetaDataEntry("quality", quality_container.getMetaDataEntry("quality_type"))
# If we are creating a container for an extruder, ensure we add that to the container
if stack_id != global_stack.getId():
quality_changes.addMetaDataEntry("extruder", stack_id)
# If the machine specifies qualities should be filtered, ensure we match the current criteria.
if not global_stack.getMetaDataEntry("has_machine_quality"):
quality_changes.setDefinition(self._container_registry.findContainers(id = "fdmprinter")[0])
else:
quality_changes.setDefinition(global_stack.getBottom())
if global_stack.getMetaDataEntry("has_materials"):
material = quality_container.getMetaDataEntry("material")
quality_changes.addMetaDataEntry("material", material)
return quality_changes

View file

@ -31,7 +31,7 @@ class ContainerSettingsModel(ListModel):
self._update()
def _update(self):
self.clear()
items = []
if len(self._container_ids) == 0:
return
@ -41,7 +41,6 @@ class ContainerSettingsModel(ListModel):
keys = keys + list(container.getAllKeys())
keys = list(set(keys)) # remove duplicate keys
keys.sort()
for key in keys:
definition = None
@ -65,13 +64,15 @@ class ContainerSettingsModel(ListModel):
else:
values.append("")
self.appendItem({
items.append({
"key": key,
"values": values,
"label": definition.label,
"unit": definition.unit,
"category": category.label
})
items.sort(key = lambda k: (k["category"], k["key"]))
self.setItems(items)
## Set the ids of the containers which have the settings this model should list.
# Also makes sure the model updates when the containers have property changes

View file

@ -58,12 +58,10 @@ class CuraContainerRegistry(ContainerRegistry):
## Exports an profile to a file
#
# \param instance_id \type{str} the ID of the profile to export.
# \param instance_ids \type{list} the IDs of the profiles to export.
# \param file_name \type{str} the full path and filename to export to.
# \param file_type \type{str} the file type with the format "<description> (*.<extension>)"
def exportProfile(self, instance_id, file_name, file_type):
Logger.log('d', 'exportProfile instance_id: '+str(instance_id))
def exportProfile(self, instance_ids, file_name, file_type):
# Parse the fileType to deduce what plugin can save the file format.
# fileType has the format "<description> (*.<extension>)"
split = file_type.rfind(" (*.") # Find where the description ends and the extension starts.
@ -82,16 +80,16 @@ class CuraContainerRegistry(ContainerRegistry):
catalog.i18nc("@label", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?").format(file_name))
if result == QMessageBox.No:
return
containers = ContainerRegistry.getInstance().findInstanceContainers(id=instance_id)
if not containers:
return
container = containers[0]
found_containers = []
for instance_id in instance_ids:
containers = ContainerRegistry.getInstance().findInstanceContainers(id=instance_id)
if containers:
found_containers.append(containers[0])
profile_writer = self._findProfileWriter(extension, description)
try:
success = profile_writer.write(file_name, container)
success = profile_writer.write(file_name, found_containers)
except Exception as e:
Logger.log("e", "Failed to export profile to %s: %s", file_name, str(e))
m = Message(catalog.i18nc("@info:status", "Failed to export profile to <filename>{0}</filename>: <message>{1}</message>", file_name, str(e)), lifetime = 0)
@ -130,6 +128,7 @@ class CuraContainerRegistry(ContainerRegistry):
return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, "Invalid path")}
plugin_registry = PluginRegistry.getInstance()
container_registry = ContainerRegistry.getInstance()
for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
profile_reader = plugin_registry.getPluginObject(plugin_id)
try:
@ -146,7 +145,11 @@ class CuraContainerRegistry(ContainerRegistry):
return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) }
else:
for profile in profile_or_list:
self._configureProfile(profile, name_seed)
profile.setDirty(True) # Ensure the profiles are correctly saved
if profile.getId() != "":
container_registry.addContainer(profile)
else:
self._configureProfile(profile, name_seed)
if len(profile_or_list) == 1:
return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())}
@ -160,7 +163,7 @@ class CuraContainerRegistry(ContainerRegistry):
def _configureProfile(self, profile, name_seed):
profile.setReadOnly(False)
new_name = self.createUniqueName("quality", "", name_seed, catalog.i18nc("@label", "Custom profile"))
new_name = self.createUniqueName("quality_changes", "", name_seed, catalog.i18nc("@label", "Custom profile"))
profile.setName(new_name)
profile._id = new_name
@ -170,6 +173,7 @@ class CuraContainerRegistry(ContainerRegistry):
profile.addMetaDataEntry("material", self._activeMaterialId())
else:
profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0])
ContainerRegistry.getInstance().addContainer(profile)
## Gets a list of profile writer plugins

View file

@ -55,6 +55,13 @@ class ExtruderManager(QObject):
map[position] = self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()][position].getId()
return map
@pyqtSlot(str, result = str)
def getQualityChangesIdByExtruderStackId(self, id):
for position in self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()]:
extruder = self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()][position]
if extruder.getId() == id:
return extruder.findContainer(type = "quality_changes").getId()
## The instance of the singleton pattern.
#
# It's None if the extruder manager hasn't been created yet.
@ -131,9 +138,9 @@ class ExtruderManager(QObject):
# Make sure the next stack is a stack that contains only the machine definition
if not extruder_train.getNextStack():
shallowStack = UM.Settings.ContainerStack(machine_id + "_shallow")
shallowStack.addContainer(machine_definition)
extruder_train.setNextStack(shallowStack)
shallow_stack = UM.Settings.ContainerStack(machine_id + "_shallow")
shallow_stack.addContainer(machine_definition)
extruder_train.setNextStack(shallow_stack)
changed = True
if changed:
self.extrudersChanged.emit(machine_id)
@ -235,6 +242,9 @@ class ExtruderManager(QObject):
container_stack.addContainer(quality)
empty_quality_changes = container_registry.findInstanceContainers(id = "empty_quality_changes")[0]
container_stack.addContainer(empty_quality_changes)
user_profile = container_registry.findInstanceContainers(type = "user", extruder = extruder_stack_id)
if user_profile: # There was already a user profile, loaded from settings.
user_profile = user_profile[0]
@ -248,9 +258,9 @@ class ExtruderManager(QObject):
# Make sure the next stack is a stack that contains only the machine definition
if not container_stack.getNextStack():
shallowStack = UM.Settings.ContainerStack(machine_id + "_shallow")
shallowStack.addContainer(machine_definition)
container_stack.setNextStack(shallowStack)
shallow_stack = UM.Settings.ContainerStack(machine_id + "_shallow")
shallow_stack.addContainer(machine_definition)
container_stack.setNextStack(shallow_stack)
container_registry.addContainer(container_stack)
@ -274,6 +284,20 @@ class ExtruderManager(QObject):
for name in self._extruder_trains[machine_id]:
yield self._extruder_trains[machine_id][name]
## Returns a generator that will iterate over the global stack and per-extruder stacks.
#
# The first generated element is the global container stack. After that any extruder stacks are generated.
def getActiveGlobalAndExtruderStacks(self):
global_stack = UM.Application.getInstance().getGlobalContainerStack()
if not global_stack:
return
yield global_stack
global_id = global_stack.getId()
for name in self._extruder_trains[global_id]:
yield self._extruder_trains[global_id][name]
def __globalContainerStackChanged(self):
self._addCurrentMachineExtruders()
self.activeExtruderChanged.emit()
@ -313,6 +337,17 @@ class ExtruderManager(QObject):
return result
## Get all extruder values for a certain setting.
#
# This is exposed to qml for display purposes
#
# \param key The key of the setting to retieve values for.
#
# \return String representing the extruder values
@pyqtSlot(str, result="QList<int>")
def getInstanceExtruderValues(self, key):
return ExtruderManager.getExtruderValues(key)
## Get the value for a setting from a specific extruder.
#
# This is exposed to SettingFunction to use in value functions.

View file

@ -97,9 +97,10 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
changed = False
if self.rowCount() != 0:
self.clear()
changed = True
items = []
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
if global_container_stack:
if self._add_global:
@ -111,7 +112,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
"color": color,
"index": -1
}
self.appendItem(item)
items.append(item)
changed = True
manager = ExtruderManager.getInstance()
@ -133,9 +134,10 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
"color": color,
"index": position
}
self.appendItem(item)
items.append(item)
changed = True
if changed:
self.sort(lambda item: item["index"])
items.sort(key = lambda i: i["index"])
self.setItems(items)
self.modelChanged.emit()

View file

@ -8,6 +8,8 @@ from UM.Application import Application
from UM.Preferences import Preferences
from UM.Logger import Logger
from UM.Settings.SettingRelation import RelationType
import UM.Settings
from cura.PrinterOutputDevice import PrinterOutputDevice
@ -17,6 +19,7 @@ from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
import time
import os
class MachineManager(QObject):
def __init__(self, parent = None):
@ -26,16 +29,17 @@ class MachineManager(QObject):
self._global_container_stack = None
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
## When the global container is changed, active material probably needs to be updated.
self.globalContainerChanged.connect(self.activeMaterialChanged)
self.globalContainerChanged.connect(self.activeVariantChanged)
self.globalContainerChanged.connect(self.activeQualityChanged)
self._active_stack_valid = None
self._onGlobalContainerChanged()
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
self._onActiveExtruderStackChanged()
## When the global container is changed, active material probably needs to be updated.
self.globalContainerChanged.connect(self.activeMaterialChanged)
self.globalContainerChanged.connect(self.activeVariantChanged)
self.globalContainerChanged.connect(self.activeQualityChanged)
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeMaterialChanged)
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeVariantChanged)
ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeQualityChanged)
@ -47,6 +51,7 @@ class MachineManager(QObject):
self._empty_variant_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_variant")[0]
self._empty_material_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_material")[0]
self._empty_quality_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_quality")[0]
self._empty_quality_changes_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_quality_changes")[0]
Preferences.getInstance().addPreference("cura/active_machine", "")
@ -115,16 +120,7 @@ class MachineManager(QObject):
if matching_extruder and matching_extruder.findContainer({"type": "variant"}).getName() != hotend_id:
# Save the material that needs to be changed. Multiple changes will be handled by the callback.
self._auto_hotends_changed[str(index)] = containers[0].getId()
Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"),
catalog.i18nc("@label",
"Do you want to change the materials and hotends to match the material in your printer?"),
catalog.i18nc("@label",
"The materials and / or hotends on your printer were changed. For best results always slice for the materials . hotends that are inserted in your printer."),
buttons=QMessageBox.Yes + QMessageBox.No,
icon=QMessageBox.Question,
callback=self._materialHotendChangedCallback)
self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback)
else:
Logger.log("w", "No variant found for printer definition %s with id %s" % (self._global_container_stack.getBottom().getId(), hotend_id))
@ -164,10 +160,7 @@ class MachineManager(QObject):
if matching_extruder and matching_extruder.findContainer({"type":"material"}).getMetaDataEntry("GUID") != material_id:
# Save the material that needs to be changed. Multiple changes will be handled by the callback.
self._auto_materials_changed[str(index)] = containers[0].getId()
Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the materials and hotends to match the material in your printer?"),
catalog.i18nc("@label", "The materials and / or hotends on your printer were changed. For best results always slice for the materials and hotends that are inserted in your printer."),
buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialHotendChangedCallback)
self._printer_output_devices[0].materialHotendChangedMessage(self._materialHotendChangedCallback)
else:
Logger.log("w", "No material definition found for printer definition %s and GUID %s" % (definition_id, material_id))
@ -196,107 +189,11 @@ class MachineManager(QObject):
if old_index is not None:
extruder_manager.setActiveExtruderIndex(old_index)
def _onGlobalPropertyChanged(self, key, property_name):
if property_name == "value":
## We can get recursion issues. So we store a list of keys that we are still handling to prevent this.
if key in self._global_event_keys:
return
self._global_event_keys.add(key)
self.globalValueChanged.emit()
if self._active_container_stack and self._active_container_stack != self._global_container_stack:
# Make the global current settings mirror the stack values appropriate for this setting
if self._active_container_stack.getProperty("extruder_nr", "value") == int(self._active_container_stack.getProperty(key, "global_inherits_stack")):
new_value = self._active_container_stack.getProperty(key, "value")
self._global_container_stack.getTop().setProperty(key, "value", new_value)
# Global-only setting values should be set on all extruders and the global stack
if not self._global_container_stack.getProperty(key, "settable_per_extruder"):
extruder_stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
target_stack_position = int(self._active_container_stack.getProperty(key, "global_inherits_stack"))
if target_stack_position == -1: # Prevent -1 from selecting wrong stack.
target_stack = self._active_container_stack
else:
target_stack = extruder_stacks[target_stack_position]
new_value = target_stack.getProperty(key, "value")
target_stack_has_user_value = target_stack.getTop().getInstance(key) != None
for extruder_stack in extruder_stacks:
if extruder_stack != target_stack:
if target_stack_has_user_value:
extruder_stack.getTop().setProperty(key, "value", new_value)
else:
# Remove from the value from the other stacks as well, unless the
# top value from the other stacklevels is different than the new value
for container in extruder_stack.getContainers():
if container.__class__ == UM.Settings.InstanceContainer and container.getInstance(key) != None:
if container.getProperty(key, "value") != new_value:
# It could be that the setting needs to be removed instead of updated.
temp = extruder_stack
containers = extruder_stack.getContainers()
# Ensure we have the entire 'chain'
while temp.getNextStack():
temp = temp.getNextStack()
containers.extend(temp.getContainers())
instance_needs_removal = False
if len(containers) > 1:
for index in range(1, len(containers)):
deeper_container = containers[index]
if deeper_container.getProperty(key, "value") is None:
continue # Deeper container does not have the value, so continue.
if deeper_container.getProperty(key, "value") == new_value:
# Removal will result in correct value, so do that.
# We do this to prevent the reset from showing up unneeded.
instance_needs_removal = True
break
else:
# Container has the value, but it's not the same. Stop looking.
break
if instance_needs_removal:
extruder_stack.getTop().removeInstance(key)
else:
extruder_stack.getTop().setProperty(key, "value", new_value)
else:
# Check if we really need to remove something.
if extruder_stack.getProperty(key, "value") != new_value:
extruder_stack.getTop().removeInstance(key)
break
if self._global_container_stack.getProperty(key, "value") != new_value:
self._global_container_stack.getTop().setProperty(key, "value", new_value)
self._global_event_keys.remove(key)
if property_name == "global_inherits_stack":
if self._active_container_stack and self._active_container_stack != self._global_container_stack:
# Update the global user value when the "global_inherits_stack" function points to a different stack
extruder_stacks = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
target_stack_position = int(self._active_container_stack.getProperty(key, "global_inherits_stack"))
if target_stack_position == -1: # Prevent -1 from selecting wrong stack.
target_stack = self._active_container_stack
else:
target_stack = extruder_stacks[target_stack_position]
new_value = target_stack.getProperty(key, "value")
if self._global_container_stack.getProperty(key, "value") != new_value:
self._global_container_stack.getTop().setProperty(key, "value", new_value)
if property_name == "validationState":
if self._active_stack_valid:
changed_validation_state = self._active_container_stack.getProperty(key, property_name)
if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError):
self._active_stack_valid = False
self.activeValidationChanged.emit()
else:
has_errors = self._checkStackForErrors(self._active_container_stack)
if not has_errors:
self._active_stack_valid = True
self.activeValidationChanged.emit()
def _onGlobalContainerChanged(self):
if self._global_container_stack:
self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged)
self._global_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
self._global_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged)
self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
material = self._global_container_stack.findContainer({"type": "material"})
material.nameChanged.disconnect(self._onMaterialNameChanged)
@ -313,7 +210,7 @@ class MachineManager(QObject):
Preferences.getInstance().setValue("cura/active_machine", self._global_container_stack.getId())
self._global_container_stack.nameChanged.connect(self._onMachineNameChanged)
self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
self._global_container_stack.propertyChanged.connect(self._onPropertyChanged)
material = self._global_container_stack.findContainer({"type": "material"})
material.nameChanged.connect(self._onMaterialNameChanged)
@ -324,11 +221,11 @@ class MachineManager(QObject):
self.blurSettings.emit() # Ensure no-one has focus.
if self._active_container_stack and self._active_container_stack != self._global_container_stack:
self._active_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged)
self._active_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged)
self._active_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
self._active_container_stack = ExtruderManager.getInstance().getActiveExtruderStack()
if self._active_container_stack:
self._active_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
self._active_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
self._active_container_stack.propertyChanged.connect(self._onPropertyChanged)
else:
self._active_container_stack = self._global_container_stack
self._active_stack_valid = not self._checkStackForErrors(self._active_container_stack)
@ -337,17 +234,6 @@ class MachineManager(QObject):
def _onInstanceContainersChanged(self, container):
container_type = container.getMetaDataEntry("type")
if self._active_container_stack and self._active_container_stack != self._global_container_stack:
if int(self._active_container_stack.getProperty("extruder_nr", "value")) == 0:
global_container = self._global_container_stack.findContainer({"type": container_type})
if global_container and global_container != container:
container_index = self._global_container_stack.getContainerIndex(global_container)
self._global_container_stack.replaceContainer(container_index, container)
for key in container.getAllKeys():
# Make sure the values in this profile are distributed to other stacks if necessary
self._onGlobalPropertyChanged(key, "value")
if container_type == "material":
self.activeMaterialChanged.emit()
elif container_type == "variant":
@ -355,6 +241,38 @@ class MachineManager(QObject):
elif container_type == "quality":
self.activeQualityChanged.emit()
def _onPropertyChanged(self, key, property_name):
if property_name == "value":
# If a setting is not settable per extruder, but "has enabled relations" that are settable per extruder
# we need to copy the value to global, so that the front-end displays the right settings.
if not self._active_container_stack.getProperty(key, "settable_per_extruder"):
relations = self._global_container_stack.getBottom()._getDefinition(key).relations
for relation in filter(lambda r: r.role == "enabled" and r.type == RelationType.RequiredByTarget, relations):
# Target setting is settable per extruder
if self._active_container_stack.getProperty(relation.target.key, "settable_per_extruder"):
new_value = self._global_container_stack.getProperty(key, "value")
stacks = [stack for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId())]
for extruder_stack in stacks:
if extruder_stack.getProperty(key, "value") != new_value:
extruder_stack.getTop().setProperty(key, "value", new_value)
break
if property_name == "validationState":
if self._active_stack_valid:
if self._active_container_stack.getProperty(key, "settable_per_extruder"):
changed_validation_state = self._active_container_stack.getProperty(key, property_name)
else:
changed_validation_state = self._global_container_stack.getProperty(key, property_name)
if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError):
self._active_stack_valid = False
self.activeValidationChanged.emit()
else:
if not self._checkStackForErrors(self._active_container_stack) and not self._checkStackForErrors(self._global_container_stack):
self._active_stack_valid = True
self.activeValidationChanged.emit()
self.activeStackChanged.emit()
@pyqtSlot(str)
def setActiveMachine(self, stack_id):
containers = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = stack_id)
@ -374,7 +292,7 @@ class MachineManager(QObject):
variant_instance_container = self._updateVariantContainer(definition)
material_instance_container = self._updateMaterialContainer(definition, variant_instance_container)
quality_instance_container = self._updateQualityContainer(definition, material_instance_container)
quality_instance_container = self._updateQualityContainer(definition, variant_instance_container, material_instance_container)
current_settings_instance_container = UM.Settings.InstanceContainer(name + "_current_settings")
current_settings_instance_container.addMetaDataEntry("machine", name)
@ -382,7 +300,6 @@ class MachineManager(QObject):
current_settings_instance_container.setDefinition(definitions[0])
container_registry.addContainer(current_settings_instance_container)
# If a definition is found, its a list. Should only have one item.
new_global_stack.addContainer(definition)
if variant_instance_container:
new_global_stack.addContainer(variant_instance_container)
@ -390,6 +307,8 @@ class MachineManager(QObject):
new_global_stack.addContainer(material_instance_container)
if quality_instance_container:
new_global_stack.addContainer(quality_instance_container)
new_global_stack.addContainer(self._empty_quality_changes_container)
new_global_stack.addContainer(current_settings_instance_container)
ExtruderManager.getInstance().addMachineExtruders(definition, new_global_stack.getId())
@ -430,11 +349,17 @@ class MachineManager(QObject):
## Check if the global_container has instances in the user container
@pyqtProperty(bool, notify = activeStackChanged)
def hasUserSettings(self):
if not self._active_container_stack:
if not self._global_container_stack:
return False
user_settings = self._active_container_stack.getTop().findInstances(**{})
return len(user_settings) != 0
if self._global_container_stack.getTop().findInstances():
return True
for stack in ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()):
if stack.getTop().findInstances():
return True
return False
## Check if the global profile does not contain error states
# Note that the _active_stack_valid is cached due to performance issues
@ -489,9 +414,37 @@ class MachineManager(QObject):
return ""
@pyqtProperty("QVariantMap", notify = activeMaterialChanged)
def allActiveMaterialIds(self):
if not self._global_container_stack:
return {}
result = {}
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
material_container = stack.findContainer(type = "material")
if not material_container:
continue
result[stack.getId()] = material_container.getId()
return result
@pyqtProperty(str, notify=activeQualityChanged)
def activeQualityMaterialId(self):
if self._active_container_stack:
quality = self._active_container_stack.findContainer({"type": "quality"})
if quality:
return quality.getMetaDataEntry("material")
return ""
@pyqtProperty(str, notify=activeQualityChanged)
def activeQualityName(self):
if self._active_container_stack:
quality = self._active_container_stack.findContainer({"type": "quality_changes"})
if quality and quality != self._empty_quality_changes_container:
return quality.getName()
quality = self._active_container_stack.findContainer({"type": "quality"})
if quality:
return quality.getName()
@ -499,12 +452,31 @@ class MachineManager(QObject):
@pyqtProperty(str, notify=activeQualityChanged)
def activeQualityId(self):
if self._active_container_stack:
quality = self._active_container_stack.findContainer({"type": "quality"})
if self._global_container_stack:
quality = self._global_container_stack.findContainer({"type": "quality_changes"})
if quality and quality != self._empty_quality_changes_container:
return quality.getId()
quality = self._global_container_stack.findContainer({"type": "quality"})
if quality:
return quality.getId()
return ""
@pyqtProperty(str, notify = activeQualityChanged)
def activeQualityType(self):
if self._global_container_stack:
quality = self._global_container_stack.findContainer(type = "quality")
if quality:
return quality.getMetaDataEntry("quality_type")
return ""
@pyqtProperty(str, notify = activeQualityChanged)
def activeQualityChangesId(self):
if self._global_container_stack:
changes = self._global_container_stack.findContainer(type = "quality_changes")
if changes:
return changes.getId()
return ""
## Check if a container is read_only
@pyqtSlot(str, result = bool)
def isReadOnly(self, container_id):
@ -526,136 +498,49 @@ class MachineManager(QObject):
if extruder_stack != self._active_container_stack and extruder_stack.getProperty(key, "value") != new_value:
extruder_stack.getTop().setProperty(key, "value", new_value)
@pyqtSlot(result = str)
def newQualityContainerFromQualityAndUser(self):
new_container_id = self.duplicateContainer(self.activeQualityId)
if new_container_id == "":
return
self.blurSettings.emit()
self.updateQualityContainerFromUserContainer(new_container_id)
self.setActiveQuality(new_container_id)
return new_container_id
@pyqtSlot(str, result=str)
def duplicateContainer(self, container_id):
if not self._active_container_stack:
return ""
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
if containers:
new_name = self._createUniqueName("quality", "", containers[0].getName(), catalog.i18nc("@label", "Custom profile"))
new_container = containers[0].duplicate(new_name, new_name)
UM.Settings.ContainerRegistry.getInstance().addContainer(new_container)
return new_name
return ""
@pyqtSlot(str, str)
def renameQualityContainer(self, container_id, new_name):
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id, type = "quality")
if containers:
new_name = self._createUniqueName("quality", containers[0].getName(), new_name,
catalog.i18nc("@label", "Custom profile"))
if containers[0].getName() == new_name:
# Nothing to do.
return
# As we also want the id of the container to be changed (so that profile name is the name of the file
# on disk. We need to create a new instance and remove it (so the old file of the container is removed)
# If we don't do that, we might get duplicates & other weird issues.
new_container = UM.Settings.InstanceContainer("")
new_container.deserialize(containers[0].serialize())
# Actually set the name
new_container.setName(new_name)
new_container._id = new_name # Todo: Fix proper id change function for this.
# Add the "new" container.
UM.Settings.ContainerRegistry.getInstance().addContainer(new_container)
# Ensure that the renamed profile is saved -before- we remove the old profile.
Application.getInstance().saveSettings()
# Actually set & remove new / old quality.
self.setActiveQuality(new_name)
self.removeQualityContainer(containers[0].getId())
@pyqtSlot(str)
def removeQualityContainer(self, container_id):
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id)
if not containers or not self._active_container_stack:
return
# If the container that is being removed is the currently active container, set another machine as the active container
activate_new_container = container_id == self.activeQualityId
UM.Settings.ContainerRegistry.getInstance().removeContainer(container_id)
if activate_new_container:
definition_id = "fdmprinter" if not self.filterQualityByMachine else self.activeDefinitionId
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "quality", definition = definition_id)
if containers:
self.setActiveQuality(containers[0].getId())
self.activeQualityChanged.emit()
@pyqtSlot(str)
@pyqtSlot()
def updateQualityContainerFromUserContainer(self, quality_id = None):
if not self._active_container_stack:
return
if quality_id:
quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = quality_id, type = "quality")
if quality:
quality = quality[0]
else:
quality = self._active_container_stack.findContainer({"type": "quality"})
if not quality:
return
user_settings = self._active_container_stack.getTop()
for key in user_settings.getAllKeys():
quality.setProperty(key, "value", user_settings.getProperty(key, "value"))
self.clearUserSettings() # As all users settings are noq a quality, remove them.
@pyqtSlot(str)
def setActiveMaterial(self, material_id):
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = material_id)
if not containers or not self._active_container_stack:
return
old_material = self._active_container_stack.findContainer({"type":"material"})
Logger.log("d", "Attempting to change the active material to %s", material_id)
old_variant = self._active_container_stack.findContainer({"type": "variant"})
old_material = self._active_container_stack.findContainer({"type": "material"})
old_quality = self._active_container_stack.findContainer({"type": "quality"})
if old_material:
old_material.nameChanged.disconnect(self._onMaterialNameChanged)
old_quality_changes = self._active_container_stack.findContainer({"type": "quality_changes"})
if not old_material:
Logger.log("w", "While trying to set the active material, no material was found to replace it.")
return
if old_quality_changes.getId() == "empty_quality_changes": #Don't want the empty one.
old_quality_changes = None
self.blurSettings.emit()
old_material.nameChanged.disconnect(self._onMaterialNameChanged)
material_index = self._active_container_stack.getContainerIndex(old_material)
self._active_container_stack.replaceContainer(material_index, containers[0])
material_index = self._active_container_stack.getContainerIndex(old_material)
self._active_container_stack.replaceContainer(material_index, containers[0])
containers[0].nameChanged.connect(self._onMaterialNameChanged)
containers[0].nameChanged.connect(self._onMaterialNameChanged)
preferred_quality_name = None
if old_quality:
preferred_quality_name = old_quality.getName()
self.setActiveQuality(self._updateQualityContainer(self._global_container_stack.getBottom(), containers[0], preferred_quality_name).id)
if old_quality:
if old_quality_changes:
new_quality = self._updateQualityChangesContainer(old_quality.getMetaDataEntry("quality_type"), old_quality_changes.getMetaDataEntry("name"))
else:
new_quality = self._updateQualityContainer(self._global_container_stack.getBottom(), old_variant, containers[0], old_quality.getName())
else:
Logger.log("w", "While trying to set the active material, no material was found to replace.")
new_quality = self._updateQualityContainer(self._global_container_stack.getBottom(), old_variant, containers[0])
self.setActiveQuality(new_quality.getId())
@pyqtSlot(str)
def setActiveVariant(self, variant_id):
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = variant_id)
if not containers or not self._active_container_stack:
return
Logger.log("d", "Attempting to change the active variant to %s", variant_id)
old_variant = self._active_container_stack.findContainer({"type": "variant"})
old_material = self._active_container_stack.findContainer({"type": "material"})
if old_variant:
self.blurSettings.emit()
variant_index = self._active_container_stack.getContainerIndex(old_variant)
self._active_container_stack.replaceContainer(variant_index, containers[0])
@ -669,32 +554,89 @@ class MachineManager(QObject):
@pyqtSlot(str)
def setActiveQuality(self, quality_id):
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = quality_id)
if not containers or not self._active_container_stack:
if not containers or not self._global_container_stack:
return
old_quality = self._active_container_stack.findContainer({"type": "quality"})
if old_quality and old_quality != containers[0]:
old_quality.nameChanged.disconnect(self._onQualityNameChanged)
Logger.log("d", "Attempting to change the active quality to %s", quality_id)
quality_index = self._active_container_stack.getContainerIndex(old_quality)
self.blurSettings.emit()
quality_container = None
quality_changes_container = self._empty_quality_changes_container
self._active_container_stack.replaceContainer(quality_index, containers[0])
container_type = containers[0].getMetaDataEntry("type")
containers[0].nameChanged.connect(self._onQualityNameChanged)
if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
# Ask the user if the user profile should be cleared or not (discarding the current settings)
# In Simple Mode we assume the user always wants to keep the (limited) current settings
details = catalog.i18nc("@label", "You made changes to the following setting(s):")
user_settings = self._active_container_stack.getTop().findInstances(**{})
for setting in user_settings:
details = details + "\n " + setting.definition.label
Application.getInstance().messageBox(catalog.i18nc("@window:title", "Switched profiles"), catalog.i18nc("@label", "Do you want to transfer your changed settings to this profile?"),
catalog.i18nc("@label", "If you transfer your settings they will override settings in the profile."), details,
buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._keepUserSettingsDialogCallback)
if container_type == "quality":
quality_container = containers[0]
elif container_type == "quality_changes":
quality_changes_container = containers[0]
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(
quality_type = quality_changes_container.getMetaDataEntry("quality"))
if not containers:
Logger.log("e", "Could not find quality %s for changes %s, not changing quality", quality_changes_container.getMetaDataEntry("quality"), quality_changes_container.getId())
return
quality_container = containers[0]
else:
Logger.log("w", "While trying to set the active quality, no quality was found to replace.")
Logger.log("e", "Tried to set quality to a container that is not of the right type")
return
quality_type = quality_container.getMetaDataEntry("quality_type")
if not quality_type:
quality_type = quality_changes_container.getName()
for stack in ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks():
extruder_id = stack.getId() if stack != self._global_container_stack else None
criteria = { "quality_type": quality_type, "extruder": extruder_id }
material = stack.findContainer(type = "material")
if material and material is not self._empty_material_container:
criteria["material"] = material.getId()
if self._global_container_stack.getMetaDataEntry("has_machine_quality"):
criteria["definition"] = self._global_container_stack.getBottom().getId()
else:
criteria["definition"] = "fdmprinter"
stack_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria)
if not stack_quality:
criteria.pop("extruder")
stack_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**criteria)
if not stack_quality:
stack_quality = quality_container
else:
stack_quality = stack_quality[0]
else:
stack_quality = stack_quality[0]
if quality_changes_container != self._empty_quality_changes_container:
stack_quality_changes = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(name = quality_changes_container.getName(), extruder = extruder_id)[0]
else:
stack_quality_changes = self._empty_quality_changes_container
old_quality = stack.findContainer(type = "quality")
old_quality.nameChanged.disconnect(self._onQualityNameChanged)
old_changes = stack.findContainer(type = "quality_changes")
old_changes.nameChanged.disconnect(self._onQualityNameChanged)
stack.replaceContainer(stack.getContainerIndex(old_quality), stack_quality)
stack.replaceContainer(stack.getContainerIndex(old_changes), stack_quality_changes)
stack_quality.nameChanged.connect(self._onQualityNameChanged)
stack_quality_changes.nameChanged.connect(self._onQualityNameChanged)
if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1:
# Ask the user if the user profile should be cleared or not (discarding the current settings)
# In Simple Mode we assume the user always wants to keep the (limited) current settings
details = catalog.i18nc("@label", "You made changes to the following setting(s):")
user_settings = self._active_container_stack.getTop().findInstances(**{})
for setting in user_settings:
details = details + "\n " + setting.definition.label
Application.getInstance().messageBox(catalog.i18nc("@window:title", "Switched profiles"), catalog.i18nc("@label", "Do you want to transfer your changed settings to this profile?"),
catalog.i18nc("@label", "If you transfer your settings they will override settings in the profile."), details,
buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._keepUserSettingsDialogCallback)
self.activeQualityChanged.emit()
def _keepUserSettingsDialogCallback(self, button):
if button == QMessageBox.Yes:
@ -702,7 +644,11 @@ class MachineManager(QObject):
pass
elif button == QMessageBox.No:
# No, discard the settings in the user profile
self.clearUserSettings()
global_stack = Application.getInstance().getGlobalContainerStack()
for extruder in ExtruderManager.getInstance().getMachineExtruders(global_stack.getId()):
extruder.getTop().clear()
global_stack.getTop().clear()
@pyqtProperty(str, notify = activeVariantChanged)
def activeVariantName(self):
@ -731,6 +677,16 @@ class MachineManager(QObject):
return ""
## Gets how the active definition calls variants
# Caveat: per-definition-variant-title is currently not translated (though the fallback is)
@pyqtProperty(str, notify = globalContainerChanged)
def activeDefinitionVariantsName(self):
fallback_title = catalog.i18nc("@label", "Nozzle")
if self._global_container_stack:
return self._global_container_stack.getBottom().getMetaDataEntry("variants_name", fallback_title)
return fallback_title
@pyqtSlot(str, str)
def renameMachine(self, machine_id, new_name):
containers = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = machine_id)
@ -854,7 +810,8 @@ class MachineManager(QObject):
return self._empty_material_container
def _updateQualityContainer(self, definition, material_container = None, preferred_quality_name = None):
def _updateQualityContainer(self, definition, variant_container, material_container = None, preferred_quality_name = None):
container_registry = UM.Settings.ContainerRegistry.getInstance()
search_criteria = { "type": "quality" }
if definition.getMetaDataEntry("has_machine_quality"):
@ -865,28 +822,96 @@ class MachineManager(QObject):
else:
search_criteria["definition"] = "fdmprinter"
if preferred_quality_name:
if preferred_quality_name and preferred_quality_name != "empty":
search_criteria["name"] = preferred_quality_name
else:
preferred_quality = definition.getMetaDataEntry("preferred_quality")
if preferred_quality:
search_criteria["id"] = preferred_quality
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
containers = container_registry.findInstanceContainers(**search_criteria)
if containers:
return containers[0]
if "material" in search_criteria:
# First check if we can solve our material not found problem by checking if we can find quality containers
# that are assigned to the parents of this material profile.
try:
inherited_files = material_container.getInheritedFiles()
except AttributeError: # Material_container does not support inheritance.
inherited_files = []
if inherited_files:
for inherited_file in inherited_files:
# Extract the ID from the path we used to load the file.
search_criteria["material"] = os.path.basename(inherited_file).split(".")[0]
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" }
if definition.getMetaDataEntry("has_machine_quality"):
if material_container:
material_search_criteria["definition"] = material_container.getDefinition().id
if definition.getMetaDataEntry("has_variants"):
material_search_criteria["variant"] = material_container.getMetaDataEntry("variant")
else:
material_search_criteria["definition"] = definition.id
if definition.getMetaDataEntry("has_variants") and variant_container:
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()
containers = container_registry.findInstanceContainers(**search_criteria)
if containers:
return containers[0]
if "name" in search_criteria or "id" in search_criteria:
# If a quality by this name can not be found, try a wider set of search criteria
search_criteria.pop("name", None)
search_criteria.pop("id", None)
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
containers = container_registry.findInstanceContainers(**search_criteria)
if containers:
return containers[0]
return self._empty_quality_container
## Finds a quality-changes container to use if any other container
# changes.
#
# \param quality_type The quality type to find a quality-changes for.
# \param preferred_quality_changes_name The name of the quality-changes to
# pick, if any such quality-changes profile is available.
def _updateQualityChangesContainer(self, quality_type, preferred_quality_changes_name = None):
container_registry = UM.Settings.ContainerRegistry.getInstance() # Cache.
search_criteria = { "type": "quality_changes" }
search_criteria["quality"] = quality_type
if preferred_quality_changes_name:
search_criteria["name"] = preferred_quality_changes_name
# Try to search with the name in the criteria first, since we prefer to have the correct name.
containers = container_registry.findInstanceContainers(**search_criteria)
if containers: # Found one!
return containers[0]
if "name" in search_criteria:
del search_criteria["name"] # Not found, then drop the name requirement (if we had one) and search again.
containers = container_registry.findInstanceContainers(**search_criteria)
if containers:
return containers[0]
return self._empty_quality_changes_container # Didn't find anything with the required quality_type.
def _onMachineNameChanged(self):
self.globalContainerChanged.emit()

View file

@ -0,0 +1,194 @@
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
import collections
from PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt
import UM.Application
import UM.Logger
import UM.Qt
import UM.Settings
class QualitySettingsModel(UM.Qt.ListModel.ListModel):
KeyRole = Qt.UserRole + 1
LabelRole = Qt.UserRole + 2
UnitRole = Qt.UserRole + 3
ProfileValueRole = Qt.UserRole + 4
UserValueRole = Qt.UserRole + 5
CategoryRole = Qt.UserRole + 6
def __init__(self, parent = None):
super().__init__(parent = parent)
self._container_registry = UM.Settings.ContainerRegistry.getInstance()
self._extruder_id = None
self._quality = None
self._material = None
self.addRoleName(self.KeyRole, "key")
self.addRoleName(self.LabelRole, "label")
self.addRoleName(self.UnitRole, "unit")
self.addRoleName(self.ProfileValueRole, "profile_value")
self.addRoleName(self.UserValueRole, "user_value")
self.addRoleName(self.CategoryRole, "category")
def setExtruderId(self, extruder_id):
if extruder_id != self._extruder_id:
self._extruder_id = extruder_id
self._update()
self.extruderIdChanged.emit()
extruderIdChanged = pyqtSignal()
@pyqtProperty(str, fset = setExtruderId, notify = extruderIdChanged)
def extruderId(self):
return self._extruder_id
def setQuality(self, quality):
if quality != self._quality:
self._quality = quality
self._update()
self.qualityChanged.emit()
qualityChanged = pyqtSignal()
@pyqtProperty(str, fset = setQuality, notify = qualityChanged)
def quality(self):
return self._quality
def setMaterial(self, material):
if material != self._material:
self._material = material
self._update()
self.materialChanged.emit()
materialChanged = pyqtSignal()
@pyqtProperty(str, fset = setMaterial, notify = materialChanged)
def material(self):
return self._material
def _update(self):
if not self._quality:
return
items = []
settings = collections.OrderedDict()
definition_container = UM.Application.getInstance().getGlobalContainerStack().getBottom()
containers = self._container_registry.findInstanceContainers(id = self._quality)
if not containers:
UM.Logger.log("w", "Could not find a quality container with id %s", self._quality)
return
quality_container = None
quality_changes_container = None
if containers[0].getMetaDataEntry("type") == "quality":
quality_container = containers[0]
else:
quality_changes_container = containers[0]
criteria = {
"type": "quality",
"quality_type": quality_changes_container.getMetaDataEntry("quality"),
"definition": quality_changes_container.getDefinition().getId()
}
if self._material:
criteria["material"] = self._material
quality_container = self._container_registry.findInstanceContainers(**criteria)
if not quality_container:
UM.Logger.log("w", "Could not find a quality container matching quality changes %s", quality_changes_container.getId())
return
quality_container = quality_container[0]
quality_type = quality_container.getMetaDataEntry("quality_type")
definition_id = quality_container.getDefinition().getId()
criteria = {"type": "quality", "quality_type": quality_type, "definition": definition_id}
if self._material:
criteria["material"] = self._material
criteria["extruder"] = self._extruder_id
containers = self._container_registry.findInstanceContainers(**criteria)
if not containers:
# Try again, this time without extruder
new_criteria = criteria.copy()
new_criteria.pop("extruder")
containers = self._container_registry.findInstanceContainers(**new_criteria)
if not containers:
# Try again, this time without material
criteria.pop("material")
containers = self._container_registry.findInstanceContainers(**criteria)
if not containers:
# Try again, this time without material or extruder
criteria.pop("extruder") # "material" has already been popped
containers = self._container_registry.findInstanceContainers(**criteria)
if not containers:
UM.Logger.log("Could not find any quality containers matching the search criteria %s" % str(criteria))
return
if quality_changes_container:
criteria = {"type": "quality_changes", "quality": quality_type, "definition": definition_id, "name": quality_changes_container.getName()}
if self._extruder_id != "":
criteria["extruder"] = self._extruder_id
else:
criteria["extruder"] = None
changes = self._container_registry.findInstanceContainers(**criteria)
if changes:
containers.extend(changes)
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
is_multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
current_category = ""
for definition in definition_container.findDefinitions():
if definition.type == "category":
current_category = definition.label
continue
profile_value = None
for container in containers:
new_value = container.getProperty(definition.key, "value")
if new_value:
profile_value = new_value
user_value = None
if not self._extruder_id:
user_value = global_container_stack.getTop().getProperty(definition.key, "value")
else:
extruder_stack = self._container_registry.findContainerStacks(id = self._extruder_id)
if extruder_stack:
user_value = extruder_stack[0].getTop().getProperty(definition.key, "value")
if not profile_value and not user_value:
continue
if is_multi_extrusion:
settable_per_extruder = global_container_stack.getProperty(definition.key, "settable_per_extruder")
# If a setting is not settable per extruder (global) and we're looking at an extruder tab, don't show this value.
if self._extruder_id != "" and not settable_per_extruder:
continue
# If a setting is settable per extruder (not global) and we're looking at global tab, don't show this value.
if self._extruder_id == "" and settable_per_extruder:
continue
items.append({
"key": definition.key,
"label": definition.label,
"unit": definition.unit,
"profile_value": "" if profile_value is None else str(profile_value), # it is for display only
"user_value": "" if user_value is None else str(user_value),
"category": current_category
})
self.setItems(items)

View file

@ -30,7 +30,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
self._stack.addContainer(self._instance)
if cura.Settings.ExtruderManager.getInstance().extruderCount > 1:
self._extruder_stack = cura.Settings.ExtruderManager.getInstance().activeExtruderStackId
self._extruder_stack = cura.Settings.ExtruderManager.getInstance().getExtruderStack(0).getId()
else:
self._extruder_stack = None

View file

@ -10,3 +10,4 @@ from .ExtrudersModel import ExtrudersModel
from .MachineManager import MachineManager
from .MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
from .SettingOverrideDecorator import SettingOverrideDecorator
from .QualitySettingsModel import QualitySettingsModel

View file

@ -1,3 +1,101 @@
[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.
*Multi Extrusion Support
Machines with multiple extruders are now supported. If youve got the Ultimaker Original with the dual extrusion upgrade kit, weve got you covered.
*Custom Machine Support
The new custom machine plug-in allows you to change machine settings with ease. That means its now much easier to use Cura with custom machines.
*Improved Position Tool
Place objects precisely where you want them by manually entering the values for the position.
*Improved Grouping
It's now possible to transform objects that are already grouped.
Select an individual item in a group or merged object and edit as usual. Just Ctrl + Click and edit away.
*Enhanced Profile Management
Profile management is improved. You can now easily see and track changes made to your profiles.
*Improved Setting Visibility
Make multiple settings visible at once. The Visibility Overview setting indicates why a setting is not shown in the sidebar even if it is selected.
*Improved time estimation
Time estimations are more accurate. Based on our test time estimations should be within 5% accuracy.
*Optional G-code Machine Prefix
Disable the g-code prefix in Preferences. No more UM2_ on your printer display!
*Print Weight Estimates
Cura now estimates print weight as well as length.
*Automatic Import Configuration
Configurations from older installations of Cura 2.1 are automatically imported into the newest installation.
*Slicing features
*Infill Improvements
We've introduced two new infill types: Tetrahedral and Cubic. They change along with the Z-axis for more uniform strength in all directions. Also, now you can change the density of the infill based on the distance from the top layers. You print get faster, use less material, and maintain the same object strength.
*Set Acceleration and Jerk by Feature
You can now set Jerk and Acceleration by feature, (infill, walls, top/bottom, etc) for more precision.
*Outer Wall Offset
Apply an offset to where the outer wall is printed for better surface quality when the outer wall line width is smaller than the nozzle size.
*Enhanced Combing
The “No Skin” option allows you to comb over infill only to avoid scars on top surfaces.
*Z Hop
Cant avoid previously printed parts by horizontal moves? The Z Hop Only Over Printed Parts gives you the ability to Z Hop to avoid collisions for better surface quality.
*Skin and Wall Overlap
The Skin Overlap setting allows you to overlap the skin lines with the walls for better adhesion.
*Control Initial Layer Travel Speed
Set the travel speed of the initial layer(s) to reduce risk of extruder pulling the print from the bed.
*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.
Bridging works as intended again.
Rafts are no longer printed outside of build area.
Messages are now displayed 30 seconds instead of 10, making it less likely that certain messages are missed.
Drag and drop on the first run on windows works again.
You are now notified if you try to save to a locked SD card.
Combing is applied in more cases and results in better paths.
Infill thickness now supports Grid infill also for even multiples of the layer height.
Unretraction speeds are correct again.
Spiralize mode doesnt spiralize the bottom layer any more.
Spiralize now spiralizes each part in a layer.
Support is no longer removed by unprintable thin parts of the model.
Support is now generated on each layer its supposed to.
Support doesn't go outside overhang areas any more.
Line distance is now the actual line distance.
Enabling raft doesnt influence at which height the model is sliced any more.
Brim is now always printed just once.
Overlap Compensation works as intended again.
A raft now produces a reasonable amount of retractions.
No more string plume on top of one-at-a-time printed objects.
Engine log can be viewed even when slicing is finished.
Support roofs now only occur just below overhang.
Brim is now also generated under the support.
Compensate overlapping wall parts now also works for inner walls.
Bed Level and Checkup procedures for UMO+ can now be done without re-adding machine.
Undo and Redo now work correctly with multiple operations.
The last used folder is now remembered (instead of defaulting to home folder)
You can now adjust the speed at which the bed is lowered each layer.
Support roofs now only generate directly below the mesh.
Settings shared between skirt and brim now also activate when brim is selected.
Made it possible to add multiple Per Model Settings at once.
[2.1.3]
*Material Profiles

View file

@ -127,7 +127,6 @@ class CuraEngineBackend(Backend):
def close(self):
# Terminate CuraEngine if it is still running at this point
self._terminate()
super().close()
## Get the command that is used to call the engine.
# This is useful for debugging and used to actually start the engine.
@ -197,7 +196,6 @@ class CuraEngineBackend(Backend):
Logger.log("d", "Attempting to kill the engine process")
if Application.getInstance().getCommandLineOption("external-backend", False):
self._createSocket()
return
if self._process is not None:
@ -206,8 +204,12 @@ class CuraEngineBackend(Backend):
self._process.terminate()
Logger.log("d", "Engine process is killed. Received return code %s", self._process.wait())
self._process = None
except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this.
Logger.log("d", "Exception occurred while trying to kill the engine %s", str(e))
else:
# Process is none, but something did went wrong here. Try and re-create the socket
self._createSocket()
## Event handler to call when the job to initiate the slicing process is
# completed.
@ -236,15 +238,18 @@ class CuraEngineBackend(Backend):
if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice:
if Application.getInstance().getPlatformActivity:
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable objects found."))
self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable models found."))
self._error_message.show()
self.backendStateChange.emit(BackendState.Error)
else:
self.backendStateChange.emit(BackendState.NotStarted)
return
# Preparation completed, send it to the backend.
self._socket.sendMessage(job.getSliceMessage())
# Notify the user that it's now up to the backend to do it's job
self.backendStateChange.emit(BackendState.Processing)
Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time )
## Listener for when the scene has changed.
@ -374,8 +379,9 @@ class CuraEngineBackend(Backend):
#
# \param tool The tool that the user is using.
def _onToolOperationStarted(self, tool):
self._terminate() # Do not continue slicing once a tool has started
self._enabled = False # Do not reslice when a tool is doing it's 'thing'
self._terminate() # Do not continue slicing once a tool has started
## Called when the user stops using some tool.
#
@ -384,7 +390,7 @@ class CuraEngineBackend(Backend):
# \param tool The tool that the user was using.
def _onToolOperationStopped(self, tool):
self._enabled = True # Tool stop, start listening for changes again.
## Called when the user changes the active view mode.
def _onActiveViewChanged(self):
if Application.getInstance().getController().getActiveView():
@ -404,9 +410,10 @@ class CuraEngineBackend(Backend):
#
# We should reset our state and start listening for new connections.
def _onBackendQuit(self):
if not self._restart and self._process:
Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait())
self._process = None
if not self._restart:
if self._process:
Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait())
self._process = None
self._createSocket()
## Called when the global container stack changes

View file

@ -203,7 +203,13 @@ class StartSliceJob(Job):
keys = stack.getAllKeys()
settings = {}
for key in keys:
settings[key] = stack.getProperty(key, "value")
# 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
else:
# Normal case
settings[key] = stack.getProperty(key, "value")
start_gcode = settings["machine_start_gcode"]
settings["material_bed_temp_prepend"] = "{material_bed_temperature}" not in start_gcode #Pre-compute material material_bed_temp_prepend and material_print_temp_prepend

View file

@ -4,9 +4,11 @@
import os.path
from UM.Logger import Logger
from UM.Settings.InstanceContainer import InstanceContainer #The new profile to make.
from UM.Settings.InstanceContainer import InstanceContainer # The new profile to make.
from cura.ProfileReader import ProfileReader
import zipfile
## A plugin that reads profile data from Cura profile files.
#
# It reads a profile from a .curaprofile file, and returns it as a profile
@ -24,19 +26,19 @@ class CuraProfileReader(ProfileReader):
# not be read or didn't contain a valid profile, \code None \endcode is
# returned.
def read(self, file_name):
# Create an empty profile.
profile = InstanceContainer(os.path.basename(os.path.splitext(file_name)[0]))
profile.addMetaDataEntry("type", "quality")
try:
with open(file_name) as f: # Open file for reading.
archive = zipfile.ZipFile(file_name, "r")
results = []
for profile_id in archive.namelist():
# Create an empty profile.
profile = InstanceContainer(profile_id)
profile.addMetaDataEntry("type", "quality_changes")
serialized = ""
with archive.open(profile_id) as f:
serialized = f.read()
except IOError as e:
Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
return None
try:
profile.deserialize(serialized)
except Exception as e: # Parsing error. This is not a (valid) Cura profile then.
Logger.log("e", "Error while trying to parse profile: %s", str(e))
return None
return profile
try:
profile.deserialize(serialized.decode("utf-8") )
except Exception as e: # Parsing error. This is not a (valid) Cura profile then.
Logger.log("e", "Error while trying to parse profile: %s", str(e))
continue
results.append(profile)
return results

View file

@ -5,22 +5,31 @@
from UM.Logger import Logger
from UM.SaveFile import SaveFile
from cura.ProfileWriter import ProfileWriter
import zipfile
## Writes profiles to Cura's own profile format with config files.
class CuraProfileWriter(ProfileWriter):
## Writes a profile to the specified file path.
#
# \param path \type{string} The file to output to.
# \param profile \type{Profile} The profile to write to that file.
# \param profiles \type{Profile} \type{List} The profile(s) to write to that file.
# \return \code True \endcode if the writing was successful, or \code
# False \endcode if it wasn't.
def write(self, path, profile):
serialized = profile.serialize()
def write(self, path, profiles):
if type(profiles) != list:
profiles = [profiles]
stream = open(path, "wb") # Open file for writing in binary.
archive = zipfile.ZipFile(stream, "w", compression=zipfile.ZIP_DEFLATED)
try:
with SaveFile(path, "wt", -1, "utf-8") as f: # Open the specified file.
f.write(serialized)
# Open the specified file.
for profile in profiles:
serialized = profile.serialize()
profile_file = zipfile.ZipInfo(profile.getId())
archive.writestr(profile_file, serialized)
except Exception as e:
Logger.log("e", "Failed to write profile to %s: %s", path, str(e))
return False
finally:
archive.close()
return True

View file

@ -12,7 +12,7 @@ from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Job import Job
from UM.Preferences import Preferences
from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode
from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL
@ -33,7 +33,7 @@ class LayerView(View):
def __init__(self):
super().__init__()
self._shader = None
self._selection_shader = None
self._ghost_shader = None
self._num_layers = 0
self._layer_percentage = 0 # what percentage of layers need to be shown (Slider gives value between 0 - 100)
self._proxy = LayerViewProxy.LayerViewProxy()
@ -84,9 +84,9 @@ class LayerView(View):
scene = self.getController().getScene()
renderer = self.getRenderer()
if not self._selection_shader:
self._selection_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
self._selection_shader.setUniformValue("u_color", Color(32, 32, 32, 128))
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, 64))
for node in DepthFirstIterator(scene.getRoot()):
# We do not want to render ConvexHullNode as it conflicts with the bottom layers.
@ -96,8 +96,13 @@ class LayerView(View):
if not node.render(renderer):
if node.getMeshData() and node.isVisible():
if Selection.isSelected(node):
renderer.queueNode(node, transparent = True, shader = self._selection_shader)
renderer.queueNode(node,
shader = self._ghost_shader,
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:
continue

View file

@ -1,6 +1,6 @@
{
"source_version": "15.04",
"target_version": 1,
"target_version": 2,
"translation": {
"machine_nozzle_size": "nozzle_size",
@ -21,7 +21,7 @@
"retraction_amount": "retraction_amount",
"retraction_speed": "retraction_speed",
"retraction_min_travel": "retraction_min_travel",
"retraction_hop": "retraction_hop",
"retraction_hop_enabled": "retraction_hop != 0",
"speed_print": "print_speed",
"speed_infill": "infill_speed if (float(infill_speed) != 0) else print_speed",
"speed_wall_0": "inset0_speed if (float(inset0_speed) != 0) else print_speed",
@ -29,7 +29,7 @@
"speed_topbottom": "solidarea_speed if (float(solidarea_speed) != 0) else print_speed",
"speed_travel": "travel_speed if (float(travel_speed) != 0) else travel_speed",
"speed_layer_0": "bottom_layer_speed",
"retraction_combing": "True if (retraction_combing == \"All\" or retraction_combing == \"No Skin\") else False",
"retraction_combing": "\"all\" if retraction_combing == \"All\" else \"noskin\" if retraction_combing == \"No Skin\" else \"off\"",
"cool_fan_enabled": "fan_enabled",
"cool_fan_speed_min": "fan_speed",
"cool_fan_speed_max": "fan_speed_max",
@ -66,7 +66,7 @@
"meshfix_union_all_remove_holes": "fix_horrible_union_all_type_b",
"meshfix_extensive_stitching": "fix_horrible_extensive_stitching",
"meshfix_keep_open_polygons": "fix_horrible_use_open_bits",
"magic_mesh_surface_mode": "simple_mode",
"magic_mesh_surface_mode": "\"surface\" if simple_mode else \"normal\"",
"magic_spiralize": "spiralize",
"prime_tower_enable": "wipe_tower",
"prime_tower_size": "math.sqrt(float(wipe_tower_volume) / float(layer_height))",

View file

@ -111,7 +111,8 @@ class LegacyProfileReader(ProfileReader):
if "translation" not in dict_of_doom:
Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?")
return None
current_printer = Application.getInstance().getGlobalContainerStack().findContainer({ }, DefinitionContainer)
current_printer_definition = Application.getInstance().getGlobalContainerStack().getBottom()
profile.setDefinition(current_printer_definition)
for new_setting in dict_of_doom["translation"]: #Evaluate all new settings that would get a value from the translations.
old_setting_expression = dict_of_doom["translation"][new_setting]
compiled = compile(old_setting_expression, new_setting, "eval")
@ -121,10 +122,13 @@ class LegacyProfileReader(ProfileReader):
except Exception: #Probably some setting name that was missing or something else that went wrong in the ini file.
Logger.log("w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile.")
continue
if new_value != value_using_defaults and current_printer.findDefinitions(key = new_setting).default_value != new_value: #Not equal to the default in the new Cura OR the default in the legacy Cura.
profile.setSettingValue(new_setting, new_value) #Store the setting in the profile!
definitions = current_printer_definition.findDefinitions(key = new_setting)
if definitions:
if new_value != value_using_defaults and definitions[0].default_value != new_value: # Not equal to the default in the new Cura OR the default in the legacy Cura.
profile.setProperty(new_setting, "value", new_value) # Store the setting in the profile!
if len(profile.getChangedSettings()) == 0:
if len(profile.getAllKeys()) == 0:
Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.")
profile.setDirty(True)
profile.addMetaDataEntry("type", "quality")
return profile

View file

@ -0,0 +1,93 @@
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from PyQt5.QtCore import pyqtSlot
from cura.MachineAction import MachineAction
import cura.Settings.CuraContainerRegistry
import UM.Application
import UM.Settings.InstanceContainer
import UM.Settings.DefinitionContainer
import UM.Logger
import UM.i18n
catalog = UM.i18n.i18nCatalog("cura")
class MachineSettingsAction(MachineAction):
def __init__(self, parent = None):
super().__init__("MachineSettingsAction", catalog.i18nc("@action", "Machine Settings"))
self._qml_url = "MachineSettingsAction.qml"
cura.Settings.CuraContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded)
def _reset(self):
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
if global_container_stack:
variant = global_container_stack.findContainer({"type": "variant"})
if variant and variant.getId() == "empty_variant":
variant_index = global_container_stack.getContainerIndex(variant)
self._createVariant(global_container_stack, variant_index)
def _createVariant(self, global_container_stack, variant_index):
# Create and switch to a variant to store the settings in
new_variant = UM.Settings.InstanceContainer(global_container_stack.getName() + "_variant")
new_variant.addMetaDataEntry("type", "variant")
new_variant.setDefinition(global_container_stack.getBottom())
UM.Settings.ContainerRegistry.getInstance().addContainer(new_variant)
global_container_stack.replaceContainer(variant_index, new_variant)
def _onContainerAdded(self, container):
# Add this action as a supported action to all machine definitions
if isinstance(container, UM.Settings.DefinitionContainer) and container.getMetaDataEntry("type") == "machine":
if container.getProperty("machine_extruder_count", "value") > 1:
# Multiextruder printers are not currently supported
UM.Logger.log("d", "Not attaching MachineSettingsAction to %s; Multi-extrusion printers are not supported", container.getId())
return
if container.getMetaDataEntry("has_variants", False):
# Machines that use variants are not currently supported
UM.Logger.log("d", "Not attaching MachineSettingsAction to %s; Machines that use variants are not supported", container.getId())
return
UM.Application.getInstance().getMachineActionManager().addSupportedAction(container.getId(), self.getKey())
@pyqtSlot()
def forceUpdate(self):
# Force rebuilding the build volume by reloading the global container stack.
# This is a bit of a hack, but it seems quick enough.
UM.Application.getInstance().globalContainerStackChanged.emit()
@pyqtSlot()
def updateHasMaterialsMetadata(self):
# Updates the has_materials metadata flag after switching gcode flavor
global_container_stack = UM.Application.getInstance().getGlobalContainerStack()
if global_container_stack:
definition = global_container_stack.getBottom()
if definition.getProperty("machine_gcode_flavor", "value") == "UltiGCode" and not definition.getMetaDataEntry("has_materials", False):
has_materials = global_container_stack.getProperty("machine_gcode_flavor", "value") != "UltiGCode"
material_container = global_container_stack.findContainer({"type": "material"})
material_index = global_container_stack.getContainerIndex(material_container)
if has_materials:
if "has_materials" in global_container_stack.getMetaData():
global_container_stack.setMetaDataEntry("has_materials", True)
else:
global_container_stack.addMetaDataEntry("has_materials", True)
# Set the material container to a sane default
if material_container.getId() == "empty_material":
search_criteria = { "type": "material", "definition": "fdmprinter", "id": "*pla*" }
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
if containers:
global_container_stack.replaceContainer(material_index, containers[0])
else:
# The metadata entry is stored in an ini, and ini files are parsed as strings only.
# Because any non-empty string evaluates to a boolean True, we have to remove the entry to make it False.
if "has_materials" in global_container_stack.getMetaData():
global_container_stack.removeMetaDataEntry("has_materials")
empty_material = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_material")[0]
global_container_stack.replaceContainer(material_index, empty_material)
UM.Application.getInstance().globalContainerStackChanged.emit()

View file

@ -0,0 +1,476 @@
// Copyright (c) 2016 Ultimaker B.V.
// Cura is released under the terms of the AGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
import UM 1.2 as UM
import Cura 1.0 as Cura
Cura.MachineAction
{
anchors.fill: parent;
Item
{
id: bedLevelMachineAction
anchors.fill: parent;
UM.I18nCatalog { id: catalog; name: "cura"; }
Label
{
id: pageTitle
width: parent.width
text: catalog.i18nc("@title", "Machine Settings")
wrapMode: Text.WordWrap
font.pointSize: 18;
}
Label
{
id: pageDescription
anchors.top: pageTitle.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "Please enter the correct settings for your printer below:")
}
Column
{
height: parent.height - y
width: parent.width - UM.Theme.getSize("default_margin").width
spacing: UM.Theme.getSize("default_margin").height
anchors.left: parent.left
anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
Row
{
width: parent.width
spacing: UM.Theme.getSize("default_margin").height
Column
{
width: parent.width / 2
spacing: UM.Theme.getSize("default_margin").height
Label
{
text: catalog.i18nc("@label", "Printer Settings")
font.bold: true
}
Grid
{
columns: 3
columnSpacing: UM.Theme.getSize("default_margin").width
Label
{
text: catalog.i18nc("@label", "X (Width)")
}
TextField
{
id: buildAreaWidthField
text: machineWidthProvider.properties.value
validator: RegExpValidator { regExp: /[0-9]{0,6}/ }
onEditingFinished: { machineWidthProvider.setPropertyValue("value", text); manager.forceUpdate() }
}
Label
{
text: catalog.i18nc("@label", "mm")
}
Label
{
text: catalog.i18nc("@label", "Y (Depth)")
}
TextField
{
id: buildAreaDepthField
text: machineDepthProvider.properties.value
validator: RegExpValidator { regExp: /[0-9]{0,6}/ }
onEditingFinished: { machineDepthProvider.setPropertyValue("value", text); manager.forceUpdate() }
}
Label
{
text: catalog.i18nc("@label", "mm")
}
Label
{
text: catalog.i18nc("@label", "Z (Height)")
}
TextField
{
id: buildAreaHeightField
text: machineHeightProvider.properties.value
validator: RegExpValidator { regExp: /[0-9]{0,6}/ }
onEditingFinished: { machineHeightProvider.setPropertyValue("value", text); manager.forceUpdate() }
}
Label
{
text: catalog.i18nc("@label", "mm")
}
}
Column
{
CheckBox
{
id: heatedBedCheckBox
text: catalog.i18nc("@option:check", "Heated Bed")
checked: String(machineHeatedBedProvider.properties.value).toLowerCase() != 'false'
onClicked: machineHeatedBedProvider.setPropertyValue("value", checked)
}
CheckBox
{
id: centerIsZeroCheckBox
text: catalog.i18nc("@option:check", "Machine Center is Zero")
checked: String(machineCenterIsZeroProvider.properties.value).toLowerCase() != 'false'
onClicked: machineCenterIsZeroProvider.setPropertyValue("value", checked)
}
}
Row
{
spacing: UM.Theme.getSize("default_margin").width
Label
{
text: catalog.i18nc("@label", "GCode Flavor")
}
ComboBox
{
model: ["RepRap (Marlin/Sprinter)", "UltiGCode"]
currentIndex: machineGCodeFlavorProvider.properties.value != model[1] ? 0 : 1
onActivated:
{
machineGCodeFlavorProvider.setPropertyValue("value", model[index]);
manager.updateHasMaterialsMetadata();
}
}
}
}
Column
{
width: parent.width / 2
spacing: UM.Theme.getSize("default_margin").height
Label
{
text: catalog.i18nc("@label", "Printhead Settings")
font.bold: true
}
Grid
{
columns: 3
columnSpacing: UM.Theme.getSize("default_margin").width
Label
{
text: catalog.i18nc("@label", "X min")
}
TextField
{
id: printheadXMinField
text: getHeadPolygonCoord("x", "min")
validator: RegExpValidator { regExp: /[0-9]{0,6}/ }
onEditingFinished: setHeadPolygon()
}
Label
{
text: catalog.i18nc("@label", "mm")
}
Label
{
text: catalog.i18nc("@label", "Y min")
}
TextField
{
id: printheadYMinField
text: getHeadPolygonCoord("y", "min")
validator: RegExpValidator { regExp: /[0-9]{0,6}/ }
onEditingFinished: setHeadPolygon()
}
Label
{
text: catalog.i18nc("@label", "mm")
}
Label
{
text: catalog.i18nc("@label", "X max")
}
TextField
{
id: printheadXMaxField
text: getHeadPolygonCoord("x", "max")
validator: RegExpValidator { regExp: /[0-9]{0,6}/ }
onEditingFinished: setHeadPolygon()
}
Label
{
text: catalog.i18nc("@label", "mm")
}
Label
{
text: catalog.i18nc("@label", "Y max")
}
TextField
{
id: printheadYMaxField
text: getHeadPolygonCoord("y", "max")
validator: RegExpValidator { regExp: /[0-9]{0,6}/ }
onEditingFinished: setHeadPolygon()
}
Label
{
text: catalog.i18nc("@label", "mm")
}
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 }
Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height }
Label
{
text: catalog.i18nc("@label", "Gantry height")
}
TextField
{
id: gantryHeightField
text: gantryHeightProvider.properties.value
validator: RegExpValidator { regExp: /[0-9\.]{0,6}/ }
onEditingFinished: { gantryHeightProvider.setPropertyValue("value", text) }
}
Label
{
text: catalog.i18nc("@label", "mm")
}
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 }
Item { width: UM.Theme.getSize("default_margin").width; height: UM.Theme.getSize("default_margin").height }
Label
{
text: catalog.i18nc("@label", "Nozzle size")
}
TextField
{
id: nozzleSizeField
text: machineNozzleSizeProvider.properties.value
validator: RegExpValidator { regExp: /[0-9\.]{0,6}/ }
onEditingFinished: { machineNozzleSizeProvider.setPropertyValue("value", text) }
}
Label
{
text: catalog.i18nc("@label", "mm")
}
}
}
}
Row
{
spacing: UM.Theme.getSize("default_margin").width
anchors.left: parent.left
anchors.right: parent.right
height: parent.height - y
Column
{
height: parent.height
width: parent.width / 2
Label
{
text: catalog.i18nc("@label", "Start Gcode")
}
TextArea
{
id: machineStartGcodeField
width: parent.width
height: parent.height - y
text: machineStartGcodeProvider.properties.value
onActiveFocusChanged:
{
if(!activeFocus)
{
machineStartGcodeProvider.setPropertyValue("value", machineStartGcodeField.text)
}
}
}
}
Column {
height: parent.height
width: parent.width / 2
Label
{
text: catalog.i18nc("@label", "End Gcode")
}
TextArea
{
id: machineEndGcodeField
width: parent.width
height: parent.height - y
text: machineEndGcodeProvider.properties.value
onActiveFocusChanged:
{
if(!activeFocus)
{
machineEndGcodeProvider.setPropertyValue("value", machineEndGcodeField.text)
}
}
}
}
}
}
}
function getHeadPolygonCoord(axis, minMax)
{
var polygon = JSON.parse(machineHeadPolygonProvider.properties.value);
var item = (axis == "x") ? 0 : 1
var result = polygon[0][item];
for(var i = 1; i < polygon.length; i++) {
if (minMax == "min") {
result = Math.min(result, polygon[i][item]);
} else {
result = Math.max(result, polygon[i][item]);
}
}
return Math.abs(result);
}
function setHeadPolygon()
{
var polygon = [];
polygon.push([-parseFloat(printheadXMinField.text), parseFloat(printheadYMaxField.text)]);
polygon.push([-parseFloat(printheadXMinField.text),-parseFloat(printheadYMinField.text)]);
polygon.push([ parseFloat(printheadXMaxField.text), parseFloat(printheadYMaxField.text)]);
polygon.push([ parseFloat(printheadXMaxField.text),-parseFloat(printheadYMinField.text)]);
machineHeadPolygonProvider.setPropertyValue("value", JSON.stringify(polygon));
manager.forceUpdate();
}
UM.SettingPropertyProvider
{
id: machineWidthProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "machine_width"
watchedProperties: [ "value" ]
storeIndex: 4
}
UM.SettingPropertyProvider
{
id: machineDepthProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "machine_depth"
watchedProperties: [ "value" ]
storeIndex: 4
}
UM.SettingPropertyProvider
{
id: machineHeightProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "machine_height"
watchedProperties: [ "value" ]
storeIndex: 4
}
UM.SettingPropertyProvider
{
id: machineHeatedBedProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "machine_heated_bed"
watchedProperties: [ "value" ]
storeIndex: 4
}
UM.SettingPropertyProvider
{
id: machineCenterIsZeroProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "machine_center_is_zero"
watchedProperties: [ "value" ]
storeIndex: 4
}
UM.SettingPropertyProvider
{
id: machineGCodeFlavorProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "machine_gcode_flavor"
watchedProperties: [ "value" ]
storeIndex: 4
}
UM.SettingPropertyProvider
{
id: machineNozzleSizeProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "machine_nozzle_size"
watchedProperties: [ "value" ]
storeIndex: 4
}
UM.SettingPropertyProvider
{
id: gantryHeightProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "gantry_height"
watchedProperties: [ "value" ]
storeIndex: 4
}
UM.SettingPropertyProvider
{
id: machineHeadPolygonProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "machine_head_with_fans_polygon"
watchedProperties: [ "value" ]
storeIndex: 4
}
UM.SettingPropertyProvider
{
id: machineStartGcodeProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "machine_start_gcode"
watchedProperties: [ "value" ]
storeIndex: 4
}
UM.SettingPropertyProvider
{
id: machineEndGcodeProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "machine_end_gcode"
watchedProperties: [ "value" ]
storeIndex: 4
}
}

View file

@ -0,0 +1,21 @@
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from . import MachineSettingsAction
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
def getMetaData():
return {
"plugin": {
"name": catalog.i18nc("@label", "Machine Settings action"),
"author": "fieldOfView",
"version": "1.0",
"description": catalog.i18nc("@info:whatsthis", "Provides a way to change machine settings (such as build volume, nozzle size, etc)"),
"api": 3
}
}
def register(app):
return { "machine_action": MachineSettingsAction.MachineSettingsAction() }

View file

@ -31,7 +31,7 @@ Item {
spacing: UM.Theme.getSize("default_margin").width
Label
{
text: catalog.i18nc("@label", "Print object with")
text: catalog.i18nc("@label", "Print model with")
anchors.verticalCenter: extruderSelector.verticalCenter
color: UM.Theme.getColor("setting_control_text")
@ -44,13 +44,11 @@ Item {
model: Cura.ExtrudersModel
{
id: extruders_model
onRowsInserted: extruderSelector.visible = extruders_model.rowCount() > 1
onModelReset: extruderSelector.visible = extruders_model.rowCount() > 1
onModelChanged: extruderSelector.color = extruders_model.getItem(extruderSelector.currentIndex).color
id: extrudersModel
onModelChanged: extruderSelector.color = extrudersModel.getItem(extruderSelector.currentIndex).color
}
property string color: extruders_model.getItem(extruderSelector.currentIndex).color
visible: extruders_model.rowCount() > 1
property string color: extrudersModel.getItem(extruderSelector.currentIndex).color
visible: machineExtruderCount.properties.value > 1
textRole: "name"
width: UM.Theme.getSize("setting_control").width
height: UM.Theme.getSize("section").height
@ -130,19 +128,19 @@ Item {
onActivated:
{
UM.ActiveTool.setProperty("SelectedActiveExtruder", extruders_model.getItem(index).id);
extruderSelector.color = extruders_model.getItem(index).color;
UM.ActiveTool.setProperty("SelectedActiveExtruder", extrudersModel.getItem(index).id);
extruderSelector.color = extrudersModel.getItem(index).color;
}
onModelChanged: updateCurrentIndex();
function updateCurrentIndex()
{
for(var i = 0; i < extruders_model.rowCount(); ++i)
for(var i = 0; i < extrudersModel.rowCount(); ++i)
{
if(extruders_model.getItem(i).id == UM.ActiveTool.properties.getValue("SelectedActiveExtruder"))
if(extrudersModel.getItem(i).id == UM.ActiveTool.properties.getValue("SelectedActiveExtruder"))
{
extruderSelector.currentIndex = i;
extruderSelector.color = extruders_model.getItem(i).color;
extruderSelector.color = extrudersModel.getItem(i).color;
return;
}
}
@ -154,107 +152,114 @@ Item {
Column
{
spacing: UM.Theme.getSize("default_lining").height
Repeater
// This is to ensure that the panel is first increasing in size up to 200 and then shows a scrollbar.
// It kinda looks ugly otherwise (big panel, no content on it)
height: contents.count * UM.Theme.getSize("section").height < 200 ? contents.count * UM.Theme.getSize("section").height : 200
ScrollView
{
id: contents
height: childrenRect.height;
model: UM.SettingDefinitionsModel
height: parent.height
width: UM.Theme.getSize("setting").width + UM.Theme.getSize("setting").height
style: UM.Theme.styles.scrollview
ListView
{
id: addedSettingsModel;
containerId: Cura.MachineManager.activeDefinitionId
expanded: [ "*" ]
id: contents
visibilityHandler: Cura.PerObjectSettingVisibilityHandler
model: UM.SettingDefinitionsModel
{
selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId")
}
}
id: addedSettingsModel;
containerId: Cura.MachineManager.activeDefinitionId
expanded: [ "*" ]
delegate: Row
{
Loader
{
id: settingLoader
width: UM.Theme.getSize("setting").width
height: UM.Theme.getSize("section").height
property var definition: model
property var settingDefinitionsModel: addedSettingsModel
property var propertyProvider: provider
//Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
//In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
//causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
asynchronous: model.type != "enum" && model.type != "extruder"
onLoaded: {
settingLoader.item.showRevertButton = false
settingLoader.item.showInheritButton = false
settingLoader.item.showLinkedSettingIcon = false
settingLoader.item.doDepthIndentation = false
settingLoader.item.doQualityUserSettingEmphasis = false
}
sourceComponent:
visibilityHandler: Cura.PerObjectSettingVisibilityHandler
{
switch(model.type)
{
case "int":
return settingTextField
case "float":
return settingTextField
case "enum":
return settingComboBox
case "extruder":
return settingExtruder
case "bool":
return settingCheckBox
case "str":
return settingTextField
case "category":
return settingCategory
default:
return settingUnknown
selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId")
}
}
delegate: Row
{
Loader
{
id: settingLoader
width: UM.Theme.getSize("setting").width
height: UM.Theme.getSize("section").height
property var definition: model
property var settingDefinitionsModel: addedSettingsModel
property var propertyProvider: provider
//Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989
//In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes,
//causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely.
asynchronous: model.type != "enum" && model.type != "extruder"
onLoaded: {
settingLoader.item.showRevertButton = false
settingLoader.item.showInheritButton = false
settingLoader.item.showLinkedSettingIcon = false
settingLoader.item.doDepthIndentation = false
settingLoader.item.doQualityUserSettingEmphasis = false
}
}
}
Button
{
width: UM.Theme.getSize("setting").height / 2;
height: UM.Theme.getSize("setting").height;
onClicked: addedSettingsModel.setVisible(model.key, false);
style: ButtonStyle
{
background: Item
sourceComponent:
{
UM.RecolorImage
switch(model.type)
{
anchors.verticalCenter: parent.verticalCenter
width: parent.width
height: parent.height / 2
sourceSize.width: width
sourceSize.height: width
color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
source: UM.Theme.getIcon("minus")
case "int":
return settingTextField
case "float":
return settingTextField
case "enum":
return settingComboBox
case "extruder":
return settingExtruder
case "bool":
return settingCheckBox
case "str":
return settingTextField
case "category":
return settingCategory
default:
return settingUnknown
}
}
}
}
UM.SettingPropertyProvider
{
id: provider
Button
{
width: UM.Theme.getSize("setting").height / 2;
height: UM.Theme.getSize("setting").height;
containerStackId: UM.ActiveTool.properties.getValue("ContainerID")
key: model.key
watchedProperties: [ "value", "enabled", "validationState" ]
storeIndex: 0
removeUnusedValue: false
onClicked: addedSettingsModel.setVisible(model.key, false);
style: ButtonStyle
{
background: Item
{
UM.RecolorImage
{
anchors.verticalCenter: parent.verticalCenter
width: parent.width
height: parent.height / 2
sourceSize.width: width
sourceSize.height: width
color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button")
source: UM.Theme.getIcon("minus")
}
}
}
}
UM.SettingPropertyProvider
{
id: provider
containerStackId: UM.ActiveTool.properties.getValue("ContainerID")
key: model.key
watchedProperties: [ "value", "enabled", "validationState" ]
storeIndex: 0
removeUnusedValue: false
}
}
}
}
@ -306,7 +311,7 @@ Item {
UM.Dialog {
id: settingPickDialog
title: catalog.i18nc("@title:window", "Select Settings to Customize for this object")
title: catalog.i18nc("@title:window", "Select Settings to Customize for this model")
width: screenScaleFactor * 360;
property string labelFilter: ""
@ -423,6 +428,16 @@ Item {
]
}
UM.SettingPropertyProvider
{
id: machineExtruderCount
containerStackId: Cura.MachineManager.activeMachineId
key: "machine_extruder_count"
watchedProperties: [ "value" ]
storeIndex: 0
}
SystemPalette { id: palette; }
Component

View file

@ -11,15 +11,15 @@ i18n_catalog = i18nCatalog("cura")
def getMetaData():
return {
"plugin": {
"name": i18n_catalog.i18nc("@label", "Per Object Settings Tool"),
"name": i18n_catalog.i18nc("@label", "Per Model Settings Tool"),
"author": "Ultimaker",
"version": "1.0",
"description": i18n_catalog.i18nc("@info:whatsthis", "Provides the Per Object Settings."),
"description": i18n_catalog.i18nc("@info:whatsthis", "Provides the Per Model Settings."),
"api": 3
},
"tool": {
"name": i18n_catalog.i18nc("@label", "Per Object Settings"),
"description": i18n_catalog.i18nc("@info:tooltip", "Configure Per Object Settings"),
"name": i18n_catalog.i18nc("@label", "Per Model Settings"),
"description": i18n_catalog.i18nc("@info:tooltip", "Configure Per Model Settings"),
"icon": "setting_per_object",
"tool_panel": "PerObjectSettingsPanel.qml",
"weight": 3

View file

@ -112,5 +112,5 @@ class RemovableDriveOutputDevice(OutputDevice):
def _onActionTriggered(self, message, action):
if action == "eject":
Application.getInstance().getOutputDeviceManager().getOutputDevicePlugin("RemovableDriveOutputDevice").ejectDevice(self)
if Application.getInstance().getOutputDeviceManager().getOutputDevicePlugin("RemovableDriveOutputDevice").ejectDevice(self):
message.hide()

View file

@ -51,6 +51,7 @@ class RemovableDrivePlugin(OutputDevicePlugin):
else:
message = Message(catalog.i18nc("@info:status", "Failed to eject {0}. Maybe it is still in use?").format(device.getName()))
message.show()
return result
def performEjectDevice(self, device):
raise NotImplementedError()

View file

@ -32,20 +32,44 @@ UM.Dialog
}
text: {
if (manager.firmwareUpdateCompleteStatus)
if (manager.errorCode == 0)
{
//: Firmware update status label
return catalog.i18nc("@label","Firmware update completed.")
}
else if (manager.progress == 0)
{
//: Firmware update status label
return catalog.i18nc("@label","Starting firmware update, this may take a while.")
if (manager.firmwareUpdateCompleteStatus)
{
//: Firmware update status label
return catalog.i18nc("@label","Firmware update completed.")
}
else if (manager.progress == 0)
{
//: Firmware update status label
return catalog.i18nc("@label","Starting firmware update, this may take a while.")
}
else
{
//: Firmware update status label
return catalog.i18nc("@label","Updating firmware.")
}
}
else
{
//: Firmware update status label
return catalog.i18nc("@label","Updating firmware.")
switch (manager.errorCode)
{
case 1:
//: Firmware update status label
return catalog.i18nc("@label","Firmware update failed due to an unknown error.")
case 2:
//: Firmware update status label
return catalog.i18nc("@label","Firmware update failed due to an communication error.")
case 3:
//: Firmware update status label
return catalog.i18nc("@label","Firmware update failed due to an input/output error.")
case 4:
//: Firmware update status label
return catalog.i18nc("@label","Firmware update failed due to missing firmware.")
default:
//: Firmware update status label
return catalog.i18nc("@label", "Unknown error code: %1").arg(manager.errorCode)
}
}
}

View file

@ -14,7 +14,7 @@ from UM.Logger import Logger
from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState
from UM.Message import Message
from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal
from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal, pyqtProperty
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
@ -90,6 +90,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._firmware_update_finished = False
self._error_message = None
self._error_code = 0
onError = pyqtSignal()
@ -173,6 +174,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
## Private function (threaded) that actually uploads the firmware.
def _updateFirmware(self):
self._error_code = 0
self.setProgress(0, 100)
self._firmware_update_finished = False
@ -182,6 +184,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
if len(hex_file) == 0:
Logger.log("e", "Unable to read provided hex file. Could not update firmware")
self._updateFirmwareFailedMissingFirmware()
return
programmer = stk500v2.Stk500v2()
@ -197,6 +200,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
if not programmer.isConnected():
Logger.log("e", "Unable to connect with serial. Could not update firmware")
self._updateFirmwareFailedCommunicationError()
return
self._updating_firmware = True
@ -204,17 +208,57 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
try:
programmer.programChip(hex_file)
self._updating_firmware = False
except serial.SerialException as e:
Logger.log("e", "SerialException while trying to update firmware: <%s>" %(repr(e)))
self._updateFirmwareFailedIOError()
return
except Exception as e:
Logger.log("e", "Exception while trying to update firmware %s" %e)
self._updating_firmware = False
Logger.log("e", "Exception while trying to update firmware: <%s>" %(repr(e)))
self._updateFirmwareFailedUnknown()
return
programmer.close()
self._updateFirmwareCompletedSucessfully()
return
## Private function which makes sure that firmware update process has failed by missing firmware
def _updateFirmwareFailedMissingFirmware(self):
return self._updateFirmwareFailedCommon(4)
## Private function which makes sure that firmware update process has failed by an IO error
def _updateFirmwareFailedIOError(self):
return self._updateFirmwareFailedCommon(3)
## Private function which makes sure that firmware update process has failed by a communication problem
def _updateFirmwareFailedCommunicationError(self):
return self._updateFirmwareFailedCommon(2)
## Private function which makes sure that firmware update process has failed by an unknown error
def _updateFirmwareFailedUnknown(self):
return self._updateFirmwareFailedCommon(1)
## Private common function which makes sure that firmware update process has completed/ended with a set progress state
def _updateFirmwareFailedCommon(self, code):
if not code:
raise Exception("Error code not set!")
self._error_code = code
self._firmware_update_finished = True
self.resetFirmwareUpdate(update_has_finished = True)
self.progressChanged.emit()
self.firmwareUpdateComplete.emit()
return
## Private function which makes sure that firmware update process has successfully completed
def _updateFirmwareCompletedSucessfully(self):
self.setProgress(100, 100)
self._firmware_update_finished = True
self.resetFirmwareUpdate(update_has_finished = True)
self.firmwareUpdateComplete.emit()
self.firmwareUpdateChange.emit()
return
## Upload new firmware to machine
# \param filename full path of firmware file to be uploaded
@ -227,8 +271,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
def firmwareUpdateFinished(self):
return self._firmware_update_finished
def resetFirmwareUpdateFinished(self):
self._firmware_update_finished = False
def resetFirmwareUpdate(self, update_has_finished = False):
self._firmware_update_finished = update_has_finished
self.firmwareUpdateChange.emit()
@pyqtSlot()

View file

@ -57,6 +57,13 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
progress += device.progress
return progress / len(self._usb_output_devices)
@pyqtProperty(int, notify = progressChanged)
def errorCode(self):
for printer_name, device in self._usb_output_devices.items(): # TODO: @UnusedVariable "printer_name"
if device._error_code:
return device._error_code
return 0
## Return True if all printers finished firmware update
@pyqtProperty(float, notify = firmwareUpdateChange)
def firmwareUpdateCompleteStatus(self):
@ -103,7 +110,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
return
for printer_connection in self._usb_output_devices:
self._usb_output_devices[printer_connection].resetFirmwareUpdateFinished()
self._usb_output_devices[printer_connection].resetFirmwareUpdate()
self.spawnFirmwareInterface("")
for printer_connection in self._usb_output_devices:
try:
@ -111,7 +118,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
except FileNotFoundError:
# Should only happen in dev environments where the resources/firmware folder is absent.
self._usb_output_devices[printer_connection].setProgress(100, 100)
Logger.log("w", "No firmware found for printer %s", printer_connection)
Logger.log("w", "No firmware found for printer %s called '%s'" %(printer_connection, self._getDefaultFirmwareName()))
Message(i18n_catalog.i18nc("@info",
"Could not find firmware required for the printer at %s.") % printer_connection).show()
self._firmware_view.close()
@ -126,7 +133,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
self._usb_output_devices[serial_port].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName()))
except FileNotFoundError:
self._firmware_view.close()
Logger.log("e", "Could not find firmware required for this machine")
Logger.log("e", "Could not find firmware required for this machine called '%s'" %(self._getDefaultFirmwareName()))
return False
return True
return False
@ -147,12 +154,12 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
Logger.log("e", "There is no global container stack. Can not update firmware.")
self._firmware_view.close()
return ""
# The bottom of the containerstack is the machine definition
machine_id = global_container_stack.getBottom().id
machine_has_heated_bed = global_container_stack.getProperty("machine_heated_bed", "value")
if platform.system() == "Linux":
baudrate = 115200
else:
@ -166,13 +173,15 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
"bq_hephestos_2" : "MarlinHephestos2.hex",
"ultimaker_original" : "MarlinUltimaker-{baudrate}.hex",
"ultimaker_original_plus" : "MarlinUltimaker-UMOP-{baudrate}.hex",
"ultimaker_original_dual" : "MarlinUltimaker-{baudrate}-dual.hex",
"ultimaker2" : "MarlinUltimaker2.hex",
"ultimaker2_go" : "MarlinUltimaker2go.hex",
"ultimaker2_plus" : "MarlinUltimaker2plus.hex",
"ultimaker2_plus" : "MarlinUltimaker2plus.hex",
"ultimaker2_extended" : "MarlinUltimaker2extended.hex",
"ultimaker2_extended_plus" : "MarlinUltimaker2extended-plus.hex",
}
machine_with_heated_bed = {"ultimaker_original" : "MarlinUltimaker-HBK-{baudrate}.hex",
"ultimaker_original_dual" : "MarlinUltimaker-HBK-{baudrate}-dual.hex",
}
##TODO: Add check for multiple extruders
hex_file = None

View file

@ -10,7 +10,7 @@ catalog = i18nCatalog("cura")
class BedLevelMachineAction(MachineAction):
def __init__(self):
super().__init__("BedLevel", catalog.i18nc("@action", "Level bed"))
super().__init__("BedLevel", catalog.i18nc("@action", "Level build plate"))
self._qml_url = "BedLevelMachineAction.qml"
self._bed_level_position = 0

View file

@ -24,7 +24,7 @@ Cura.MachineAction
{
id: pageTitle
width: parent.width
text: catalog.i18nc("@title", "Bed Leveling")
text: catalog.i18nc("@title", "Build Plate Leveling")
wrapMode: Text.WordWrap
font.pointSize: 18;
}
@ -44,7 +44,7 @@ Cura.MachineAction
anchors.topMargin: UM.Theme.getSize("default_margin").height
width: parent.width
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "For every position; insert a piece of paper under the nozzle and adjust the print bed height. The print bed height is right when the paper is slightly gripped by the tip of the nozzle.")
text: catalog.i18nc("@label", "For every position; insert a piece of paper under the nozzle and adjust the print build plate height. The print build plate height is right when the paper is slightly gripped by the tip of the nozzle.")
}
Row
@ -59,14 +59,12 @@ Cura.MachineAction
Button
{
id: startBedLevelingButton
text: catalog.i18nc("@action:button","Start Bed Leveling")
text: catalog.i18nc("@action:button","Start Build Plate Leveling")
onClicked:
{
startBedLevelingButton.visible = false;
bedlevelingButton.visible = true;
checkupMachineAction.heatupHotendStarted = false;
checkupMachineAction.heatupBedStarted = false;
manager.startCheck();
manager.startBedLeveling();
}
}

View file

@ -220,7 +220,7 @@ Cura.MachineAction
anchors.left: parent.left
anchors.top: nozzleTempLabel.bottom
wrapMode: Text.WordWrap
text: catalog.i18nc("@label","Bed temperature check:")
text: catalog.i18nc("@label","Build plate temperature check:")
visible: checkupMachineAction.usbConnected && manager.hasHeatedBed
}

View file

@ -20,31 +20,25 @@ class UMOUpgradeSelection(MachineAction):
@pyqtProperty(bool, notify = heatedBedChanged)
def hasHeatedBed(self):
global_container_stack = Application.getInstance().getGlobalContainerStack()
return global_container_stack.getProperty("machine_heated_bed", "value")
if global_container_stack:
return global_container_stack.getProperty("machine_heated_bed", "value")
@pyqtSlot()
def addHeatedBed(self):
@pyqtSlot(bool)
def setHeatedBed(self, heated_bed = True):
global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack:
variant = global_container_stack.findContainer({"type": "variant"})
if variant:
if variant.getId() == "empty_variant":
variant_index = global_container_stack.getContainerIndex(variant)
stack_name = global_container_stack.getName()
new_variant = UM.Settings.InstanceContainer(stack_name + "_variant")
new_variant.addMetaDataEntry("type", "variant")
new_variant.setDefinition(global_container_stack.getBottom())
UM.Settings.ContainerRegistry.getInstance().addContainer(new_variant)
global_container_stack.replaceContainer(variant_index, new_variant)
variant = new_variant
variant.setProperty("machine_heated_bed", "value", True)
self.heatedBedChanged.emit()
self._createVariant(global_container_stack, variant_index)
variant.setProperty("machine_heated_bed", "value", heated_bed)
self.heatedBedChanged.emit()
@pyqtSlot()
def removeHeatedBed(self):
global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack:
variant = global_container_stack.findContainer({"type": "variant"})
if variant:
variant.setProperty("machine_heated_bed", "value", False)
self.heatedBedChanged.emit()
def _createVariant(self, global_container_stack, variant_index):
# Create and switch to a variant to store the settings in
new_variant = UM.Settings.InstanceContainer(global_container_stack.getName() + "_variant")
new_variant.addMetaDataEntry("type", "variant")
new_variant.setDefinition(global_container_stack.getBottom())
UM.Settings.ContainerRegistry.getInstance().addContainer(new_variant)
global_container_stack.replaceContainer(variant_index, new_variant)

View file

@ -42,9 +42,9 @@ Cura.MachineAction
anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
text: catalog.i18nc("@label", "Heated bed (official kit or self-built)")
text: catalog.i18nc("@label", "Heated Build Plate (official kit or self-built)")
checked: manager.hasHeatedBed
onClicked: checked ? manager.addHeatedBed() : manager.removeHeatedBed()
onClicked: manager.setHeatedBed(checked)
}
UM.I18nCatalog { id: catalog; name: "cura"; }

View file

@ -69,18 +69,42 @@ class MachineInstance:
config.add_section("general")
config.set("general", "name", self._name)
config.set("general", "id", self._name)
config.set("general", "type", self._type_name)
config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function.
import VersionUpgrade21to22 # Import here to prevent circular dependencies.
has_machine_qualities = self._type_name in VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.machinesWithMachineQuality()
type_name = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._type_name)
active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_material_name)
active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateMaterial(self._active_material_name)
variant = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._variant_name, type_name)
variant_materials = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariantForMaterials(self._variant_name, type_name)
active_profile = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name)
#Convert to quality profile if we have one of the built-in profiles, otherwise convert to a quality-changes profile.
if has_machine_qualities:
material_name_in_quality = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateMaterialForProfiles(self._active_material_name)
variant_name_in_quality = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariantForProfiles(self._variant_name)
if self._active_profile_name in VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.builtInProfiles(): #This is a built-in profile name. Convert to quality.
quality_name = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name)
else:
quality_name = "normal" #We have a quality-changes profile. Base it on normal, since we have no information to indicate which one it should be based on.
if self._active_material_name == "PLA" and self._type_name == "ultimaker2plus": #UM2+ uses a different naming scheme for PLA profiles.
active_quality = material_name_in_quality + "_" + variant_name_in_quality + "_" + quality_name
else:
printer_name_in_quality = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinterForProfile(self._type_name)
active_quality = printer_name_in_quality + "_" + material_name_in_quality + "_" + variant_name_in_quality + "_" + quality_name
if self._active_profile_name in VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.builtInProfiles():
active_quality_changes = "empty_quality_changes"
else: #No built-in profile. Translate this profile to quality-changes.
active_quality_changes = material_name_in_quality + "_" + variant_name_in_quality + "_" + quality_name
else:
if self._active_profile_name in VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.builtInProfiles():
active_quality = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name)
active_quality_changes = "empty_quality_changes"
else:
active_quality = "normal"
active_quality_changes = self._active_profile_name
if has_machine_qualities: #This machine now has machine-quality profiles.
active_profile += "_" + active_material + "_" + variant
active_material += "_" + variant_materials #That means that the profile was split into multiple.
current_settings = "empty" #The profile didn't know the definition ID when it was upgraded, so it will have been invalid. Sorry, your current settings are lost now.
else:
@ -88,7 +112,8 @@ class MachineInstance:
containers = [
current_settings,
active_profile,
active_quality_changes,
active_quality,
active_material,
variant,
type_name

View file

@ -99,7 +99,7 @@ class Profile:
else:
config.set("metadata", "type", "quality")
if self._weight:
config.set("metadata", "weight", self._weight)
config.set("metadata", "weight", str(self._weight))
if self._machine_variant_name:
if self._machine_type_id:
config.set("metadata", "variant", VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._machine_variant_name, self._machine_type_id))

View file

@ -23,25 +23,49 @@ from . import Profile # To upgrade profiles.
# may have changed in later versions than 2.2.
_machines_with_machine_quality = {
"ultimaker2plus": {
"materials": { "generic_abs", "generic_cpe", "generic_pla", "generic_pva" },
"materials": { "generic_abs", "generic_cpe", "generic_pla", "generic_pva", "generic_cpe_plus", "generic_nylon", "generic_pc", "generic_tpu" },
"variants": { "0.25 mm", "0.4 mm", "0.6 mm", "0.8 mm" }
},
"ultimaker2_extended_plus": {
"materials": { "generic_abs", "generic_cpe", "generic_pla", "generic_pva" },
"materials": { "generic_abs", "generic_cpe", "generic_pla", "generic_pva", "generic_cpe_plus", "generic_nylon", "generic_pc", "generic_tpu" },
"variants": { "0.25 mm", "0.4 mm", "0.6 mm", "0.8 mm" }
}
}
## How to translate material names from the old version to the new.
_material_translations = {
"PLA": "generic_pla",
"ABS": "generic_abs",
"CPE": "generic_cpe",
"CPE+": "generic_cpe_plus",
"Nylon": "generic_nylon",
"PC": "generic_pc",
"TPU": "generic_tpu",
}
## How to translate material names for in the profile names.
_material_translations_profiles = {
"PLA": "pla",
"ABS": "abs",
"CPE": "cpe",
"CPE+": "cpep",
"Nylon": "nylon",
"PC": "pc",
"TPU": "tpu",
}
## How to translate printer names from the old version to the new.
_printer_translations = {
"ultimaker2plus": "ultimaker2_plus"
}
_printer_translations_profiles = {
"ultimaker2plus": "um2p", #Does NOT get included in PLA profiles!
"ultimaker2_extended_plus": "um2ep" #Has no profiles for CPE+, Nylon, PC and TPU!
}
## How to translate profile names from the old version to the new.
_profile_translations = {
"PLA": "generic_pla",
"ABS": "generic_abs",
"CPE": "generic_cpe",
"Low Quality": "low",
"Normal Quality": "normal",
"High Quality": "high",
@ -84,6 +108,14 @@ _variant_translations = {
}
}
## How to translate variant names for in the profile names.
_variant_translations_profiles = {
"0.25 mm": "0.25",
"0.4 mm": "0.4",
"0.6 mm": "0.6",
"0.8 mm": "0.8"
}
## Cura 2.2's material profiles use a different naming scheme for variants.
#
# Getting pretty stressed out by this sort of thing...
@ -118,6 +150,14 @@ class VersionUpgrade21to22(VersionUpgrade):
parser.read_string(serialised)
return int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised.
## Gets the set of built-in profile names in Cura 2.1.
#
# This is required to test if profiles should be converted to a quality
# profile or a quality-changes profile.
@staticmethod
def builtInProfiles():
return _profile_translations.keys()
## Gets a set of the machines which now have per-material quality profiles.
#
# \return A set of machine identifiers.
@ -142,7 +182,7 @@ class VersionUpgrade21to22(VersionUpgrade):
## Converts preferences from format version 2 to version 3.
#
# \param serialised The serialised preferences file in version 2.
# \param filename THe supposed file name of the preferences file, without
# \param filename The supposed file name of the preferences file, without
# extension.
# \return A tuple containing the new filename and the serialised
# preferences in version 3, or None if the input was not of the correct
@ -166,6 +206,28 @@ class VersionUpgrade21to22(VersionUpgrade):
return filename, None
return profile.export()
## Translates a material name for the change from Cura 2.1 to 2.2.
#
# \param material A material name in Cura 2.1.
# \return The name of the corresponding material in Cura 2.2.
@staticmethod
def translateMaterial(material):
if material in _material_translations:
return _material_translations[material]
return material
## Translates a material name for the change from Cura 2.1 to 2.2 in
# quality profile names.
#
# \param material A material name in Cura 2.1.
# \return The name of the corresponding material in the quality profiles
# in Cura 2.2.
@staticmethod
def translateMaterialForProfiles(material):
if material in _material_translations_profiles:
return _material_translations_profiles[material]
return material
## Translates a printer name that might have changed since the last
# version.
#
@ -177,6 +239,17 @@ class VersionUpgrade21to22(VersionUpgrade):
return _printer_translations[printer]
return printer #Doesn't need to be translated.
## Translates a printer name for the change from Cura 2.1 to 2.2 in quality
# profile names.
#
# \param printer A printer name in 2.1.
# \return The name of the corresponding printer in Cura 2.2.
@staticmethod
def translatePrinterForProfile(printer):
if printer in _printer_translations_profiles:
return _printer_translations_profiles[printer]
return printer
## Translates a built-in profile name that might have changed since the
# last version.
#
@ -242,4 +315,16 @@ class VersionUpgrade21to22(VersionUpgrade):
def translateVariantForMaterials(variant, machine):
if machine in _variant_translations_materials and variant in _variant_translations_materials[machine]:
return _variant_translations_materials[machine][variant]
return variant
## Translates a variant name for the change from Cura 2.1 to 2.2 in quality
# profiles.
#
# \param variant The name of the variant in Cura 2.1.
# \return The name of the corresponding variant for in quality profiles in
# Cura 2.2.
@staticmethod
def translateVariantForProfiles(variant):
if variant in _variant_translations_profiles:
return _variant_translations_profiles[variant]
return variant

View file

@ -7,7 +7,10 @@ import io
import xml.etree.ElementTree as ET
import uuid
from UM.Resources import Resources
from UM.Logger import Logger
from UM.Util import parseBool
from cura.CuraApplication import CuraApplication
import UM.Dictionary
@ -17,17 +20,16 @@ import UM.Settings
class XmlMaterialProfile(UM.Settings.InstanceContainer):
def __init__(self, container_id, *args, **kwargs):
super().__init__(container_id, *args, **kwargs)
self._inherited_files = []
## Overridden from InstanceContainer
def duplicate(self, new_id, new_name = None):
base_file = self.getMetaDataEntry("base_file", None)
new_uuid = str(uuid.uuid4())
if base_file:
if base_file != self.id:
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = base_file)
if containers:
new_basefile = containers[0].duplicate(self.getMetaDataEntry("brand") + "_" + new_id, new_name)
new_basefile.setMetaDataEntry("GUID", new_uuid)
base_file = new_basefile.id
UM.Settings.ContainerRegistry.getInstance().addContainer(new_basefile)
@ -37,19 +39,28 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = variant)
if variant_containers:
new_id += "_" + variant_containers[0].getName().replace(" ", "_")
has_base_file = True
else:
has_base_file = False
new_id = UM.Settings.ContainerRegistry.getInstance().createUniqueName("material", self._id, new_id, "")
result = super().duplicate(new_id, new_name)
result.setMetaDataEntry("GUID", new_uuid)
if result.getMetaDataEntry("base_file", None):
if has_base_file:
result.setMetaDataEntry("base_file", base_file)
else:
result.setMetaDataEntry("base_file", result.id)
return result
def getInheritedFiles(self):
return self._inherited_files
## Overridden from InstanceContainer
def setReadOnly(self, read_only):
super().setReadOnly(read_only)
for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")):
container._read_only = read_only
basefile = self.getMetaDataEntry("base_file", self._id) # if basefile is self.id, this is a basefile.
for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile):
container._read_only = read_only # prevent loop instead of calling setReadOnly
## Overridden from InstanceContainer
def setMetaDataEntry(self, key, value):
@ -58,23 +69,41 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
super().setMetaDataEntry(key, value)
if key == "material":
self.setName(value)
for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")):
basefile = self.getMetaDataEntry("base_file", self._id) #if basefile is self.id, this is a basefile.
# Update all containers that share GUID and basefile
for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile):
container.setMetaData(copy.deepcopy(self._metadata))
if key == "material":
container.setName(value)
## Overridden from InstanceContainer
def setProperty(self, key, property_name, property_value, container = None):
## Overridden from InstanceContainer, similar to setMetaDataEntry.
# without this function the setName would only set the name of the specific nozzle / material / machine combination container
# The function is a bit tricky. It will not set the name of all containers if it has the correct name itself.
def setName(self, new_name):
if self.isReadOnly():
return
super().setProperty(key, property_name, property_value)
# Not only is this faster, it also prevents a major loop that causes a stack overflow.
if self.getName() == new_name:
return
for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")):
container._dirty = True
super().setName(new_name)
basefile = self.getMetaDataEntry("base_file", self._id) # if basefile is none, 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)
for container in containers:
container.setName(new_name)
## Overridden from InstanceContainer
# def setProperty(self, key, property_name, property_value, container = None):
# if self.isReadOnly():
# return
#
# super().setProperty(key, property_name, property_value)
#
# basefile = self.getMetaDataEntry("base_file", self._id) #if basefile is self.id, this is a basefile.
# for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(base_file = basefile):
# container._dirty = True
## Overridden from InstanceContainer
def serialize(self):
@ -118,6 +147,10 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
builder.data(metadata.pop("color_name", ""))
builder.end("color")
builder.start("label")
builder.data(self._name)
builder.end("label")
builder.end("name")
## End Name Block
@ -219,15 +252,126 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
return stream.getvalue()
# Recursively resolve loading inherited files
def _resolveInheritance(self, file_name):
xml = self._loadFile(file_name)
inherits = xml.find("./um:inherits", self.__namespaces)
if inherits is not None:
inherited = self._resolveInheritance(inherits.text)
xml = self._mergeXML(inherited, xml)
return xml
def _loadFile(self, file_name):
path = Resources.getPath(CuraApplication.getInstance().ResourceTypes.MaterialInstanceContainer, file_name + ".xml.fdm_material")
with open(path, encoding="utf-8") as f:
contents = f.read()
self._inherited_files.append(path)
return ET.fromstring(contents)
# 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):
settings_element = element.find("./um:settings", self.__namespaces)
machines = settings_element.iterfind("./um:machine", self.__namespaces)
machines_to_add = []
machines_to_remove = []
for machine in machines:
identifiers = list(machine.iterfind("./um:machine_identifier", self.__namespaces))
has_multiple_identifiers = len(identifiers) > 1
if has_multiple_identifiers:
# Multiple identifiers found. We need to create a new machine element and copy all it's settings there.
for identifier in identifiers:
new_machine = copy.deepcopy(machine)
# Create list of identifiers that need to be removed from the copied element.
other_identifiers = [self._createKey(other_identifier) for other_identifier in identifiers if other_identifier is not identifier]
# As we can only remove by exact object reference, we need to look through the identifiers of copied machine.
new_machine_identifiers = list(new_machine.iterfind("./um:machine_identifier", self.__namespaces))
for new_machine_identifier in new_machine_identifiers:
key = self._createKey(new_machine_identifier)
# Key was in identifiers to remove, so this element needs to be purged
if key in other_identifiers:
new_machine.remove(new_machine_identifier)
machines_to_add.append(new_machine)
machines_to_remove.append(machine)
else:
pass # Machine only has one identifier. Nothing to do.
# Remove & add all required machines.
for machine_to_remove in machines_to_remove:
settings_element.remove(machine_to_remove)
for machine_to_add in machines_to_add:
settings_element.append(machine_to_add)
return element
def _mergeXML(self, first, second):
result = copy.deepcopy(first)
self._combineElement(self._flattenMachinesXML(result), self._flattenMachinesXML(second))
return result
def _createKey(self, element):
key = element.tag.split("}")[-1]
if "key" in element.attrib:
key += " key:" + element.attrib["key"]
if "manufacturer" in element.attrib:
key += " manufacturer:" + element.attrib["manufacturer"]
if "product" in element.attrib:
key += " product:" + element.attrib["product"]
if key == "machine":
for item in element:
if "machine_identifier" in item.tag:
key += " " + item.attrib["product"]
return key
# Recursively merges XML elements. Updates either the text or children if another element is found in first.
# If it does not exist, copies it from second.
def _combineElement(self, first, second):
# Create a mapping from tag name to element.
mapping = {}
for element in first:
key = self._createKey(element)
mapping[key] = element
for element in second:
key = self._createKey(element)
if len(element): # Check if element has children.
try:
if "setting " in key:
# 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)
for child in element:
mapping[key].append(child)
else:
self._combineElement(mapping[key], element) # Multiple elements, handle those.
except KeyError:
mapping[key] = element
first.append(element)
else:
try:
mapping[key].text = element.text
except KeyError: # Not in the mapping, so simply add it
mapping[key] = element
first.append(element)
## Overridden from InstanceContainer
def deserialize(self, serialized):
data = ET.fromstring(serialized)
self.addMetaDataEntry("type", "material")
self.addMetaDataEntry("base_file", self.id)
# TODO: Add material verfication
self.addMetaDataEntry("status", "unknown")
inherits = data.find("./um:inherits", self.__namespaces)
if inherits is not None:
inherited = self._resolveInheritance(inherits.text)
data = self._mergeXML(inherited, data)
metadata = data.iterfind("./um:metadata/*", self.__namespaces)
for entry in metadata:
tag_name = _tag_without_namespace(entry)
@ -236,8 +380,12 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
brand = entry.find("./um:brand", self.__namespaces)
material = entry.find("./um:material", self.__namespaces)
color = entry.find("./um:color", self.__namespaces)
label = entry.find("./um:label", self.__namespaces)
self.setName(material.text)
if label is not None:
self.setName(label.text)
else:
self.setName(self._profile_name(material.text, color.text))
self.addMetaDataEntry("brand", brand.text)
self.addMetaDataEntry("material", material.text)
@ -266,6 +414,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
self.setDefinition(UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0])
global_compatibility = True
global_setting_values = {}
settings = data.iterfind("./um:settings/um:setting", self.__namespaces)
for entry in settings:
@ -273,6 +422,9 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
if key in self.__material_property_setting_map:
self.setProperty(self.__material_property_setting_map[key], "value", entry.text, self._definition)
global_setting_values[self.__material_property_setting_map[key]] = entry.text
elif key in self.__unmapped_settings:
if key == "hardware compatible":
global_compatibility = parseBool(entry.text)
else:
Logger.log("d", "Unsupported material setting %s", key)
@ -280,12 +432,16 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
machines = data.iterfind("./um:settings/um:machine", self.__namespaces)
for machine in machines:
machine_compatibility = global_compatibility
machine_setting_values = {}
settings = machine.iterfind("./um:setting", self.__namespaces)
for entry in settings:
key = entry.get("key")
if key in self.__material_property_setting_map:
machine_setting_values[self.__material_property_setting_map[key]] = entry.text
elif key in self.__unmapped_settings:
if key == "hardware compatible":
machine_compatibility = parseBool(entry.text)
else:
Logger.log("d", "Unsupported material setting %s", key)
@ -293,7 +449,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
for identifier in identifiers:
machine_id = self.__product_id_map.get(identifier.get("product"), None)
if machine_id is None:
Logger.log("w", "Cannot create material for unknown machine %s", machine_id)
Logger.log("w", "Cannot create material for unknown machine %s", identifier.get("product"))
continue
definitions = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = machine_id)
@ -303,21 +459,21 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
definition = definitions[0]
new_material = XmlMaterialProfile(self.id + "_" + machine_id)
new_material.setName(self.getName())
new_material.setMetaData(copy.deepcopy(self.getMetaData()))
new_material.setDefinition(definition)
new_material.addMetaDataEntry("base_file", self.id)
if machine_compatibility:
new_material = XmlMaterialProfile(self.id + "_" + machine_id)
new_material.setName(self.getName())
new_material.setMetaData(copy.deepcopy(self.getMetaData()))
new_material.setDefinition(definition)
for key, value in global_setting_values.items():
new_material.setProperty(key, "value", value, definition)
for key, value in global_setting_values.items():
new_material.setProperty(key, "value", value, definition)
for key, value in machine_setting_values.items():
new_material.setProperty(key, "value", value, definition)
for key, value in machine_setting_values.items():
new_material.setProperty(key, "value", value, definition)
new_material._dirty = False
new_material._dirty = False
UM.Settings.ContainerRegistry.getInstance().addContainer(new_material)
UM.Settings.ContainerRegistry.getInstance().addContainer(new_material)
hotends = machine.iterfind("./um:hotend", self.__namespaces)
for hotend in hotends:
@ -334,12 +490,26 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
Logger.log("d", "No variants found with ID or name %s for machine %s", hotend_id, definition.id)
continue
hotend_compatibility = machine_compatibility
hotend_setting_values = {}
settings = hotend.iterfind("./um:setting", self.__namespaces)
for entry in settings:
key = entry.get("key")
if key in self.__material_property_setting_map:
hotend_setting_values[self.__material_property_setting_map[key]] = entry.text
elif key in self.__unmapped_settings:
if key == "hardware compatible":
hotend_compatibility = parseBool(entry.text)
else:
Logger.log("d", "Unsupported material setting %s", key)
if not hotend_compatibility:
continue
new_hotend_material = XmlMaterialProfile(self.id + "_" + machine_id + "_" + hotend_id.replace(" ", "_"))
new_hotend_material.setName(self.getName())
new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData()))
new_hotend_material.setDefinition(definition)
new_hotend_material.addMetaDataEntry("base_file", self.id)
new_hotend_material.addMetaDataEntry("variant", variant_containers[0].id)
for key, value in global_setting_values.items():
@ -348,17 +518,18 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
for key, value in machine_setting_values.items():
new_hotend_material.setProperty(key, "value", value, definition)
settings = hotend.iterfind("./um:setting", self.__namespaces)
for entry in settings:
key = entry.get("key")
if key in self.__material_property_setting_map:
new_hotend_material.setProperty(self.__material_property_setting_map[key], "value", entry.text, definition)
else:
Logger.log("d", "Unsupported material setting %s", key)
for key, value in hotend_setting_values.items():
new_hotend_material.setProperty(key, "value", value, definition)
new_hotend_material._dirty = False
UM.Settings.ContainerRegistry.getInstance().addContainer(new_hotend_material)
if not global_compatibility:
# Change the type of this container so it is not shown as an option in menus.
# This uses InstanceContainer.setMetaDataEntry because otherwise all containers that
# share this basefile are also updated.
super().setMetaDataEntry("type", "incompatible_material")
def _addSettingElement(self, builder, instance):
try:
key = UM.Dictionary.findKey(self.__material_property_setting_map, instance.definition.key)
@ -369,24 +540,34 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer):
builder.data(str(instance.value))
builder.end("setting")
def _profile_name(self, material_name, color_name):
if color_name != "Generic":
return "%s %s" % (color_name, material_name)
else:
return material_name
# Map XML file setting names to internal names
__material_property_setting_map = {
"print temperature": "material_print_temperature",
"heated bed temperature": "material_bed_temperature",
"standby temperature": "material_standby_temperature",
"processing temperature graph": "material_flow_temp_graph",
"print cooling": "cool_fan_speed",
"retraction amount": "retraction_amount",
"retraction speed": "retraction_speed",
"retraction speed": "retraction_speed"
}
__unmapped_settings = [
"hardware compatible"
]
# Map XML file product names to internal ids
# TODO: Move this to definition's metadata
__product_id_map = {
"Ultimaker2": "ultimaker2",
"Ultimaker2+": "ultimaker2_plus",
"Ultimaker2go": "ultimaker2_go",
"Ultimaker2extended": "ultimaker2_extended",
"Ultimaker2extended+": "ultimaker2_extended_plus",
"Ultimaker 2": "ultimaker2",
"Ultimaker 2+": "ultimaker2_plus",
"Ultimaker 2 Go": "ultimaker2_go",
"Ultimaker 2 Extended": "ultimaker2_extended",
"Ultimaker 2 Extended+": "ultimaker2_extended_plus",
"Ultimaker Original": "ultimaker_original",
"Ultimaker Original+": "ultimaker_original_plus"
}

View file

@ -0,0 +1,15 @@
{
"id": "custom",
"version": 2,
"name": "Custom FDM printer",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "Ultimaker",
"manufacturer": "Custom",
"category": "Custom",
"file_formats": "text/x-gcode",
"has_materials": true,
"first_start_actions": ["MachineSettingsAction"]
}
}

View file

@ -161,7 +161,7 @@
},
"platform_adhesion":
{
"label": "Platform Adhesion",
"label": "Build Plate Adhesion",
"type": "category",
"icon": "category_adhesion",
"description": "Adhesion",
@ -177,8 +177,7 @@
"minimum_value_warning": "machine_nozzle_offset_x",
"maximum_value": "machine_width",
"settable_per_mesh": false,
"settable_per_extruder": true,
"enabled": false
"settable_per_extruder": true
},
"extruder_prime_pos_y":
{
@ -190,8 +189,7 @@
"minimum_value_warning": "machine_nozzle_offset_y",
"maximum_value_warning": "machine_depth",
"settable_per_mesh": false,
"settable_per_extruder": true,
"enabled": false
"settable_per_extruder": true
}
}
}

View file

@ -10,8 +10,8 @@
"manufacturer": "Ultimaker",
"file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g",
"visible": false,
"preferred_material": "pla",
"preferred_quality": "normal",
"preferred_material": "*generic_pla*",
"preferred_quality": "*normal*",
"machine_extruder_trains":
{
"0": "fdmextruder"
@ -67,8 +67,8 @@
},
"material_bed_temp_wait":
{
"label": "Wait for bed heatup",
"description": "Whether to insert a command to wait until the bed temperature is reached at the start.",
"label": "Wait for build plate heatup",
"description": "Whether to insert a command to wait until the build plate temperature is reached at the start.",
"default_value": true,
"type": "bool",
"settable_per_mesh": false,
@ -95,6 +95,16 @@
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
"material_bed_temp_prepend":
{
"label": "Include build plate temperature",
"description": "Whether to include build plate temperature commands at the start of the gcode. When the start_gcode already contains build plate temperature commands Cura frontend will automatically disable this setting.",
"default_value": true,
"type": "bool",
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
"machine_width":
{
"label": "Machine width",
@ -127,8 +137,8 @@
},
"machine_heated_bed":
{
"label": "Has heated bed",
"description": "Whether the machine has a heated bed present.",
"label": "Has heated build plate",
"description": "Whether the machine has a heated build plate present.",
"default_value": false,
"type": "bool",
"settable_per_mesh": false,
@ -618,7 +628,8 @@
"value": "line_width",
"enabled": "adhesion_type == \"skirt\" or adhesion_type == \"brim\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"support_line_width":
{
@ -784,7 +795,7 @@
"wall_0_inset":
{
"label": "Outer Wall Inset",
"description": "Inset applied to the path of the outer wall. If the outer wall is smaller than the nozzle, and printed after the inner walls, use this offset to get the hole in the nozzle to overlap with the inner walls instead of the outside of the object.",
"description": "Inset applied to the path of the outer wall. If the outer wall is smaller than the nozzle, and printed after the inner walls, use this offset to get the hole in the nozzle to overlap with the inner walls instead of the outside of the model.",
"unit": "mm",
"type": "float",
"default_value": 0.0,
@ -1085,10 +1096,11 @@
"settable_per_extruder": true
},
"material_bed_temperature": {
"label": "Bed Temperature",
"description": "The temperature used for the heated bed. Set at 0 to pre-heat the printer manually.",
"label": "Build Plate Temperature",
"description": "The temperature used for the heated build plate. Set at 0 to pre-heat the printer manually.",
"unit": "°C",
"type": "float",
"resolve": "sum(extruderValues('material_bed_temperature')) / len(extruderValues('material_bed_temperature'))",
"default_value": 60,
"minimum_value": "-273.15",
"maximum_value_warning": "260",
@ -1564,12 +1576,13 @@
"value": "speed_layer_0",
"enabled": "adhesion_type == \"skirt\" or adhesion_type == \"brim\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"max_feedrate_z_override":
{
"label": "Maximum Z Speed",
"description": "The maximum speed with which the bed is moved. Setting this to zero causes the print to use the firmware defaults for the maximum z speed.",
"description": "The maximum speed with which the build plate is moved. Setting this to zero causes the print to use the firmware defaults for the maximum z speed.",
"unit": "mm/s",
"type": "float",
"default_value": 0,
@ -1582,7 +1595,7 @@
"speed_slowdown_layers":
{
"label": "Number of Slower Layers",
"description": "The first few layers are printed slower than the rest of the object, to get better adhesion to the build plate and improve the overall success rate of prints. The speed is gradually increased over these layers.",
"description": "The first few layers are printed slower than the rest of the model, to get better adhesion to the build plate and improve the overall success rate of prints. The speed is gradually increased over these layers.",
"type": "int",
"default_value": 2,
"minimum_value": "0",
@ -1829,7 +1842,8 @@
"minimum_value_warning": "100",
"maximum_value_warning": "10000",
"enabled": "acceleration_enabled",
"settable_per_mesh": false
"settable_per_mesh": false,
"global_inherits_stack": "adhesion_extruder_nr"
},
@ -2048,7 +2062,8 @@
"maximum_value_warning": "50",
"value": "jerk_layer_0",
"enabled": "jerk_enabled",
"settable_per_mesh": false
"settable_per_mesh": false,
"global_inherits_stack": "adhesion_extruder_nr"
}
}
},
@ -2110,8 +2125,8 @@
{
"cool_fan_enabled":
{
"label": "Enable Cooling Fans",
"description": "Enables the cooling fans while printing. The fans improve print quality on layers with short layer times and bridging / overhangs.",
"label": "Enable Print Cooling",
"description": "Enables the print cooling fans while printing. The fans improve print quality on layers with short layer times and bridging / overhangs.",
"type": "bool",
"default_value": true,
"settable_per_mesh": false,
@ -2120,7 +2135,7 @@
"cool_fan_speed":
{
"label": "Fan Speed",
"description": "The speed at which the cooling fans spin.",
"description": "The speed at which the print cooling fans spin.",
"unit": "%",
"type": "float",
"minimum_value": "0",
@ -2633,7 +2648,7 @@
},
"platform_adhesion":
{
"label": "Platform Adhesion",
"label": "Build Plate Adhesion",
"type": "category",
"icon": "category_adhesion",
"description": "Adhesion",
@ -2667,8 +2682,8 @@
},
"adhesion_type":
{
"label": "Platform Adhesion Type",
"description": "Different options that help to improve both priming your extrusion and adhesion to the build plate. Brim adds a single layer flat area around the base of your object to prevent warping. Raft adds a thick grid with a roof below the object. Skirt is a line printed around the object, but not connected to the model.",
"label": "Build Plate Adhesion Type",
"description": "Different options that help to improve both priming your extrusion and adhesion to the build plate. Brim adds a single layer flat area around the base of your model to prevent warping. Raft adds a thick grid with a roof below the model. Skirt is a line printed around the model, but not connected to the model.",
"type": "enum",
"options":
{
@ -2683,14 +2698,15 @@
"skirt_line_count":
{
"label": "Skirt Line Count",
"description": "Multiple skirt lines help to prime your extrusion better for small objects. Setting this to 0 will disable the skirt.",
"description": "Multiple skirt lines help to prime your extrusion better for small models. Setting this to 0 will disable the skirt.",
"type": "int",
"default_value": 1,
"minimum_value": "0",
"maximum_value_warning": "10",
"enabled": "adhesion_type == \"skirt\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"skirt_gap":
{
@ -2703,7 +2719,8 @@
"maximum_value_warning": "100",
"enabled": "adhesion_type == \"skirt\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"skirt_brim_minimal_length":
{
@ -2717,7 +2734,8 @@
"maximum_value_warning": "2500",
"enabled": "adhesion_type == \"skirt\" or adhesion_type == \"brim\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"brim_width":
{
@ -2731,6 +2749,7 @@
"enabled": "adhesion_type == \"brim\"",
"settable_per_mesh": false,
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr",
"children":
{
"brim_line_count":
@ -2744,7 +2763,8 @@
"value": "math.ceil(brim_width / skirt_brim_line_width)",
"enabled": "adhesion_type == \"brim\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
}
}
},
@ -2756,23 +2776,25 @@
"default_value": true,
"enabled": "adhesion_type == \"brim\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_margin":
{
"label": "Raft Extra Margin",
"description": "If the raft is enabled, this is the extra raft area around the object which is also given a raft. Increasing this margin will create a stronger raft while using more material and leaving less area for your print.",
"description": "If the raft is enabled, this is the extra raft area around the model which is also given a raft. Increasing this margin will create a stronger raft while using more material and leaving less area for your print.",
"unit": "mm",
"type": "float",
"default_value": 15,
"minimum_value_warning": "0",
"maximum_value_warning": "10",
"enabled": "adhesion_type == \"raft\""
"enabled": "adhesion_type == \"raft\"",
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_airgap":
{
"label": "Raft Air Gap",
"description": "The gap between the final raft layer and the first layer of the object. Only the first layer is raised by this amount to lower the bonding between the raft layer and the object. Makes it easier to peel off the raft.",
"description": "The gap between the final raft layer and the first layer of the model. Only the first layer is raised by this amount to lower the bonding between the raft layer and the model. Makes it easier to peel off the raft.",
"unit": "mm",
"type": "float",
"default_value": 0.3,
@ -2780,11 +2802,12 @@
"maximum_value_warning": "1.0",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"layer_0_z_overlap": {
"label": "Initial Layer Z Overlap",
"description": "Make the first and second layer of the object overlap in the Z direction to compensate for the filament lost in the airgap. All models above the first model layer will be shifted down by this amount.",
"description": "Make the first and second layer of the model overlap in the Z direction to compensate for the filament lost in the airgap. All models above the first model layer will be shifted down by this amount.",
"unit": "mm",
"type": "float",
"default_value": 0.22,
@ -2793,19 +2816,21 @@
"maximum_value_warning": "layer_height",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_surface_layers":
{
"label": "Raft Top Layers",
"description": "The number of top layers on top of the 2nd raft layer. These are fully filled layers that the object sits on. 2 layers result in a smoother top surface than 1.",
"description": "The number of top layers on top of the 2nd raft layer. These are fully filled layers that the model sits on. 2 layers result in a smoother top surface than 1.",
"type": "int",
"default_value": 2,
"minimum_value": "0",
"maximum_value_warning": "20",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_surface_thickness":
{
@ -2819,7 +2844,8 @@
"maximum_value_warning": "2.0",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_surface_line_width":
{
@ -2833,7 +2859,8 @@
"maximum_value_warning": "machine_nozzle_size * 2",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_surface_line_spacing":
{
@ -2847,7 +2874,8 @@
"enabled": "adhesion_type == \"raft\"",
"value": "raft_surface_line_width",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_interface_thickness":
{
@ -2861,12 +2889,13 @@
"maximum_value_warning": "5.0",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_interface_line_width":
{
"label": "Raft Middle Line Width",
"description": "Width of the lines in the middle raft layer. Making the second layer extrude more causes the lines to stick to the bed.",
"description": "Width of the lines in the middle raft layer. Making the second layer extrude more causes the lines to stick to the build plate.",
"unit": "mm",
"type": "float",
"default_value": 0.7,
@ -2875,7 +2904,8 @@
"maximum_value_warning": "machine_nozzle_size * 2",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_interface_line_spacing":
{
@ -2889,12 +2919,13 @@
"maximum_value_warning": "15.0",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_base_thickness":
{
"label": "Raft Base Thickness",
"description": "Layer thickness of the base raft layer. This should be a thick layer which sticks firmly to the printer bed.",
"description": "Layer thickness of the base raft layer. This should be a thick layer which sticks firmly to the printer build plate.",
"unit": "mm",
"type": "float",
"default_value": 0.3,
@ -2903,12 +2934,13 @@
"maximum_value_warning": "5.0",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_base_line_width":
{
"label": "Raft Base Line Width",
"description": "Width of the lines in the base raft layer. These should be thick lines to assist in bed adhesion.",
"description": "Width of the lines in the base raft layer. These should be thick lines to assist in build plate adhesion.",
"unit": "mm",
"type": "float",
"default_value": 0.8,
@ -2917,7 +2949,8 @@
"maximum_value_warning": "machine_nozzle_size * 3",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_base_line_spacing":
{
@ -2931,7 +2964,8 @@
"maximum_value_warning": "100",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_speed":
{
@ -2947,6 +2981,7 @@
"value": "speed_print / 60 * 30",
"settable_per_mesh": false,
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr",
"children":
{
"raft_surface_speed":
@ -2962,7 +2997,8 @@
"enabled": "adhesion_type == \"raft\"",
"value": "raft_speed",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_interface_speed":
{
@ -2977,7 +3013,8 @@
"maximum_value_warning": "150",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_base_speed":
{
@ -2992,13 +3029,11 @@
"enabled": "adhesion_type == \"raft\"",
"value": "0.75 * raft_speed",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
}
}
},
"raft_acceleration": {
"label": "Raft Print Acceleration",
"description": "The acceleration with which the raft is printed.",
@ -3011,6 +3046,7 @@
"value": "acceleration_print",
"enabled": "adhesion_type == \"raft\" and acceleration_enabled",
"settable_per_mesh": false,
"global_inherits_stack": "adhesion_extruder_nr",
"children": {
"raft_surface_acceleration": {
"label": "Raft Top Print Acceleration",
@ -3023,7 +3059,8 @@
"minimum_value_warning": "100",
"maximum_value_warning": "10000",
"enabled": "adhesion_type == \"raft\" and acceleration_enabled",
"settable_per_mesh": false
"settable_per_mesh": false,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_interface_acceleration": {
"label": "Raft Middle Print Acceleration",
@ -3036,7 +3073,8 @@
"minimum_value_warning": "100",
"maximum_value_warning": "10000",
"enabled": "adhesion_type == \"raft\" and acceleration_enabled",
"settable_per_mesh": false
"settable_per_mesh": false,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_base_acceleration": {
"label": "Raft Base Print Acceleration",
@ -3049,13 +3087,11 @@
"minimum_value_warning": "100",
"maximum_value_warning": "10000",
"enabled": "adhesion_type == \"raft\" and acceleration_enabled",
"settable_per_mesh": false
"settable_per_mesh": false,
"global_inherits_stack": "adhesion_extruder_nr"
}
}
},
"raft_jerk": {
"label": "Raft Print Jerk",
"description": "The jerk with which the raft is printed.",
@ -3068,6 +3104,7 @@
"value": "jerk_print",
"enabled": "adhesion_type == \"raft\" and jerk_enabled",
"settable_per_mesh": false,
"global_inherits_stack": "adhesion_extruder_nr",
"children": {
"raft_surface_jerk": {
"label": "Raft Top Print Jerk",
@ -3080,7 +3117,8 @@
"minimum_value_warning": "5",
"maximum_value_warning": "100",
"enabled": "adhesion_type == \"raft\" and jerk_enabled",
"settable_per_mesh": false
"settable_per_mesh": false,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_interface_jerk": {
"label": "Raft Middle Print Jerk",
@ -3093,7 +3131,8 @@
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"enabled": "adhesion_type == \"raft\" and jerk_enabled",
"settable_per_mesh": false
"settable_per_mesh": false,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_base_jerk": {
"label": "Raft Base Print Jerk",
@ -3106,12 +3145,11 @@
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"enabled": "adhesion_type == \"raft\" and jerk_enabled",
"settable_per_mesh": false
"settable_per_mesh": false,
"global_inherits_stack": "adhesion_extruder_nr"
}
}
},
"raft_fan_speed": {
"label": "Raft Fan Speed",
"description": "The fan speed for the raft.",
@ -3123,6 +3161,7 @@
"settable_per_mesh": false,
"settable_per_extruder": true,
"enabled": "adhesion_type == \"raft\"",
"global_inherits_stack": "adhesion_extruder_nr",
"children":
{
"raft_surface_fan_speed":
@ -3137,7 +3176,8 @@
"value": "raft_fan_speed",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_interface_fan_speed":
{
@ -3151,7 +3191,8 @@
"value": "raft_fan_speed",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
},
"raft_base_fan_speed":
{
@ -3165,7 +3206,8 @@
"value": "raft_fan_speed",
"enabled": "adhesion_type == \"raft\"",
"settable_per_mesh": false,
"settable_per_extruder": true
"settable_per_extruder": true,
"global_inherits_stack": "adhesion_extruder_nr"
}
}
}
@ -3181,7 +3223,7 @@
{
"adhesion_extruder_nr":
{
"label": "Platform Adhesion Extruder",
"label": "Build Plate Adhesion Extruder",
"description": "The extruder train to use for printing the skirt/brim/raft. This is used in multi-extrusion.",
"type": "extruder",
"default_value": "0",
@ -3239,6 +3281,7 @@
"label": "Enable Prime Tower",
"description": "Print a tower next to the print which serves to prime the material after each nozzle switch.",
"type": "bool",
"enabled": "machine_extruder_count > 1",
"default_value": false,
"settable_per_mesh": false,
"settable_per_extruder": false
@ -3310,7 +3353,7 @@
"multiple_mesh_overlap":
{
"label": "Dual Extrusion Overlap",
"description": "Make the objects printed with different extruder trains overlap a bit. This makes the different materials bond together better.",
"description": "Make the models printed with different extruder trains overlap a bit. This makes the different materials bond together better.",
"type": "float",
"unit": "mm",
"default_value": 0.15,
@ -3321,7 +3364,7 @@
"ooze_shield_enabled":
{
"label": "Enable Ooze Shield",
"description": "Enable exterior ooze shield. This will create a shell around the object which is likely to wipe a second nozzle if it's at the same height as the first nozzle.",
"description": "Enable exterior ooze shield. This will create a shell around the model which is likely to wipe a second nozzle if it's at the same height as the first nozzle.",
"type": "bool",
"default_value": false,
"settable_per_mesh": false,
@ -3408,7 +3451,7 @@
"print_sequence":
{
"label": "Print Sequence",
"description": "Whether to print all objects one layer at a time or to wait for one object to finish, before moving on to the next. One at a time mode is only possible if all models are separated in such a way that the whole print head can move in between and all models are lower than the distance between the nozzle and the X/Y axes.",
"description": "Whether to print all models one layer at a time or to wait for one model to finish, before moving on to the next. One at a time mode is only possible if all models are separated in such a way that the whole print head can move in between and all models are lower than the distance between the nozzle and the X/Y axes.",
"type": "enum",
"options":
{
@ -3462,7 +3505,7 @@
"magic_spiralize":
{
"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 object 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 used to be called Joris in older versions.",
"type": "bool",
"default_value": false,
"settable_per_mesh": true
@ -3480,7 +3523,7 @@
"draft_shield_enabled":
{
"label": "Enable Draft Shield",
"description": "This will create a wall around the object, which traps (hot) air and shields against exterior airflow. Especially useful for materials which warp easily.",
"description": "This will create a wall around the model, which traps (hot) air and shields against exterior airflow. Especially useful for materials which warp easily.",
"type": "bool",
"default_value": false,
"settable_per_mesh": false,
@ -3502,7 +3545,7 @@
"draft_shield_height_limitation":
{
"label": "Draft Shield Limitation",
"description": "Set the height of the draft shield. Choose to print the draft shield at the full height of the object or at a limited height.",
"description": "Set the height of the draft shield. Choose to print the draft shield at the full height of the model or at a limited height.",
"type": "enum",
"options":
{
@ -3806,7 +3849,7 @@
"wireframe_printspeed_flat":
{
"label": "WP Horizontal Printing Speed",
"description": "Speed of printing the horizontal contours of the object. Only applies to Wire Printing.",
"description": "Speed of printing the horizontal contours of the model. Only applies to Wire Printing.",
"unit": "mm/s",
"type": "float",
"default_value": 5,

View file

@ -27,7 +27,7 @@
"default_value": true
},
"machine_center_is_zero": {
"default_value": false
"default_value": true
},
"machine_nozzle_size": {
"default_value": 0.4
@ -56,7 +56,7 @@
"default_value": "G28 ; Home extruder\nM107 ; Turn off fan\nG90 ; Absolute positioning\nM82 ; Extruder in absolute mode\n{IF_BED}M190 S{BED}\n{IF_EXT0}M104 T0 S{TEMP0}\n{IF_EXT0}M109 T0 S{TEMP0}\n{IF_EXT1}M104 T1 S{TEMP1}\n{IF_EXT1}M109 T1 S{TEMP1}\nG32 S3 ; auto level\nG92 E0 ; Reset extruder position"
},
"machine_end_gcode": {
"default_value": "M104 S0\nG91 ; relative positioning\nG1 E-2 F5000; retract 2mm\nG28 Z; move bed down\nG90 ; absolute positioning\nM84 ; disable motors"
"default_value": "M104 S0 ; turn off extruders\nM140 S0 ; heated bed heater off\nG91 ; relative positioning\nG1 E-2 F5000; retract 2mm\nG28 Z; move bed down\nG90 ; absolute positioning\nM84 ; disable motors"
},
"layer_height": {
"default_value": 0.15
@ -65,10 +65,10 @@
"default_value": 0.8
},
"top_bottom_thickness": {
"default_value": 0.3
"default_value": 1.2
},
"material_print_temperature": {
"default_value": 215
"default_value": 205
},
"material_bed_temperature": {
"default_value": 60
@ -77,23 +77,30 @@
"default_value": 1.75
},
"speed_print": {
"default_value": 60
"default_value": 50
},
"speed_wall_0": {
"default_value": 25
},
"speed_wall_x": {
"default_value": 40
},
"speed_infill": {
"default_value": 100
"default_value": 80
},
"speed_topbottom": {
"default_value": 30
},
"speed_support_interface":
{
"default_value": 20
},
"speed_travel": {
"default_value": 150
},
"speed_layer_0": {
"default_value": 30.0,
"minimum_value": 0.1
},
"infill_overlap": {
"default_value": 10.0
}
}
}
}

View file

@ -8,6 +8,7 @@
"author": "Calvindog717",
"manufacturer": "PrintrBot",
"category": "Other",
"platform": "printrbot_simple_metal_platform.stl",
"file_formats": "text/x-gcode"
},
@ -17,7 +18,7 @@
"machine_height": { "default_value": 150 },
"machine_depth": { "default_value": 140 },
"machine_center_is_zero": { "default_value": false },
"machine_nozzle_size": { "default_value": 0.3 },
"machine_nozzle_size": { "default_value": 0.4 },
"material_diameter": { "default_value": 1.75 },
"machine_nozzle_heat_up_speed": { "default_value": 2 },
"machine_nozzle_cool_down_speed": { "default_value": 2 },
@ -33,10 +34,10 @@
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_start_gcode": {
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG29 ; auto bed-levelling\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;home X/Y\nG28 Z0 ;home Z\nG92 E0 ;zero the extruded length\nG29 ;initiate auto bed leveling sequence\nG92 X132.4 Y20 ;correct bed origin (G29 changes it)"
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nM106 S0 ;fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit\nG1 Z+1 E-5 F9000 ;move Z up a bit and retract even more\nG28 X0 Y0 ;home X/Y, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
}
}
}

View file

@ -18,10 +18,12 @@
},
"overrides": {
"machine_start_gcode" : {
"default_value": ""
"default_value": "",
"value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"G21 ;metric values\\nG90 ;absolute positioning\\nM82 ;set extruder to absolute mode\\nM107 ;start with the fan off\\nG1 X10 Y0 F4000;move X/Y to min endstops\\nG28 Z0 ;move Z to bottom endstops\\nG1 Z15.0 F9000 ;move the platform to 15mm\\nG92 E0 ;zero the extruded length\\nG1 F200 E10 ;extrude 10 mm of feed stock\\nG92 E0 ;zero the extruded length again\\nG1 F9000\\n;Put printing message on LCD screen\\nM117 Printing...\""
},
"machine_end_gcode" : {
"default_value": ""
"default_value": "",
"value": "\"\" if machine_gcode_flavor == \"UltiGCode\" else \"M104 S0 ;extruder heater off\\nM140 S0 ;heated bed heater off (if you have it)\\nG91 ;relative positioning\\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\\nM84 ;steppers off\\nG90 ;absolute positioning\""
},
"machine_width": {
"default_value": 223

View file

@ -11,9 +11,7 @@
"file_formats": "text/x-gcode",
"platform": "ultimaker2_platform.obj",
"platform_texture": "Ultimaker2Plusbackplate.png",
"preferred_variant": "ultimaker2_plus_0.4",
"preferred_material": "*pla*",
"preferred_quality": "*normal*",
"preferred_variant": "*0.4*",
"has_variants": true,
"has_materials": true,
"has_machine_materials": true,

View file

@ -13,8 +13,6 @@
"icon": "icon_ultimaker.png",
"platform": "ultimaker_platform.stl",
"has_materials": true,
"preferred_material": "*pla*",
"preferred_quality": "*normal*",
"first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"],
"supported_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel", "UpgradeFirmware"]
},

View file

@ -13,12 +13,10 @@
"icon": "icon_ultimaker.png",
"platform": "ultimaker_platform.stl",
"has_materials": true,
"preferred_material": "*pla*",
"preferred_quality": "*normal*",
"machine_extruder_trains":
{
"0": "ultimaker_original_dual_left",
"1": "ultimaker_original_dual_right"
"0": "ultimaker_original_dual_1st",
"1": "ultimaker_original_dual_2nd"
},
"first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"],
"supported_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel", "UpgradeFirmware"]
@ -65,12 +63,22 @@
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
"default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nT1 ;Switch to the 2nd extruder\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F200 E-{switch_extruder_retraction_amount}\nT0 ;Switch to the 1st extruder\nG92 E0 ;zero the extruded length\nG1 F200 E6 ;extrude 6 mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..."
},
"machine_end_gcode": {
"default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
"default_value": "M104 T0 S0 ;1st extruder heater off\nM104 T1 S0 ;2nd extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
},
"machine_extruder_count": { "default_value": 2 },
"print_sequence": {"enabled": false}
"machine_extruder_count": {
"default_value": 2
},
"print_sequence": {
"enabled": false
},
"prime_tower_position_x": {
"default_value": 185
},
"prime_tower_position_y": {
"default_value": 175
}
}
}

View file

@ -0,0 +1,26 @@
{
"id": "ultimaker_original_dual_1st",
"version": 2,
"name": "1st Extruder",
"inherits": "fdmextruder",
"metadata": {
"machine": "ultimaker_original_dual",
"position": "0"
},
"overrides": {
"extruder_nr": {
"default_value": 0,
"maximum_value": "1"
},
"machine_nozzle_offset_x": { "default_value": 0.0 },
"machine_nozzle_offset_y": { "default_value": 0.0 },
"machine_extruder_start_pos_abs": { "default_value": true },
"machine_extruder_start_pos_x": { "value": "prime_tower_position_x" },
"machine_extruder_start_pos_y": { "value": "prime_tower_position_y" },
"machine_extruder_end_pos_abs": { "default_value": true },
"machine_extruder_end_pos_x": { "value": "prime_tower_position_x" },
"machine_extruder_end_pos_y": { "value": "prime_tower_position_y" }
}
}

View file

@ -0,0 +1,26 @@
{
"id": "ultimaker_original_dual_2nd",
"version": 2,
"name": "2nd Extruder",
"inherits": "fdmextruder",
"metadata": {
"machine": "ultimaker_original_dual",
"position": "1"
},
"overrides": {
"extruder_nr": {
"default_value": 1,
"maximum_value": "1"
},
"machine_nozzle_offset_x": { "default_value": 0.0 },
"machine_nozzle_offset_y": { "default_value": 21.6 },
"machine_extruder_start_pos_abs": { "default_value": true },
"machine_extruder_start_pos_x": { "value": "prime_tower_position_x" },
"machine_extruder_start_pos_y": { "value": "prime_tower_position_y" },
"machine_extruder_end_pos_abs": { "default_value": true },
"machine_extruder_end_pos_x": { "value": "prime_tower_position_x" },
"machine_extruder_end_pos_y": { "value": "prime_tower_position_y" }
}
}

View file

@ -1,19 +0,0 @@
{
"id": "ultimaker_original_dual_left",
"version": 2,
"name": "Left Extruder",
"inherits": "fdmextruder",
"metadata": {
"machine": "ultimaker_original_dual",
"position": "0"
},
"overrides": {
"extruder_nr": {
"default_value": 0,
"maximum_value": "1"
},
"machine_nozzle_offset_x": { "default_value": 0.0 },
"machine_nozzle_offset_y": { "default_value": 0.0 }
}
}

View file

@ -1,19 +0,0 @@
{
"id": "ultimaker_original_dual_right",
"version": 2,
"name": "Right Extruder",
"inherits": "fdmextruder",
"metadata": {
"machine": "ultimaker_original_dual",
"position": "1"
},
"overrides": {
"extruder_nr": {
"default_value": 1,
"maximum_value": "1"
},
"machine_nozzle_offset_x": { "default_value": 0.0 },
"machine_nozzle_offset_y": { "default_value": 21.6 }
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Generic PLA profile. Serves as an example file, data in this file is not correct.
Generic ABS profile. Serves as an example file, data in this file is not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
@ -10,20 +10,21 @@ Generic PLA profile. Serves as an example file, data in this file is not correct
<color>Generic</color>
</name>
<GUID>60636bb4-518f-42e7-8237-fe77b194ebe0</GUID>
<version>0</version>
<version>1</version>
<color_code>#8cb219</color_code>
</metadata>
<properties>
<density>1.07</density>
<density>1.10</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="print temperature">240</setting>
<setting key="heated bed temperature">80</setting>
<setting key="standby temperature">200</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker2extended+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Generic PLA profile. Serves as an example file, data in this file is not correct.
Generic CPE profile. Serves as an example file, data in this file is not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
@ -10,20 +10,21 @@ Generic PLA profile. Serves as an example file, data in this file is not correct
<color>Generic</color>
</name>
<GUID>12f41353-1a33-415e-8b4f-a775a6c70cc6</GUID>
<version>0</version>
<version>1</version>
<color_code>#159499</color_code>
</metadata>
<properties>
<density>0.94</density>
<density>1.27</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">70</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker2extended+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Generic CPE+ profile. Serves as an example file, data in this file is not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Generic</brand>
<material>CPE+</material>
<color>Generic</color>
</name>
<GUID>e2409626-b5a0-4025-b73e-b58070219259</GUID>
<version>1</version>
<color_code>#3633F2</color_code>
</metadata>
<properties>
<density>1.18</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="hardware compatible">no</setting><!-- This material is not supported on most printers due to high temperatures -->
<setting key="print temperature">260</setting>
<setting key="heated bed temperature">110</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<setting key="hardware compatible">yes</setting>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Generic Nylon profile. Serves as an example file, data in this file is not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Generic</brand>
<material>Nylon</material>
<color>Generic</color>
</name>
<GUID>35ebb8df-66d2-41cd-9662-b7d96b9c2cbd</GUID>
<version>1</version>
<color_code>#3DF266</color_code>
</metadata>
<properties>
<density>1.19</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="hardware compatible">no</setting><!-- This material is not supported on most printers due to high temperatures -->
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<setting key="hardware compatible">yes</setting>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Generic PC profile. Serves as an example file, data in this file is not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Generic</brand>
<material>PC</material>
<color>Generic</color>
</name>
<GUID>98c05714-bf4e-4455-ba27-57d74fe331e4</GUID>
<version>1</version>
<color_code>#F29030</color_code>
</metadata>
<properties>
<density>1.18</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="hardware compatible">no</setting><!-- This material is not supported on most printers due to high temperatures -->
<setting key="print temperature">260</setting>
<setting key="heated bed temperature">110</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<setting key="hardware compatible">yes</setting>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -10,25 +10,42 @@ Generic PLA profile. Serves as an example file, data in this file is not correct
<color>Generic</color>
</name>
<GUID>506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9</GUID>
<version>0</version>
<version>1</version>
<color_code>#ffc924</color_code>
</metadata>
<properties>
<density>1.3</density>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">210</setting>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker2extended+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">150</setting>
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Generic TPU 95A profile. Serves as an example file, data in this file is not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Generic</brand>
<material>TPU 95A</material>
<color>Generic</color>
</name>
<GUID>1d52b2be-a3a2-41de-a8b1-3bcdb5618695</GUID>
<version>1</version>
<color_code>#B22744</color_code>
</metadata>
<properties>
<density>1.19</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="hardware compatible">no</setting><!-- This material is not supported on most printers due to high temperatures -->
<setting key="print temperature">260</setting>
<setting key="heated bed temperature">110</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<setting key="hardware compatible">yes</setting>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated ABS profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>ABS</material>
<color>Black</color>
</name>
<GUID>2f9d2279-9b0e-4765-bf9b-d1e1e13f3c49</GUID>
<version>0</version>
<color_code>#2a292a</color_code>
</metadata>
<properties>
<density>1.10</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">240</setting>
<setting key="heated bed temperature">80</setting>
<setting key="standby temperature">200</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated ABS profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>ABS</material>
<color>Blue</color>
</name>
<GUID>7c9575a6-c8d6-40ec-b3dd-18d7956bfaae</GUID>
<version>0</version>
<color_code>#00387b</color_code>
</metadata>
<properties>
<density>1.10</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">240</setting>
<setting key="heated bed temperature">80</setting>
<setting key="standby temperature">200</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated ABS profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>ABS</material>
<color>Green</color>
</name>
<GUID>3400c0d1-a4e3-47de-a444-7b704f287171</GUID>
<version>0</version>
<color_code>#61993b</color_code>
</metadata>
<properties>
<density>1.10</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">240</setting>
<setting key="heated bed temperature">80</setting>
<setting key="standby temperature">200</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated ABS profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>ABS</material>
<color>Grey</color>
</name>
<GUID>8b75b775-d3f2-4d0f-8fb2-2a3dd53cf673</GUID>
<version>0</version>
<color_code>#52595d</color_code>
</metadata>
<properties>
<density>1.10</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">240</setting>
<setting key="heated bed temperature">80</setting>
<setting key="standby temperature">200</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated ABS profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>ABS</material>
<color>Orange</color>
</name>
<GUID>0b4ca6ef-eac8-4b23-b3ca-5f21af00e54f</GUID>
<version>0</version>
<color_code>#ed6b21</color_code>
</metadata>
<properties>
<density>1.10</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">240</setting>
<setting key="heated bed temperature">80</setting>
<setting key="standby temperature">200</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated ABS profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>ABS</material>
<color>Pearl Gold</color>
</name>
<GUID>7cbdb9ca-081a-456f-a6ba-f73e4e9cb856</GUID>
<version>0</version>
<color_code>#80643f</color_code>
</metadata>
<properties>
<density>1.10</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">240</setting>
<setting key="heated bed temperature">80</setting>
<setting key="standby temperature">200</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated ABS profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>ABS</material>
<color>Red</color>
</name>
<GUID>5df7afa6-48bd-4c19-b314-839fe9f08f1f</GUID>
<version>0</version>
<color_code>#bb1e10</color_code>
</metadata>
<properties>
<density>1.10</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">240</setting>
<setting key="heated bed temperature">80</setting>
<setting key="standby temperature">200</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated ABS profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>ABS</material>
<color>Silver Metallic</color>
</name>
<GUID>763c926e-a5f7-4ba0-927d-b4e038ea2735</GUID>
<version>0</version>
<color_code>#a1a1a0</color_code>
</metadata>
<properties>
<density>1.10</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">240</setting>
<setting key="heated bed temperature">80</setting>
<setting key="standby temperature">200</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated ABS profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>ABS</material>
<color>White</color>
</name>
<GUID>5253a75a-27dc-4043-910f-753ae11bc417</GUID>
<version>0</version>
<color_code>#ecece7</color_code>
</metadata>
<properties>
<density>1.10</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">240</setting>
<setting key="heated bed temperature">80</setting>
<setting key="standby temperature">200</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated ABS profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>ABS</material>
<color>Yellow</color>
</name>
<GUID>e873341d-d9b8-45f9-9a6f-5609e1bcff68</GUID>
<version>0</version>
<color_code>#f7b500</color_code>
</metadata>
<properties>
<density>1.10</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">240</setting>
<setting key="heated bed temperature">80</setting>
<setting key="standby temperature">200</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated CPE profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>CPE</material>
<color>Black</color>
</name>
<GUID>a8955dc3-9d7e-404d-8c03-0fd6fee7f22d</GUID>
<version>0</version>
<color_code>#2a292a</color_code>
</metadata>
<properties>
<density>1.27</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">70</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated CPE profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>CPE</material>
<color>Blue</color>
</name>
<GUID>4d816290-ce2e-40e0-8dc8-3f702243131e</GUID>
<version>0</version>
<color_code>#00a3e0</color_code>
</metadata>
<properties>
<density>1.27</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">70</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated CPE profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>CPE</material>
<color>Dark Grey</color>
</name>
<GUID>10961c00-3caf-48e9-a598-fa805ada1e8d</GUID>
<version>0</version>
<color_code>#4f5250</color_code>
</metadata>
<properties>
<density>1.27</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">70</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated CPE profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>CPE</material>
<color>Green</color>
</name>
<GUID>7ff6d2c8-d626-48cd-8012-7725fa537cc9</GUID>
<version>0</version>
<color_code>#78be20</color_code>
</metadata>
<properties>
<density>1.27</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">70</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated CPE profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>CPE</material>
<color>Light Grey</color>
</name>
<GUID>173a7bae-5e14-470e-817e-08609c61e12b</GUID>
<version>0</version>
<color_code>#c5c7c4</color_code>
</metadata>
<properties>
<density>1.27</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">70</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated CPE profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>CPE</material>
<color>Red</color>
</name>
<GUID>00181d6c-7024-479a-8eb7-8a2e38a2619a</GUID>
<version>0</version>
<color_code>#c8102e</color_code>
</metadata>
<properties>
<density>1.27</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">70</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated CPE profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>CPE</material>
<color>Transparent</color>
</name>
<GUID>bd0d9eb3-a920-4632-84e8-dcd6086746c5</GUID>
<version>0</version>
<color_code>#d0d0d0</color_code>
</metadata>
<properties>
<density>1.27</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">70</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated CPE profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>CPE</material>
<color>White</color>
</name>
<GUID>881c888e-24fb-4a64-a4ac-d5c95b096cd7</GUID>
<version>0</version>
<color_code>#f1ece1</color_code>
</metadata>
<properties>
<density>1.27</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">70</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated CPE profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>CPE</material>
<color>Yellow</color>
</name>
<GUID>b9176a2a-7a0f-4821-9f29-76d882a88682</GUID>
<version>0</version>
<color_code>#f6b600</color_code>
</metadata>
<properties>
<density>1.27</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">250</setting>
<setting key="heated bed temperature">70</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated PLA profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>PLA</material>
<color>Black</color>
</name>
<GUID>3ee70a86-77d8-4b87-8005-e4a1bc57d2ce</GUID>
<version>0</version>
<color_code>#0e0e10</color_code>
</metadata>
<properties>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">150</setting>
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated PLA profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>PLA</material>
<color>Blue</color>
</name>
<GUID>44a029e6-e31b-4c9e-a12f-9282e29a92ff</GUID>
<version>0</version>
<color_code>#00387b</color_code>
</metadata>
<properties>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">150</setting>
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated PLA profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>PLA</material>
<color>Green</color>
</name>
<GUID>2433b8fb-dcd6-4e36-9cd5-9f4ee551c04c</GUID>
<version>0</version>
<color_code>#61993b</color_code>
</metadata>
<properties>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">150</setting>
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated PLA profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>PLA</material>
<color>Magenta</color>
</name>
<GUID>fe3982c8-58f4-4d86-9ac0-9ff7a3ab9cbc</GUID>
<version>0</version>
<color_code>#bc4077</color_code>
</metadata>
<properties>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">150</setting>
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated PLA profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>PLA</material>
<color>Orange</color>
</name>
<GUID>d9549dba-b9df-45b9-80a5-f7140a9a2f34</GUID>
<version>0</version>
<color_code>#ed6b21</color_code>
</metadata>
<properties>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">150</setting>
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated PLA profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>PLA</material>
<color>Pearl-White</color>
</name>
<GUID>d9fc79db-82c3-41b5-8c99-33b3747b8fb3</GUID>
<version>0</version>
<color_code>#e3d9c6</color_code>
</metadata>
<properties>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">150</setting>
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated PLA profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>PLA</material>
<color>Red</color>
</name>
<GUID>9cfe5bf1-bdc5-4beb-871a-52c70777842d</GUID>
<version>0</version>
<color_code>#bb1e10</color_code>
</metadata>
<properties>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">150</setting>
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated PLA profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<inherits>generic_pla</inherits>
<metadata>
<name>
<brand>Ultimaker</brand>
<material>PLA</material>
<color>Silver Metallic</color>
</name>
<GUID>0e01be8c-e425-4fb1-b4a3-b79f255f1db9</GUID>
<version>0</version>
<color_code>#a1a1a0</color_code>
</metadata>
<properties>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="processing temperature graph">
<point flow="2" temperature="20"/>
<point flow="10" temperature="20"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">180</setting>
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated PLA profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>PLA</material>
<color>Transparent</color>
</name>
<GUID>532e8b3d-5fd4-4149-b936-53ada9bd6b85</GUID>
<version>0</version>
<color_code>#d0d0d0</color_code>
</metadata>
<properties>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">150</setting>
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated PLA profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>PLA</material>
<color>White</color>
</name>
<GUID>e509f649-9fe6-4b14-ac45-d441438cb4ef</GUID>
<version>0</version>
<color_code>#f1ece1</color_code>
</metadata>
<properties>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">150</setting>
</machine>
</settings>
</fdmmaterial>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Automatically generated PLA profile. Data in this file may not be not correct.
-->
<fdmmaterial xmlns="http://www.ultimaker.com/material">
<metadata>
<name>
<brand>Ultimaker</brand>
<material>PLA</material>
<color>Yellow</color>
</name>
<GUID>9c1959d0-f597-46ec-9131-34020c7a54fc</GUID>
<version>0</version>
<color_code>#f9a800</color_code>
</metadata>
<properties>
<density>1.24</density>
<diameter>2.85</diameter>
</properties>
<settings>
<setting key="print temperature">200</setting>
<setting key="heated bed temperature">60</setting>
<setting key="standby temperature">175</setting>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2+"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended+"/>
<hotend id="0.25 mm" />
<hotend id="0.4 mm" />
<hotend id="0.6 mm" />
<hotend id="0.8 mm" />
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Go"/>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker 2 Extended"/>
<setting key="standby temperature">150</setting>
<setting key="processing temperature graph">
<point flow="2" temperature="180"/>
<point flow="10" temperature="230"/>
</setting>
</machine>
<machine>
<machine_identifier manufacturer="Ultimaker" product="Ultimaker Original"/>
<setting key="standby temperature">150</setting>
</machine>
</settings>
</fdmmaterial>

Binary file not shown.

View file

@ -122,7 +122,7 @@ Item
id: updateProfileAction;
enabled: Cura.MachineManager.isActiveStackValid && Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId)
text: catalog.i18nc("@action:inmenu menubar:profile","&Update profile with current settings");
onTriggered: Cura.MachineManager.updateQualityContainerFromUserContainer()
onTriggered: Cura.ContainerManager.updateQualityChanges();
}
Action
@ -130,7 +130,7 @@ Item
id: resetProfileAction;
enabled: Cura.MachineManager.hasUserSettings
text: catalog.i18nc("@action:inmenu menubar:profile","&Discard current settings");
onTriggered: Cura.MachineManager.clearUserSettings();
onTriggered: Cura.ContainerManager.clearUserContainers();
}
Action
@ -183,7 +183,7 @@ Item
Action
{
id: deleteObjectAction;
text: catalog.i18nc("@action:inmenu","Delete Object");
text: catalog.i18nc("@action:inmenu","Delete Model");
enabled: UM.Controller.toolsEnabled;
iconName: "edit-delete";
}
@ -191,13 +191,13 @@ Item
Action
{
id: centerObjectAction;
text: catalog.i18nc("@action:inmenu","Ce&nter Object on Platform");
text: catalog.i18nc("@action:inmenu","Ce&nter Model on Platform");
}
Action
{
id: groupObjectsAction
text: catalog.i18nc("@action:inmenu menubar:edit","&Group Objects");
text: catalog.i18nc("@action:inmenu menubar:edit","&Group Models");
enabled: UM.Scene.numObjectsSelected > 1 ? true: false
iconName: "object-group"
shortcut: "Ctrl+G";
@ -207,7 +207,7 @@ Item
Action
{
id: unGroupObjectsAction
text: catalog.i18nc("@action:inmenu menubar:edit","Ungroup Objects");
text: catalog.i18nc("@action:inmenu menubar:edit","Ungroup Models");
enabled: UM.Scene.isGroupSelected
iconName: "object-ungroup"
shortcut: "Ctrl+Shift+G";
@ -217,7 +217,7 @@ Item
Action
{
id: mergeObjectsAction
text: catalog.i18nc("@action:inmenu menubar:edit","&Merge Objects");
text: catalog.i18nc("@action:inmenu menubar:edit","&Merge Models");
enabled: UM.Scene.numObjectsSelected > 1 ? true: false
iconName: "merge";
shortcut: "Ctrl+Alt+G";
@ -227,14 +227,14 @@ Item
Action
{
id: multiplyObjectAction;
text: catalog.i18nc("@action:inmenu","&Duplicate Object");
text: catalog.i18nc("@action:inmenu","&Duplicate Model");
iconName: "edit-duplicate"
}
Action
{
id: selectAllAction;
text: catalog.i18nc("@action:inmenu menubar:edit","&Select All Objects");
text: catalog.i18nc("@action:inmenu menubar:edit","&Select All Models");
enabled: UM.Controller.toolsEnabled;
iconName: "edit-select-all";
shortcut: "Ctrl+A";
@ -244,7 +244,7 @@ Item
Action
{
id: deleteAllAction;
text: catalog.i18nc("@action:inmenu menubar:edit","&Clear Build Platform");
text: catalog.i18nc("@action:inmenu menubar:edit","&Clear Build Plate");
enabled: UM.Controller.toolsEnabled;
iconName: "edit-delete";
shortcut: "Ctrl+D";
@ -254,7 +254,7 @@ Item
Action
{
id: reloadAllAction;
text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Objects");
text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Models");
iconName: "document-revert";
onTriggered: Printer.reloadAll();
}
@ -262,14 +262,14 @@ Item
Action
{
id: resetAllTranslationAction;
text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Object Positions");
text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model Positions");
onTriggered: Printer.resetAllTranslation();
}
Action
{
id: resetAllAction;
text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Object &Transformations");
text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model &Transformations");
onTriggered: Printer.resetAll();
}
@ -292,7 +292,7 @@ Item
Action
{
id: configureSettingVisibilityAction
text: catalog.i18nc("@action:menu", "Configure setting visiblity...");
text: catalog.i18nc("@action:menu", "Configure setting visibility...");
iconName: "configure"
}
}

View file

@ -143,20 +143,21 @@ UM.MainWindow
model: Cura.ExtrudersModel { }
Menu {
title: model.name
visible: machineExtruderCount.properties.value > 1
NozzleMenu { title: catalog.i18nc("@title:menu", "&Nozzle"); visible: Cura.MachineManager.hasVariants }
NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants }
MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials }
ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); }
MenuSeparator { }
MenuItem { text: "Set as Active Extruder" }
MenuItem { text: catalog.i18nc("@action:inmenu", "Set as Active Extruder"); onTriggered: ExtruderManager.setActiveExtruderIndex(model.index) }
}
onObjectAdded: settingsMenu.insertItem(index, object)
onObjectRemoved: settingsMenu.removeItem(object)
}
NozzleMenu { title: catalog.i18nc("@title:menu", "&Nozzle"); visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasVariants }
NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasVariants }
MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasMaterials }
ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); visible: machineExtruderCount.properties.value <= 1 }
@ -401,7 +402,7 @@ UM.MainWindow
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenterOffset: - UM.Theme.getSize("sidebar").width / 2
visible: base.monitoringPrint
source: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].cameraImage : ""
source: Cura.MachineManager.printerOutputDevices.length > 0 && Cura.MachineManager.printerOutputDevices[0].cameraImage ? Cura.MachineManager.printerOutputDevices[0].cameraImage : ""
}
UM.MessageStack
@ -427,6 +428,9 @@ UM.MainWindow
removePage(0);
insertPage(0, catalog.i18nc("@title:tab","General"), Qt.resolvedUrl("Preferences/GeneralPage.qml"));
removePage(1);
insertPage(1, catalog.i18nc("@title:tab","Settings"), Qt.resolvedUrl("Preferences/SettingVisibilityPage.qml"));
insertPage(2, catalog.i18nc("@title:tab", "Printers"), Qt.resolvedUrl("Preferences/MachinesPage.qml"));
insertPage(3, catalog.i18nc("@title:tab", "Materials"), Qt.resolvedUrl("Preferences/MaterialsPage.qml"));
@ -459,7 +463,7 @@ UM.MainWindow
target: Cura.Actions.addProfile
onTriggered:
{
Cura.MachineManager.newQualityContainerFromQualityAndUser();
Cura.ContainerManager.createQualityChanges(null);
preferences.setPage(4);
preferences.show();

View file

@ -14,44 +14,10 @@ Menu
Instantiator
{
model: UM.InstanceContainersModel
{
filter:
{
var result = { "type": "material" }
if(Cura.MachineManager.filterMaterialsByMachine)
{
result.definition = Cura.MachineManager.activeDefinitionId
if(Cura.MachineManager.hasVariants)
{
result.variant = Cura.MachineManager.activeVariantId
}
}
else
{
result.definition = "fdmprinter"
}
return result
}
}
model: genericMaterialsModel
MenuItem
{
text:
{
var result = model.name
if(model.metadata.brand != undefined && model.metadata.brand != "Generic")
{
result = model.metadata.brand + " " + result
}
if(model.metadata.color_name != undefined && model.metadata.color_name != "Generic")
{
result = result + " (%1)".arg(model.metadata.color_name)
}
return result
}
text: model.name
checkable: true;
checked: model.id == Cura.MachineManager.activeMaterialId;
exclusiveGroup: group;
@ -63,10 +29,150 @@ Menu
onObjectAdded: menu.insertItem(index, object)
onObjectRemoved: menu.removeItem(object)
}
MenuSeparator { }
Instantiator
{
model: brandModel
Menu
{
id: brandMenu
title: brandName
property string brandName: model.name
property var brandMaterials: model.materials
Instantiator
{
model: brandMaterials
Menu
{
id: brandMaterialsMenu
title: materialName
property string materialName: model.name
property var brandMaterialColors: model.colors
Instantiator
{
model: brandMaterialColors
MenuItem
{
text: model.name
checkable: true;
checked: model.id == Cura.MachineManager.activeMaterialId;
exclusiveGroup: group;
onTriggered:
{
Cura.MachineManager.setActiveMaterial(model.id);
}
}
onObjectAdded: brandMaterialsMenu.insertItem(index, object)
onObjectRemoved: brandMaterialsMenu.removeItem(object)
}
}
onObjectAdded: brandMenu.insertItem(index, object)
onObjectRemoved: brandMenu.removeItem(object)
}
}
onObjectAdded: menu.insertItem(index, object)
onObjectRemoved: menu.removeItem(object)
}
ListModel
{
id: genericMaterialsModel
Component.onCompleted: populateMenuModels()
}
ListModel
{
id: brandModel
}
//: Model used to populate the brandModel
UM.InstanceContainersModel
{
id: materialsModel
filter: materialFilter()
onModelReset: populateMenuModels()
}
ExclusiveGroup { id: group }
MenuSeparator { }
MenuItem { action: Cura.Actions.manageMaterials }
function materialFilter()
{
var result = { "type": "material" };
if(Cura.MachineManager.filterMaterialsByMachine)
{
result.definition = Cura.MachineManager.activeDefinitionId;
if(Cura.MachineManager.hasVariants)
{
result.variant = Cura.MachineManager.activeVariantId;
}
}
else
{
result.definition = "fdmprinter";
}
return result;
}
function populateMenuModels()
{
// Create a structure of unique brands and their material-types
genericMaterialsModel.clear()
brandModel.clear();
var items = materialsModel.items;
var materialsByBrand = {};
for (var i in items) {
var brandName = items[i]["metadata"]["brand"];
var materialName = items[i]["metadata"]["material"];
if (brandName == "Generic")
{
// Add to top section
var materialId = items[i].id;
genericMaterialsModel.append({
id:materialId,
name:items[i].name
});
}
else
{
// Add to per-brand, per-material menu
if (!materialsByBrand.hasOwnProperty(brandName))
{
materialsByBrand[brandName] = {};
}
if (!materialsByBrand[brandName].hasOwnProperty(materialName))
{
materialsByBrand[brandName][materialName] = [];
}
materialsByBrand[brandName][materialName].push({
id: items[i].id,
name: items[i].name
});
}
}
for (var brand in materialsByBrand)
{
var materialsByBrandModel = [];
var materials = materialsByBrand[brand];
for (var material in materials)
{
materialsByBrandModel.push({
name: material,
colors: materials[material]
})
}
brandModel.append({
name: brand,
materials: materialsByBrandModel
});
}
}
}

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