diff --git a/cura/ConvexHullDecorator.py b/cura/ConvexHullDecorator.py
index 783d74e41b..5185579633 100644
--- a/cura/ConvexHullDecorator.py
+++ b/cura/ConvexHullDecorator.py
@@ -287,5 +287,5 @@ class ConvexHullDecorator(SceneNodeDecorator):
_affected_settings = [
"adhesion_type", "raft_base_thickness", "raft_interface_thickness", "raft_surface_layers",
- "raft_surface_thickness", "raft_airgap", "print_sequence",
+ "raft_surface_thickness", "raft_airgap", "raft_margin", "print_sequence",
"skirt_gap", "skirt_line_count", "skirt_brim_line_width", "skirt_distance"]
diff --git a/cura/ConvexHullNode.py b/cura/ConvexHullNode.py
index 232b30e317..8e5acf9518 100644
--- a/cura/ConvexHullNode.py
+++ b/cura/ConvexHullNode.py
@@ -77,7 +77,7 @@ class ConvexHullNode(SceneNode):
convex_hull_head = self._node.callDecoration("getConvexHullHead")
if convex_hull_head:
convex_hull_head_builder = MeshBuilder()
- convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height-self._thickness)
+ convex_hull_head_builder.addConvexPolygon(convex_hull_head.getPoints(), self._mesh_height - self._thickness)
self._convex_hull_head_mesh = convex_hull_head_builder.build()
if not node:
diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py
index 5daae559d6..62d5fb7ae7 100644
--- a/cura/CuraApplication.py
+++ b/cura/CuraApplication.py
@@ -203,7 +203,7 @@ class CuraApplication(QtApplication):
"dialog_profile_path",
"dialog_material_path"]:
- Preferences.getInstance().addPreference("local_file/%s" % key, "~/")
+ Preferences.getInstance().addPreference("local_file/%s" % key, os.path.expanduser("~/"))
Preferences.getInstance().setDefault("local_file/last_used_type", "text/x-gcode")
@@ -555,12 +555,18 @@ class CuraApplication(QtApplication):
def deleteSelection(self):
if not self.getController().getToolsEnabled():
return
-
+ removed_group_nodes = []
op = GroupedOperation()
nodes = Selection.getAllSelectedObjects()
for node in nodes:
op.addOperation(RemoveSceneNodeOperation(node))
-
+ group_node = node.getParent()
+ if group_node and group_node.callDecoration("isGroup") and group_node not in removed_group_nodes:
+ remaining_nodes_in_group = list(set(group_node.getChildren()) - set(nodes))
+ if len(remaining_nodes_in_group) == 1:
+ removed_group_nodes.append(group_node)
+ op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent()))
+ op.addOperation(RemoveSceneNodeOperation(group_node))
op.push()
pass
@@ -586,8 +592,7 @@ class CuraApplication(QtApplication):
op.push()
if group_node:
if len(group_node.getChildren()) == 1 and group_node.callDecoration("isGroup"):
- group_node.getChildren()[0].translate(group_node.getPosition())
- group_node.getChildren()[0].setParent(group_node.getParent())
+ op.addOperation(SetParentOperation(group_node.getChildren()[0], group_node.getParent()))
op = RemoveSceneNodeOperation(group_node)
op.push()
@@ -628,7 +633,23 @@ class CuraApplication(QtApplication):
if node:
op = SetTransformOperation(node, Vector())
op.push()
-
+
+ ## Select all nodes containing mesh data in the scene.
+ @pyqtSlot()
+ def selectAll(self):
+ if not self.getController().getToolsEnabled():
+ return
+
+ Selection.clear()
+ for node in DepthFirstIterator(self.getController().getScene().getRoot()):
+ if type(node) is not SceneNode:
+ continue
+ if not node.getMeshData() and not node.callDecoration("isGroup"):
+ continue # Node that doesnt have a mesh and is not a group.
+ if node.getParent() and node.getParent().callDecoration("isGroup"):
+ continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
+ Selection.add(node)
+
## Delete all nodes containing mesh data in the scene.
@pyqtSlot()
def deleteAll(self):
@@ -652,6 +673,7 @@ class CuraApplication(QtApplication):
op.addOperation(RemoveSceneNodeOperation(node))
op.push()
+ Selection.clear()
## Reset all translation on nodes with mesh data.
@pyqtSlot()
@@ -896,4 +918,4 @@ class CuraApplication(QtApplication):
self._additional_components[area_id] = []
self._additional_components[area_id].append(component)
- self.additionalComponentsChanged.emit(area_id)
\ No newline at end of file
+ self.additionalComponentsChanged.emit(area_id)
diff --git a/cura/MachineActionManager.py b/cura/MachineActionManager.py
index b50bb95e7f..6061a2e49b 100644
--- a/cura/MachineActionManager.py
+++ b/cura/MachineActionManager.py
@@ -57,9 +57,10 @@ class MachineActionManager(QObject):
def addRequiredAction(self, definition_id, action_key):
if action_key in self._machine_actions:
if definition_id in self._required_actions:
- self._required_actions[definition_id] |= {self._machine_actions[action_key]}
+ if self._machine_actions[action_key] not in self._required_actions[definition_id]:
+ self._required_actions[definition_id].append(self._machine_actions[action_key])
else:
- self._required_actions[definition_id] = {self._machine_actions[action_key]}
+ self._required_actions[definition_id] = [self._machine_actions[action_key]]
else:
raise UnknownMachineActionError("Action %s, which is required for %s is not known." % (action_key, definition_id))
@@ -67,9 +68,10 @@ class MachineActionManager(QObject):
def addSupportedAction(self, definition_id, action_key):
if action_key in self._machine_actions:
if definition_id in self._supported_actions:
- self._supported_actions[definition_id] |= {self._machine_actions[action_key]}
+ if self._machine_actions[action_key] not in self._supported_actions[definition_id]:
+ self._supported_actions[definition_id].append(self._machine_actions[action_key])
else:
- self._supported_actions[definition_id] = {self._machine_actions[action_key]}
+ self._supported_actions[definition_id] = [self._machine_actions[action_key]]
else:
Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id)
diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py
index 9184db109a..82be7c480f 100644
--- a/cura/Settings/ContainerManager.py
+++ b/cura/Settings/ContainerManager.py
@@ -244,6 +244,7 @@ class ContainerManager(QObject):
if not type_name or entry["type"] == type_name:
filters.append(filter_string)
+ filters.append("All Files (*)")
return filters
## Export a container to a file
@@ -280,6 +281,9 @@ class ContainerManager(QObject):
return { "status": "error", "message": "Container not found"}
container = containers[0]
+ if UM.Platform.isOSX() and "." in file_url:
+ file_url = file_url[:file_url.rfind(".")]
+
for suffix in mime_type.suffixes:
if file_url.endswith(suffix):
break
@@ -301,7 +305,7 @@ class ContainerManager(QObject):
with UM.SaveFile(file_url, "w") as f:
f.write(contents)
- return { "status": "success", "message": "Succesfully exported container"}
+ return { "status": "success", "message": "Succesfully exported container", "path": file_url}
## Imports a profile from a file
#
@@ -371,11 +375,20 @@ class ContainerManager(QObject):
"container": container_type
}
- suffix_list = "*." + mime_type.preferredSuffix
+ suffix = mime_type.preferredSuffix
+ if UM.Platform.isOSX() and "." in suffix:
+ # OSX's File dialog is stupid and does not allow selecting files with a . in its name
+ suffix = suffix[suffix.index(".") + 1:]
+
+ suffix_list = "*." + suffix
for suffix in mime_type.suffixes:
if suffix == mime_type.preferredSuffix:
continue
+ if UM.Platform.isOSX() and "." in suffix:
+ # OSX's File dialog is stupid and does not allow selecting files with a . in its name
+ suffix = suffix[suffix.index("."):]
+
suffix_list += ", *." + suffix
name_filter = "{0} ({1})".format(mime_type.comment, suffix_list)
diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py
index 5bc0ff65fa..6a180593c5 100644
--- a/cura/Settings/MachineManager.py
+++ b/cura/Settings/MachineManager.py
@@ -26,7 +26,7 @@ class MachineManager(QObject):
self._global_container_stack = None
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
- self._global_stack_valid = None
+ self._active_stack_valid = None
self._onGlobalContainerChanged()
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
@@ -74,7 +74,7 @@ class MachineManager(QObject):
activeStackChanged = pyqtSignal()
globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed.
- globalValidationChanged = pyqtSignal() # Emitted whenever a validation inside global container is changed
+ activeValidationChanged = pyqtSignal() # Emitted whenever a validation inside active container is changed
blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly
@@ -281,16 +281,17 @@ class MachineManager(QObject):
self._global_container_stack.getTop().setProperty(key, "value", new_value)
if property_name == "validationState":
- if self._global_stack_valid:
+ if self._active_stack_valid:
changed_validation_state = self._active_container_stack.getProperty(key, property_name)
if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError):
- self._global_stack_valid = False
- self.globalValidationChanged.emit()
+ self._active_stack_valid = False
+ self.activeValidationChanged.emit()
else:
has_errors = self._checkStackForErrors(self._active_container_stack)
if not has_errors:
- self._global_stack_valid = True
- self.globalValidationChanged.emit()
+ self._active_stack_valid = True
+ self.activeValidationChanged.emit()
+
def _onGlobalContainerChanged(self):
if self._global_container_stack:
self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged)
@@ -313,8 +314,6 @@ class MachineManager(QObject):
self._global_container_stack.nameChanged.connect(self._onMachineNameChanged)
self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged)
self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
- self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack)
- self.globalValidationChanged.emit()
material = self._global_container_stack.findContainer({"type": "material"})
material.nameChanged.connect(self._onMaterialNameChanged)
@@ -332,6 +331,8 @@ class MachineManager(QObject):
self._active_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged)
else:
self._active_container_stack = self._global_container_stack
+ self._active_stack_valid = not self._checkStackForErrors(self._active_container_stack)
+ self.activeValidationChanged.emit()
def _onInstanceContainersChanged(self, container):
container_type = container.getMetaDataEntry("type")
@@ -436,11 +437,11 @@ class MachineManager(QObject):
return len(user_settings) != 0
## Check if the global profile does not contain error states
- # Note that the _global_stack_valid is cached due to performance issues
+ # Note that the _active_stack_valid is cached due to performance issues
# Calling _checkStackForErrors on every change is simply too expensive
- @pyqtProperty(bool, notify = globalValidationChanged)
- def isGlobalStackValid(self):
- return bool(self._global_stack_valid)
+ @pyqtProperty(bool, notify = activeValidationChanged)
+ def isActiveStackValid(self):
+ return bool(self._active_stack_valid)
@pyqtProperty(str, notify = activeStackChanged)
def activeUserProfileId(self):
@@ -552,7 +553,7 @@ class MachineManager(QObject):
return ""
@pyqtSlot(str, str)
- def renameQualityContainer(self, container_id, nbalew_name):
+ def renameQualityContainer(self, container_id, new_name):
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id, type = "quality")
if containers:
new_name = self._createUniqueName("quality", containers[0].getName(), new_name,
@@ -743,10 +744,7 @@ class MachineManager(QObject):
# If the machine that is being removed is the currently active machine, set another machine as the active machine.
activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id)
- stacks = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = machine_id)
- if not stacks:
- return
- ExtruderManager.getInstance().removeMachineExtruders(stacks[0].getBottom().getId())
+ ExtruderManager.getInstance().removeMachineExtruders(machine_id)
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "user", machine = machine_id)
for container in containers:
diff --git a/cura/Settings/SettingOverrideDecorator.py b/cura/Settings/SettingOverrideDecorator.py
index 24360ed992..183d84d2f4 100644
--- a/cura/Settings/SettingOverrideDecorator.py
+++ b/cura/Settings/SettingOverrideDecorator.py
@@ -37,10 +37,15 @@ class SettingOverrideDecorator(SceneNodeDecorator):
self._updateNextStack()
def __deepcopy__(self, memo):
+ print("deepcopy settingoverridedecorator")
## Create a fresh decorator object
deep_copy = SettingOverrideDecorator()
## Copy the instance
deep_copy._instance = copy.deepcopy(self._instance, memo)
+
+ # Properly set the right extruder on the copy
+ deep_copy.setActiveExtruder(self._extruder_stack)
+
## Set the copied instance as the first (and only) instance container of the stack.
deep_copy._stack.replaceContainer(0, deep_copy._instance)
return deep_copy
@@ -75,6 +80,7 @@ class SettingOverrideDecorator(SceneNodeDecorator):
# \param extruder_stack_id The new extruder stack to print with.
def setActiveExtruder(self, extruder_stack_id):
self._extruder_stack = extruder_stack_id
+ self._updateNextStack()
self.activeExtruderChanged.emit()
def getStack(self):
diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py
index 57d76b2783..ea3ca30118 100644
--- a/plugins/3MFReader/ThreeMFReader.py
+++ b/plugins/3MFReader/ThreeMFReader.py
@@ -9,18 +9,20 @@ from UM.Math.Vector import Vector
from UM.Scene.SceneNode import SceneNode
from UM.Scene.GroupDecorator import GroupDecorator
from UM.Math.Quaternion import Quaternion
-
from UM.Job import Job
import math
import zipfile
-import xml.etree.ElementTree as ET
+try:
+ import xml.etree.cElementTree as ET
+except ImportError:
+ import xml.etree.ElementTree as ET
## Base implementation for reading 3MF files. Has no support for textures. Only loads meshes!
class ThreeMFReader(MeshReader):
def __init__(self):
- super(ThreeMFReader, self).__init__()
+ super().__init__()
self._supported_extensions = [".3mf"]
self._namespaces = {
@@ -116,4 +118,10 @@ class ThreeMFReader(MeshReader):
except Exception as e:
Logger.log("e", "exception occured in 3mf reader: %s", e)
- return result
+ try: # Selftest - There might be more functions that should fail
+ boundingBox = result.getBoundingBox()
+ boundingBox.isValid()
+ except:
+ return None
+
+ return result
diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto
index 0c4803cc19..289c0a98a0 100644
--- a/plugins/CuraEngineBackend/Cura.proto
+++ b/plugins/CuraEngineBackend/Cura.proto
@@ -13,6 +13,7 @@ message Slice
repeated ObjectList object_lists = 1; // The meshgroups to be printed one after another
SettingList global_settings = 2; // The global settings used for the whole print job
repeated Extruder extruders = 3; // The settings sent to each extruder object
+ repeated SettingExtruder global_inherits_stack = 4; //From which stack the setting would inherit if not defined in a stack.
}
message Extruder
@@ -108,8 +109,14 @@ message Setting {
bytes value = 2; // The value of the setting
}
+message SettingExtruder {
+ string name = 1; //The setting key.
+
+ int32 extruder = 2; //From which extruder stack the setting should inherit.
+}
+
message GCodePrefix {
- bytes data = 2; // Header string to be prenpended before the rest of the gcode sent from the engine
+ bytes data = 2; //Header string to be prepended before the rest of the g-code sent from the engine.
}
message SlicingFinished {
diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py
index aa6f2b0807..a822512218 100644
--- a/plugins/CuraEngineBackend/CuraEngineBackend.py
+++ b/plugins/CuraEngineBackend/CuraEngineBackend.py
@@ -13,9 +13,11 @@ from UM.Resources import Resources
from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then.
from UM.Platform import Platform
+
import cura.Settings
from cura.OneAtATimeIterator import OneAtATimeIterator
+from cura.Settings.ExtruderManager import ExtruderManager
from . import ProcessSlicedLayersJob
from . import ProcessGCodeJob
from . import StartSliceJob
@@ -40,7 +42,8 @@ class CuraEngineBackend(Backend):
def __init__(self):
super().__init__()
- # Find out where the engine is located, and how it is called. This depends on how Cura is packaged and which OS we are running on.
+ # Find out where the engine is located, and how it is called.
+ # This depends on how Cura is packaged and which OS we are running on.
default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", "CuraEngine")
if hasattr(sys, "frozen"):
default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "CuraEngine")
@@ -59,7 +62,7 @@ class CuraEngineBackend(Backend):
self._stored_layer_data = []
self._stored_optimized_layer_data = []
- #Triggers for when to (re)start slicing:
+ # Triggers for when to (re)start slicing:
self._global_container_stack = None
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
self._onGlobalStackChanged()
@@ -68,15 +71,15 @@ class CuraEngineBackend(Backend):
cura.Settings.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
self._onActiveExtruderChanged()
- #When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
- #This timer will group them up, and only slice for the last setting changed signal.
- #TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction.
+ # When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
+ # This timer will group them up, and only slice for the last setting changed signal.
+ # TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction.
self._change_timer = QTimer()
self._change_timer.setInterval(500)
self._change_timer.setSingleShot(True)
self._change_timer.timeout.connect(self.slice)
- #Listeners for receiving messages from the back-end.
+ # Listeners for receiving messages from the back-end.
self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
self._message_handlers["cura.proto.LayerOptimized"] = self._onOptimizedLayerMessage
self._message_handlers["cura.proto.Progress"] = self._onProgressMessage
@@ -86,19 +89,19 @@ class CuraEngineBackend(Backend):
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
self._start_slice_job = None
- self._slicing = False #Are we currently slicing?
- self._restart = False #Back-end is currently restarting?
- self._enabled = True #Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around.
- self._always_restart = True #Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
- self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers.
+ self._slicing = False # Are we currently slicing?
+ self._restart = False # Back-end is currently restarting?
+ self._enabled = True # Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around.
+ self._always_restart = True # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
+ self._process_layers_job = None # The currently active job to process layers, or None if it is not processing layers.
- self._backend_log_max_lines = 20000 # Maximum number of lines to buffer
- self._error_message = None #Pop-up message that shows errors.
+ self._backend_log_max_lines = 20000 # Maximum number of lines to buffer
+ self._error_message = None # Pop-up message that shows errors.
self.backendQuit.connect(self._onBackendQuit)
self.backendConnected.connect(self._onBackendConnected)
- #When a tool operation is in progress, don't slice. So we need to listen for tool operations.
+ # When a tool operation is in progress, don't slice. So we need to listen for tool operations.
Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
@@ -117,9 +120,10 @@ class CuraEngineBackend(Backend):
# \return list of commands and args / parameters.
def getEngineCommand(self):
json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json")
- return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, "-vv"]
+ return [Preferences.getInstance().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""]
- ## Emitted when we get a message containing print duration and material amount. This also implies the slicing has finished.
+ ## Emitted when we get a message containing print duration and material amount.
+ # This also implies the slicing has finished.
# \param time The amount of time the print will take.
# \param material_amount The amount of material the print will use.
printDurationMessage = Signal()
@@ -133,7 +137,7 @@ class CuraEngineBackend(Backend):
## Perform a slice of the scene.
def slice(self):
self._slice_start_time = time()
- if not self._enabled or not self._global_container_stack: #We shouldn't be slicing.
+ if not self._enabled or not self._global_container_stack: # We shouldn't be slicing.
# try again in a short time
self._change_timer.start()
return
@@ -143,10 +147,10 @@ class CuraEngineBackend(Backend):
self._stored_layer_data = []
self._stored_optimized_layer_data = []
- if self._slicing: #We were already slicing. Stop the old job.
+ if self._slicing: # We were already slicing. Stop the old job.
self._terminate()
- if self._process_layers_job: #We were processing layers. Stop that, the layers are going to change soon.
+ if self._process_layers_job: # We were processing layers. Stop that, the layers are going to change soon.
self._process_layers_job.abort()
self._process_layers_job = None
@@ -183,7 +187,7 @@ class CuraEngineBackend(Backend):
self._process.terminate()
Logger.log("d", "Engine process is killed. Received return code %s", self._process.wait())
self._process = None
- except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this.
+ except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this.
Logger.log("d", "Exception occurred while trying to kill the engine %s", str(e))
## Event handler to call when the job to initiate the slicing process is
@@ -204,7 +208,7 @@ class CuraEngineBackend(Backend):
if job.getResult() == StartSliceJob.StartJobResult.SettingError:
if Application.getInstance().getPlatformActivity:
- self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."), lifetime = 10)
+ self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."))
self._error_message.show()
self.backendStateChange.emit(BackendState.Error)
else:
@@ -213,7 +217,7 @@ class CuraEngineBackend(Backend):
if job.getResult() == StartSliceJob.StartJobResult.NothingToSlice:
if Application.getInstance().getPlatformActivity:
- self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable objects found."), lifetime = 10)
+ self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice. No suitable objects found."))
self._error_message.show()
self.backendStateChange.emit(BackendState.Error)
else:
@@ -265,7 +269,7 @@ class CuraEngineBackend(Backend):
# \param instance The setting instance that has changed.
# \param property The property of the setting instance that has changed.
def _onSettingChanged(self, instance, property):
- if property == "value": #Only reslice if the value has changed.
+ if property == "value": # Only reslice if the value has changed.
self._onChanged()
## Called when a sliced layer data message is received from the engine.
@@ -316,7 +320,7 @@ class CuraEngineBackend(Backend):
## Called when a print time message is received from the engine.
#
- # \param message The protobuf message containing the print time and
+ # \param message The protobuff message containing the print time and
# material amount per extruder
def _onPrintTimeMaterialEstimates(self, message):
material_amounts = []
@@ -351,8 +355,8 @@ class CuraEngineBackend(Backend):
#
# \param tool The tool that the user is using.
def _onToolOperationStarted(self, tool):
- self._terminate() # Do not continue slicing once a tool has started
- self._enabled = False # Do not reslice when a tool is doing it's 'thing'
+ self._terminate() # Do not continue slicing once a tool has started
+ self._enabled = False # Do not reslice when a tool is doing it's 'thing'
## Called when the user stops using some tool.
#
@@ -360,13 +364,13 @@ class CuraEngineBackend(Backend):
#
# \param tool The tool that the user was using.
def _onToolOperationStopped(self, tool):
- self._enabled = True # Tool stop, start listening for changes again.
+ self._enabled = True # Tool stop, start listening for changes again.
## Called when the user changes the active view mode.
def _onActiveViewChanged(self):
if Application.getInstance().getController().getActiveView():
view = Application.getInstance().getController().getActiveView()
- if view.getPluginId() == "LayerView": #If switching to layer view, we should process the layers if that hasn't been done yet.
+ if view.getPluginId() == "LayerView": # If switching to layer view, we should process the layers if that hasn't been done yet.
self._layer_view_active = True
# There is data and we're not slicing at the moment
# if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
@@ -391,22 +395,35 @@ class CuraEngineBackend(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()))
+ if extruders:
+ for extruder in extruders:
+ extruder.propertyChanged.disconnect(self._onSettingChanged)
self._global_container_stack = Application.getInstance().getGlobalContainerStack()
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.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()))
+ if extruders:
+ for extruder in extruders:
+ extruder.propertyChanged.connect(self._onSettingChanged)
self._onActiveExtruderChanged()
self._onChanged()
def _onActiveExtruderChanged(self):
+ if self._global_container_stack:
+ # Connect all extruders of the active machine. This might cause a few connects that have already happend,
+ # but that shouldn't cause issues as only new / unique connections are added.
+ extruders = list(ExtruderManager.getInstance().getMachineExtruders(self._global_container_stack.getId()))
+ if extruders:
+ for extruder in extruders:
+ extruder.propertyChanged.connect(self._onSettingChanged)
if self._active_extruder_stack:
- self._active_extruder_stack.propertyChanged.disconnect(self._onSettingChanged)
self._active_extruder_stack.containersChanged.disconnect(self._onChanged)
self._active_extruder_stack = cura.Settings.ExtruderManager.getInstance().getActiveExtruderStack()
if self._active_extruder_stack:
- self._active_extruder_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed.
self._active_extruder_stack.containersChanged.connect(self._onChanged)
diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py
index c2f73cf5b7..7443340c5b 100644
--- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py
+++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py
@@ -56,10 +56,9 @@ class ProcessSlicedLayersJob(Job):
## Remove old layer data (if any)
for node in DepthFirstIterator(self._scene.getRoot()):
- if type(node) is SceneNode and node.getMeshData():
- if node.callDecoration("getLayerData"):
- self._scene.getRoot().removeChild(node)
- Job.yieldThread()
+ if node.callDecoration("getLayerData"):
+ node.getParent().removeChild(node)
+ break
if self._abort_requested:
if self._progress:
self._progress.hide()
@@ -74,7 +73,7 @@ class ProcessSlicedLayersJob(Job):
# instead simply offset all other layers so the lowest layer is always 0.
min_layer_number = 0
for layer in self._layers:
- if(layer.id < min_layer_number):
+ if layer.id < min_layer_number:
min_layer_number = layer.id
current_layer = 0
@@ -98,7 +97,7 @@ class ProcessSlicedLayersJob(Job):
points = numpy.fromstring(polygon.points, dtype="f4") # Convert bytearray to numpy array
if polygon.point_type == 0: # Point2D
points = points.reshape((-1,2)) # We get a linear list of pairs that make up the points, so make numpy interpret them correctly.
- else: # Point3D
+ else: # Point3D
points = points.reshape((-1,3))
line_widths = numpy.fromstring(polygon.line_width, dtype="f4") # Convert bytearray to numpy array
@@ -108,15 +107,14 @@ class ProcessSlicedLayersJob(Job):
# This uses manual array creation + copy rather than numpy.insert since this is
# faster.
new_points = numpy.empty((len(points), 3), numpy.float32)
- if polygon.point_type == 0: # Point2D
- new_points[:,0] = points[:,0]
- new_points[:,1] = layer.height/1000 # layer height value is in backend representation
- new_points[:,2] = -points[:,1]
+ if polygon.point_type == 0: # Point2D
+ new_points[:, 0] = points[:, 0]
+ new_points[:, 1] = layer.height / 1000 # layer height value is in backend representation
+ new_points[:, 2] = -points[:, 1]
else: # Point3D
- new_points[:,0] = points[:,0]
- new_points[:,1] = points[:,2]
- new_points[:,2] = -points[:,1]
-
+ new_points[:, 0] = points[:, 0]
+ new_points[:, 1] = points[:, 2]
+ new_points[:, 2] = -points[:, 1]
this_poly = LayerPolygon.LayerPolygon(layer_data, extruder, line_types, new_points, line_widths)
this_poly.buildCache()
@@ -185,4 +183,3 @@ class ProcessSlicedLayersJob(Job):
else:
if self._progress:
self._progress.hide()
-
diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py
index a726e239e0..5b948c90ab 100644
--- a/plugins/CuraEngineBackend/StartSliceJob.py
+++ b/plugins/CuraEngineBackend/StartSliceJob.py
@@ -13,6 +13,7 @@ from UM.Scene.SceneNode import SceneNode
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Settings.Validator import ValidatorState
+from UM.Settings.SettingRelation import RelationType
from cura.OneAtATimeIterator import OneAtATimeIterator
@@ -24,6 +25,7 @@ class StartJobResult(IntEnum):
SettingError = 3
NothingToSlice = 4
+
## Formatter class that handles token expansion in start/end gcod
class GcodeStartEndFormatter(Formatter):
def get_value(self, key, args, kwargs): # [CodeStyle: get_value is an overridden function from the Formatter class]
@@ -37,6 +39,7 @@ class GcodeStartEndFormatter(Formatter):
Logger.log("w", "Incorrectly formatted placeholder '%s' in start/end gcode", key)
return "{" + str(key) + "}"
+
## Job class that builds up the message of scene data to send to CuraEngine.
class StartSliceJob(Job):
def __init__(self, slice_message):
@@ -71,7 +74,7 @@ class StartSliceJob(Job):
return
# Don't slice if there is a setting with an error value.
- if self._checkStackForErrors(stack):
+ if not Application.getInstance().getMachineManager().isActiveStackValid:
self.setResult(StartJobResult.SettingError)
return
@@ -123,11 +126,15 @@ class StartSliceJob(Job):
if temp_list:
object_groups.append(temp_list)
+ # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
+ # able to find a possible sequence or because there are no objects on the build plate (or they are outside
+ # the build volume)
if not object_groups:
self.setResult(StartJobResult.NothingToSlice)
return
self._buildGlobalSettingsMessage(stack)
+ self._buildGlobalInheritsStackMessage(stack)
for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
self._buildExtruderMessage(extruder_stack)
@@ -171,6 +178,7 @@ class StartSliceJob(Job):
Logger.logException("w", "Unable to do token replacement on start/end gcode")
return str(value).encode("utf-8")
+ ## Create extruder message from stack
def _buildExtruderMessage(self, stack):
message = self._slice_message.addRepeatedMessage("extruders")
message.id = int(stack.getMetaDataEntry("position"))
@@ -209,11 +217,54 @@ class StartSliceJob(Job):
else:
setting_message.value = str(value).encode("utf-8")
+ ## Sends for some settings which extruder they should fallback to if not
+ # set.
+ #
+ # This is only set for settings that have the global_inherits_stack
+ # property.
+ #
+ # \param stack The global stack with all settings, from which to read the
+ # global_inherits_stack property.
+ def _buildGlobalInheritsStackMessage(self, stack):
+ for key in stack.getAllKeys():
+ extruder = int(stack.getProperty(key, "global_inherits_stack"))
+ if extruder >= 0: #Set to a specific extruder.
+ setting_extruder = self._slice_message.addRepeatedMessage("global_inherits_stack")
+ setting_extruder.name = key
+ setting_extruder.extruder = extruder
+
+ ## Check if a node has per object settings and ensure that they are set correctly in the message
+ # \param node \type{SceneNode} Node to check.
+ # \param message object_lists message to put the per object settings in
def _handlePerObjectSettings(self, node, message):
stack = node.callDecoration("getStack")
+ # Check if the node has a stack attached to it and the stack has any settings in the top container.
if stack:
- for key in stack.getAllKeys():
+ # Check all settings for relations, so we can also calculate the correct values for dependant settings.
+ changed_setting_keys = set(stack.getTop().getAllKeys())
+ for key in stack.getTop().getAllKeys():
+ instance = stack.getTop().getInstance(key)
+ self._addRelations(changed_setting_keys, instance.definition.relations)
+ Job.yieldThread()
+
+ # Ensure that the engine is aware what the build extruder is
+ if stack.getProperty("machine_extruder_count", "value") > 1:
+ changed_setting_keys.add("extruder_nr")
+
+ # Get values for all changed settings
+ for key in changed_setting_keys:
setting = message.addRepeatedMessage("settings")
setting.name = key
setting.value = str(stack.getProperty(key, "value")).encode("utf-8")
Job.yieldThread()
+
+ ## Recursive function to put all settings that require eachother for value changes in a list
+ # \param relations_set \type{set} Set of keys (strings) of settings that are influenced
+ # \param relations list of relation objects that need to be checked.
+ def _addRelations(self, relations_set, relations):
+ for relation in filter(lambda r: r.role == "value", relations):
+ if relation.type == RelationType.RequiresTarget:
+ continue
+
+ relations_set.add(relation.target.key)
+ self._addRelations(relations_set, relation.target.relations)
\ No newline at end of file
diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py
index 11c5dc5f84..8e3be15ffe 100644
--- a/plugins/GCodeWriter/GCodeWriter.py
+++ b/plugins/GCodeWriter/GCodeWriter.py
@@ -9,6 +9,8 @@ import UM.Settings.ContainerRegistry
from cura.CuraApplication import CuraApplication
from cura.Settings.ExtruderManager import ExtruderManager
+from UM.Settings.InstanceContainer import InstanceContainer
+
import re #For escaping characters in the settings.
import json
@@ -61,6 +63,20 @@ class GCodeWriter(MeshWriter):
return False
+ ## Create a new container with container 2 as base and container 1 written over it.
+ def _createFlattenedContainerInstance(self, instance_container1, instance_container2):
+ flat_container = InstanceContainer(instance_container2.getName())
+ flat_container.setDefinition(instance_container2.getDefinition())
+ flat_container.setMetaData(instance_container2.getMetaData())
+
+ for key in instance_container2.getAllKeys():
+ flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value"))
+
+ for key in instance_container1.getAllKeys():
+ flat_container.setProperty(key, "value", instance_container1.getProperty(key, "value"))
+ return flat_container
+
+
## Serialises a container stack to prepare it for writing at the end of the
# g-code.
#
@@ -74,38 +90,13 @@ class GCodeWriter(MeshWriter):
prefix_length = len(prefix)
container_with_profile = stack.findContainer({"type": "quality"})
- machine_manager = CuraApplication.getInstance().getMachineManager()
-
- # Duplicate the current quality profile and update it with any user settings.
- flat_quality_id = machine_manager.duplicateContainer(container_with_profile.getId())
-
- flat_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = flat_quality_id)[0]
- flat_quality._dirty = False
- user_settings = stack.getTop()
-
- # We don't want to send out any signals, so disconnect them.
- flat_quality.propertyChanged.disconnectAll()
-
- for key in user_settings.getAllKeys():
- flat_quality.setProperty(key, "value", user_settings.getProperty(key, "value"))
-
- serialized = flat_quality.serialize()
-
+ flat_global_container = self._createFlattenedContainerInstance(stack.getTop(),container_with_profile)
+ serialized = flat_global_container.serialize()
data = {"global_quality": serialized}
- manager = ExtruderManager.getInstance()
- for extruder in manager.getMachineExtruders(stack.getId()):
+
+ for extruder in ExtruderManager.getInstance().getMachineExtruders(stack.getId()):
extruder_quality = extruder.findContainer({"type": "quality"})
-
- flat_extruder_quality_id = machine_manager.duplicateContainer(extruder_quality.getId())
- flat_extruder_quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id=flat_extruder_quality_id)[0]
- flat_extruder_quality._dirty = False
- extruder_user_settings = extruder.getTop()
-
- # We don't want to send out any signals, so disconnect them.
- flat_extruder_quality.propertyChanged.disconnectAll()
-
- for key in extruder_user_settings.getAllKeys():
- flat_extruder_quality.setProperty(key, "value", extruder_user_settings.getProperty(key, "value"))
+ flat_extruder_quality = self._createFlattenedContainerInstance(extruder.getTop(), extruder_quality)
extruder_serialized = flat_extruder_quality.serialize()
data.setdefault("extruder_quality", []).append(extruder_serialized)
diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py
index 87e1a30834..7c7246361e 100644
--- a/plugins/LayerView/LayerView.py
+++ b/plugins/LayerView/LayerView.py
@@ -11,6 +11,7 @@ from UM.Math.Color import Color
from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Job import Job
from UM.Preferences import Preferences
+from UM.Logger import Logger
from UM.View.RenderBatch import RenderBatch
from UM.View.GL.OpenGL import OpenGL
@@ -32,9 +33,9 @@ class LayerView(View):
def __init__(self):
super().__init__()
self._shader = None
- self._selection_shader = None
+ self._ghost_shader = None
self._num_layers = 0
- self._layer_percentage = 0 # what percentage of layers need to be shown (SLider gives value between 0 - 100)
+ self._layer_percentage = 0 # what percentage of layers need to be shown (Slider gives value between 0 - 100)
self._proxy = LayerViewProxy.LayerViewProxy()
self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
self._max_layers = 0
@@ -43,12 +44,14 @@ class LayerView(View):
self._current_layer_jumps = None
self._top_layers_job = None
self._activity = False
+ self._old_max_layers = 0
Preferences.getInstance().addPreference("view/top_layer_count", 5)
+ Preferences.getInstance().addPreference("view/only_show_top_layers", False)
Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count"))
-
+ self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers"))
self._busy = False
def getActivity(self):
@@ -81,10 +84,9 @@ class LayerView(View):
scene = self.getController().getScene()
renderer = self.getRenderer()
- if not self._selection_shader:
- self._selection_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
- self._selection_shader.setUniformValue("u_color", Color(32, 32, 32, 128))
-
+ if not self._ghost_shader:
+ self._ghost_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader"))
+ self._ghost_shader.setUniformValue("u_color", Color(32, 32, 32, 96))
for node in DepthFirstIterator(scene.getRoot()):
# We do not want to render ConvexHullNode as it conflicts with the bottom layers.
# However, it is somewhat relevant when the node is selected, so do render it then.
@@ -93,14 +95,13 @@ class LayerView(View):
if not node.render(renderer):
if node.getMeshData() and node.isVisible():
- if Selection.isSelected(node):
- renderer.queueNode(node, transparent = True, shader = self._selection_shader)
+ renderer.queueNode(node, transparent = True, shader = self._ghost_shader)
layer_data = node.callDecoration("getLayerData")
if not layer_data:
continue
# Render all layers below a certain number as line mesh instead of vertices.
- if self._current_layer_num - self._solid_layers > -1:
+ if self._current_layer_num - self._solid_layers > -1 and not self._only_show_top_layers:
start = 0
end = 0
element_counts = layer_data.getElementCounts()
@@ -110,13 +111,13 @@ class LayerView(View):
end += counts
# This uses glDrawRangeElements internally to only draw a certain range of lines.
- renderer.queueNode(node, mesh = layer_data, mode = RenderBatch.RenderMode.Lines, range = (start, end))
+ renderer.queueNode(node, mesh = layer_data, mode = RenderBatch.RenderMode.Lines, overlay = True, range = (start, end))
if self._current_layer_mesh:
- renderer.queueNode(node, mesh = self._current_layer_mesh)
+ renderer.queueNode(node, mesh = self._current_layer_mesh, overlay = True)
if self._current_layer_jumps:
- renderer.queueNode(node, mesh = self._current_layer_jumps)
+ renderer.queueNode(node, mesh = self._current_layer_jumps, overlay = True)
def setLayer(self, value):
if self._current_layer_num != value:
@@ -126,14 +127,12 @@ class LayerView(View):
if self._current_layer_num > self._max_layers:
self._current_layer_num = self._max_layers
- self.resetLayerData()
self._startUpdateTopLayers()
self.currentLayerNumChanged.emit()
def calculateMaxLayers(self):
scene = self.getController().getScene()
- renderer = self.getRenderer() # TODO: @UnusedVariable
self._activity = True
self._old_max_layers = self._max_layers
@@ -199,7 +198,7 @@ class LayerView(View):
if not job.getResult():
return
-
+ self.resetLayerData() # Reset the layer data only when job is done. Doing it now prevents "blinking" data.
self._current_layer_mesh = job.getResult().get("layers")
self._current_layer_jumps = job.getResult().get("jumps")
self._controller.getScene().sceneChanged.emit(self._controller.getScene().getRoot())
@@ -207,14 +206,15 @@ class LayerView(View):
self._top_layers_job = None
def _onPreferencesChanged(self, preference):
- if preference != "view/top_layer_count":
+ if preference != "view/top_layer_count" and preference != "view/only_show_top_layers":
return
self._solid_layers = int(Preferences.getInstance().getValue("view/top_layer_count"))
+ self._only_show_top_layers = bool(Preferences.getInstance().getValue("view/only_show_top_layers"))
- self.resetLayerData()
self._startUpdateTopLayers()
+
class _CreateTopLayersJob(Job):
def __init__(self, scene, layer_number, solid_layers):
super().__init__()
@@ -242,20 +242,20 @@ class _CreateTopLayersJob(Job):
try:
layer = layer_data.getLayer(layer_number).createMesh()
- except Exception as e:
- print(e)
+ except Exception:
+ Logger.logException("w", "An exception occurred while creating layer mesh.")
return
if not layer or layer.getVertices() is None:
continue
- layer_mesh.addIndices(layer_mesh._vertex_count+layer.getIndices())
+ layer_mesh.addIndices(layer_mesh.getVertexCount() + layer.getIndices())
layer_mesh.addVertices(layer.getVertices())
# Scale layer color by a brightness factor based on the current layer number
# This will result in a range of 0.5 - 1.0 to multiply colors by.
- brightness = numpy.ones((1,4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0
- brightness[0, 3] = 1.0;
+ brightness = numpy.ones((1, 4), dtype=numpy.float32) * (2.0 - (i / self._solid_layers)) / 2.0
+ brightness[0, 3] = 1.0
layer_mesh.addColors(layer.getColors() * brightness)
if self._cancel:
@@ -271,7 +271,7 @@ class _CreateTopLayersJob(Job):
if not jump_mesh or jump_mesh.getVertices() is None:
jump_mesh = None
- self.setResult({ "layers": layer_mesh.build(), "jumps": jump_mesh })
+ self.setResult({"layers": layer_mesh.build(), "jumps": jump_mesh})
def cancel(self):
self._cancel = True
diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py
index 2182a0748a..0329fb53e2 100644
--- a/plugins/SolidView/SolidView.py
+++ b/plugins/SolidView/SolidView.py
@@ -3,6 +3,7 @@
from UM.View.View import View
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
+from UM.Scene.Selection import Selection
from UM.Resources import Resources
from UM.Application import Application
from UM.Preferences import Preferences
@@ -57,25 +58,28 @@ class SolidView(View):
# renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(),mode = Renderer.RenderLines)
uniforms = {}
- if self._extruders_model.rowCount() > 0:
+ if self._extruders_model.rowCount() == 0:
+ material = Application.getInstance().getGlobalContainerStack().findContainer({ "type": "material" })
+ material_color = material.getMetaDataEntry("color_code", default = self._extruders_model.defaultColours[0]) if material else self._extruders_model.defaultColours[0]
+ else:
# Get color to render this mesh in from ExtrudersModel
extruder_index = 0
extruder_id = node.callDecoration("getActiveExtruder")
if extruder_id:
extruder_index = max(0, self._extruders_model.find("id", extruder_id))
- extruder_color = self._extruders_model.getItem(extruder_index)["colour"]
- try:
- # Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs
- # an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0])
- uniforms["diffuse_color"] = [
- int(extruder_color[1:3], 16) / 255,
- int(extruder_color[3:5], 16) / 255,
- int(extruder_color[5:7], 16) / 255,
- 1.0
- ]
- except ValueError:
- pass
+ material_color = self._extruders_model.getItem(extruder_index)["colour"]
+ try:
+ # Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs
+ # an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0])
+ uniforms["diffuse_color"] = [
+ int(material_color[1:3], 16) / 255,
+ int(material_color[3:5], 16) / 255,
+ int(material_color[5:7], 16) / 255,
+ 1.0
+ ]
+ except ValueError:
+ pass
if hasattr(node, "_outside_buildarea"):
if node._outside_buildarea:
@@ -84,7 +88,7 @@ class SolidView(View):
renderer.queueNode(node, shader = self._enabled_shader, uniforms = uniforms)
else:
renderer.queueNode(node, material = self._enabled_shader, uniforms = uniforms)
- if node.callDecoration("isGroup"):
+ if node.callDecoration("isGroup") and Selection.isSelected(node):
renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(), mode = Renderer.RenderLines)
def endRendering(self):
diff --git a/plugins/UltimakerMachineActions/BedLevelMachineAction.py b/plugins/UltimakerMachineActions/BedLevelMachineAction.py
index 7dad841340..12df7d6843 100644
--- a/plugins/UltimakerMachineActions/BedLevelMachineAction.py
+++ b/plugins/UltimakerMachineActions/BedLevelMachineAction.py
@@ -18,6 +18,11 @@ class BedLevelMachineAction(MachineAction):
pass
def _reset(self):
+ self._bed_level_position = 0
+ pass
+
+ @pyqtSlot()
+ def startBedLeveling(self):
self._bed_level_position = 0
printer_output_devices = self._getPrinterOutputDevices()
if printer_output_devices:
@@ -52,4 +57,5 @@ class BedLevelMachineAction(MachineAction):
output_device.moveHead(0, 0, -3)
self._bed_level_position += 1
elif self._bed_level_position >= 3:
+ output_device.sendCommand("M18") # Turn off all motors so the user can move the axes
self.setFinished()
\ No newline at end of file
diff --git a/plugins/UltimakerMachineActions/BedLevelMachineAction.qml b/plugins/UltimakerMachineActions/BedLevelMachineAction.qml
index d043c20df5..c7c4074120 100644
--- a/plugins/UltimakerMachineActions/BedLevelMachineAction.qml
+++ b/plugins/UltimakerMachineActions/BedLevelMachineAction.qml
@@ -47,37 +47,37 @@ Cura.MachineAction
text: catalog.i18nc("@label", "For every position; insert a piece of paper under the nozzle and adjust the print bed height. The print bed height is right when the paper is slightly gripped by the tip of the nozzle.")
}
- Item
+ Row
{
id: bedlevelingWrapper
anchors.top: bedlevelingText.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
- height: skipBedlevelingButton.height
- width: bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height < bedLevelMachineAction.width ? bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height : bedLevelMachineAction.width
+ width: childrenRect.width
+ spacing: UM.Theme.getSize("default_margin").width
+
Button
{
- id: bedlevelingButton
- anchors.top: parent.top
- anchors.left: parent.left
- text: catalog.i18nc("@action:button","Move to Next Position");
+ id: startBedLevelingButton
+ text: catalog.i18nc("@action:button","Start Bed Leveling")
onClicked:
{
- manager.moveToNextLevelPosition()
+ startBedLevelingButton.visible = false;
+ bedlevelingButton.visible = true;
+ checkupMachineAction.heatupHotendStarted = false;
+ checkupMachineAction.heatupBedStarted = false;
+ manager.startCheck();
}
}
Button
{
- id: skipBedlevelingButton
- anchors.top: parent.width < bedLevelMachineAction.width ? parent.top : bedlevelingButton.bottom
- anchors.topMargin: parent.width < bedLevelMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height/2
- anchors.left: parent.width < bedLevelMachineAction.width ? bedlevelingButton.right : parent.left
- anchors.leftMargin: parent.width < bedLevelMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
- text: catalog.i18nc("@action:button","Skip bed leveling");
+ id: bedlevelingButton
+ text: catalog.i18nc("@action:button","Move to Next Position")
+ visible: false
onClicked:
{
- manager.setFinished()
+ manager.moveToNextLevelPosition();
}
}
}
diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py
index b46c92a82c..355b4dd0c1 100644
--- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py
+++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py
@@ -155,6 +155,7 @@ class UMOCheckupMachineAction(MachineAction):
if output_devices:
self._output_device = output_devices[0]
try:
+ self._output_device.sendCommand("M18") # Turn off all motors so the user can move the axes
self._output_device.startPollEndstop()
self._output_device.bedTemperatureChanged.connect(self.bedTemperatureChanged)
self._output_device.hotendTemperaturesChanged.connect(self.hotendTemperatureChanged)
diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml
index 14ed1e2c51..0bdbc27527 100644
--- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml
+++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml
@@ -39,38 +39,26 @@ Cura.MachineAction
text: catalog.i18nc("@label", "It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional");
}
- Item
+ Row
{
id: startStopButtons
anchors.top: pageDescription.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
- height: childrenRect.height
- width: startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height < checkupMachineAction.width ? startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height : checkupMachineAction.width
+ width: childrenRect.width
+ spacing: UM.Theme.getSize("default_margin").width
Button
{
id: startCheckButton
- anchors.top: parent.top
- anchors.left: parent.left
text: catalog.i18nc("@action:button","Start Printer Check");
onClicked:
{
- checkupMachineAction.heatupHotendStarted = false
- checkupMachineAction.heatupBedStarted = false
- manager.startCheck()
+ checkupMachineAction.heatupHotendStarted = false;
+ checkupMachineAction.heatupBedStarted = false;
+ manager.startCheck();
+ startCheckButton.visible = false;
}
}
-
- Button
- {
- id: skipCheckButton
- anchors.top: parent.width < checkupMachineAction.width ? parent.top : startCheckButton.bottom
- anchors.topMargin: parent.width < checkupMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height/2
- anchors.left: parent.width < checkupMachineAction.width ? startCheckButton.right : parent.left
- anchors.leftMargin: parent.width < checkupMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
- text: catalog.i18nc("@action:button", "Skip Printer Check");
- onClicked: manager.setFinished()
- }
}
Item
diff --git a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml
index 37e4eae2d3..0c9b80c010 100644
--- a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml
+++ b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.qml
@@ -56,29 +56,21 @@ Cura.MachineAction
wrapMode: Text.WordWrap
text: catalog.i18nc("@label", "Cura requires these new features and thus your firmware will most likely need to be upgraded. You can do so now.");
}
- Item
+ Row
{
anchors.top: upgradeText2.bottom
anchors.topMargin: UM.Theme.getSize("default_margin").height
anchors.horizontalCenter: parent.horizontalCenter
- width: upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height < upgradeFirmwareMachineAction.width ? upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height : upgradeFirmwareMachineAction.width
+ width: childrenRect.width
+ spacing: UM.Theme.getSize("default_margin").width
Button
{
id: upgradeButton
- anchors.top: parent.top
- anchors.left: parent.left
text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware");
- onClicked: Cura.USBPrinterManager.updateAllFirmware()
- }
- Button
- {
- id: skipUpgradeButton
- anchors.top: parent.width < upgradeFirmwareMachineAction.width ? parent.top : upgradeButton.bottom
- anchors.topMargin: parent.width < upgradeFirmwareMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height / 2
- anchors.left: parent.width < upgradeFirmwareMachineAction.width ? upgradeButton.right : parent.left
- anchors.leftMargin: parent.width < upgradeFirmwareMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
- text: catalog.i18nc("@action:button", "Skip Upgrade");
- onClicked: manager.setFinished()
+ onClicked:
+ {
+ Cura.USBPrinterManager.updateAllFirmware()
+ }
}
}
}
diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json
index 9866e1a35d..1c009877ac 100644
--- a/resources/definitions/fdmprinter.def.json
+++ b/resources/definitions/fdmprinter.def.json
@@ -150,6 +150,8 @@
"label": "Number extruders",
"description": "Number of extruder trains. An extruder train is the combination of a feeder, bowden tube, and nozzle.",
"default_value": 1,
+ "minimum_value": "1",
+ "maximum_value": "16",
"type": "int",
"settable_per_mesh": false,
"settable_per_extruder": false,
@@ -1064,6 +1066,7 @@
"unit": "[[mm³,°C]]",
"type": "str",
"default_value": "[[3.5,200],[7.0,240]]",
+ "enabled": "False",
"comments": "old enabled function: material_flow_dependent_temperature",
"settable_per_mesh": false,
"settable_per_extruder": true
@@ -1236,30 +1239,28 @@
"default_value": false,
"enabled": "retraction_enable",
"settable_per_mesh": false,
- "settable_per_extruder": true,
- "children": {
- "retraction_hop_only_when_collides": {
- "label": "Z Hop Only Over Printed Parts",
- "description": "Only perform a Z Hop when moving over printed parts which cannot be avoided by horizontal motion by Avoid Printed Parts when Traveling.",
- "type": "bool",
- "default_value": false,
- "enabled": "retraction_enable and retraction_hop_enabled and travel_avoid_other_parts",
- "settable_per_mesh": false,
- "settable_per_extruder": true
- },
- "retraction_hop": {
- "label": "Z Hop Height",
- "description": "The height difference when performing a Z Hop.",
- "unit": "mm",
- "type": "float",
- "default_value": 1,
- "minimum_value_warning": "-0.0001",
- "maximum_value_warning": "10",
- "enabled": "retraction_enable and retraction_hop_enabled",
- "settable_per_mesh": false,
- "settable_per_extruder": true
- }
- }
+ "settable_per_extruder": true
+ },
+ "retraction_hop_only_when_collides": {
+ "label": "Z Hop Only Over Printed Parts",
+ "description": "Only perform a Z Hop when moving over printed parts which cannot be avoided by horizontal motion by Avoid Printed Parts when Traveling.",
+ "type": "bool",
+ "default_value": false,
+ "enabled": "retraction_enable and retraction_hop_enabled and travel_avoid_other_parts",
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
+ },
+ "retraction_hop": {
+ "label": "Z Hop Height",
+ "description": "The height difference when performing a Z Hop.",
+ "unit": "mm",
+ "type": "float",
+ "default_value": 1,
+ "minimum_value_warning": "-0.0001",
+ "maximum_value_warning": "10",
+ "enabled": "retraction_enable and retraction_hop_enabled",
+ "settable_per_mesh": false,
+ "settable_per_extruder": true
},
"material_standby_temperature":
{
@@ -1511,14 +1512,44 @@
},
"speed_layer_0": {
"label": "Initial Layer Speed",
- "description": "The print speed for the initial layer. A lower value is advised to improve adhesion to the build plate.",
+ "description": "The speed for the initial layer. A lower value is advised to improve adhesion to the build plate.",
"unit": "mm/s",
"type": "float",
"default_value": 30,
"minimum_value": "0.1",
"maximum_value": "299792458000",
"maximum_value_warning": "300",
- "settable_per_mesh": true
+ "settable_per_mesh": true,
+ "children":
+ {
+ "speed_print_layer_0":
+ {
+ "label": "Initial Layer Print Speed",
+ "description": "The speed of printing for the initial layer. A lower value is advised to improve adhesion to the build plate.",
+ "unit": "mm/s",
+ "type": "float",
+ "default_value": 30,
+ "value": "speed_layer_0",
+ "minimum_value": "0.1",
+ "maximum_value": "299792458000",
+ "maximum_value_warning": "300",
+ "settable_per_mesh": true
+ },
+ "speed_travel_layer_0":
+ {
+ "label": "Initial Layer Travel Speed",
+ "description": "The speed of travel moves in the initial layer. A lower value is advised to prevent pulling previously printed parts away from the build plate.",
+ "unit": "mm/s",
+ "type": "float",
+ "default_value": 60,
+ "value": "speed_layer_0 * speed_travel / speed_print",
+ "minimum_value": "0.1",
+ "maximum_value": "299792458000",
+ "maximum_value_warning": "300",
+ "settable_per_extruder": true,
+ "settable_per_mesh": false
+ }
+ }
},
"skirt_brim_speed": {
"label": "Skirt/Brim Speed",
@@ -1546,8 +1577,8 @@
"settable_per_mesh": false,
"settable_per_extruder": false
},
-
-
+
+
"acceleration_enabled": {
"label": "Enable Acceleration Control",
"description": "Enables adjusting the print head acceleration. Increasing the accelerations can reduce printing time at the cost of print quality.",
@@ -1718,7 +1749,38 @@
"minimum_value_warning": "100",
"maximum_value_warning": "10000",
"enabled": "acceleration_enabled",
- "settable_per_mesh": true
+ "settable_per_mesh": true,
+ "children": {
+ "acceleration_print_layer_0":
+ {
+ "label": "Initial Layer Print Acceleration",
+ "description": "The acceleration during the printing of the initial layer.",
+ "unit": "mm/s",
+ "type": "float",
+ "default_value": 3000,
+ "value": "acceleration_layer_0",
+ "minimum_value": "0.1",
+ "minimum_value_warning": "100",
+ "maximum_value_warning": "10000",
+ "enabled": "acceleration_enabled",
+ "settable_per_mesh": true
+ },
+ "acceleration_travel_layer_0":
+ {
+ "label": "Initial Layer Travel Acceleration",
+ "description": "The acceleration for travel moves in the initial layer.",
+ "unit": "mm/s",
+ "type": "float",
+ "default_value": 3000,
+ "value": "acceleration_layer_0 * acceleration_travel / acceleration_print",
+ "minimum_value": "0.1",
+ "minimum_value_warning": "100",
+ "maximum_value_warning": "10000",
+ "enabled": "acceleration_enabled",
+ "settable_per_extruder": true,
+ "settable_per_mesh": false
+ }
+ }
},
"acceleration_skirt_brim": {
"label": "Skirt/Brim Acceleration",
@@ -1734,8 +1796,8 @@
"settable_per_mesh": false
},
-
-
+
+
"jerk_enabled": {
"label": "Enable Jerk Control",
"description": "Enables adjusting the jerk of print head when the velocity in the X or Y axis changes. Increasing the jerk can reduce printing time at the cost of print quality.",
@@ -1906,7 +1968,38 @@
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"enabled": "jerk_enabled",
- "settable_per_mesh": true
+ "settable_per_mesh": true,
+ "children": {
+ "jerk_print_layer_0":
+ {
+ "label": "Initial Layer Print Jerk",
+ "description": "The maximum instantaneous velocity change during the printing of the initial layer.",
+ "unit": "mm/s",
+ "type": "float",
+ "default_value": 20,
+ "value": "jerk_layer_0",
+ "minimum_value": "0.1",
+ "minimum_value_warning": "5",
+ "maximum_value_warning": "50",
+ "enabled": "jerk_enabled",
+ "settable_per_mesh": true
+ },
+ "jerk_travel_layer_0":
+ {
+ "label": "Initial Layer Travel Jerk",
+ "description": "The acceleration for travel moves in the initial layer.",
+ "unit": "mm/s",
+ "type": "float",
+ "default_value": 20,
+ "value": "jerk_layer_0 * jerk_travel / jerk_print",
+ "minimum_value": "0.1",
+ "minimum_value_warning": "5",
+ "maximum_value_warning": "50",
+ "enabled": "jerk_enabled",
+ "settable_per_extruder": true,
+ "settable_per_mesh": false
+ }
+ }
},
"jerk_skirt_brim": {
"label": "Skirt/Brim Jerk",
@@ -3378,8 +3471,8 @@
"type": "float",
"minimum_value": "0",
"maximum_value_warning": "9999",
- "default_value": 0,
- "value": "9999 if draft_shield_height_limitation == 'full' and draft_shield_enabled else 0.0",
+ "default_value": 10,
+ "value": "10",
"enabled": "draft_shield_enabled and draft_shield_height_limitation == \"limited\"",
"settable_per_mesh": false,
"settable_per_extruder": false
diff --git a/resources/definitions/mankati_fullscale_xt_plus.def.json b/resources/definitions/mankati_fullscale_xt_plus.def.json
index eb8b99aaf9..72c8ebdd86 100644
--- a/resources/definitions/mankati_fullscale_xt_plus.def.json
+++ b/resources/definitions/mankati_fullscale_xt_plus.def.json
@@ -54,13 +54,13 @@
"speed_travel": { "default_value": 150 },
"speed_layer_0": {
"minimum_value": "0.1",
- "default": 15.0
+ "default_value": 15.0
},
- "infill_overlap": { "default": 10 },
- "cool_fan_enabled": { "default": false },
- "cool_fan_speed": { "default": 0 },
- "skirt_line_count": { "default": 3 },
- "skirt_gap": { "default": 4 },
- "skirt_brim_minimal_length": { "default": 200 }
+ "infill_overlap": { "default_value": 10 },
+ "cool_fan_enabled": { "default_value": false },
+ "cool_fan_speed": { "default_value": 0 },
+ "skirt_line_count": { "default_value": 3 },
+ "skirt_gap": { "default_value": 4 },
+ "skirt_brim_minimal_length": { "default_value": 200 }
}
}
diff --git a/resources/definitions/printrbot_simple.def.json b/resources/definitions/printrbot_simple.def.json
index 0116ba6244..a1963fe20e 100644
--- a/resources/definitions/printrbot_simple.def.json
+++ b/resources/definitions/printrbot_simple.def.json
@@ -7,6 +7,7 @@
"visible": true,
"author": "Calvindog717",
"manufacturer": "PrintrBot",
+ "category": "Other",
"file_formats": "text/x-gcode"
},
diff --git a/resources/definitions/ultimaker2.def.json b/resources/definitions/ultimaker2.def.json
index d8e73a7e14..9985dea764 100644
--- a/resources/definitions/ultimaker2.def.json
+++ b/resources/definitions/ultimaker2.def.json
@@ -8,6 +8,7 @@
"author": "Ultimaker",
"manufacturer": "Ultimaker",
"category": "Ultimaker",
+ "weight": 3,
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker2.png",
"platform": "ultimaker2_platform.obj",
@@ -82,10 +83,10 @@
"default_value": 45
},
"material_print_temperature": {
- "enabled": "False"
+ "enabled": "not (material_flow_dependent_temperature) and machine_gcode_flavor != \"UltiGCode\""
},
"material_bed_temperature": {
- "enabled": "False"
+ "enabled": "machine_heated_bed and machine_gcode_flavor != \"UltiGCode\""
},
"machine_max_feedrate_x": {
"default_value": 300
@@ -103,22 +104,22 @@
"default_value": 3000
},
"material_diameter": {
- "enabled": "False"
+ "enabled": "machine_gcode_flavor != \"UltiGCode\""
},
"material_flow": {
- "enabled": "False"
+ "enabled": "machine_gcode_flavor != \"UltiGCode\""
},
"retraction_amount": {
- "enabled": "False"
+ "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
},
"retraction_speed": {
- "enabled": "False"
+ "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
},
"retraction_retract_speed": {
- "enabled": "False"
+ "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
},
"retraction_prime_speed": {
- "enabled": "False"
+ "enabled": "retraction_enable and machine_gcode_flavor != \"UltiGCode\""
}
}
}
diff --git a/resources/definitions/ultimaker2_extended.def.json b/resources/definitions/ultimaker2_extended.def.json
index fb443365f2..889046694c 100644
--- a/resources/definitions/ultimaker2_extended.def.json
+++ b/resources/definitions/ultimaker2_extended.def.json
@@ -7,6 +7,7 @@
"author": "Ultimaker",
"manufacturer": "Ultimaker",
"category": "Ultimaker",
+ "weight": 3,
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker2.png",
"platform": "ultimaker2_platform.obj",
diff --git a/resources/definitions/ultimaker2_extended_plus.def.json b/resources/definitions/ultimaker2_extended_plus.def.json
index f4190be83d..ea97033878 100644
--- a/resources/definitions/ultimaker2_extended_plus.def.json
+++ b/resources/definitions/ultimaker2_extended_plus.def.json
@@ -7,6 +7,7 @@
"author": "Ultimaker",
"manufacturer": "Ultimaker",
"category": "Ultimaker",
+ "weight": 2,
"file_formats": "text/x-gcode",
"platform": "ultimaker2_platform.obj",
"platform_texture": "Ultimaker2ExtendedPlusbackplate.png",
diff --git a/resources/definitions/ultimaker2_go.def.json b/resources/definitions/ultimaker2_go.def.json
index 27b179eef9..107d413296 100644
--- a/resources/definitions/ultimaker2_go.def.json
+++ b/resources/definitions/ultimaker2_go.def.json
@@ -7,6 +7,7 @@
"author": "Ultimaker",
"manufacturer": "Ultimaker",
"category": "Ultimaker",
+ "weight": 3,
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker2.png",
"platform": "ultimaker2go_platform.obj",
diff --git a/resources/definitions/ultimaker2_plus.def.json b/resources/definitions/ultimaker2_plus.def.json
index d5a7c9f4f1..4c7279b178 100644
--- a/resources/definitions/ultimaker2_plus.def.json
+++ b/resources/definitions/ultimaker2_plus.def.json
@@ -7,6 +7,7 @@
"author": "Ultimaker",
"manufacturer": "Ultimaker",
"category": "Ultimaker",
+ "weight": 1,
"file_formats": "text/x-gcode",
"platform": "ultimaker2_platform.obj",
"platform_texture": "Ultimaker2Plusbackplate.png",
diff --git a/resources/definitions/ultimaker_original.def.json b/resources/definitions/ultimaker_original.def.json
index d2174ece80..3dd4766afa 100644
--- a/resources/definitions/ultimaker_original.def.json
+++ b/resources/definitions/ultimaker_original.def.json
@@ -8,6 +8,7 @@
"author": "Ultimaker",
"manufacturer": "Ultimaker",
"category": "Ultimaker",
+ "weight": 4,
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker.png",
"platform": "ultimaker_platform.stl",
@@ -15,7 +16,7 @@
"preferred_material": "*pla*",
"preferred_quality": "*normal*",
"first_start_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel"],
- "supported_actions": ["UMOCheckup", "UpgradeFirmware", "BedLevel", "UMOUpgradeSelection"]
+ "supported_actions": ["UMOUpgradeSelection", "UMOCheckup", "BedLevel", "UpgradeFirmware"]
},
"overrides": {
diff --git a/resources/definitions/ultimaker_original_plus.def.json b/resources/definitions/ultimaker_original_plus.def.json
index 4d39a39a78..4f787896d0 100644
--- a/resources/definitions/ultimaker_original_plus.def.json
+++ b/resources/definitions/ultimaker_original_plus.def.json
@@ -7,6 +7,7 @@
"author": "Ultimaker",
"manufacturer": "Ultimaker",
"category": "Ultimaker",
+ "weight": 4,
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker.png",
"platform": "ultimaker2_platform.obj",
diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml
index 67bc5fe149..ed88433e33 100644
--- a/resources/qml/Actions.qml
+++ b/resources/qml/Actions.qml
@@ -27,6 +27,7 @@ Item
property alias multiplyObject: multiplyObjectAction;
+ property alias selectAll: selectAllAction;
property alias deleteAll: deleteAllAction;
property alias reloadAll: reloadAllAction;
property alias resetAllTranslation: resetAllTranslationAction;
@@ -119,7 +120,7 @@ Item
Action
{
id: updateProfileAction;
- enabled: Cura.MachineManager.isGlobalStackValid && Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId)
+ enabled: Cura.MachineManager.isActiveStackValid && Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId)
text: catalog.i18nc("@action:inmenu menubar:profile","&Update profile with current settings");
onTriggered: Cura.MachineManager.updateQualityContainerFromUserContainer()
}
@@ -135,7 +136,7 @@ Item
Action
{
id: addProfileAction;
- enabled: Cura.MachineManager.isGlobalStackValid && Cura.MachineManager.hasUserSettings
+ enabled: Cura.MachineManager.isActiveStackValid && Cura.MachineManager.hasUserSettings
text: catalog.i18nc("@action:inmenu menubar:profile","&Create profile from current settings...");
}
@@ -230,6 +231,16 @@ Item
iconName: "edit-duplicate"
}
+ Action
+ {
+ id: selectAllAction;
+ text: catalog.i18nc("@action:inmenu menubar:edit","&Select All Objects");
+ enabled: UM.Controller.toolsEnabled;
+ iconName: "edit-select-all";
+ shortcut: "Ctrl+A";
+ onTriggered: Printer.selectAll();
+ }
+
Action
{
id: deleteAllAction;
diff --git a/resources/qml/AddMachineDialog.qml b/resources/qml/AddMachineDialog.qml
index 38221030ea..a2b7c1586f 100644
--- a/resources/qml/AddMachineDialog.qml
+++ b/resources/qml/AddMachineDialog.qml
@@ -16,12 +16,24 @@ UM.Dialog
{
id: base
title: catalog.i18nc("@title:window", "Add Printer")
- property string activeManufacturer: "Ultimaker";
+ property bool firstRun: false
+ property string preferredCategory: "Ultimaker"
+ property string activeCategory: preferredCategory
+
+ onVisibilityChanged:
+ {
+ // Reset selection and machine name
+ if (visible) {
+ activeCategory = preferredCategory;
+ machineList.currentIndex = 0;
+ machineName.text = getMachineName();
+ }
+ }
signal machineAdded(string id)
function getMachineName()
{
- var name = machineList.model.getItem(machineList.currentIndex).name
+ var name = machineList.model.getItem(machineList.currentIndex) != undefined ? machineList.model.getItem(machineList.currentIndex).name : ""
return name
}
@@ -36,6 +48,7 @@ UM.Dialog
right: parent.right;
bottom: parent.bottom;
}
+
ListView
{
id: machineList
@@ -43,9 +56,12 @@ UM.Dialog
model: UM.DefinitionContainersModel
{
id: machineDefinitionsModel
- filter: {"visible":true}
+ filter: { "visible": true }
+ sectionProperty: "category"
+ preferredSectionValue: preferredCategory
}
- section.property: "manufacturer"
+
+ section.property: "section"
section.delegate: Button
{
text: section
@@ -76,16 +92,25 @@ UM.Dialog
sourceSize.width: width
sourceSize.height: width
color: palette.windowText
- source: base.activeManufacturer == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right")
+ source: base.activeCategory == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right")
}
}
}
onClicked:
{
- base.activeManufacturer = section;
- machineList.currentIndex = machineList.model.find("manufacturer", section)
- machineName.text = getMachineName()
+ base.activeCategory = section;
+ if (machineList.model.getItem(machineList.currentIndex).section != section) {
+ // Find the first machine from this section
+ for(var i = 0; i < sortedMachineDefinitionsModel.count; i++) {
+ var item = sortedMachineDefinitionsModel.getItem(i);
+ if (item.section == section) {
+ machineList.currentIndex = i;
+ break;
+ }
+ }
+ }
+ machineName.text = getMachineName();
}
}
@@ -114,7 +139,7 @@ UM.Dialog
states: State
{
name: "collapsed";
- when: base.activeManufacturer != model.manufacturer;
+ when: base.activeCategory != model.section;
PropertyChanges { target: machineButton; opacity: 0; height: 0; }
}
diff --git a/resources/qml/AddMachineWizard.qml b/resources/qml/AddMachineWizard.qml
deleted file mode 100644
index 1c743834de..0000000000
--- a/resources/qml/AddMachineWizard.qml
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2015 Ultimaker B.V.
-// Cura is released under the terms of the AGPLv3 or higher.
-
-import QtQuick 2.2
-import QtQuick.Controls 1.1
-import QtQuick.Layouts 1.1
-import QtQuick.Window 2.1
-
-import UM 1.1 as UM
-import Cura 1.0 as Cura
-
-import "WizardPages"
-
-UM.Wizard
-{
- id: base;
-
- title: catalog.i18nc("@title:window", "Add Printer")
-
- // This part is optional
- // This part checks whether there is a printer -> if not: some of the functions (delete for example) are disabled
- firstRun: false
-
- Component.onCompleted: {
- base.appendPage(Qt.resolvedUrl("WizardPages/AddMachine.qml"), catalog.i18nc("@title", "Add Printer"));
- base.currentPage = 0;
- }
-
- Item {
- UM.I18nCatalog { id: catalog; name: "cura"; }
- }
-}
diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml
index d0870991d2..5f45a062b8 100644
--- a/resources/qml/Cura.qml
+++ b/resources/qml/Cura.qml
@@ -54,10 +54,7 @@ UM.MainWindow
Keys.onPressed: {
if (event.key == Qt.Key_Backspace)
{
- if(objectContextMenu.objectId != 0)
- {
- Printer.deleteObject(objectContextMenu.objectId);
- }
+ Cura.Actions.deleteSelection.trigger()
}
}
@@ -121,6 +118,7 @@ UM.MainWindow
MenuItem { action: Cura.Actions.undo; }
MenuItem { action: Cura.Actions.redo; }
MenuSeparator { }
+ MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.deleteSelection; }
MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; }
@@ -540,6 +538,7 @@ UM.MainWindow
MenuItem { action: Cura.Actions.deleteObject; }
MenuItem { action: Cura.Actions.multiplyObject; }
MenuSeparator { }
+ MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.reloadAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; }
@@ -592,6 +591,7 @@ UM.MainWindow
Menu
{
id: contextMenu;
+ MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.reloadAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; }
@@ -677,6 +677,7 @@ UM.MainWindow
id: addMachineDialog
onMachineAdded:
{
+ machineActionsWizard.firstRun = addMachineDialog.firstRun
machineActionsWizard.start(id)
}
}
diff --git a/resources/qml/MachineAction.qml b/resources/qml/MachineAction.qml
index 59fb3946a3..2c43f9f96d 100644
--- a/resources/qml/MachineAction.qml
+++ b/resources/qml/MachineAction.qml
@@ -3,6 +3,10 @@ import QtQuick 2.2
Item
{
id: contentItem
+
+ // Point to the dialog containing the displayItem
+ property var dialog
+
// Connect the finished property change to completed signal.
property var finished: manager.finished
onFinishedChanged: if(manager.finished) {completed()}
diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml
index 7a87eb4f60..4a33b347d3 100644
--- a/resources/qml/MonitorButton.qml
+++ b/resources/qml/MonitorButton.qml
@@ -30,6 +30,8 @@ Rectangle
return UM.Theme.getColor("status_paused")
else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error")
return UM.Theme.getColor("status_stopped")
+ else if (Cura.MachineManager.printerOutputDevices[0].jobState == "offline")
+ return UM.Theme.getColor("status_offline")
else
return UM.Theme.getColor("text")
}
@@ -41,7 +43,10 @@ Rectangle
{
if(!printerConnected)
{
- return catalog.i18nc("@label:", "Please check your printer connections")
+ return catalog.i18nc("@label:", "Not connected to a printer")
+ } else if(Cura.MachineManager.printerOutputDevices[0].jobState == "offline")
+ {
+ return catalog.i18nc("@label:", "Lost connection with the printer")
} else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing")
{
return catalog.i18nc("@label:", "Printing...")
diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml
index 7e5b4efadc..223b7eda85 100644
--- a/resources/qml/Preferences/GeneralPage.qml
+++ b/resources/qml/Preferences/GeneralPage.qml
@@ -192,6 +192,7 @@ UM.PreferencesPage
}
}
+
UM.TooltipArea {
width: childrenRect.width;
height: childrenRect.height;
@@ -215,6 +216,19 @@ UM.PreferencesPage
}
}
}
+ UM.TooltipArea {
+ width: childrenRect.width
+ height: childrenRect.height
+ text: catalog.i18nc("@info:tooltip", "Should only the top layers be displayed in layerview?")
+
+ CheckBox
+ {
+ id: topLayersOnlyCheckbox
+ text: catalog.i18nc("@option:check", "Only display top layer(s) in layer view")
+ checked: boolCheck(UM.Preferences.getValue("view/only_show_top_layers"))
+ onCheckedChanged: UM.Preferences.setValue("view/only_show_top_layers", checked)
+ }
+ }
Item
{
diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml
index 0dc3bb32eb..c149185808 100644
--- a/resources/qml/Preferences/MachinesPage.qml
+++ b/resources/qml/Preferences/MachinesPage.qml
@@ -83,16 +83,17 @@ UM.ManagementPage
Repeater
{
id: machineActionRepeater
- model: Cura.MachineActionManager.getSupportedActions(Cura.MachineManager.getDefinitionByMachineId(base.currentItem.id))
+ model: base.currentItem ? Cura.MachineActionManager.getSupportedActions(Cura.MachineManager.getDefinitionByMachineId(base.currentItem.id)) : null
Button
{
- text: machineActionRepeater.model[index].label;
+ text: machineActionRepeater.model[index].label
onClicked:
{
- actionDialog.content = machineActionRepeater.model[index].displayItem
- machineActionRepeater.model[index].displayItem.reset()
- actionDialog.show()
+ actionDialog.content = machineActionRepeater.model[index].displayItem;
+ machineActionRepeater.model[index].displayItem.reset();
+ actionDialog.title = machineActionRepeater.model[index].label;
+ actionDialog.show();
}
}
}
@@ -106,6 +107,13 @@ UM.ManagementPage
{
contents = content;
content.onCompleted.connect(hide)
+ content.dialog = actionDialog
+ }
+ rightButtons: Button
+ {
+ text: catalog.i18nc("@action:button", "Close")
+ iconName: "dialog-close"
+ onClicked: actionDialog.reject()
}
}
@@ -118,8 +126,14 @@ UM.ManagementPage
spacing: UM.Theme.getSize("default_margin").height
- Label { text: catalog.i18nc("@label", "Type") }
- Label { text: base.currentItem ? base.currentItem.metadata.definition_name : "" }
+ Label
+ {
+ text: catalog.i18nc("@label", "Type")
+ visible: base.currentItem && base.currentItem.metadata
+ }
+ Label {
+ text: (base.currentItem && base.currentItem.metadata) ? base.currentItem.metadata.definition_name : ""
+ }
}
UM.I18nCatalog { id: catalog; name: "uranium"; }
diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml
index 9da5522762..f4a8df1dcf 100644
--- a/resources/qml/Preferences/MaterialsPage.qml
+++ b/resources/qml/Preferences/MaterialsPage.qml
@@ -255,7 +255,7 @@ UM.ManagementPage
else if(result.status == "success")
{
messageDialog.icon = StandardIcon.Information
- messageDialog.text = catalog.i18nc("@info:status", "Successfully exported material to %1").arg(fileUrl)
+ messageDialog.text = catalog.i18nc("@info:status", "Successfully exported material to %1").arg(result.path)
messageDialog.open()
}
CuraApplication.setDefaultPath("dialog_material_path", folder)
diff --git a/resources/qml/Settings/SettingComboBox.qml b/resources/qml/Settings/SettingComboBox.qml
index 5308e45f5a..283e309e6a 100644
--- a/resources/qml/Settings/SettingComboBox.qml
+++ b/resources/qml/Settings/SettingComboBox.qml
@@ -86,18 +86,18 @@ SettingItem
}
}
- onActivated: { forceActiveFocus(); provider.setPropertyValue("value", definition.options[index].key) }
+ onActivated: { forceActiveFocus(); propertyProvider.setPropertyValue("value", definition.options[index].key) }
onModelChanged: updateCurrentIndex();
Connections
{
- target: provider
+ target: propertyProvider
onPropertiesChanged: control.updateCurrentIndex()
}
function updateCurrentIndex() {
for(var i = 0; i < definition.options.length; ++i) {
- if(definition.options[i].key == provider.properties.value) {
+ if(definition.options[i].key == propertyProvider.properties.value) {
currentIndex = i;
return;
}
diff --git a/resources/qml/Settings/SettingExtruder.qml b/resources/qml/Settings/SettingExtruder.qml
index 72c5299b15..d88fc9c457 100644
--- a/resources/qml/Settings/SettingExtruder.qml
+++ b/resources/qml/Settings/SettingExtruder.qml
@@ -105,13 +105,13 @@ SettingItem
onActivated:
{
forceActiveFocus();
- provider.setPropertyValue("value", extruders_model.getItem(index).index)
+ propertyProvider.setPropertyValue("value", extruders_model.getItem(index).index)
}
onModelChanged: updateCurrentIndex();
Connections
{
- target: provider
+ target: propertyProvider
onPropertiesChanged: control.updateCurrentIndex();
}
@@ -119,7 +119,7 @@ SettingItem
{
for(var i = 0; i < extruders_model.rowCount(); ++i)
{
- if(extruders_model.getItem(i).index == provider.properties.value)
+ if(extruders_model.getItem(i).index == propertyProvider.properties.value)
{
currentIndex = i;
return;
diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml
index eb6c37f73c..dae9953690 100644
--- a/resources/qml/Settings/SettingItem.qml
+++ b/resources/qml/Settings/SettingItem.qml
@@ -138,7 +138,7 @@ Item {
{
id: linkedSettingIcon;
- visible: base.settablePerExtruder != "True" && base.showLinkedSettingIcon
+ visible: Cura.MachineManager.activeStackId != Cura.MachineManager.activeMachineId && base.settablePerExtruder != "True" && base.showLinkedSettingIcon
height: parent.height;
width: height;
diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml
index 1542e24f5d..8ab776af6a 100644
--- a/resources/qml/Sidebar.qml
+++ b/resources/qml/Sidebar.qml
@@ -116,6 +116,8 @@ Rectangle
return UM.Theme.getIcon("tab_monitor_paused")
else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error")
return UM.Theme.getIcon("tab_monitor_stopped")
+ else if (Cura.MachineManager.printerOutputDevices[0].jobState == "offline")
+ return UM.Theme.getIcon("tab_monitor_offline")
else
return UM.Theme.getIcon("tab_monitor")
}