Merge branch 'master' into feature_preheat_extruder

This commit is contained in:
fieldOfView 2018-01-17 17:36:40 +01:00
commit 6e0717a967
58 changed files with 806 additions and 328 deletions

View file

@ -73,7 +73,7 @@ class CuraActions(QObject):
# \param count The number of times to multiply the selection.
@pyqtSlot(int)
def multiplySelection(self, count: int) -> None:
job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, 8)
job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset = 8)
job.start()
## Delete all selected objects.

View file

@ -266,6 +266,7 @@ class CuraApplication(QtApplication):
self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity)
self.getController().toolOperationStopped.connect(self._onToolOperationStopped)
self.getController().contextMenuRequested.connect(self._onContextMenuRequested)
self.getCuraSceneController().activeBuildPlateChanged.connect(self.updatePlatformActivity)
Resources.addType(self.ResourceTypes.QmlFiles, "qml")
Resources.addType(self.ResourceTypes.Firmware, "firmware")
@ -298,6 +299,7 @@ class CuraApplication(QtApplication):
empty_quality_changes_container = copy.deepcopy(empty_container)
empty_quality_changes_container.setMetaDataEntry("id", "empty_quality_changes")
empty_quality_changes_container.addMetaDataEntry("type", "quality_changes")
empty_quality_changes_container.addMetaDataEntry("quality_type", "not_supported")
ContainerRegistry.getInstance().addContainer(empty_quality_changes_container)
with ContainerRegistry.getInstance().lockFile():
@ -646,10 +648,10 @@ class CuraApplication(QtApplication):
if parsed_args["help"]:
parser.print_help()
sys.exit(0)
def run(self):
self.preRun()
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Setting up scene..."))
self._setUpSingleInstanceServer()
@ -804,6 +806,7 @@ class CuraApplication(QtApplication):
qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type")
qmlRegisterType(InstanceContainer, "Cura", 1, 0, "InstanceContainer")
qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel")
qmlRegisterType(ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel")
qmlRegisterSingletonType(ProfilesModel, "Cura", 1, 0, "ProfilesModel", ProfilesModel.createProfilesModel)
@ -890,12 +893,18 @@ class CuraApplication(QtApplication):
def getSceneBoundingBoxString(self):
return self._i18n_catalog.i18nc("@info 'width', 'depth' and 'height' are variable names that must NOT be translated; just translate the format of ##x##x## mm.", "%(width).1f x %(depth).1f x %(height).1f mm") % {'width' : self._scene_bounding_box.width.item(), 'depth': self._scene_bounding_box.depth.item(), 'height' : self._scene_bounding_box.height.item()}
## Update scene bounding box for current build plate
def updatePlatformActivity(self, node = None):
count = 0
scene_bounding_box = None
is_block_slicing_node = False
active_build_plate = self.getBuildPlateModel().activeBuildPlate
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not issubclass(type(node), SceneNode) or (not node.getMeshData() and not node.callDecoration("getLayerData")):
if (
not issubclass(type(node), CuraSceneNode) or
(not node.getMeshData() and not node.callDecoration("getLayerData")) or
(node.callDecoration("getBuildPlateNumber") != active_build_plate)):
continue
if node.callDecoration("isBlockSlicing"):
is_block_slicing_node = True
@ -915,7 +924,7 @@ class CuraApplication(QtApplication):
if not scene_bounding_box:
scene_bounding_box = AxisAlignedBox.Null
if repr(self._scene_bounding_box) != repr(scene_bounding_box) and scene_bounding_box.isValid():
if repr(self._scene_bounding_box) != repr(scene_bounding_box):
self._scene_bounding_box = scene_bounding_box
self.sceneBoundingBoxChanged.emit()
@ -1012,7 +1021,7 @@ class CuraApplication(QtApplication):
Selection.clear()
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not issubclass(type(node), SceneNode):
if not isinstance(node, SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@ -1034,10 +1043,12 @@ class CuraApplication(QtApplication):
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) not in {SceneNode, CuraSceneNode}:
if not isinstance(node, SceneNode):
continue
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if not node.isSelectable():
continue # Only remove nodes that are selectable.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
nodes.append(node)
@ -1050,7 +1061,12 @@ class CuraApplication(QtApplication):
op.push()
Selection.clear()
self.getCuraSceneController().setActiveBuildPlate(0) # Select first build plate
# Reset the print information:
self.getController().getScene().sceneChanged.emit(node)
# self._print_information.setToZeroPrintInformation(self.getBuildPlateModel().activeBuildPlate)
# stay on the same build plate
#self.getCuraSceneController().setActiveBuildPlate(0) # Select first build plate
## Reset all translation on nodes with mesh data.
@pyqtSlot()
@ -1058,7 +1074,7 @@ class CuraApplication(QtApplication):
Logger.log("i", "Resetting all scene translations")
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not issubclass(type(node), SceneNode):
if not isinstance(node, SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@ -1086,7 +1102,7 @@ class CuraApplication(QtApplication):
Logger.log("i", "Resetting all scene transformations")
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not issubclass(type(node), SceneNode):
if not isinstance(node, SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@ -1113,7 +1129,7 @@ class CuraApplication(QtApplication):
def arrangeObjectsToAllBuildPlates(self):
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not issubclass(type(node), SceneNode):
if not isinstance(node, SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@ -1134,7 +1150,7 @@ class CuraApplication(QtApplication):
nodes = []
active_build_plate = self.getBuildPlateModel().activeBuildPlate
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not issubclass(type(node), SceneNode):
if not isinstance(node, SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@ -1158,7 +1174,7 @@ class CuraApplication(QtApplication):
# What nodes are on the build plate and are not being moved
fixed_nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not issubclass(type(node), SceneNode):
if not isinstance(node, SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@ -1186,7 +1202,7 @@ class CuraApplication(QtApplication):
Logger.log("i", "Reloading all loaded mesh data.")
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not issubclass(type(node), SceneNode) or not node.getMeshData():
if not isinstance(node, SceneNode) or not node.getMeshData():
continue
nodes.append(node)
@ -1421,16 +1437,20 @@ class CuraApplication(QtApplication):
filename = job.getFileName()
self._currently_loading_files.remove(filename)
root = self.getController().getScene().getRoot()
arranger = Arrange.create(scene_root = root)
min_offset = 8
self.fileLoaded.emit(filename)
arrange_objects_on_load = (
not Preferences.getInstance().getValue("cura/use_multi_build_plate") or
not Preferences.getInstance().getValue("cura/not_arrange_objects_on_load"))
target_build_plate = self.getBuildPlateModel().activeBuildPlate if arrange_objects_on_load else -1
root = self.getController().getScene().getRoot()
fixed_nodes = []
for node_ in DepthFirstIterator(root):
if node_.callDecoration("isSliceable") and node_.callDecoration("getBuildPlateNumber") == target_build_plate:
fixed_nodes.append(node_)
arranger = Arrange.create(fixed_nodes = fixed_nodes)
min_offset = 8
for original_node in nodes:
# Create a CuraSceneNode just if the original node is not that type
@ -1442,11 +1462,7 @@ class CuraApplication(QtApplication):
extension = os.path.splitext(filename)[1]
if extension.lower() in self._non_sliceable_extensions:
self.getController().setActiveView("SimulationView")
view = self.getController().getActiveView()
view.resetLayerData()
view.setLayer(9999999)
view.calculateMaxLayers()
self.callLater(lambda: self.getController().setActiveView("SimulationView"))
block_slicing_decorator = BlockSlicingDecorator()
node.addDecorator(block_slicing_decorator)
@ -1500,12 +1516,11 @@ class CuraApplication(QtApplication):
"""
Checks if the given file URL is a valid project file.
"""
file_path = QUrl(file_url).toLocalFile()
workspace_reader = self.getWorkspaceFileHandler().getReaderForFile(file_path)
if workspace_reader is None:
return False # non-project files won't get a reader
try:
file_path = QUrl(file_url).toLocalFile()
workspace_reader = self.getWorkspaceFileHandler().getReaderForFile(file_path)
if workspace_reader is None:
return False # non-project files won't get a reader
result = workspace_reader.preRead(file_path, show_dialog=False)
return result == WorkspaceReader.PreReadResult.accepted
except Exception as e:

View file

@ -28,7 +28,7 @@ class ObjectsModel(ListModel):
active_build_plate_number = self._build_plate_number
group_nr = 1
for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
if not issubclass(type(node), SceneNode):
if not isinstance(node, SceneNode):
continue
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
continue

View file

@ -61,7 +61,7 @@ class PlatformPhysics:
random.shuffle(nodes)
for node in nodes:
if node is root or not issubclass(type(node), SceneNode) or node.getBoundingBox() is None:
if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None:
continue
bbox = node.getBoundingBox()

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
@ -65,6 +65,7 @@ class PrintInformation(QObject):
self._backend = Application.getInstance().getBackend()
if self._backend:
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
Application.getInstance().getController().getScene().sceneChanged.connect(self.setToZeroPrintInformation)
self._base_name = ""
self._abbr_machine = ""
@ -171,7 +172,7 @@ class PrintInformation(QObject):
def printTimes(self):
return self._print_time_message_values[self._active_build_plate]
def _onPrintDurationMessage(self, build_plate_number, print_time, material_amounts):
def _onPrintDurationMessage(self, build_plate_number, print_time: Dict[str, int], material_amounts: list):
self._updateTotalPrintTimePerFeature(build_plate_number, print_time)
self.currentPrintTimeChanged.emit()

View file

@ -41,6 +41,9 @@ class PrinterOutputDevice(QObject, OutputDevice):
# # Signal to indicate that the hotend of the active printer on the remote changed.
hotendIdChanged = pyqtSignal()
# Signal to indicate that the info text about the connection has changed.
connectionTextChanged = pyqtSignal()
def __init__(self, device_id, parent = None):
super().__init__(device_id = device_id, parent = parent)
@ -65,11 +68,21 @@ class PrinterOutputDevice(QObject, OutputDevice):
self._connection_state = ConnectionState.closed
self._address = ""
self._connection_text = ""
@pyqtProperty(str, constant = True)
@pyqtProperty(str, notify = connectionTextChanged)
def address(self):
return self._address
def setConnectionText(self, connection_text):
if self._connection_text != connection_text:
self._connection_text = connection_text
self.connectionTextChanged.emit()
@pyqtProperty(str, constant=True)
def connectionText(self):
return self._connection_text
def materialHotendChangedMessage(self, callback):
Logger.log("w", "materialHotendChangedMessage needs to be implemented, returning 'Yes'")
callback(QMessageBox.Yes)
@ -122,7 +135,6 @@ class PrinterOutputDevice(QObject, OutputDevice):
def controlItem(self):
if not self._control_component:
self._createControlViewFromQML()
return self._control_item
def _createControlViewFromQML(self):

View file

@ -13,7 +13,7 @@ class BuildPlateDecorator(SceneNodeDecorator):
# Make sure that groups are set correctly
# setBuildPlateForSelection in CuraActions makes sure that no single childs are set.
self._build_plate_number = nr
if issubclass(type(self._node), CuraSceneNode):
if isinstance(self._node, CuraSceneNode):
self._node.transformChanged() # trigger refresh node without introducing a new signal
if self._node and self._node.callDecoration("isGroup"):
for child in self._node.getChildren():

View file

@ -65,7 +65,7 @@ class ConvexHullNode(SceneNode):
ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
if self.getParent():
if self.getMeshData() and issubclass(type(self._node), SceneNode) and self._node.callDecoration("getBuildPlateNumber") == Application.getInstance().getBuildPlateModel().activeBuildPlate:
if self.getMeshData() and isinstance(self._node, SceneNode) and self._node.callDecoration("getBuildPlateNumber") == Application.getInstance().getBuildPlateModel().activeBuildPlate:
renderer.queueNode(self, transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8)
if self._convex_hull_head_mesh:
renderer.queueNode(self, shader = ConvexHullNode.shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8)

View file

@ -10,9 +10,12 @@ from UM.Application import Application
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Scene.SceneNode import SceneNode
from UM.Scene.Selection import Selection
from UM.Signal import Signal
class CuraSceneController(QObject):
activeBuildPlateChanged = Signal()
def __init__(self, objects_model: ObjectsModel, build_plate_model: BuildPlateModel):
super().__init__()
@ -30,7 +33,7 @@ class CuraSceneController(QObject):
source = args[0]
else:
source = None
if not issubclass(type(source), SceneNode):
if not isinstance(source, SceneNode):
return
max_build_plate = self._calcMaxBuildPlate()
changed = False
@ -101,6 +104,7 @@ class CuraSceneController(QObject):
self._build_plate_model.setActiveBuildPlate(nr)
self._objects_model.setActiveBuildPlate(nr)
self.activeBuildPlateChanged.emit()
@staticmethod
def createCuraSceneController():

View file

@ -94,7 +94,7 @@ class CuraContainerRegistry(ContainerRegistry):
def _containerExists(self, container_type, container_name):
container_class = ContainerStack if container_type == "machine" else InstanceContainer
return self.findContainersMetadata(id = container_name, type = container_type, ignore_case = True) or \
return self.findContainersMetadata(container_type = container_class, id = container_name, type = container_type, ignore_case = True) or \
self.findContainersMetadata(container_type = container_class, name = container_name, type = container_type)
## Exports an profile to a file

View file

@ -144,7 +144,7 @@ class CuraContainerStack(ContainerStack):
## Set the material container.
#
# \param new_quality_changes The new material container. It is expected to have a "type" metadata entry with the value "quality_changes".
# \param new_material The new material container. It is expected to have a "type" metadata entry with the value "material".
def setMaterial(self, new_material: InstanceContainer, postpone_emit = False) -> None:
self.replaceContainer(_ContainerIndexes.Material, new_material, postpone_emit = postpone_emit)
@ -155,7 +155,7 @@ class CuraContainerStack(ContainerStack):
# to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultMaterial
# for details.
#
# \param new_quality_changes_id The ID of the new material container.
# \param new_material_id The ID of the new material container.
#
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
def setMaterialById(self, new_material_id: str) -> None:
@ -182,7 +182,7 @@ class CuraContainerStack(ContainerStack):
## Set the variant container.
#
# \param new_quality_changes The new variant container. It is expected to have a "type" metadata entry with the value "quality_changes".
# \param new_variant The new variant container. It is expected to have a "type" metadata entry with the value "variant".
def setVariant(self, new_variant: InstanceContainer) -> None:
self.replaceContainer(_ContainerIndexes.Variant, new_variant)
@ -193,13 +193,13 @@ class CuraContainerStack(ContainerStack):
# to whatever the machine definition specifies as "preferred" container, or a fallback value. See findDefaultVariant
# for details.
#
# \param new_quality_changes_id The ID of the new variant container.
# \param new_variant_id The ID of the new variant container.
#
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
def setVariantById(self, new_variant_id: str) -> None:
variant = self._empty_variant
if new_variant_id == "default":
new_variant = self.findDefaultVariant()
new_variant = self.findDefaultVariantBuildplate() if self.getMetaDataEntry("type") == "machine" else self.findDefaultVariant()
if new_variant:
variant = new_variant
else:
@ -220,13 +220,13 @@ class CuraContainerStack(ContainerStack):
## Set the definition changes container.
#
# \param new_quality_changes The new definition changes container. It is expected to have a "type" metadata entry with the value "quality_changes".
# \param new_definition_changes The new definition changes container. It is expected to have a "type" metadata entry with the value "definition_changes".
def setDefinitionChanges(self, new_definition_changes: InstanceContainer) -> None:
self.replaceContainer(_ContainerIndexes.DefinitionChanges, new_definition_changes)
## Set the definition changes container by an ID.
#
# \param new_quality_changes_id The ID of the new definition changes container.
# \param new_definition_changes_id The ID of the new definition changes container.
#
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
def setDefinitionChangesById(self, new_definition_changes_id: str) -> None:
@ -245,13 +245,13 @@ class CuraContainerStack(ContainerStack):
## Set the definition container.
#
# \param new_quality_changes The new definition container. It is expected to have a "type" metadata entry with the value "quality_changes".
# \param new_definition The new definition container. It is expected to have a "type" metadata entry with the value "definition".
def setDefinition(self, new_definition: DefinitionContainerInterface) -> None:
self.replaceContainer(_ContainerIndexes.Definition, new_definition)
## Set the definition container by an ID.
#
# \param new_quality_changes_id The ID of the new definition container.
# \param new_definition_id The ID of the new definition container.
#
# \throws Exceptions.InvalidContainerError Raised when no container could be found with the specified ID.
def setDefinitionById(self, new_definition_id: str) -> None:
@ -435,6 +435,51 @@ class CuraContainerStack(ContainerStack):
Logger.log("w", "Could not find a valid default variant for stack {stack}", stack = self.id)
return None
## Find the global variant that should be used as "default". This is used for the buildplates.
#
# This will search for variants that match the current definition and pick the preferred one,
# if specified by the machine definition.
#
# The following criteria are used to find the default global variant:
# - If the machine definition does not have a metadata entry "has_variant_buildplates" set to True, return None
# - The definition of the variant should be the same as the machine definition for this stack.
# - The container should have a metadata entry "type" with value "variant" and "hardware_type" with value "buildplate".
# - If the machine definition has a metadata entry "preferred_variant_buildplate", filter the variant IDs based on that.
#
# \return The container that should be used as default, or None if nothing was found or the machine does not use variants.
#
# \note This method assumes the stack has a valid machine definition.
def findDefaultVariantBuildplate(self) -> Optional[ContainerInterface]:
definition = self._getMachineDefinition()
# has_variant_buildplates can be overridden in other containers and stacks.
# In the case of UM2, it is overridden in the GlobalStack
if not self.getMetaDataEntry("has_variant_buildplates"):
# If the machine does not use variants, we should never set a variant.
return None
# First add any variant. Later, overwrite with preference if the preference is valid.
variant = None
definition_id = self._findInstanceContainerDefinitionId(definition)
variants = ContainerRegistry.getInstance().findInstanceContainers(definition = definition_id, type = "variant", hardware_type = "buildplate")
if variants:
variant = variants[0]
preferred_variant_buildplate_id = definition.getMetaDataEntry("preferred_variant_buildplate")
if preferred_variant_buildplate_id:
preferred_variant_buildplates = ContainerRegistry.getInstance().findInstanceContainers(id = preferred_variant_buildplate_id, definition = definition_id, type = "variant")
if preferred_variant_buildplates:
variant = preferred_variant_buildplates[0]
else:
Logger.log("w", "The preferred variant buildplate \"{variant}\" of stack {stack} does not exist or is not a variant.",
variant = preferred_variant_buildplate_id, stack = self.id)
# And leave it at the default variant.
if variant:
return variant
Logger.log("w", "Could not find a valid default buildplate variant for stack {stack}", stack = self.id)
return None
## Find the material that should be used as "default" material.
#
# This will search for materials that match the current definition and pick the preferred one,

View file

@ -49,6 +49,9 @@ class ExtruderManager(QObject):
## Notify when the user switches the currently active extruder.
activeExtruderChanged = pyqtSignal()
## The signal notifies subscribers if extruders are added
extrudersAdded = pyqtSignal()
## Gets the unique identifier of the currently active extruder stack.
#
# The currently active extruder stack is the stack that is currently being
@ -406,6 +409,7 @@ class ExtruderManager(QObject):
if extruders_changed:
self.extrudersChanged.emit(global_stack_id)
self.extrudersAdded.emit()
self.setActiveExtruderIndex(0)
## Get all extruder values for a certain setting.

View file

@ -50,6 +50,7 @@ class MachineManager(QObject):
# Used to store the new containers until after confirming the dialog
self._new_variant_container = None
self._new_buildplate_container = None
self._new_material_container = None
self._new_quality_containers = []
@ -157,6 +158,10 @@ class MachineManager(QObject):
def newVariant(self):
return self._new_variant_container
@property
def newBuildplate(self):
return self._new_buildplate_container
@property
def newMaterial(self):
return self._new_material_container
@ -309,10 +314,11 @@ class MachineManager(QObject):
self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
self._global_container_stack.propertyChanged.connect(self._onPropertyChanged)
# set the global variant to empty as we now use the extruder stack at all times - CURA-4482
# Global stack can have only a variant if it is a buildplate
global_variant = self._global_container_stack.variant
if global_variant != self._empty_variant_container:
self._global_container_stack.setVariant(self._empty_variant_container)
if global_variant.getMetaDataEntry("hardware_type") != "buildplate":
self._global_container_stack.setVariant(self._empty_variant_container)
# set the global material to empty as we now use the extruder stack at all times - CURA-4482
global_material = self._global_container_stack.material
@ -675,6 +681,14 @@ class MachineManager(QObject):
return quality.getId()
return ""
@pyqtProperty(str, notify=activeVariantChanged)
def globalVariantId(self) -> str:
if self._global_container_stack:
variant = self._global_container_stack.variant
if variant and not isinstance(variant, type(self._empty_variant_container)):
return variant.getId()
return ""
@pyqtProperty(str, notify = activeQualityChanged)
def activeQualityType(self) -> str:
if self._active_container_stack:
@ -855,6 +869,24 @@ class MachineManager(QObject):
else:
Logger.log("w", "While trying to set the active variant, no variant was found to replace.")
@pyqtSlot(str)
def setActiveVariantBuildplate(self, variant_buildplate_id: str):
with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue):
containers = ContainerRegistry.getInstance().findInstanceContainers(id = variant_buildplate_id)
if not containers or not self._global_container_stack:
return
Logger.log("d", "Attempting to change the active buildplate to %s", variant_buildplate_id)
old_buildplate = self._global_container_stack.variant
if old_buildplate:
self.blurSettings.emit()
self._new_buildplate_container = containers[0] # self._active_container_stack will be updated with a delay
Logger.log("d", "Active buildplate changed to {active_variant_buildplate_id}".format(active_variant_buildplate_id = containers[0].getId()))
# Force set the active quality as it is so the values are updated
self.setActiveMaterial(self._active_container_stack.material.getId())
else:
Logger.log("w", "While trying to set the active buildplate, no buildplate was found to replace.")
## set the active quality
# \param quality_id The quality_id of either a quality or a quality_changes
@pyqtSlot(str)
@ -932,6 +964,10 @@ class MachineManager(QObject):
self._active_container_stack.variant = self._new_variant_container
self._new_variant_container = None
if self._new_buildplate_container is not None:
self._global_container_stack.variant = self._new_buildplate_container
self._new_buildplate_container = None
if self._new_material_container is not None:
self._active_container_stack.material = self._new_material_container
self._new_material_container = None
@ -952,6 +988,7 @@ class MachineManager(QObject):
# Used for ignoring any changes when switching between printers (setActiveMachine)
def _cancelDelayedActiveContainerStackChanges(self):
self._new_material_container = None
self._new_buildplate_container = None
self._new_variant_container = None
## Determine the quality and quality changes settings for the current machine for a quality name.
@ -1116,6 +1153,15 @@ class MachineManager(QObject):
return ""
@pyqtProperty(str, notify = activeVariantChanged)
def activeVariantBuildplateName(self) -> str:
if self._global_container_stack:
variant = self._global_container_stack.variant
if variant:
return variant.getName()
return ""
@pyqtProperty(str, notify = globalContainerChanged)
def activeDefinitionId(self) -> str:
if self._global_container_stack:
@ -1213,7 +1259,6 @@ class MachineManager(QObject):
def hasMaterials(self) -> bool:
if self._global_container_stack:
return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False))
return False
@pyqtProperty(bool, notify = globalContainerChanged)
@ -1222,6 +1267,53 @@ class MachineManager(QObject):
return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_variants", False))
return False
@pyqtProperty(bool, notify = globalContainerChanged)
def hasVariantBuildplates(self) -> bool:
if self._global_container_stack:
return Util.parseBool(self._global_container_stack.getMetaDataEntry("has_variant_buildplates", False))
return False
## The selected buildplate is compatible if it is compatible with all the materials in all the extruders
@pyqtProperty(bool, notify = activeMaterialChanged)
def variantBuildplateCompatible(self) -> bool:
if not self._global_container_stack:
return True
buildplate_compatible = True # It is compatible by default
extruder_stacks = self._global_container_stack.extruders.values()
for stack in extruder_stacks:
material_container = stack.material
if material_container == self._empty_material_container:
continue
if material_container.getMetaDataEntry("buildplate_compatible"):
buildplate_compatible = buildplate_compatible and material_container.getMetaDataEntry("buildplate_compatible")[self.activeVariantBuildplateName]
return buildplate_compatible
## The selected buildplate is usable if it is usable for all materials OR it is compatible for one but not compatible
# for the other material but the buildplate is still usable
@pyqtProperty(bool, notify = activeMaterialChanged)
def variantBuildplateUsable(self) -> bool:
if not self._global_container_stack:
return True
# Here the next formula is being calculated:
# result = (not (material_left_compatible and material_right_compatible)) and
# (material_left_compatible or material_left_usable) and
# (material_right_compatible or material_right_usable)
result = not self.variantBuildplateCompatible
extruder_stacks = self._global_container_stack.extruders.values()
for stack in extruder_stacks:
material_container = stack.material
if material_container == self._empty_material_container:
continue
buildplate_compatible = material_container.getMetaDataEntry("buildplate_compatible")[self.activeVariantBuildplateName] if material_container.getMetaDataEntry("buildplate_compatible") else True
buildplate_usable = material_container.getMetaDataEntry("buildplate_recommended")[self.activeVariantBuildplateName] if material_container.getMetaDataEntry("buildplate_recommended") else True
result = result and (buildplate_compatible or buildplate_usable)
return result
## Property to indicate if a machine has "specialized" material profiles.
# Some machines have their own material profiles that "override" the default catch all profiles.
@pyqtProperty(bool, notify = globalContainerChanged)

View file

@ -36,6 +36,8 @@ class ProfilesModel(InstanceContainersModel):
Application.getInstance().getMachineManager().activeStackChanged.connect(self._update)
Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._update)
self._empty_quality = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0]
# Factory function, used by QML
@staticmethod
def createProfilesModel(engine, js_engine):
@ -85,13 +87,10 @@ class ProfilesModel(InstanceContainersModel):
if quality.getMetaDataEntry("quality_type") not in quality_type_set:
result.append(quality)
# if still profiles are found, add a single empty_quality ("Not supported") instance to the drop down list
if len(result) == 0:
# If not qualities are found we dynamically create a not supported container for this machine + material combination
not_supported_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0]
result.append(not_supported_container)
if len(result) > 1 and self._empty_quality in result:
result.remove(self._empty_quality)
return {item.getId():item for item in result}, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.
return {item.getId(): item for item in result}, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.
## Re-computes the items in this model, and adds the layer height role.
def _recomputeItems(self):
@ -114,7 +113,6 @@ class ProfilesModel(InstanceContainersModel):
# active machine and material, and later yield the right ones.
tmp_all_quality_items = OrderedDict()
for item in super()._recomputeItems():
profiles = container_registry.findContainersMetadata(id = item["id"])
if not profiles or "quality_type" not in profiles[0]:
quality_type = ""

View file

@ -1,17 +1,21 @@
# Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Application import Application
from UM.Settings.ContainerRegistry import ContainerRegistry
from cura.QualityManager import QualityManager
from cura.Settings.ProfilesModel import ProfilesModel
from cura.Settings.ExtruderManager import ExtruderManager
## QML Model for listing the current list of valid quality and quality changes profiles.
#
class QualityAndUserProfilesModel(ProfilesModel):
def __init__(self, parent = None):
super().__init__(parent)
self._empty_quality = ContainerRegistry.getInstance().findInstanceContainers(id = "empty_quality")[0]
## Fetch the list of containers to display.
#
# See UM.Settings.Models.InstanceContainersModel._fetchInstanceContainers().
@ -35,12 +39,16 @@ class QualityAndUserProfilesModel(ProfilesModel):
# Filter the quality_change by the list of available quality_types
quality_type_set = set([x.getMetaDataEntry("quality_type") for x in quality_list])
filtered_quality_changes = {qc.getId():qc for qc in quality_changes_list if
# Also show custom profiles based on "Not Supported" quality profile
quality_type_set.add(self._empty_quality.getMetaDataEntry("quality_type"))
filtered_quality_changes = {qc.getId(): qc for qc in quality_changes_list if
qc.getMetaDataEntry("quality_type") in quality_type_set and
qc.getMetaDataEntry("extruder") is not None and
(qc.getMetaDataEntry("extruder") == active_extruder.definition.getMetaDataEntry("quality_definition") or
qc.getMetaDataEntry("extruder") == active_extruder.definition.getId())}
result = filtered_quality_changes
result.update({q.getId():q for q in quality_list})
return result, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.
for q in quality_list:
if q.getId() != "empty_quality":
result[q.getId()] = q
return result, {} #Only return true profiles for now, no metadata. The quality manager is not able to get only metadata yet.

View file

@ -1,8 +1,6 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import collections
from PyQt5.QtCore import pyqtProperty, pyqtSignal, Qt
from UM.Logger import Logger
@ -42,6 +40,8 @@ class QualitySettingsModel(UM.Qt.ListModel.ListModel):
self.addRoleName(self.UserValueRole, "user_value")
self.addRoleName(self.CategoryRole, "category")
self._empty_quality = self._container_registry.findInstanceContainers(id = "empty_quality")[0]
def setExtruderId(self, extruder_id):
if extruder_id != self._extruder_id:
self._extruder_id = extruder_id
@ -107,77 +107,87 @@ class QualitySettingsModel(UM.Qt.ListModel.ListModel):
else:
quality_changes_container = containers[0]
criteria = {
"type": "quality",
"quality_type": quality_changes_container.getMetaDataEntry("quality_type"),
"definition": quality_changes_container.getDefinition().getId()
}
if quality_changes_container.getMetaDataEntry("quality_type") == self._empty_quality.getMetaDataEntry("quality_type"):
quality_container = self._empty_quality
else:
criteria = {
"type": "quality",
"quality_type": quality_changes_container.getMetaDataEntry("quality_type"),
"definition": quality_changes_container.getDefinition().getId()
}
quality_container = self._container_registry.findInstanceContainers(**criteria)
if not quality_container:
Logger.log("w", "Could not find a quality container matching quality changes %s", quality_changes_container.getId())
return
quality_container = quality_container[0]
quality_container = self._container_registry.findInstanceContainers(**criteria)
if not quality_container:
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 = Application.getInstance().getMachineManager().getQualityDefinitionId(quality_container.getDefinition())
definition = quality_container.getDefinition()
# Check if the definition container has a translation file.
definition_suffix = ContainerRegistry.getMimeTypeForContainer(type(definition)).preferredSuffix
catalog = i18nCatalog(os.path.basename(definition_id + "." + definition_suffix))
if catalog.hasTranslationLoaded():
self._i18n_catalog = catalog
if quality_type == "not_supported":
containers = []
else:
definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(quality_container.getDefinition())
definition = quality_container.getDefinition()
for file_name in quality_container.getDefinition().getInheritedFiles():
catalog = i18nCatalog(os.path.basename(file_name))
# Check if the definition container has a translation file.
definition_suffix = ContainerRegistry.getMimeTypeForContainer(type(definition)).preferredSuffix
catalog = i18nCatalog(os.path.basename(definition_id + "." + definition_suffix))
if catalog.hasTranslationLoaded():
self._i18n_catalog = catalog
criteria = {"type": "quality", "quality_type": quality_type, "definition": definition_id}
for file_name in quality_container.getDefinition().getInheritedFiles():
catalog = i18nCatalog(os.path.basename(file_name))
if catalog.hasTranslationLoaded():
self._i18n_catalog = catalog
if self._material_id and self._material_id != "empty_material":
criteria["material"] = self._material_id
criteria = {"type": "quality", "quality_type": quality_type, "definition": definition_id}
criteria["extruder"] = self._extruder_id
if self._material_id and self._material_id != "empty_material":
criteria["material"] = self._material_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)
criteria["extruder"] = self._extruder_id
if not containers and "material" in criteria:
# Try again, this time without material
criteria.pop("material", None)
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 or extruder
criteria.pop("extruder") # "material" has already been popped
containers = self._container_registry.findInstanceContainers(**criteria)
if not containers and "material" in criteria:
# Try again, this time without material
criteria.pop("material", None)
containers = self._container_registry.findInstanceContainers(**criteria)
if not containers:
Logger.log("w", "Could not find any quality containers matching the search criteria %s" % str(criteria))
return
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:
Logger.log("w", "Could not find any quality containers matching the search criteria %s" % str(criteria))
return
if quality_changes_container:
criteria = {"type": "quality_changes", "quality_type": quality_type, "definition": definition_id, "name": quality_changes_container.getName()}
if self._extruder_definition_id != "":
extruder_definitions = self._container_registry.findDefinitionContainers(id = self._extruder_definition_id)
if extruder_definitions:
criteria["extruder"] = Application.getInstance().getMachineManager().getQualityDefinitionId(extruder_definitions[0])
criteria["name"] = quality_changes_container.getName()
if quality_type == "not_supported":
criteria = {"type": "quality_changes", "quality_type": quality_type, "name": quality_changes_container.getName()}
else:
criteria["extruder"] = None
criteria = {"type": "quality_changes", "quality_type": quality_type, "definition": definition_id, "name": quality_changes_container.getName()}
if self._extruder_definition_id != "":
extruder_definitions = self._container_registry.findDefinitionContainers(id = self._extruder_definition_id)
if extruder_definitions:
criteria["extruder"] = Application.getInstance().getMachineManager().getQualityDefinitionId(extruder_definitions[0])
criteria["name"] = quality_changes_container.getName()
else:
criteria["extruder"] = None
changes = self._container_registry.findInstanceContainers(**criteria)
if changes:
containers.extend(changes)
global_container_stack = Application.getInstance().getGlobalContainerStack()
is_multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
current_category = ""
for definition in definition_container.findDefinitions():
@ -213,15 +223,14 @@ class QualitySettingsModel(UM.Qt.ListModel.ListModel):
if profile_value is None and user_value is None:
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
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
# 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
label = definition.label
if self._i18n_catalog:

