diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index c5eb4b699e..d3da01e590 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -16,7 +16,7 @@ class LayerPolygon: MoveRetractionType = 9 SupportInterfaceType = 10 - __jump_map = numpy.logical_or( numpy.arange(11) == NoneType, numpy.arange(11) >= MoveRetractionType ) + __jump_map = numpy.logical_or(numpy.logical_or(numpy.arange(11) == NoneType, numpy.arange(11) == MoveCombingType), numpy.arange(11) == MoveRetractionType) def __init__(self, mesh, extruder, line_types, data, line_widths): self._mesh = mesh @@ -42,7 +42,7 @@ class LayerPolygon: # When type is used as index returns true if type == LayerPolygon.InfillType or type == LayerPolygon.SkinType or type == LayerPolygon.SupportInfillType # Should be generated in better way, not hardcoded. - self._isInfillOrSkinTypeMap = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0], dtype=numpy.bool) + self._isInfillOrSkinTypeMap = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1], dtype=numpy.bool) self._build_cache_line_mesh_mask = None self._build_cache_needed_points = None diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index d718f25b87..d25a74bf91 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -48,6 +48,8 @@ class PlatformPhysics: # same direction. transformed_nodes = [] + group_nodes = [] + for node in BreadthFirstIterator(root): if node is root or type(node) is not SceneNode or node.getBoundingBox() is None: continue @@ -69,6 +71,9 @@ class PlatformPhysics: if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection: node._outside_buildarea = True + if node.callDecoration("isGroup"): + group_nodes.append(node) # Keep list of affected group_nodes + # Move it downwards if bottom is above platform move_vector = Vector() if Preferences.getInstance().getValue("physics/automatic_drop_down") and not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down @@ -144,7 +149,6 @@ class PlatformPhysics: overlap = convex_hull.intersectsPolygon(area) if overlap is None: continue - node._outside_buildarea = True if not Vector.Null.equals(move_vector, epsilon=1e-5): @@ -152,6 +156,12 @@ class PlatformPhysics: op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector) op.push() + # Group nodes should override the _outside_buildarea property of their children. + for group_node in group_nodes: + for child_node in group_node.getAllChildren(): + child_node._outside_buildarea = group_node._outside_buildarea + + def _onToolOperationStarted(self, tool): self._enabled = False diff --git a/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py b/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py index 37f4422a11..90adac73b1 100644 --- a/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py +++ b/plugins/RemovableDriveOutputDevice/RemovableDrivePlugin.py @@ -49,7 +49,7 @@ class RemovableDrivePlugin(OutputDevicePlugin): message = Message(catalog.i18nc("@info:status", "Ejected {0}. You can now safely remove the drive.").format(device.getName())) message.show() else: - message = Message(catalog.i18nc("@info:status", "Failed to eject {0}. Maybe it is still in use?").format(device.getName())) + message = Message(catalog.i18nc("@info:status", "Failed to eject {0}. Another program may be using the drive.").format(device.getName())) message.show() return result diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 3b56ac1881..2d017f829f 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -13,6 +13,7 @@ from UM.Settings.Validator import ValidatorState from UM.View.GL.OpenGL import OpenGL import cura.Settings +from cura.Settings.ExtruderManager import ExtruderManager import math @@ -45,8 +46,16 @@ class SolidView(View): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack: + multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1 + + if multi_extrusion: + support_extruder_nr = global_container_stack.getProperty("support_extruder_nr", "value") + support_angle_stack = ExtruderManager.getInstance().getExtruderStack(support_extruder_nr) + else: + support_angle_stack = global_container_stack + if Preferences.getInstance().getValue("view/show_overhang"): - angle = global_container_stack.getProperty("support_angle", "value") + angle = support_angle_stack.getProperty("support_angle", "value") # Make sure the overhang angle is valid before passing it to the shader # Note: if the overhang angle is set to its default value, it does not need to get validated (validationState = None) if angle is not None and global_container_stack.getProperty("support_angle", "validationState") in [None, ValidatorState.Valid]: @@ -56,7 +65,6 @@ class SolidView(View): else: self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0))) - multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1 for node in DepthFirstIterator(scene.getRoot()): if not node.render(renderer): diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 920a71d40f..4838fe9b96 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -140,7 +140,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): # \param gcode_list List with gcode (strings). def printGCode(self, gcode_list): if self._progress or self._connection_state != ConnectionState.connected: - self._error_message = Message(catalog.i18nc("@info:status", "Printer is busy or not connected. Unable to start a new job.")) + self._error_message = Message(catalog.i18nc("@info:status", "Unable to start a new job because the printer is busy or not connected.")) self._error_message.show() Logger.log("d", "Printer is busy or not connected, aborting print") self.writeError.emit(self) diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 248649f431..4dec2e3a06 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -105,9 +105,10 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): @pyqtSlot(str) def updateAllFirmware(self, file_name): - file_name = file_name.replace("file://", "") # File dialogs prepend the path with file://, which we don't need / want + if file_name.startswith("file://"): + file_name = QUrl(file_name).toLocalFile() # File dialogs prepend the path with file://, which we don't need / want if not self._usb_output_devices: - Message(i18n_catalog.i18nc("@info", "Cannot update firmware, there were no connected printers found.")).show() + Message(i18n_catalog.i18nc("@info", "Unable to update firmware because there are no printers connected.")).show() return for printer_connection in self._usb_output_devices: diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 42c2ad9cf4..005a0892a3 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -464,12 +464,11 @@ UM.MainWindow target: Cura.Actions.addProfile onTriggered: { - Cura.ContainerManager.createQualityChanges(null); preferences.setPage(4); preferences.show(); - // Show the renameDialog after a very short delay so the preference page has time to initiate - showProfileNameDialogTimer.start(); + // Create a new profile after a very short delay so the preference page has time to initiate + createProfileTimer.start(); } } @@ -516,11 +515,11 @@ UM.MainWindow Timer { - id: showProfileNameDialogTimer + id: createProfileTimer repeat: false interval: 1 - onTriggered: preferences.getCurrentItem().showProfileNameDialog() + onTriggered: preferences.getCurrentItem().createProfile() } // BlurSettings is a way to force the focus away from any of the setting items. diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index 56d8bd41a4..df8de3f00d 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -141,11 +141,12 @@ UM.ManagementPage scrollviewCaption: catalog.i18nc("@label %1 is printer name","Printer: %1").arg(Cura.MachineManager.activeMachineName) - signal showProfileNameDialog() - onShowProfileNameDialog: + signal createProfile() + onCreateProfile: { - renameDialog.open(); - renameDialog.selectText(); + newNameDialog.object = base.currentItem != null ? base.currentItem.name : ""; + newNameDialog.open(); + newNameDialog.selectText(); } signal selectContainer(string name) @@ -267,6 +268,7 @@ UM.ManagementPage UM.RenameDialog { + title: catalog.i18nc("@title:window", "Rename Profile") id: renameDialog; object: base.currentItem != null ? base.currentItem.name : "" onAccepted: @@ -279,6 +281,7 @@ UM.ManagementPage // Dialog to request a name when creating a new profile UM.RenameDialog { + title: catalog.i18nc("@title:window", "Create Profile") id: newNameDialog; object: ""; onAccepted: @@ -292,6 +295,7 @@ UM.ManagementPage // Dialog to request a name when duplicating a new profile UM.RenameDialog { + title: catalog.i18nc("@title:window", "Duplicate Profile") id: newDuplicateNameDialog; object: ""; onAccepted: