diff --git a/cura/CuraActions.py b/cura/CuraActions.py index df26a9a9a6..5c89232639 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -1,10 +1,21 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + from PyQt5.QtCore import QObject, QUrl from PyQt5.QtGui import QDesktopServices from UM.FlameProfiler import pyqtSlot from UM.Event import CallFunctionEvent from UM.Application import Application +from UM.Math.Vector import Vector +from UM.Scene.Selection import Selection +from UM.Operations.GroupedOperation import GroupedOperation +from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation +from UM.Operations.SetTransformOperation import SetTransformOperation +from cura.SetParentOperation import SetParentOperation +from cura.MultiplyObjectsJob import MultiplyObjectsJob +from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation class CuraActions(QObject): def __init__(self, parent = None): @@ -23,5 +34,58 @@ class CuraActions(QObject): event = CallFunctionEvent(self._openUrl, [QUrl("http://github.com/Ultimaker/Cura/issues")], {}) Application.getInstance().functionEvent(event) + ## Center all objects in the selection + @pyqtSlot() + def centerSelection(self) -> None: + operation = GroupedOperation() + for node in Selection.getAllSelectedObjects(): + current_node = node + while current_node.getParent() and current_node.getParent().callDecoration("isGroup"): + current_node = current_node.getParent() + + center_operation = SetTransformOperation(current_node, Vector()) + operation.addOperation(center_operation) + operation.push() + + ## Multiply all objects in the selection + # + # \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.start() + + ## Delete all selected objects. + @pyqtSlot() + def deleteSelection(self) -> None: + if not Application.getInstance().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() + + ## Set the extruder that should be used to print the selection. + # + # \param extruder_id The ID of the extruder stack to use for the selected objects. + @pyqtSlot(str) + def setExtruderForSelection(self, extruder_id: str) -> None: + operation = GroupedOperation() + for node in Selection.getAllSelectedObjects(): + if node.callDecoration("getActiveExtruder") == extruder_id: + continue + operation.addOperation(SetObjectExtruderOperation(node, extruder_id)) + operation.push() + def _openUrl(self, url): - QDesktopServices.openUrl(url) \ No newline at end of file + QDesktopServices.openUrl(url) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c3e5390736..5f71495f77 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -26,6 +26,7 @@ from UM.Message import Message from UM.i18n import i18nCatalog from UM.Workspace.WorkspaceReader import WorkspaceReader from UM.Platform import Platform +from UM.Decorators import deprecated from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation @@ -109,6 +110,10 @@ class CuraApplication(QtApplication): Q_ENUMS(ResourceTypes) def __init__(self): + # this list of dir names will be used by UM to detect an old cura directory + for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]: + Resources.addExpectedDirNameInData(dir_name) + Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura", "resources")) if not hasattr(sys, "frozen"): Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources")) @@ -214,6 +219,7 @@ class CuraApplication(QtApplication): self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity) self.getController().toolOperationStopped.connect(self._onToolOperationStopped) + self.getController().contextMenuRequested.connect(self._onContextMenuRequested) Resources.addType(self.ResourceTypes.QmlFiles, "qml") Resources.addType(self.ResourceTypes.Firmware, "firmware") @@ -803,6 +809,7 @@ class CuraApplication(QtApplication): # Remove all selected objects from the scene. @pyqtSlot() + @deprecated("Moved to CuraActions", "2.6") def deleteSelection(self): if not self.getController().getToolsEnabled(): return @@ -823,6 +830,7 @@ class CuraApplication(QtApplication): ## Remove an object from the scene. # Note that this only removes an object if it is selected. @pyqtSlot("quint64") + @deprecated("Use deleteSelection instead", "2.6") def deleteObject(self, object_id): if not self.getController().getToolsEnabled(): return @@ -850,13 +858,22 @@ class CuraApplication(QtApplication): # \param count number of copies # \param min_offset minimum offset to other objects. @pyqtSlot("quint64", int) + @deprecated("Use CuraActions::multiplySelection", "2.6") def multiplyObject(self, object_id, count, min_offset = 8): - job = MultiplyObjectsJob(object_id, count, min_offset) + node = self.getController().getScene().findObject(object_id) + if not node: + node = Selection.getSelectedObject(0) + + while node.getParent() and node.getParent().callDecoration("isGroup"): + node = node.getParent() + + job = MultiplyObjectsJob([node], count, min_offset) job.start() return ## Center object on platform. @pyqtSlot("quint64") + @deprecated("Use CuraActions::centerSelection", "2.6") def centerObject(self, object_id): node = self.getController().getScene().findObject(object_id) if not node and object_id != 0: # Workaround for tool handles overlapping the selected object @@ -1316,3 +1333,10 @@ class CuraApplication(QtApplication): except Exception as e: Logger.log("e", "Could not check file %s: %s", file_url, e) return False + + def _onContextMenuRequested(self, x: float, y: float) -> None: + # Ensure we select the object if we request a context menu over an object without having a selection. + if not Selection.hasSelection(): + node = self.getController().getScene().findObject(self.getRenderer().getRenderPass("selection").getIdAtPosition(x, y)) + if node: + Selection.add(node) diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 40dbc221d6..a795e0bc10 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -24,9 +24,9 @@ from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation class MultiplyObjectsJob(Job): - def __init__(self, object_id, count, min_offset = 8): + def __init__(self, objects, count, min_offset = 8): super().__init__() - self._object_id = object_id + self._objects = objects self._count = count self._min_offset = min_offset @@ -35,38 +35,42 @@ class MultiplyObjectsJob(Job): dismissable=False, progress=0) status_message.show() scene = Application.getInstance().getController().getScene() - node = scene.findObject(self._object_id) - if not node and self._object_id != 0: # Workaround for tool handles overlapping the selected object - node = Selection.getSelectedObject(0) - - # If object is part of a group, multiply group - current_node = node - while current_node.getParent() and current_node.getParent().callDecoration("isGroup"): - current_node = current_node.getParent() + total_progress = len(self._objects) * self._count + current_progress = 0 root = scene.getRoot() arranger = Arrange.create(scene_root=root) - node_too_big = False - if node.getBoundingBox().width < 300 or node.getBoundingBox().depth < 300: - offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(current_node, min_offset=self._min_offset) - else: - node_too_big = True nodes = [] - found_solution_for_all = True - for i in range(self._count): - # We do place the nodes one by one, as we want to yield in between. - if not node_too_big: - node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr) - if node_too_big or not solution_found: - found_solution_for_all = False - new_location = node.getPosition() - new_location = new_location.set(z = 100 - i * 20) - node.setPosition(new_location) + for node in self._objects: + # If object is part of a group, multiply group + current_node = node + while current_node.getParent() and current_node.getParent().callDecoration("isGroup"): + current_node = current_node.getParent() + + node_too_big = False + if node.getBoundingBox().width < 300 or node.getBoundingBox().depth < 300: + offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(current_node, min_offset=self._min_offset) + else: + node_too_big = True + + found_solution_for_all = True + for i in range(self._count): + # We do place the nodes one by one, as we want to yield in between. + if not node_too_big: + node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr) + if node_too_big or not solution_found: + found_solution_for_all = False + new_location = node.getPosition() + new_location = new_location.set(z = 100 - i * 20) + node.setPosition(new_location) + + nodes.append(node) + current_progress += 1 + status_message.setProgress((current_progress / total_progress) * 100) + Job.yieldThread() - nodes.append(node) Job.yieldThread() - status_message.setProgress((i + 1) / self._count * 100) if nodes: op = GroupedOperation() diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index d21480b11b..75fa1ece3a 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -8,12 +8,13 @@ from UM.Application import Application #To get the global container stack to fin from UM.Logger import Logger from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.SceneNode import SceneNode +from UM.Scene.Selection import Selection from UM.Settings.ContainerRegistry import ContainerRegistry #Finding containers by ID. from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.SettingFunction import SettingFunction from UM.Settings.ContainerStack import ContainerStack from UM.Settings.DefinitionContainer import DefinitionContainer -from typing import Optional +from typing import Optional, List ## Manages all existing extruder stacks. # @@ -34,10 +35,13 @@ class ExtruderManager(QObject): super().__init__(parent) self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs. self._active_extruder_index = 0 + self._selected_object_extruders = [] Application.getInstance().globalContainerStackChanged.connect(self.__globalContainerStackChanged) self._global_container_stack_definition_id = None self._addCurrentMachineExtruders() + Selection.selectionChanged.connect(self.resetSelectedObjectExtruders) + ## Gets the unique identifier of the currently active extruder stack. # # The currently active extruder stack is the stack that is currently being @@ -117,6 +121,34 @@ class ExtruderManager(QObject): except IndexError: return "" + ## Emitted whenever the selectedObjectExtruders property changes. + selectedObjectExtrudersChanged = pyqtSignal() + + ## Provides a list of extruder IDs used by the current selected objects. + @pyqtProperty("QVariantList", notify = selectedObjectExtrudersChanged) + def selectedObjectExtruders(self) -> List[str]: + if not self._selected_object_extruders: + object_extruders = set() + for node in Selection.getAllSelectedObjects(): + extruder = node.callDecoration("getActiveExtruder") + if extruder: + object_extruders.add(extruder) + else: + global_stack = Application.getInstance().getGlobalContainerStack() + object_extruders.add(self._extruder_trains[global_stack.getId()]["0"].getId()) + + self._selected_object_extruders = list(object_extruders) + + return self._selected_object_extruders + + ## Reset the internal list used for the selectedObjectExtruders property + # + # This will trigger a recalculation of the extruders used for the + # selection. + def resetSelectedObjectExtruders(self) -> None: + self._selected_object_extruders = [] + self.selectedObjectExtrudersChanged.emit() + def getActiveExtruderStack(self) -> ContainerStack: global_container_stack = Application.getInstance().getGlobalContainerStack() @@ -444,6 +476,8 @@ class ExtruderManager(QObject): self.globalContainerStackDefinitionChanged.emit() self.activeExtruderChanged.emit() + self.resetSelectedObjectExtruders() + ## Adds the extruders of the currently active machine. def _addCurrentMachineExtruders(self) -> None: global_stack = Application.getInstance().getGlobalContainerStack() diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 7f4a77eb5f..62b1f0af2c 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty +from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty, pyqtSlot import UM.Qt.ListModel from UM.Application import Application @@ -33,6 +33,12 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): # The ID of the definition of the extruder. DefinitionRole = Qt.UserRole + 5 + # The material of the extruder. + MaterialRole = Qt.UserRole + 6 + + # The variant of the extruder. + VariantRole = Qt.UserRole + 7 + ## List of colours to display if there is no material or the material has no known # colour. defaultColors = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"] @@ -49,6 +55,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): self.addRoleName(self.ColorRole, "color") self.addRoleName(self.IndexRole, "index") self.addRoleName(self.DefinitionRole, "definition") + self.addRoleName(self.MaterialRole, "material") + self.addRoleName(self.VariantRole, "variant") self._add_global = False self._simple_names = False @@ -140,6 +148,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): for extruder in manager.getMachineExtruders(global_container_stack.getId()): extruder_name = extruder.getName() material = extruder.findContainer({ "type": "material" }) + variant = extruder.findContainer({"type": "variant"}) position = extruder.getMetaDataEntry("position", default = "0") # Get the position try: position = int(position) @@ -152,7 +161,9 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): "name": extruder_name, "color": color, "index": position, - "definition": extruder.getBottom().getId() + "definition": extruder.getBottom().getId(), + "material": material.getName() if material else "", + "variant": variant.getName() if variant else "", } items.append(item) changed = True diff --git a/cura/Settings/SetObjectExtruderOperation.py b/cura/Settings/SetObjectExtruderOperation.py new file mode 100644 index 0000000000..31c996529a --- /dev/null +++ b/cura/Settings/SetObjectExtruderOperation.py @@ -0,0 +1,27 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +from UM.Scene.SceneNode import SceneNode +from UM.Operations.Operation import Operation + +from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator + +## Simple operation to set the extruder a certain object should be printed with. +class SetObjectExtruderOperation(Operation): + def __init__(self, node: SceneNode, extruder_id: str) -> None: + self._node = node + self._extruder_id = extruder_id + self._previous_extruder_id = None + self._decorator_added = False + + def undo(self): + if self._previous_extruder_id: + self._node.callDecoration("setActiveExtruder", self._previous_extruder_id) + + def redo(self): + stack = self._node.callDecoration("getStack") #Don't try to get the active extruder since it may be None anyway. + if not stack: + self._node.addDecorator(SettingOverrideDecorator()) + + self._previous_extruder_id = self._node.callDecoration("getActiveExtruder") + self._node.callDecoration("setActiveExtruder", self._extruder_id) diff --git a/cura/Settings/SettingInheritanceManager.py b/cura/Settings/SettingInheritanceManager.py index 2f81526813..ff0d1d81c0 100644 --- a/cura/Settings/SettingInheritanceManager.py +++ b/cura/Settings/SettingInheritanceManager.py @@ -109,10 +109,13 @@ class SettingInheritanceManager(QObject): self._settings_with_inheritance_warning.remove(key) settings_with_inheritance_warning_changed = True - # Find the topmost parent (Assumed to be a category) parent = definitions[0].parent - while parent.parent is not None: - parent = parent.parent + # Find the topmost parent (Assumed to be a category) + if parent is not None: + while parent.parent is not None: + parent = parent.parent + else: + parent = definitions[0] # Already at a category if parent.key not in self._settings_with_inheritance_warning and has_overwritten_inheritance: # Category was not in the list yet, so needs to be added now. diff --git a/cura/Settings/SettingOverrideDecorator.py b/cura/Settings/SettingOverrideDecorator.py index 76c155cb99..d754b6bc6d 100644 --- a/cura/Settings/SettingOverrideDecorator.py +++ b/cura/Settings/SettingOverrideDecorator.py @@ -109,6 +109,7 @@ class SettingOverrideDecorator(SceneNodeDecorator): def setActiveExtruder(self, extruder_stack_id): self._extruder_stack = extruder_stack_id self._updateNextStack() + ExtruderManager.getInstance().resetSelectedObjectExtruders() self.activeExtruderChanged.emit() def getStack(self): diff --git a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py index 0dd31652ce..459de3248d 100755 --- a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py @@ -283,10 +283,8 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): # # /param temperature The new target temperature of the bed. def _setTargetBedTemperature(self, temperature): - if self._target_bed_temperature == temperature: + if not self._updateTargetBedTemperature(temperature): return - self._target_bed_temperature = temperature - self.targetBedTemperatureChanged.emit() url = QUrl("http://" + self._address + self._api_prefix + "printer/bed/temperature/target") data = str(temperature) @@ -294,6 +292,17 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): put_request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") self._manager.put(put_request, data.encode()) + ## Updates the target bed temperature from the printer, and emit a signal if it was changed. + # + # /param temperature The new target temperature of the bed. + # /return boolean, True if the temperature was changed, false if the new temperature has the same value as the already stored temperature + def _updateTargetBedTemperature(self, temperature): + if self._target_bed_temperature == temperature: + return False + self._target_bed_temperature = temperature + self.targetBedTemperatureChanged.emit() + return True + def _stopCamera(self): self._camera_timer.stop() if self._image_reply: @@ -528,7 +537,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): bed_temperature = self._json_printer_state["bed"]["temperature"]["current"] self._setBedTemperature(bed_temperature) target_bed_temperature = self._json_printer_state["bed"]["temperature"]["target"] - self._setTargetBedTemperature(target_bed_temperature) + self._updateTargetBedTemperature(target_bed_temperature) head_x = self._json_printer_state["heads"][0]["position"]["x"] head_y = self._json_printer_state["heads"][0]["position"]["y"] diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index fcf6d99688..e813f6e686 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -3,6 +3,7 @@ import copy import io +from typing import Optional import xml.etree.ElementTree as ET from UM.Resources import Resources @@ -11,7 +12,7 @@ from UM.Util import parseBool from cura.CuraApplication import CuraApplication import UM.Dictionary -from UM.Settings.InstanceContainer import InstanceContainer +from UM.Settings.InstanceContainer import InstanceContainer, InvalidInstanceError from UM.Settings.ContainerRegistry import ContainerRegistry ## Handles serializing and deserializing material containers from an XML file @@ -370,8 +371,30 @@ class XmlMaterialProfile(InstanceContainer): self._dirty = False self._path = "" + def getConfigurationTypeFromSerialized(self, serialized: str) -> Optional[str]: + return "material" + + def getVersionFromSerialized(self, serialized: str) -> Optional[int]: + version = None + data = ET.fromstring(serialized) + metadata = data.iterfind("./um:metadata/*", self.__namespaces) + for entry in metadata: + tag_name = _tag_without_namespace(entry) + if tag_name == "version": + try: + version = int(entry.text) + except Exception as e: + raise InvalidInstanceError("Invalid version string '%s': %s" % (entry.text, e)) + break + if version is None: + raise InvalidInstanceError("Missing version in metadata") + return version + ## Overridden from InstanceContainer def deserialize(self, serialized): + # update the serialized data first + from UM.Settings.Interfaces import ContainerInterface + serialized = ContainerInterface.deserialize(self, serialized) data = ET.fromstring(serialized) # Reset previous metadata @@ -406,10 +429,10 @@ class XmlMaterialProfile(InstanceContainer): continue meta_data[tag_name] = entry.text - if not "description" in meta_data: + if "description" not in meta_data: meta_data["description"] = "" - if not "adhesion_info" in meta_data: + if "adhesion_info" not in meta_data: meta_data["adhesion_info"] = "" property_values = {} @@ -583,7 +606,8 @@ class XmlMaterialProfile(InstanceContainer): "Ultimaker 2 Extended": "ultimaker2_extended", "Ultimaker 2 Extended+": "ultimaker2_extended_plus", "Ultimaker Original": "ultimaker_original", - "Ultimaker Original+": "ultimaker_original_plus" + "Ultimaker Original+": "ultimaker_original_plus", + "IMADE3D JellyBOX": "imade3d_jellybox" } # Map of recognised namespaces with a proper prefix. diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 0616b20b65..ca05191185 100755 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -845,7 +845,7 @@ "unit": "mm", "default_value": 0.8, "minimum_value": "0", - "minimum_value_warning": "3 * resolveOrValue('layer_height')", + "minimum_value_warning": "0.2 + resolveOrValue('layer_height')", "maximum_value": "machine_height", "type": "float", "value": "top_bottom_thickness", @@ -860,7 +860,7 @@ "minimum_value": "0", "maximum_value_warning": "100", "type": "int", - "minimum_value_warning": "4", + "minimum_value_warning": "2", "value": "0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))", "settable_per_mesh": true } @@ -873,7 +873,7 @@ "unit": "mm", "default_value": 0.6, "minimum_value": "0", - "minimum_value_warning": "3 * resolveOrValue('layer_height')", + "minimum_value_warning": "0.2 + resolveOrValue('layer_height')", "type": "float", "value": "top_bottom_thickness", "maximum_value": "machine_height", @@ -885,7 +885,7 @@ "label": "Bottom Layers", "description": "The number of bottom layers. When calculated by the bottom thickness, this value is rounded to a whole number.", "minimum_value": "0", - "minimum_value_warning": "4", + "minimum_value_warning": "2", "default_value": 6, "type": "int", "value": "999999 if infill_sparse_density == 100 else math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))", @@ -1301,7 +1301,7 @@ "type": "int", "minimum_value": "0", "maximum_value_warning": "4", - "maximum_value": "(20 - math.log(infill_line_distance) / math.log(2)) if infill_line_distance > 0 and not spaghetti_infill_enabled else 0", + "maximum_value": "0 if spaghetti_infill_enabled else (999999 if infill_line_distance == 0 else (20 - math.log(infill_line_distance) / math.log(2)))", "enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv' and not spaghetti_infill_enabled", "settable_per_mesh": true }, @@ -2332,7 +2332,6 @@ "unit": "mm/s", "type": "float", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "default_value": 20, "enabled": "resolveOrValue('jerk_enabled')", @@ -2346,7 +2345,6 @@ "unit": "mm/s", "type": "float", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "default_value": 20, "value": "jerk_print", @@ -2360,7 +2358,6 @@ "unit": "mm/s", "type": "float", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "default_value": 20, "value": "jerk_print", @@ -2375,7 +2372,6 @@ "unit": "mm/s", "type": "float", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "default_value": 20, "value": "jerk_wall", @@ -2389,7 +2385,6 @@ "unit": "mm/s", "type": "float", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "default_value": 20, "value": "jerk_wall", @@ -2405,7 +2400,6 @@ "unit": "mm/s", "type": "float", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "default_value": 20, "value": "jerk_print", @@ -2419,7 +2413,6 @@ "unit": "mm/s", "type": "float", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "default_value": 20, "value": "jerk_print", @@ -2438,7 +2431,6 @@ "default_value": 20, "value": "jerk_support", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "enabled": "resolveOrValue('jerk_enabled') and support_enable", "limit_to_extruder": "support_infill_extruder_nr", @@ -2454,7 +2446,6 @@ "default_value": 20, "value": "jerk_support", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "enabled": "resolveOrValue('jerk_enabled') and extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", "limit_to_extruder": "support_interface_extruder_nr", @@ -2470,7 +2461,6 @@ "unit": "mm/s", "type": "float", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "default_value": 20, "value": "jerk_print", @@ -2487,7 +2477,6 @@ "type": "float", "default_value": 30, "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "value": "jerk_print if magic_spiralize else 30", "enabled": "resolveOrValue('jerk_enabled')", @@ -2502,7 +2491,6 @@ "default_value": 20, "value": "jerk_print", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "enabled": "resolveOrValue('jerk_enabled')", "settable_per_mesh": true, @@ -2517,7 +2505,6 @@ "default_value": 20, "value": "jerk_layer_0", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "enabled": "resolveOrValue('jerk_enabled')", "settable_per_mesh": true @@ -2531,7 +2518,6 @@ "default_value": 20, "value": "jerk_layer_0 * jerk_travel / jerk_print", "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "enabled": "resolveOrValue('jerk_enabled')", "settable_per_extruder": true, @@ -2547,7 +2533,6 @@ "type": "float", "default_value": 20, "minimum_value": "0.1", - "minimum_value_warning": "5", "maximum_value_warning": "50", "value": "jerk_layer_0", "enabled": "resolveOrValue('jerk_enabled')", diff --git a/resources/definitions/imade3d_jellybox.def.json b/resources/definitions/imade3d_jellybox.def.json index f8077f2e95..86b34bfd5c 100644 --- a/resources/definitions/imade3d_jellybox.def.json +++ b/resources/definitions/imade3d_jellybox.def.json @@ -32,7 +32,7 @@ "machine_center_is_zero": { "default_value": false }, "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": ";---------------------------------------\n; ; ; Jellybox Start Script Begin ; ; ;\n;_______________________________________\n; M92 E140 ;optionally adjust steps per mm for your filament\n\n; Print Settings Summary\n; (leave these alone: this is only a list of the slicing settings)\n; (overwriting these values will NOT change your printer's behavior)\n; sliced for : {machine_name}\n; nozzle diameter : {machine_nozzle_size}\n; filament diameter : {material_diameter}\n; layer height : {layer_height}\n; 1st layer height : {layer_height_0}\n; line width : {line_width}\n; outer wall wipe dist. : {wall_0_wipe_dist}\n; infill line width : {infill_line_width}\n; wall thickness : {wall_thickness}\n; top thickness : {top_thickness}\n; bottom thickness : {bottom_thickness}\n; infill density : {infill_sparse_density}\n; infill pattern : {infill_pattern}\n; print temperature : {material_print_temperature}\n; 1st layer print temp. : {material_print_temperature_layer_0}\n; heated bed temperature : {material_bed_temperature}\n; 1st layer bed temp. : {material_bed_temperature_layer_0}\n; regular fan speed : {cool_fan_speed_min}\n; max fan speed : {cool_fan_speed_max}\n; retraction amount : {retraction_amount}\n; retr. retract speed : {retraction_retract_speed}\n; retr. prime speed : {retraction_prime_speed}\n; build plate adhesion : {adhesion_type}\n; support ? {support_enable}\n; spiralized ? {magic_spiralize}\n\nM117 Preparing ;write Preparing\nM140 S{material_bed_temperature_layer_0} ;set bed temperature and move on\nM104 S{material_print_temperature_layer_0} ;set extruder temperature and move on\nM206 X10.0 Y0.0 ;set x homing offset for default bed leveling\nG21 ;metric values\nG90 ;absolute positioning\nM107 ;start with the fan off\nM82 ;set extruder to absolute mode\nG28 ;home all axes\nM203 Z4 ;slow Z speed down for greater accuracy when probing\nG29 ;auto bed leveling procedure\nM203 Z7 ;pick up z speed again for printing\nM190 S{material_bed_temperature_layer_0} ;wait for the bed to reach desired temperature\nM109 S{material_print_temperature_layer_0} ;wait for the extruder to reach desired temperature\nG92 E0 ;reset the extruder position\nG1 F1500 E15 ;extrude 15mm of feed stock\nG92 E0 ;reset the extruder position again\nM117 Print starting ;write Print starting\n;---------------------------------------------\n; ; ; Jellybox Printer Start Script End ; ; ;\n;_____________________________________________\n" + "default_value": ";---------------------------------------\n; ; ; Jellybox Start Script Begin ; ; ;\n;_______________________________________\n; M92 E140 ;optionally adjust steps per mm for your filament\n\n; Print Settings Summary\n; (leave these alone: this is only a list of the slicing settings)\n; (overwriting these values will NOT change your printer's behavior)\n; sliced for : {machine_name}\n; nozzle diameter : {machine_nozzle_size}\n; filament diameter : {material_diameter}\n; layer height : {layer_height}\n; 1st layer height : {layer_height_0}\n; line width : {line_width}\n; outer wall wipe dist. : {wall_0_wipe_dist}\n; infill line width : {infill_line_width}\n; wall thickness : {wall_thickness}\n; top thickness : {top_thickness}\n; bottom thickness : {bottom_thickness}\n; infill density : {infill_sparse_density}\n; infill pattern : {infill_pattern}\n; print temperature : {material_print_temperature}\n; 1st layer print temp. : {material_print_temperature_layer_0}\n; heated bed temperature : {material_bed_temperature}\n; 1st layer bed temp. : {material_bed_temperature_layer_0}\n; regular fan speed : {cool_fan_speed_min}\n; max fan speed : {cool_fan_speed_max}\n; retraction amount : {retraction_amount}\n; retr. retract speed : {retraction_retract_speed}\n; retr. prime speed : {retraction_prime_speed}\n; build plate adhesion : {adhesion_type}\n; support ? {support_enable}\n; spiralized ? {magic_spiralize}\n\nM117 Preparing ;write Preparing\nM140 S{material_bed_temperature_layer_0} ;set bed temperature and move on\nM109 S{material_print_temperature} ; wait for the extruder to reach desired temperature\nM206 X10.0 Y0.0 ;set x homing offset for default bed leveling\nG21 ;metric values\nG90 ;absolute positioning\nM107 ;start with the fan off\nM82 ;set extruder to absolute mode\nG28 ;home all axes\nM203 Z4 ;slow Z speed down for greater accuracy when probing\nG29 ;auto bed leveling procedure\nM203 Z7 ;pick up z speed again for printing\nM190 S{material_bed_temperature_layer_0} ;wait for the bed to reach desired temperature\nM109 S{material_print_temperature_layer_0} ;wait for the extruder to reach desired temperature\nG92 E0 ;reset the extruder position\nG1 F1500 E15 ;extrude 15mm of feed stock\nG92 E0 ;reset the extruder position again\nM117 Print starting ;write Print starting\n;---------------------------------------------\n; ; ; Jellybox Printer Start Script End ; ; ;\n;_____________________________________________\n" }, "machine_end_gcode": { "default_value": "\n;---------------------------------\n;;; Jellybox End Script Begin ;;;\n;_________________________________\nM117 Finishing Up ;write Finishing Up\n\nM104 S0 ;extruder heater off\nM140 S0 ;bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG90 ;absolute positioning\nG28 X ;home x, so the head is out of the way\nG1 Y100 ;move Y forward, so the print is more accessible\nM84 ;steppers off\n\nM117 Print finished ;write Print finished\n;---------------------------------------\n;;; Jellybox End Script End ;;;\n;_______________________________________" diff --git a/resources/definitions/rigid3d_zero2.def.json b/resources/definitions/rigid3d_zero2.def.json new file mode 100644 index 0000000000..73b50f0950 --- /dev/null +++ b/resources/definitions/rigid3d_zero2.def.json @@ -0,0 +1,130 @@ +{ + "id": "rigid3d_zero2", + "name": "Rigid3D Zero2", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Rigid3D", + "manufacturer": "Rigid3D", + "category": "Other", + "has_materials": false, + "file_formats": "text/x-gcode", + "platform": "rigid3d_zero2_platform.stl", + "platform_offset": [ 5, 0, -35] + }, + "overrides": { + "machine_name": { "default_value": "Rigid3D Zero2" }, + "machine_head_with_fans_polygon": { + "default_value": [[ 30, 30], [ 30, 70], [ 30, 70], [ 30, 30]] + }, + "z_seam_type": { + "default_value": "random" + }, + "machine_heated_bed": { + "default_value": true + }, + "layer_height": { + "default_value": 0.2 + }, + "layer_height_0": { + "default_value": 0.2 + }, + "wall_thickness": { + "default_value": 0.8 + }, + "top_bottom_thickness": { + "default_value": 0.8 + }, + "xy_offset": { + "default_value": -0.2 + }, + "material_print_temperature": { + "value": 235 + }, + "material_bed_temperature": { + "default_value": 100 + }, + "material_diameter": { + "default_value": 1.75 + }, + "speed_print": { + "default_value": 40 + }, + "speed_layer_0": { + "value": 15 + }, + "speed_tarvel": { + "value": 100 + }, + "support_enable": { + "default_value": false + }, + "infill_sparse_density": { + "default_value": 15 + }, + "infill_pattern": { + "default_value": "lines", + "value": "lines" + }, + "retraction_amount": { + "default_value": 1 + }, + "machine_width": { + "default_value": 200 + }, + "machine_height": { + "default_value": 200 + }, + "machine_depth": { + "default_value": 200 + }, + "machine_center_is_zero": { + "default_value": false + }, + "machine_nozzle_size": { + "default_value": 0.4 + }, + "gantry_height": { + "default_value": 25 + }, + "machine_gcode_flavor": { + "default_value": "RepRap" + }, + "cool_fan_enabled": { + "default_value": false + }, + "cool_fan_speed": { + "default_value": 50, + "value": 50 + }, + "cool_fan_speed_min": { + "default_value": 0 + }, + "cool_fan_full_at_height": { + "default_value": 1.0, + "value": 1.0 + }, + "support_z_distance": { + "default_value": 0.2 + }, + "support_interface_enable": { + "default_value": true + }, + "support_interface_height": { + "default_value": 0.8 + }, + "support_interface_density": { + "default_value": 70 + }, + "support_interface_pattern": { + "default_value": "grid" + }, + "machine_start_gcode": { + "default_value": "G21\nG28 ; Home extruder\nM107 ; Turn off fan\nG91 ; Relative positioning\nG1 Z5 F180;\nG1 X100 Y100 F3000;\nG1 Z-5 F180;\nG90 ; Absolute positioning\nM82 ; Extruder in absolute mode\nG92 E0 ; Reset extruder position\n" + }, + "machine_end_gcode": { + "default_value": "G1 X0 Y180 ; Get extruder out of way.\nM107 ; Turn off fan\nG91 ; Relative positioning\nG0 Z20 ; Lift extruder up\nT0\nG1 E-1 ; Reduce filament pressure\nM104 T0 S0 ; Turn extruder heater off\nG90 ; Absolute positioning\nG92 E0 ; Reset extruder position\nM140 S0 ; Disable heated bed\nM84 ; Turn steppers off\n" + } + } +} diff --git a/resources/definitions/ultimaker3.def.json b/resources/definitions/ultimaker3.def.json index afac8f3226..ad8b08dfa1 100644 --- a/resources/definitions/ultimaker3.def.json +++ b/resources/definitions/ultimaker3.def.json @@ -70,7 +70,7 @@ "machine_start_gcode": { "default_value": "" }, "machine_end_gcode": { "default_value": "" }, "prime_tower_position_x": { "default_value": 175 }, - "prime_tower_position_y": { "default_value": 179 }, + "prime_tower_position_y": { "default_value": 178 }, "prime_tower_wipe_enabled": { "default_value": false }, "acceleration_enabled": { "value": "True" }, diff --git a/resources/meshes/rigid3d_zero2_platform.stl b/resources/meshes/rigid3d_zero2_platform.stl new file mode 100644 index 0000000000..ef81aaf9ec Binary files /dev/null and b/resources/meshes/rigid3d_zero2_platform.stl differ diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index b5f5823ece..b9eef11a55 100755 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -18,6 +18,8 @@ Item property alias redo: redoAction; property alias deleteSelection: deleteSelectionAction; + property alias centerSelection: centerSelectionAction; + property alias multiplySelection: multiplySelectionAction; property alias deleteObject: deleteObjectAction; property alias centerObject: centerObjectAction; @@ -181,11 +183,28 @@ Item Action { id: deleteSelectionAction; - text: catalog.i18nc("@action:inmenu menubar:edit","Delete &Selection"); - enabled: UM.Controller.toolsEnabled; + text: catalog.i18ncp("@action:inmenu menubar:edit", "Delete &Selected Model", "Delete &Selected Models", UM.Selection.selectionCount); + enabled: UM.Controller.toolsEnabled && UM.Selection.hasSelection; iconName: "edit-delete"; shortcut: StandardKey.Delete; - onTriggered: CuraApplication.deleteSelection(); + onTriggered: CuraActions.deleteSelection(); + } + + Action + { + id: centerSelectionAction; + text: catalog.i18ncp("@action:inmenu menubar:edit", "Center Selected Model", "Center Selected Models", UM.Selection.selectionCount); + enabled: UM.Controller.toolsEnabled && UM.Selection.hasSelection; + iconName: "align-vertical-center"; + onTriggered: CuraActions.centerSelection(); + } + + Action + { + id: multiplySelectionAction; + text: catalog.i18ncp("@action:inmenu menubar:edit", "Multiply Selected Model", "Multiply Selected Models", UM.Selection.selectionCount); + enabled: UM.Controller.toolsEnabled && UM.Selection.hasSelection; + iconName: "edit-duplicate"; } Action diff --git a/resources/qml/AddMachineDialog.qml b/resources/qml/AddMachineDialog.qml index 756badc4d2..ba3f40260d 100644 --- a/resources/qml/AddMachineDialog.qml +++ b/resources/qml/AddMachineDialog.qml @@ -180,7 +180,7 @@ UM.Dialog anchors.bottom:parent.bottom spacing: UM.Theme.getSize("default_margin").width - Label + Text { text: catalog.i18nc("@label", "Printer Name:") anchors.verticalCenter: machineName.verticalCenter diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index b0e6d09080..0a48725011 100755 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -594,102 +594,8 @@ UM.MainWindow } } - Menu - { - id: objectContextMenu; - - property variant objectId: -1; - MenuItem { action: Cura.Actions.centerObject; } - MenuItem { action: Cura.Actions.deleteObject; } - MenuItem { action: Cura.Actions.multiplyObject; } - MenuSeparator { } - MenuItem { action: Cura.Actions.selectAll; } - MenuItem { action: Cura.Actions.arrangeAll; } - MenuItem { action: Cura.Actions.deleteAll; } - MenuItem { action: Cura.Actions.reloadAll; } - MenuItem { action: Cura.Actions.resetAllTranslation; } - MenuItem { action: Cura.Actions.resetAll; } - MenuSeparator { } - MenuItem { action: Cura.Actions.groupObjects; } - MenuItem { action: Cura.Actions.mergeObjects; } - MenuItem { action: Cura.Actions.unGroupObjects; } - - Connections - { - target: Cura.Actions.deleteObject - onTriggered: - { - if(objectContextMenu.objectId != 0) - { - CuraApplication.deleteObject(objectContextMenu.objectId); - objectContextMenu.objectId = 0; - } - } - } - - MultiplyObjectOptions - { - id: multiplyObjectOptions - } - - Connections - { - target: Cura.Actions.multiplyObject - onTriggered: - { - if(objectContextMenu.objectId != 0) - { - multiplyObjectOptions.objectId = objectContextMenu.objectId; - multiplyObjectOptions.visible = true; - multiplyObjectOptions.reset(); - objectContextMenu.objectId = 0; - } - } - } - - Connections - { - target: Cura.Actions.centerObject - onTriggered: - { - if(objectContextMenu.objectId != 0) - { - CuraApplication.centerObject(objectContextMenu.objectId); - objectContextMenu.objectId = 0; - } - } - } - } - - Menu - { - id: contextMenu; - MenuItem { action: Cura.Actions.selectAll; } - MenuItem { action: Cura.Actions.arrangeAll; } - MenuItem { action: Cura.Actions.deleteAll; } - MenuItem { action: Cura.Actions.reloadAll; } - MenuItem { action: Cura.Actions.resetAllTranslation; } - MenuItem { action: Cura.Actions.resetAll; } - MenuSeparator { } - MenuItem { action: Cura.Actions.groupObjects; } - MenuItem { action: Cura.Actions.mergeObjects; } - MenuItem { action: Cura.Actions.unGroupObjects; } - } - - Connections - { - target: UM.Controller - onContextMenuRequested: - { - if(objectId == 0) - { - contextMenu.popup(); - } else - { - objectContextMenu.objectId = objectId; - objectContextMenu.popup(); - } - } + ContextMenu { + id: contextMenu } Connections diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 39b7f42ea0..54b559f794 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -132,7 +132,7 @@ Item { } } - Label + Text { id: boundingSpec anchors.top: jobNameRow.bottom @@ -169,7 +169,7 @@ Item { color: UM.Theme.getColor("text_subtext") source: UM.Theme.getIcon("print_time") } - Label + Text { id: timeSpec anchors.right: lengthIcon.left @@ -192,7 +192,7 @@ Item { color: UM.Theme.getColor("text_subtext") source: UM.Theme.getIcon("category_material") } - Label + Text { id: lengthSpec anchors.right: parent.right diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml new file mode 100644 index 0000000000..43867a4cc1 --- /dev/null +++ b/resources/qml/Menus/ContextMenu.qml @@ -0,0 +1,138 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Dialogs 1.2 +import QtQuick.Window 2.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Menu +{ + id: base + + property bool shouldShowExtruders: machineExtruderCount.properties.value > 1; + + // Selection-related actions. + MenuItem { action: Cura.Actions.centerSelection; } + MenuItem { action: Cura.Actions.deleteSelection; } + MenuItem { action: Cura.Actions.multiplySelection; } + + // Extruder selection - only visible if there is more than 1 extruder + MenuSeparator { visible: base.shouldShowExtruders } + MenuItem { id: extruderHeader; text: catalog.i18ncp("@label", "Print Selected Model With:", "Print Selected Models With:", UM.Selection.selectionCount); enabled: false; visible: base.shouldShowExtruders } + Instantiator + { + model: Cura.ExtrudersModel { id: extrudersModel } + MenuItem { + text: "%1: %2 - %3".arg(model.name).arg(model.material).arg(model.variant) + visible: base.shouldShowExtruders + enabled: UM.Selection.hasSelection + checkable: true + checked: ExtruderManager.selectedObjectExtruders.indexOf(model.id) != -1 + onTriggered: CuraActions.setExtruderForSelection(model.id) + shortcut: "Ctrl+" + (model.index + 1) + } + onObjectAdded: base.insertItem(base.findItemIndex(extruderHeader) + index, object) + onObjectRemoved: base.removeItem(object) + } + + // Global actions + MenuSeparator { } + MenuItem { action: Cura.Actions.selectAll; } + MenuItem { action: Cura.Actions.arrangeAll; } + MenuItem { action: Cura.Actions.deleteAll; } + MenuItem { action: Cura.Actions.reloadAll; } + MenuItem { action: Cura.Actions.resetAllTranslation; } + MenuItem { action: Cura.Actions.resetAll; } + + // Group actions + MenuSeparator { } + MenuItem { action: Cura.Actions.groupObjects; } + MenuItem { action: Cura.Actions.mergeObjects; } + MenuItem { action: Cura.Actions.unGroupObjects; } + + Connections + { + target: UM.Controller + onContextMenuRequested: base.popup(); + } + + Connections + { + target: Cura.Actions.multiplySelection + onTriggered: multiplyDialog.open() + } + + UM.SettingPropertyProvider + { + id: machineExtruderCount + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_extruder_count" + watchedProperties: [ "value" ] + } + + Dialog + { + id: multiplyDialog + + title: catalog.i18ncp("@title:window", "Multiply Selected Model", "Multiply Selected Models", UM.Selection.selectionCount) + + width: 400 * Screen.devicePixelRatio + height: 80 * Screen.devicePixelRatio + + onAccepted: CuraActions.multiplySelection(copiesField.value) + + signal reset() + onReset: + { + copiesField.value = 1; + copiesField.focus = true; + } + + standardButtons: StandardButton.Ok | StandardButton.Cancel + + Row + { + spacing: UM.Theme.getSize("default_margin").width + + Label + { + text: catalog.i18nc("@label", "Number of Copies") + anchors.verticalCenter: copiesField.verticalCenter + } + + SpinBox + { + id: copiesField + minimumValue: 1 + maximumValue: 99 + } + } + } + + // Find the index of an item in the list of child items of this menu. + // + // This is primarily intended as a helper function so we do not have to + // hard-code the position of the extruder selection actions. + // + // \param item The item to find the index of. + // + // \return The index of the item or -1 if it was not found. + function findItemIndex(item) + { + for(var i in base.items) + { + if(base.items[i] == item) + { + return i; + } + } + return -1; + } + + UM.I18nCatalog { id: catalog; name: "cura" } +} diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index 9051f8a8fa..8568acc4ce 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -66,7 +66,7 @@ UM.ManagementPage visible: base.currentItem != null anchors.fill: parent - Label + Text { id: machineName text: base.currentItem && base.currentItem.name ? base.currentItem.name : "" @@ -146,26 +146,28 @@ UM.ManagementPage property var connectedPrinter: printerConnected ? Cura.MachineManager.printerOutputDevices[0] : null property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - Label + Text { text: catalog.i18nc("@label", "Printer type:") visible: base.currentItem && "definition_name" in base.currentItem.metadata } - Label { + Text + { text: (base.currentItem && "definition_name" in base.currentItem.metadata) ? base.currentItem.metadata.definition_name : "" } - Label + Text { text: catalog.i18nc("@label", "Connection:") visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId } - Label { + Text + { width: parent.width * 0.7 text: machineInfo.printerConnected ? machineInfo.connectedPrinter.connectionText : catalog.i18nc("@info:status", "The printer is not connected.") visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId wrapMode: Text.WordWrap } - Label + Text { text: catalog.i18nc("@label", "State:") visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId && machineInfo.printerAcceptsCommands diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 3e17943310..226fd349bf 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -273,17 +273,28 @@ TabView { id: spinBox anchors.left: label.right - value: parseFloat(provider.properties.value); - width: base.secondColumnWidth; + value: { + if (!isNaN(parseFloat(materialPropertyProvider.properties.value))) + { + return parseFloat(materialPropertyProvider.properties.value); + } + if (!isNaN(parseFloat(machinePropertyProvider.properties.value))) + { + return parseFloat(machinePropertyProvider.properties.value); + } + return 0; + } + width: base.secondColumnWidth readOnly: !base.editingEnabled - suffix: model.unit + suffix: " " + model.unit maximumValue: 99999 decimals: model.unit == "mm" ? 2 : 0 - onEditingFinished: provider.setPropertyValue("value", value) + onEditingFinished: materialPropertyProvider.setPropertyValue("value", value) } - UM.ContainerPropertyProvider { id: provider; containerId: base.containerId; watchedProperties: [ "value" ]; key: model.key } + UM.ContainerPropertyProvider { id: materialPropertyProvider; containerId: base.containerId; watchedProperties: [ "value" ]; key: model.key } + UM.ContainerPropertyProvider { id: machinePropertyProvider; containerId: Cura.MachineManager.activeDefinitionId; watchedProperties: [ "value" ]; key: model.key } } } } diff --git a/resources/qml/PrintMonitor.qml b/resources/qml/PrintMonitor.qml index 1b500d5b20..eacdc17883 100644 --- a/resources/qml/PrintMonitor.qml +++ b/resources/qml/PrintMonitor.qml @@ -649,6 +649,7 @@ Column sourceComponent: monitorItem property string label: catalog.i18nc("@label", "Estimated time left") property string value: connectedPrinter != null ? getPrettyTime(connectedPrinter.timeTotal - connectedPrinter.timeElapsed) : "" + visible: connectedPrinter != null && (connectedPrinter.jobState == "printing" || connectedPrinter.jobState == "resuming" || connectedPrinter.jobState == "pausing" || connectedPrinter.jobState == "paused") } Component diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml old mode 100644 new mode 100755 index f4f439439f..ba5106c767 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -407,14 +407,81 @@ Rectangle } } ExclusiveGroup { id: modeMenuGroup; } - ListView{ - id: modesList - property var index: 0 - model: modesListModel - delegate: wizardDelegate - anchors.top: parent.top - anchors.left: parent.left - width: parent.width + + Text + { + id: toggleLeftText + anchors.right: modeToggleSwitch.left + anchors.rightMargin: UM.Theme.getSize("toggle_button_text_anchoring_margin").width + anchors.verticalCenter: parent.verticalCenter + text: "" + color: UM.Theme.getColor("toggle_active_text") + font: UM.Theme.getFont("default") + + MouseArea + { + anchors.fill: parent + onClicked: + { + modeToggleSwitch.checked = false; + } + + Component.onCompleted: + { + clicked.connect(modeToggleSwitch.clicked) + } + } + } + + Switch + { + id: modeToggleSwitch + checked: false + anchors.right: toggleRightText.left + anchors.rightMargin: UM.Theme.getSize("toggle_button_text_anchoring_margin").width + anchors.verticalCenter: parent.verticalCenter + + onClicked: + { + var index = 0; + if (checked) + { + index = 1; + } + updateActiveMode(index); + } + + function updateActiveMode(index) + { + base.currentModeIndex = index; + UM.Preferences.setValue("cura/active_mode", index); + } + + style: UM.Theme.styles.toggle_button + } + + Text + { + id: toggleRightText + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + text: "" + color: UM.Theme.getColor("toggle_active_text") + font: UM.Theme.getFont("default") + + MouseArea + { + anchors.fill: parent + onClicked: + { + modeToggleSwitch.checked = true; + } + + Component.onCompleted: + { + clicked.connect(modeToggleSwitch.clicked) + } + } } } @@ -541,10 +608,14 @@ Rectangle }) sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true }); - var index = parseInt(UM.Preferences.getValue("cura/active_mode")) - if(index) + toggleLeftText.text = modesListModel.get(0).text; + toggleRightText.text = modesListModel.get(1).text; + + var index = parseInt(UM.Preferences.getValue("cura/active_mode")); + if (index) { currentModeIndex = index; + modeToggleSwitch.checked = index > 0; } } @@ -567,4 +638,4 @@ Rectangle watchedProperties: [ "value" ] storeIndex: 0 } -} \ No newline at end of file +} diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg index 41272a7718..cc50189e8c 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg @@ -20,7 +20,6 @@ cool_min_layer_time_fan_speed_max = 5 cool_min_speed = 5 infill_line_width = =round(line_width * 0.4 / 0.35, 2) infill_overlap = 0 -infill_overlap_mm = 0.05 infill_pattern = triangles infill_wipe_dist = 0.1 jerk_enabled = True diff --git a/resources/themes/cura/styles.qml b/resources/themes/cura/styles.qml index 64b4436622..a7c7dcb6cd 100644 --- a/resources/themes/cura/styles.qml +++ b/resources/themes/cura/styles.qml @@ -8,6 +8,44 @@ import QtQuick.Controls.Styles 1.1 import UM 1.1 as UM QtObject { + property Component toggle_button: Component { + SwitchStyle { + groove: Rectangle { + implicitWidth: UM.Theme.getSize("toggle_button_background_implicit_size").width + implicitHeight: UM.Theme.getSize("toggle_button_background_implicit_size").height + radius: UM.Theme.getSize("toggle_button_radius").width + border.color: { + if (control.pressed || (control.checkable && control.checked)) { + return UM.Theme.getColor("sidebar_header_active"); + } else if(control.hovered) { + return UM.Theme.getColor("sidebar_header_hover"); + } else { + return UM.Theme.getColor("sidebar_header_bar"); + } + } + Behavior on border.color { ColorAnimation { duration: 50; } } + border.width: 1 + } + + handle: Rectangle { + implicitWidth: UM.Theme.getSize("toggle_button_knob_implicit_size").width + implicitHeight: UM.Theme.getSize("toggle_button_knob_implicit_size").height + radius: UM.Theme.getSize("toggle_button_radius").width + + color: { + if (control.pressed || (control.checkable && control.checked)) { + return UM.Theme.getColor("sidebar_header_active"); + } else if(control.hovered) { + return UM.Theme.getColor("sidebar_header_hover"); + } else { + return UM.Theme.getColor("sidebar_header_bar"); + } + } + Behavior on color { ColorAnimation { duration: 50; } } + } + } + } + property Component sidebar_header_button: Component { ButtonStyle { background: Rectangle { diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index fa4bf2ee92..084ee27bb2 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -319,6 +319,11 @@ "infill_button_margin": [0.5, 0.5], - "jobspecs_line": [2.0, 2.0] + "jobspecs_line": [2.0, 2.0], + + "toggle_button_text_anchoring_margin": [1.0, 1.0], + "toggle_button_radius": [1.0, 1.0], + "toggle_button_background_implicit_size": [2.0, 1.0], + "toggle_button_knob_implicit_size": [1.0, 1.0] } }