View file

@ -2,6 +2,8 @@
# Cura is released under the terms of the LGPLv3 or higher.
from UM.Application import Application
from UM.Settings.ContainerRegistry import ContainerRegistry
from cura.QualityManager import QualityManager
from cura.Settings.ProfilesModel import ProfilesModel
from cura.Settings.ExtruderManager import ExtruderManager
@ -22,6 +24,8 @@ class UserProfilesModel(ProfilesModel):
for material in self.__current_materials:
material.metaDataChanged.connect(self._onContainerChanged)
self._empty_quality = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0]
## Fetch the list of containers to display.
#
# See UM.Settings.Models.InstanceContainersModel._fetchInstanceContainers().
@ -45,6 +49,7 @@ class UserProfilesModel(ProfilesModel):
# Filter the quality_change by the list of available quality_types
quality_type_set = set([x.getMetaDataEntry("quality_type") for x in quality_list])
quality_type_set.add(self._empty_quality.getMetaDataEntry("quality_type"))
filtered_quality_changes = {qc.getId():qc for qc in quality_changes_list if
qc.getMetaDataEntry("quality_type") in quality_type_set and

View file

@ -81,8 +81,10 @@ class ThreeMFReader(MeshReader):
self._object_count += 1
node_name = "Object %s" % self._object_count
active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate
um_node = CuraSceneNode()
um_node.addDecorator(BuildPlateDecorator(0))
um_node.addDecorator(BuildPlateDecorator(active_build_plate))
um_node.setName(node_name)
transformation = self._createMatrixFromTransformationString(savitar_node.getTransformation())
um_node.setTransformation(transformation)

View file

@ -168,11 +168,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
Logger.log("w", "Unknown definition container type %s for %s",
definition_container_type, each_definition_container_file)
Job.yieldThread()
# sanity check
if machine_definition_container_count != 1:
msg = "Expecting one machine definition container but got %s" % machine_definition_container_count
Logger.log("e", msg)
raise RuntimeError(msg)
return WorkspaceReader.PreReadResult.failed #Not a workspace file but ordinary 3MF.
material_labels = []
material_conflict = False
@ -271,7 +269,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
# if the global stack is found, we check if there are conflicts in the extruder stacks
if containers_found_dict["machine"] and not machine_conflict:
for extruder_stack_file in extruder_stack_files:
container_id = self._stripFileToId(extruder_stack_file)
serialized = archive.open(extruder_stack_file).read().decode("utf-8")
parser = configparser.ConfigParser()
parser.read_string(serialized)
@ -303,7 +300,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
break
num_visible_settings = 0
has_visible_settings_string = False
try:
temp_preferences = Preferences()
serialized = archive.open("Cura/preferences.cfg").read().decode("utf-8")
@ -609,7 +605,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
instance_container.setName(self._container_registry.uniqueName(instance_container.getName()))
new_changes_container_id = self.getNewId(instance_container.getId())
instance_container._id = new_changes_container_id
instance_container.setMetaDataEntry("id", new_changes_container_id)
# TODO: we don't know the following is correct or not, need to verify
# AND REFACTOR!!!

View file

@ -6,8 +6,9 @@ from UM.Math.Vector import Vector
from UM.Logger import Logger
from UM.Math.Matrix import Matrix
from UM.Application import Application
import UM.Scene.SceneNode
from cura.Scene.CuraSceneNode import CuraSceneNode
from UM.Scene.SceneNode import SceneNode
from cura.CuraApplication import CuraApplication
import Savitar
@ -62,11 +63,15 @@ class ThreeMFWriter(MeshWriter):
self._store_archive = store_archive
## Convenience function that converts an Uranium SceneNode object to a SavitarSceneNode
# \returns Uranium Scenen node.
# \returns Uranium Scene node.
def _convertUMNodeToSavitarNode(self, um_node, transformation = Matrix()):
if type(um_node) not in [UM.Scene.SceneNode.SceneNode, CuraSceneNode]:
if not isinstance(um_node, SceneNode):
return None
active_build_plate_nr = CuraApplication.getInstance().getBuildPlateModel().activeBuildPlate
if um_node.callDecoration("getBuildPlateNumber") != active_build_plate_nr:
return
savitar_node = Savitar.SceneNode()
node_matrix = um_node.getLocalTransformation()
@ -97,6 +102,9 @@ class ThreeMFWriter(MeshWriter):
savitar_node.setSetting(key, str(stack.getProperty(key, "value")))
for child_node in um_node.getChildren():
# only save the nodes on the active build plate
if child_node.callDecoration("getBuildPlateNumber") != active_build_plate_nr:
continue
savitar_child_node = self._convertUMNodeToSavitarNode(child_node)
if savitar_child_node is not None:
savitar_node.addChild(savitar_child_node)

View file

@ -88,7 +88,7 @@ class CuraEngineBackend(QObject, Backend):
#
self._global_container_stack = None
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
Application.getInstance().getExtruderManager().activeExtruderChanged.connect(self._onGlobalStackChanged)
Application.getInstance().getExtruderManager().extrudersAdded.connect(self._onGlobalStackChanged)
self._onGlobalStackChanged()
Application.getInstance().stacksValidationFinished.connect(self._onStackErrorCheckFinished)
@ -423,7 +423,7 @@ class CuraEngineBackend(QObject, Backend):
#
# \param source The scene node that was changed.
def _onSceneChanged(self, source):
if not issubclass(type(source), SceneNode):
if not isinstance(source, SceneNode):
return
build_plate_changed = set()
@ -722,7 +722,7 @@ class CuraEngineBackend(QObject, Backend):
if self._global_container_stack:
self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged)
self._global_container_stack.containersChanged.disconnect(self._onChanged)
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
extruders = list(self._global_container_stack.extruders.values())
for extruder in extruders:
extruder.propertyChanged.disconnect(self._onSettingChanged)
@ -733,7 +733,7 @@ class CuraEngineBackend(QObject, Backend):
if self._global_container_stack:
self._global_container_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed.
self._global_container_stack.containersChanged.connect(self._onChanged)
extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
extruders = list(self._global_container_stack.extruders.values())
for extruder in extruders:
extruder.propertyChanged.connect(self._onSettingChanged)
extruder.containersChanged.connect(self._onChanged)

View file

@ -122,6 +122,12 @@ class StartSliceJob(Job):
self.setResult(StartJobResult.BuildPlateError)
return
# Don't slice if the buildplate or the nozzle type is incompatible with the materials
if not Application.getInstance().getMachineManager().variantBuildplateCompatible and \
not Application.getInstance().getMachineManager().variantBuildplateUsable:
self.setResult(StartJobResult.MaterialIncompatible)
return
for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
material = extruder_stack.findContainer({"type": "material"})
if material:

View file

@ -225,7 +225,10 @@ class MachineSettingsAction(MachineAction):
material_approximate_diameter = str(round(material_diameter))
machine_diameter = extruder_stack.definitionChanges.getProperty("material_diameter", "value")
if not machine_diameter:
machine_diameter = extruder_stack.definition.getProperty("material_diameter", "value")
if extruder_stack.definition.hasProperty("material_diameter", "value"):
machine_diameter = extruder_stack.definition.getProperty("material_diameter", "value")
else:
machine_diameter = self._global_container_stack.definition.getProperty("material_diameter", "value")
machine_approximate_diameter = str(round(machine_diameter))
if material_approximate_diameter != machine_approximate_diameter:

View file

@ -54,7 +54,11 @@ class PostProcessingPlugin(QObject, Extension):
## Execute all post-processing scripts on the gcode.
def execute(self, output_device):
scene = Application.getInstance().getController().getScene()
gcode_dict = getattr(scene, "gcode_dict")
gcode_dict = None
if hasattr(scene, "gcode_dict"):
gcode_dict = getattr(scene, "gcode_dict")
if not gcode_dict:
return

View file

@ -106,7 +106,7 @@ class SimulationPass(RenderPass):
nozzle_node = node
nozzle_node.setVisible(False)
elif issubclass(type(node), SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
layer_data = node.callDecoration("getLayerData")
if not layer_data:
continue

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
import sys
@ -344,7 +344,12 @@ class SimulationView(View):
self._max_feedrate = max(float(p.lineFeedrates.max()), self._max_feedrate)
self._min_feedrate = min(float(p.lineFeedrates.min()), self._min_feedrate)
self._max_thickness = max(float(p.lineThicknesses.max()), self._max_thickness)
self._min_thickness = min(float(p.lineThicknesses.min()), self._min_thickness)
try:
self._min_thickness = min(float(p.lineThicknesses[numpy.nonzero(p.lineThicknesses)].min()), self._min_thickness)
except:
# Sometimes, when importing a GCode the line thicknesses are zero and so the minimum (avoiding
# the zero) can't be calculated
Logger.log("i", "Min thickness can't be calculated because all the values are zero")
if max_layer_number < layer_id:
max_layer_number = layer_id
if min_layer_number > layer_id:

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty
@ -117,7 +117,7 @@ class SimulationViewProxy(QObject):
def setSimulationViewType(self, layer_view_type):
active_view = self._controller.getActiveView()
if isinstance(active_view, SimulationView.SimulationView.SimulationView):
active_view.setSimulationViewisinstance(layer_view_type)
active_view.setSimulationViewType(layer_view_type)
@pyqtSlot(result=int)
def getSimulationViewType(self):

View file

@ -1,4 +1,4 @@
# Copyright (c) 2017 Ultimaker B.V.
# Copyright (c) 2018 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtQml import qmlRegisterSingletonType
@ -18,7 +18,7 @@ def getMetaData():
}
def createSimulationViewProxy(engine, script_engine):
return SimulationViewProxy.SimulatorViewProxy()
return SimulationViewProxy.SimulationViewProxy()
def register(app):
simulation_view = SimulationView.SimulationView()

View file

@ -52,13 +52,19 @@ Component
{
id: addRemovePrintersLabel
anchors.right: parent.right
text: "Add / remove printers"
text: catalog.i18nc("@label link to connect manager", "Add/Remove printers")
font: UM.Theme.getFont("default")
color: UM.Theme.getColor("text")
linkColor: UM.Theme.getColor("text_link")
}
MouseArea
{
anchors.fill: addRemovePrintersLabel
hoverEnabled: true
onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel()
onEntered: addRemovePrintersLabel.font.underline = true
onExited: addRemovePrintersLabel.font.underline = false
}
}

View file

@ -69,6 +69,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self.setShortDescription(i18n_catalog.i18nc("@action:button Preceded by 'Ready to'.", "Print over network"))
self.setDescription(i18n_catalog.i18nc("@properties:tooltip", "Print over network"))
self.setConnectionText(i18n_catalog.i18nc("@info:status", "Connected over the network"))
self._printer_uuid_to_unique_name_mapping = {}
self._finished_jobs = []
@ -76,20 +78,26 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._cluster_size = int(properties.get(b"cluster_size", 0))
def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs):
# Notify the UI that a switch to the print monitor should happen
Application.getInstance().getController().setActiveStage("MonitorStage")
self.writeStarted.emit(self)
self._gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list", [])
if not self._gcode:
gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict", [])
active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate
gcode_list = gcode_dict[active_build_plate_id]
if not gcode_list:
# Unable to find g-code. Nothing to send
return
self._gcode = gcode_list
if len(self._printers) > 1:
self._spawnPrinterSelectionDialog()
else:
self.sendPrintJob()
# Notify the UI that a switch to the print monitor should happen
Application.getInstance().getController().setActiveStage("MonitorStage")
def _spawnPrinterSelectionDialog(self):
if self._printer_selection_dialog is None:
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "PrintWindow.qml")
@ -240,7 +248,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
newly_finished_jobs = [job for job in finished_jobs if job not in self._finished_jobs and job.owner == username]
for job in newly_finished_jobs:
job_completed_text = i18n_catalog.i18nc("@info:status", "Printer '{printer_name}' has finished printing '{job_name}'.".format(printer_name=job.assignedPrinter.name, job_name = job.name))
if job.assignedPrinter:
job_completed_text = i18n_catalog.i18nc("@info:status", "Printer '{printer_name}' has finished printing '{job_name}'.".format(printer_name=job.assignedPrinter.name, job_name = job.name))
else:
job_completed_text = i18n_catalog.i18nc("@info:status", "The print job '{job_name}' was finished.".format(job_name = job.name))
job_completed_message = Message(text=job_completed_text, title = i18n_catalog.i18nc("@info:status", "Print finished"))
job_completed_message.show()

View file

@ -78,10 +78,16 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
def _onAuthenticationStateChanged(self):
# We only accept commands if we are authenticated.
self._setAcceptsCommands(self._authentication_state == AuthState.Authenticated)
if self._authentication_state == AuthState.Authenticated:
self._setAcceptsCommands(True)
else:
self._setAcceptsCommands(False)
self.setConnectionText(i18n_catalog.i18nc("@info:status", "Connected over the network."))
elif self._authentication_state == AuthState.AuthenticationRequested:
self.setConnectionText(i18n_catalog.i18nc("@info:status",
"Connected over the network. Please approve the access request on the printer."))
elif self._authentication_state == AuthState.AuthenticationDenied:
self.setConnectionText(i18n_catalog.i18nc("@info:status", "Connected over the network. No access to control the printer."))
def _setupMessages(self):
self._authentication_requested_message = Message(i18n_catalog.i18nc("@info:status",
@ -175,15 +181,18 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
# Not authenticated, so unable to send job.
return
# Notify the UI that a switch to the print monitor should happen
Application.getInstance().getController().setActiveStage("MonitorStage")
self.writeStarted.emit(self)
self._gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list", [])
if not self._gcode:
gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict", [])
active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate
gcode_list = gcode_dict[active_build_plate_id]
if not gcode_list:
# Unable to find g-code. Nothing to send
return
self._gcode = gcode_list
errors = self._checkForErrors()
if errors:
text = i18n_catalog.i18nc("@label", "Unable to start a new print job.")
@ -229,6 +238,9 @@ class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice):
# No warnings or errors, so we're good to go.
self._startPrint()
# Notify the UI that a switch to the print monitor should happen
Application.getInstance().getController().setActiveStage("MonitorStage")
def _startPrint(self):
Logger.log("i", "Sending print job to printer.")
if self._sending_gcode:

View file

@ -31,6 +31,7 @@ UM.Dialog
property var printersModel: ListModel{}
function resetPrintersModel() {
printersModel.clear()
printersModel.append({ name: "Automatic", key: ""})
for (var index in OutputDevice.printers)

View file

@ -115,24 +115,8 @@ Item
{
tooltip: catalog.i18nc("@info:tooltip", "Load the configuration of the printer into Cura")
text: catalog.i18nc("@action:button", "Activate Configuration")
visible: printerConnected && !isClusterPrinter()
visible: false // printerConnected && !isClusterPrinter()
onClicked: manager.loadConfigurationFromPrinter()
function isClusterPrinter() {
return false
//TODO: Hardcoded this for the moment now. These info components might also need to move.
/*if(Cura.MachineManager.printerOutputDevices.length == 0)
{
return false;
}
var clusterSize = Cura.MachineManager.printerOutputDevices[0].clusterSize;
// This is not a cluster printer or the cluster it is just one printer
if(clusterSize == undefined || clusterSize == 1)
{
return false;
}
return true;*/
}
}
}

View file

@ -80,6 +80,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._firmware_progress = 0
self._firmware_update_state = FirmwareUpdateState.idle
self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB"))
# Queue for commands that need to be send. Used when command is sent when a print is active.
self._command_queue = Queue()

View file

@ -576,6 +576,42 @@ class XmlMaterialProfile(InstanceContainer):
if is_new_material:
containers_to_add.append(new_material)
# Find the buildplates compatibility
buildplates = machine.iterfind("./um:buildplate", self.__namespaces)
buildplate_map = {}
buildplate_map["buildplate_compatible"] = {}
buildplate_map["buildplate_recommended"] = {}
for buildplate in buildplates:
buildplate_id = buildplate.get("id")
if buildplate_id is None:
continue
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(
id = buildplate_id)
if not variant_containers:
# It is not really properly defined what "ID" is so also search for variants by name.
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(
definition = machine_id, name = buildplate_id)
if not variant_containers:
continue
buildplate_compatibility = machine_compatibility
buildplate_recommended = machine_compatibility
settings = buildplate.iterfind("./um:setting", self.__namespaces)
for entry in settings:
key = entry.get("key")
if key in self.__unmapped_settings:
if key == "hardware compatible":
buildplate_compatibility = self._parseCompatibleValue(entry.text)
elif key == "hardware recommended":
buildplate_recommended = self._parseCompatibleValue(entry.text)
else:
Logger.log("d", "Unsupported material setting %s", key)
buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_compatibility
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_recommended
hotends = machine.iterfind("./um:hotend", self.__namespaces)
for hotend in hotends:
hotend_id = hotend.get("id")
@ -605,8 +641,7 @@ class XmlMaterialProfile(InstanceContainer):
new_hotend_id = self.getId() + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
# Same as machine compatibility, keep the derived material containers consistent with the parent
# material
# Same as machine compatibility, keep the derived material containers consistent with the parent material
if ContainerRegistry.getInstance().isLoaded(new_hotend_id):
new_hotend_material = ContainerRegistry.getInstance().findContainers(id = new_hotend_id)[0]
is_new_material = False
@ -623,6 +658,9 @@ class XmlMaterialProfile(InstanceContainer):
new_hotend_material.getMetaData()["compatible"] = hotend_compatibility
new_hotend_material.getMetaData()["machine_manufacturer"] = machine_manufacturer
new_hotend_material.getMetaData()["definition"] = machine_id
if buildplate_map["buildplate_compatible"]:
new_hotend_material.getMetaData()["buildplate_compatible"] = buildplate_map["buildplate_compatible"]
new_hotend_material.getMetaData()["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
cached_hotend_setting_properties = cached_machine_setting_properties.copy()
cached_hotend_setting_properties.update(hotend_setting_values)
@ -763,6 +801,34 @@ class XmlMaterialProfile(InstanceContainer):
if len(found_materials) == 0: #This is a new material.
result_metadata.append(new_material_metadata)
buildplates = machine.iterfind("./um:buildplate", cls.__namespaces)
buildplate_map = {}
buildplate_map["buildplate_compatible"] = {}
buildplate_map["buildplate_recommended"] = {}
for buildplate in buildplates:
buildplate_id = buildplate.get("id")
if buildplate_id is None:
continue
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = buildplate_id)
if not variant_containers:
# It is not really properly defined what "ID" is so also search for variants by name.
variant_containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(definition = machine_id, name = buildplate_id)
if not variant_containers:
continue
settings = buildplate.iterfind("./um:setting", cls.__namespaces)
for entry in settings:
key = entry.get("key")
if key == "hardware compatible":
buildplate_compatibility = cls._parseCompatibleValue(entry.text)
elif key == "hardware recommended":
buildplate_recommended = cls._parseCompatibleValue(entry.text)
buildplate_map["buildplate_compatible"][buildplate_id] = buildplate_map["buildplate_compatible"]
buildplate_map["buildplate_recommended"][buildplate_id] = buildplate_map["buildplate_recommended"]
for hotend in machine.iterfind("./um:hotend", cls.__namespaces):
hotend_id = hotend.get("id")
if hotend_id is None:
@ -781,8 +847,7 @@ class XmlMaterialProfile(InstanceContainer):
new_hotend_id = container_id + "_" + machine_id + "_" + hotend_id.replace(" ", "_")
# Same as machine compatibility, keep the derived material containers consistent with the parent
# material
# Same as machine compatibility, keep the derived material containers consistent with the parent material
found_materials = ContainerRegistry.getInstance().findInstanceContainersMetadata(id = new_hotend_id)
if found_materials:
new_hotend_material_metadata = found_materials[0]
@ -799,6 +864,9 @@ class XmlMaterialProfile(InstanceContainer):
new_hotend_material_metadata["machine_manufacturer"] = machine_manufacturer
new_hotend_material_metadata["id"] = new_hotend_id
new_hotend_material_metadata["definition"] = machine_id
if buildplate_map["buildplate_compatible"]:
new_hotend_material_metadata["buildplate_compatible"] = buildplate_map["buildplate_compatible"]
new_hotend_material_metadata["buildplate_recommended"] = buildplate_map["buildplate_recommended"]
if len(found_materials) == 0:
result_metadata.append(new_hotend_material_metadata)
@ -874,7 +942,7 @@ class XmlMaterialProfile(InstanceContainer):
# Map XML file setting names to internal names
__material_settings_setting_map = {
"print temperature": "default_material_print_temperature",
"heated bed temperature": "material_bed_temperature",
"heated bed temperature": "default_material_bed_temperature",
"standby temperature": "material_standby_temperature",
"processing temperature graph": "material_flow_temp_graph",
"print cooling": "cool_fan_speed",
@ -884,7 +952,8 @@ class XmlMaterialProfile(InstanceContainer):
"surface energy": "material_surface_energy"
}
__unmapped_settings = [
"hardware compatible"
"hardware compatible",
"hardware recommended"
]
__material_properties_setting_map = {
"diameter": "material_diameter"

View file

@ -47,9 +47,9 @@
"default_value": 30
},
"machine_start_gcode": {
"default_value": ";Sliced at: {day} {date} {time}\nM104 S{material_print_temperature} ;set temperatures\nM140 S{material_bed_temperature}\nM109 S{material_print_temperature} ;wait for temperatures\nM190 S{material_bed_temperature}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 Z0 ;move Z to min endstops\nG28 X0 Y0 ;move X/Y to min endstops\nG29 ;Auto Level\nG1 Z0.6 F{travel_speed} ;move the Nozzle near the Bed\nG92 E0\nG1 Y0 ;zero the extruded length\nG1 X10 E30 F500 ;printing a Line from right to left\nG92 E0 ;zero the extruded length again\nG1 Z2\nG1 F{travel_speed}\nM117 Printing...;Put printing message on LCD screen\nM150 R255 U255 B255 P4 ;Change LED Color to white" },
"default_value": ";Sliced at: {day} {date} {time}\nM104 S{material_print_temperature} ;set temperatures\nM140 S{material_bed_temperature}\nM109 S{material_print_temperature} ;wait for temperatures\nM190 S{material_bed_temperature}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 Z0 ;move Z to min endstops\nG28 X0 Y0 ;move X/Y to min endstops\nG29 ;Auto Level\nG1 Z0.6 F{speed_travel} ;move the Nozzle near the Bed\nG92 E0\nG1 Y0 ;zero the extruded length\nG1 X10 E30 F500 ;printing a Line from right to left\nG92 E0 ;zero the extruded length again\nG1 Z2\nG1 F{speed_travel}\nM117 Printing...;Put printing message on LCD screen\nM150 R255 U255 B255 P4 ;Change LED Color to white" },
"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 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 ;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)\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 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning"
}
}
}

View file

@ -40,10 +40,10 @@
"default_value": "RepRap"
},
"machine_start_gcode": {
"default_value": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;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 max endstops\nG1 Z115.0 F{travel_speed} ;move th e platform up 20mm\nG28 Z0 ;move Z to max endstop\nG1 Z15.0 F{travel_speed} ;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 F{travel_speed}\nM301 H1 P26.38 I2.57 D67.78\n;Put printing message on LCD screen\nM117 Printing..."
"default_value": ";Sliced at: {day} {date} {time}\n;Basic settings: Layer height: {layer_height} Walls: {wall_thickness} Fill: {fill_density}\n;Print time: {print_time}\n;Filament used: {filament_amount}m {filament_weight}g\n;Filament cost: {filament_cost}\n;M190 S{print_bed_temperature} ;Uncomment to add your own bed temperature line\n;M109 S{print_temperature} ;Uncomment to add your own temperature line\nG21 ;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 max endstops\nG1 Z115.0 F{speed_travel} ;move th e platform up 20mm\nG28 Z0 ;move Z to max endstop\nG1 Z15.0 F{speed_travel} ;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 F{speed_travel}\nM301 H1 P26.38 I2.57 D67.78\n;Put printing message on LCD screen\nM117 Printing..."
},
"machine_end_gcode": {
"default_value": ";End GCode\nM104 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 F{travel_speed} ;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\nG28 Z0\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}"
"default_value": ";End GCode\nM104 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 F{speed_travel} ;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\nG28 Z0\nM84 ;steppers off\nG90 ;absolute positioning\n;{profile_string}"
}
}
}

View file

@ -1,55 +1,69 @@
{
"version":2,
"name":"Anycubic i3 Mega",
"inherits":"fdmprinter",
"metadata":{
"visible":true,
"author":"TheTobby",
"manufacturer":"Anycubic",
"file_formats":"text/x-gcode",
"icon":"icon_ultimaker2",
"platform":"anycubic_i3_mega_platform.stl",
"has_materials": false,
"has_machine_quality": true,
"preferred_quality": "*normal*"
"version": 2,
"name": "Anycubic i3 Mega",
"inherits": "fdmprinter",
"metadata":
{
"visible": true,
"author": "TheTobby",
"manufacturer": "Anycubic",
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker2",
"platform": "anycubic_i3_mega_platform.stl",
"has_materials": false,
"has_machine_quality": true,
"preferred_quality": "*normal*"
},
"overrides":{
"machine_name":{
"default_value":"Anycubic i3 Mega"
"overrides":
{
"machine_name":
{
"default_value": "Anycubic i3 Mega"
},
"machine_heated_bed":{
"default_value":true
"machine_heated_bed":
{
"default_value": true
},
"machine_width":{
"default_value":210
"machine_width":
{
"default_value": 210
},
"machine_height":{
"default_value":205
"machine_height":
{
"default_value": 205
},
"machine_depth":{
"default_value":210
"machine_depth":
{
"default_value": 210
},
"machine_center_is_zero":{
"default_value":false
"machine_center_is_zero":
{
"default_value": false
},
"machine_nozzle_size":{
"default_value":0.4
"machine_nozzle_size":
{
"default_value": 0.4
},
"material_diameter":{
"default_value":1.75
"material_diameter":
{
"default_value": 1.75
},
"gantry_height":{
"default_value":0
"gantry_height":
{
"default_value": 0
},
"machine_gcode_flavor":{
"default_value":"RepRap (Marlin/Sprinter)"
"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\nG1 Z15.0 F{travel_speed} ;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 F{travel_speed}\nM117 Printing...\nG5"
"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 F{speed_travel} ;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 F{speed_travel}\nM117 Printing...\nG5"
},
"machine_end_gcode":{
"default_value":"M104 S0 ; turn off extruder\nM140 S0 ; turn off bed\nM84 ; disable motors\nM107\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle\nto release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\nG28 X0 ;Y0 ;move X/Y to min endstops\nso the head is out of the way\nG1 Y180 F2000\nM84 ;steppers off\nG90\nM300 P300 S4000"
"machine_end_gcode":
{
"default_value": "M104 S0 ; turn off extruder\nM140 S0 ; turn off bed\nM84 ; disable motors\nM107\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle\nto release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 ;Y0 ;move X/Y to min endstops\nso the head is out of the way\nG1 Y180 F2000\nM84 ;steppers off\nG90\nM300 P300 S4000"
}
}
}

View file

@ -10,7 +10,7 @@
},
"overrides": {
"machine_start_gcode": {
"default_value": "; -- START GCODE --\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 \nG29 Z0.12 ;Auto-bedleveling with Z offset \nG92 E0 ;zero the extruded length \nG1 F2000 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{travel_speed}\nM117 Printing...\n; -- end of START GCODE --"
"default_value": "; -- START GCODE --\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 \nG29 Z0.12 ;Auto-bedleveling with Z offset \nG92 E0 ;zero the extruded length \nG1 F2000 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F{speed_travel}\nM117 Printing...\n; -- end of START GCODE --"
},
"machine_end_gcode": {
"default_value": "; -- START GCODE --\nG28 ; Home all axes\nM104 S0 ;extruder heater off\n;M140 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\n;M84 ;steppers off\nG90 ;absolute positioning\n; -- end of START GCODE --"

View file

@ -181,27 +181,6 @@
}
}
},
"material": {
"label": "Material",
"icon": "category_material",
"description": "Material",
"type": "category",
"children": {
"material_diameter": {
"label": "Diameter",
"description": "Adjusts the diameter of the filament used. Match this value with the diameter of the used filament.",
"unit": "mm",
"type": "float",
"default_value": 2.85,
"minimum_value": "0.0001",
"minimum_value_warning": "0.4",
"maximum_value_warning": "3.5",
"enabled": "machine_gcode_flavor != \"UltiGCode\"",
"settable_per_mesh": false,
"settable_per_extruder": true
}
}
},
"platform_adhesion":
{
"label": "Build Plate Adhesion",

View file

@ -154,6 +154,21 @@
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
"machine_buildplate_type":
{
"label": "Build Plate Material",
"description": "The material of the build plate installed on the printer.",
"default_value": "glass",
"type": "enum",
"options":
{
"glass": "Glass",
"aluminium": "Aluminium"
},
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
"machine_height":
{
"label": "Machine Height",
@ -1981,14 +1996,30 @@
"settable_per_mesh": false,
"settable_per_extruder": true
},
"default_material_bed_temperature":
{
"label": "Default Build Plate Temperature",
"description": "The default temperature used for the heated build plate. This should be the \"base\" temperature of a build plate. All other print temperatures should use offsets based on this value",
"unit": "°C",
"type": "float",
"resolve": "max(extruderValues('default_material_bed_temperature'))",
"default_value": 60,
"minimum_value": "-273.15",
"minimum_value_warning": "0",
"maximum_value_warning": "130",
"enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\"",
"settable_per_mesh": false,
"settable_per_extruder": false,
"settable_per_meshgroup": false
},
"material_bed_temperature":
{
"label": "Build Plate Temperature",
"description": "The temperature used for the heated build plate. If this is 0, the bed temperature will not be adjusted.",
"unit": "°C",
"type": "float",
"resolve": "max(extruderValues('material_bed_temperature'))",
"default_value": 60,
"value": "default_material_bed_temperature",
"minimum_value": "-273.15",
"minimum_value_warning": "0",
"maximum_value_warning": "130",

View file

@ -8,46 +8,59 @@
"manufacturer": "Tevo",
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker2",
"has_materials": false,
"has_materials": false,
"has_machine_quality": true,
"platform": "prusai3_platform.stl",
"preferred_quality": "*normal*"
"platform": "tevo_blackwidow.stl",
"preferred_quality": "*normal*"
},
"overrides": {
"machine_name": {
"overrides":
{
"machine_name":
{
"default_value": "Tevo Black Widow"
},
"machine_heated_bed": {
"machine_heated_bed":
{
"default_value": true
},
"machine_width": {
"machine_width":
{
"default_value": 350
},
"machine_height": {
"machine_height":
{
"default_value": 250
},
"machine_depth": {
"machine_depth":
{
"default_value": 250
},
"machine_center_is_zero": {
"machine_center_is_zero":
{
"default_value": false
},
"machine_nozzle_size": {
"machine_nozzle_size":
{
"default_value": 0.4
},
"material_diameter": {
"default_value": 1.75
"material_diameter":
{
"default_value": 1.75
},
"gantry_height": {
"gantry_height":
{
"default_value": 0
},
"machine_gcode_flavor": {
"machine_gcode_flavor":
{
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"machine_start_gcode":
{
"default_value": "M280 P0 S160 ; release BLTouch alarm (OK to send for Non BLTouch)\nM420 Z2 ; set fade leveling at 2mm for BLTouch (OK to send for Non BLTouch)\nG28 ; home all\nG29 ; probe bed\nG92 E0 ;zero the extruded length\nG1 X0.0 Y50.0 Z10.0 F3600\n; perform wipe and prime\nG1 Z0.0 F1000\nG1 Z0.2 Y70.0 E9.0 F1000.0 ; prime\nG1 Y100.0 E12.5 F1000.0 ; prime\nG92 E0 ; zero extruder again\nM117 Printing..."
},
"machine_end_gcode": {
"machine_end_gcode":
{
"default_value": "G92 E0 ; zero the extruded length again\nG1 E-1.5 F500 ; retract the filament to release some of the pressure\nM104 S0 ; turn off extruder\nM140 S0 ; turn off bed\nG28 X0 ; home X axis\nG1 Y245 ; move Y axis to end position\nM84 ; disable motors\nM107 ; turn off fan"
}
}

Binary file not shown.

View file

@ -204,6 +204,7 @@ UM.MainWindow
onObjectRemoved: settingsMenu.removeItem(object)
}
BuildplateMenu { title: catalog.i18nc("@title:menu", "&Build plate"); visible: Cura.MachineManager.hasVariantBuildplates }
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 }

View file

@ -0,0 +1,87 @@
// Copyright (c) 2018 Ultimaker B.V.
// Cura is released under the terms of the LGPLv3 or higher.
import QtQuick 2.2
import QtQuick.Controls 1.1
import UM 1.2 as UM
import Cura 1.0 as Cura
Menu
{
id: menu
title: "Build plate"
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
property bool isClusterPrinter:
{
if(Cura.MachineManager.printerOutputDevices.length == 0)
{
return false;
}
var clusterSize = Cura.MachineManager.printerOutputDevices[0].clusterSize;
// This is not a cluster printer or the cluster it is just one printer
if(clusterSize == undefined || clusterSize == 1)
{
return false;
}
return true;
}
MenuItem
{
id: automaticBuildplate
text:
{
if(printerConnected && Cura.MachineManager.printerOutputDevices[0].buildplateId != "" && !isClusterPrinter)
{
var buildplateName = Cura.MachineManager.printerOutputDevices[0].buildplateId
return catalog.i18nc("@title:menuitem %1 is the buildplate currently loaded in the printer", "Automatic: %1").arg(buildplateName)
}
return ""
}
visible: printerConnected && Cura.MachineManager.printerOutputDevices[0].buildplateId != "" && !isClusterPrinter
onTriggered:
{
var buildplateId = Cura.MachineManager.printerOutputDevices[0].buildplateId
var itemIndex = buildplateInstantiator.model.find("name", buildplateId)
if(itemIndex > -1)
{
Cura.MachineManager.setActiveVariantBuildplate(buildplateInstantiator.model.getItem(itemIndex).id)
}
}
}
MenuSeparator
{
visible: automaticBuildplate.visible
}
Instantiator
{
id: buildplateInstantiator
model: UM.InstanceContainersModel
{
filter:
{
"type": "variant",
"hardware_type": "buildplate",
"definition": Cura.MachineManager.activeDefinitionId //Only show variants of this machine
}
}
MenuItem {
text: model.name
checkable: true
checked: model.id == Cura.MachineManager.globalVariantId
exclusiveGroup: group
onTriggered:
{
Cura.MachineManager.setActiveVariantBuildplate(model.id);
}
}
onObjectAdded: menu.insertItem(index, object)
onObjectRemoved: menu.removeItem(object)
}
ExclusiveGroup { id: group }
}

View file

@ -68,8 +68,17 @@ Menu
{
filter:
{
"type": "variant",
"definition": Cura.MachineManager.activeQualityDefinitionId //Only show variants of this machine
var filter_dict =
{
"type": "variant",
"definition": Cura.MachineManager.activeQualityDefinitionId //Only show variants of this machine
}
if (Cura.MachineManager.hasVariantBuildplates)
{
filter_dict["hardware_type"] = "nozzle"
}
return filter_dict
}
}
MenuItem {

View file

@ -143,7 +143,7 @@ UM.ManagementPage
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
property var connectedPrinter: printerConnected ? Cura.MachineManager.printerOutputDevices[0] : null
property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands
property var printJob: connectedPrinter != null ? connectedPrinter.activePrintJob: null
Label
{
text: catalog.i18nc("@label", "Printer type:")
@ -178,7 +178,12 @@ UM.ManagementPage
return "";
}
switch(Cura.MachineManager.printerOutputDevices[0].jobState)
if (machineInfo.printJob == null)
{
return catalog.i18nc("@label:MonitorStatus", "Waiting for a printjob");
}
switch(machineInfo.printJob.state)
{
case "printing":
return catalog.i18nc("@label:MonitorStatus", "Printing...");
@ -194,10 +199,9 @@ UM.ManagementPage
return catalog.i18nc("@label:MonitorStatus", "In maintenance. Please check the printer");
case "abort": // note sure if this jobState actually occurs in the wild
return catalog.i18nc("@label:MonitorStatus", "Aborting print...");
case "ready": // ready to print or getting ready
case "": // ready to print or getting ready
return catalog.i18nc("@label:MonitorStatus", "Waiting for a printjob");
}
return ""
}
visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId && machineInfo.printerAcceptsCommands
wrapMode: Text.WordWrap

View file

@ -72,7 +72,7 @@ TabView
width: scrollView.columnWidth;
text: properties.name;
readOnly: !base.editingEnabled;
onEditingFinished: base.setName(properties.name, text)
onEditingFinished: base.updateMaterialDisplayName(properties.name, text)
}
Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") }
@ -82,11 +82,7 @@ TabView
width: scrollView.columnWidth;
text: properties.supplier;
readOnly: !base.editingEnabled;
onEditingFinished:
{
base.setMetaDataEntry("brand", properties.supplier, text);
pane.objectList.currentIndex = pane.getIndexById(base.containerId);
}
onEditingFinished: base.updateMaterialSupplier(properties.supplier, text)
}
Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") }
@ -95,15 +91,10 @@ TabView
width: scrollView.columnWidth;
text: properties.material_type;
readOnly: !base.editingEnabled;
onEditingFinished:
{
base.setMetaDataEntry("material", properties.material_type, text);
pane.objectList.currentIndex = pane.getIndexById(base.containerId)
}
onEditingFinished: base.updateMaterialType(properties.material_type, text)
}
Label { width: scrollView.columnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") }
Row {
width: scrollView.columnWidth
height: parent.rowHeight
@ -128,13 +119,6 @@ TabView
}
}
// make sure the color stays connected after changing the color
Binding {
target: colorSelector
property: "color"
value: properties.color_code
}
// pretty color name text field
ReadOnlyTextField {
id: colorLabel;
@ -453,14 +437,28 @@ TabView
return 0;
}
function setName(old_value, new_value)
{
if(old_value != new_value)
{
Cura.ContainerManager.setContainerName(base.containerId, new_value);
// update material name label. not so pretty, but it works
materialProperties.name = new_value;
pane.objectList.currentIndex = pane.getIndexById(base.containerId)
// update the display name of the material
function updateMaterialDisplayName (old_name, new_name) {
// don't change when new name is the same
if (old_name == new_name) {
return
}
// update the values
Cura.ContainerManager.setContainerName(base.containerId, new_name)
materialProperties.name = new_name
}
// update the type of the material
function updateMaterialType (old_type, new_type) {
base.setMetaDataEntry("material", old_type, new_type)
materialProperties.material_type = new_type
}
// update the supplier of the material
function updateMaterialSupplier (old_supplier, new_supplier) {
base.setMetaDataEntry("brand", old_supplier, new_supplier)
materialProperties.supplier = new_supplier
}
}

View file

@ -153,15 +153,6 @@ UM.ManagementPage
forceActiveFocus()
Cura.ContainerManager.createMaterial()
}
Connections
{
target: base.objectList.model
onItemsChanged:
{
base.objectList.currentIndex = base.getIndexById(Cura.MachineManager.activeMaterialId);
}
}
},
// Duplicate button

View file

@ -213,8 +213,8 @@ UM.ManagementPage
ProfileTab
{
title: catalog.i18nc("@title:tab", "Global Settings");
quality: base.currentItem != null ? base.currentItem.id : "";
material: Cura.MachineManager.allActiveMaterialIds[Cura.MachineManager.activeMachineId]
quality: Cura.MachineManager.activeMachine.qualityChanges.id
material: Cura.MachineManager.activeMachine.material.id
}
Repeater

View file

@ -170,8 +170,8 @@ Item {
tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process")
// 1 = not started, 2 = Processing
enabled: base.backendState != "undefined" && (base.backendState == 1 || base.backendState == 2) && base.activity == true
visible: base.backendState != "undefined" && !autoSlice && (base.backendState == 1 || base.backendState == 2) && base.activity == true
enabled: base.backendState != "undefined" && ([1, 2].indexOf(base.backendState) != -1) && base.activity
visible: base.backendState != "undefined" && !autoSlice && ([1, 2, 4].indexOf(base.backendState) != -1) && base.activity
property bool autoSlice
height: UM.Theme.getSize("save_button_save_to_button").height
@ -179,8 +179,8 @@ Item {
anchors.right: parent.right
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
// 1 = not started, 5 = disabled
text: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel")
// 1 = not started, 4 = error, 5 = disabled
text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel")
onClicked:
{
sliceOrStopSlicing();

View file

@ -135,14 +135,6 @@ SettingItem
}
}
onEditingFinished:
{
if (textHasChanged)
{
propertyProvider.setPropertyValue("value", text)
}
}
onActiveFocusChanged:
{
if(activeFocus)

View file

@ -63,11 +63,9 @@ Item
menu: ProfileMenu { }
function generateActiveQualityText () {
var result = catalog.i18nc("@", "No Profile Available") // default text
if (Cura.MachineManager.isActiveQualitySupported ) {
result = Cura.MachineManager.activeQualityName
var result = Cura.MachineManager.activeQualityName;
if (Cura.MachineManager.isActiveQualitySupported) {
if (Cura.MachineManager.activeQualityLayerHeight > 0) {
result += " <font color=\"" + UM.Theme.getColor("text_detail") + "\">"
result += " - "

View file

@ -96,7 +96,7 @@ Rectangle
SidebarHeader {
id: header
width: parent.width
visible: (machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants) && !monitoringPrint
visible: !hideSettings && (machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants) && !monitoringPrint
anchors.top: machineSelection.bottom
onShowTooltip: base.showTooltip(item, location, text)
@ -128,7 +128,7 @@ Rectangle
text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox", "Print Setup disabled\nG-code files cannot be modified")
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width
anchors.top: headerSeparator.bottom
anchors.top: hideSettings ? machineSelection.bottom : headerSeparator.bottom
anchors.topMargin: UM.Theme.getSize("sidebar_margin").height
width: Math.floor(parent.width * 0.45)
font: UM.Theme.getFont("large")

View file

@ -242,7 +242,7 @@ Column
Label
{
id: materialLabel
text: catalog.i18nc("@label","Material");
text: catalog.i18nc("@label", "Material");
width: Math.floor(parent.width * 0.45 - UM.Theme.getSize("default_margin").width)
font: UM.Theme.getFont("default");
color: UM.Theme.getColor("text");
@ -314,6 +314,62 @@ Column
}
}
//Buildplate row separator
Rectangle {
id: separator
anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width
anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width
anchors.horizontalCenter: parent.horizontalCenter
visible: buildplateRow.visible
width: parent.width - UM.Theme.getSize("sidebar_margin").width * 2
height: visible ? UM.Theme.getSize("sidebar_lining_thin").height / 2 : 0
color: UM.Theme.getColor("sidebar_lining_thin")
}
//Buildplate row
Item
{
id: buildplateRow
height: UM.Theme.getSize("sidebar_setup").height
visible: Cura.MachineManager.hasVariantBuildplates && !sidebar.monitoringPrint && !sidebar.hideSettings
anchors
{
left: parent.left
leftMargin: UM.Theme.getSize("sidebar_margin").width
right: parent.right
rightMargin: UM.Theme.getSize("sidebar_margin").width
}
Label
{
id: bulidplateLabel
text: catalog.i18nc("@label", "Build plate");
width: Math.floor(parent.width * 0.45 - UM.Theme.getSize("default_margin").width)
font: UM.Theme.getFont("default");
color: UM.Theme.getColor("text");
}
ToolButton {
id: buildplateSelection
text: Cura.MachineManager.activeVariantBuildplateName
tooltip: Cura.MachineManager.activeVariantBuildplateName
visible: Cura.MachineManager.hasVariantBuildplates
height: UM.Theme.getSize("setting_control").height
width: Math.floor(parent.width * 0.7 + UM.Theme.getSize("sidebar_margin").width)
anchors.right: parent.right
style: UM.Theme.styles.sidebar_header_button
activeFocusOnPress: true;
menu: BuildplateMenu {}
property var valueError: !Cura.MachineManager.variantBuildplateCompatible && !Cura.MachineManager.variantBuildplateUsable
property var valueWarning: Cura.MachineManager.variantBuildplateUsable
}
}
// Material info row
Item
{

View file

@ -43,6 +43,7 @@
"sidebar_header_text_hover": [255, 255, 255, 255],
"sidebar_header_text_inactive": [255, 255, 255, 127],
"sidebar_lining": [31, 36, 39, 255],
"sidebar_lining_thin": [255, 255, 255, 30],
"button": [39, 44, 48, 255],
"button_hover": [39, 44, 48, 255],

View file

@ -864,9 +864,10 @@ QtObject {
}
}
label: Label {
text: control.text;
color: Theme.getColor("checkbox_text");
font: Theme.getFont("default");
text: control.text
color: Theme.getColor("checkbox_text")
font: Theme.getFont("default")
elide: Text.ElideRight
}
}
}

View file

@ -91,6 +91,7 @@
"sidebar_header_text_active": [255, 255, 255, 255],
"sidebar_header_text_hover": [255, 255, 255, 255],
"sidebar_lining": [245, 245, 245, 255],
"sidebar_lining_thin": [127, 127, 127, 255],
"button": [31, 36, 39, 255],
"button_hover": [68, 72, 75, 255],