diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 70a15bcbb5..4d58dcee34 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -144,7 +144,9 @@ jobs: run: echo -n "$GPG_PRIVATE_KEY" | base64 --decode | gpg --import - name: Get Conan configuration - run: conan config install https://github.com/Ultimaker/conan-config.git + run: | + conan config install https://github.com/Ultimaker/conan-config.git + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - name: Use Conan download cache (Bash) run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 736c906dab..4690d27878 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -144,15 +144,13 @@ jobs: p12-password: ${{ secrets.MACOS_CERT_PASSPHRASE }} - name: Get Conan configuration - run: conan config install https://github.com/Ultimaker/conan-config.git + run: | + conan config install https://github.com/Ultimaker/conan-config.git + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - name: Use Conan download cache (Bash) run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" - - name: Set architecture conan profile - if: ${{ inputs.architecture == 'X64' }} - run: conan profile update settings.arch=x86_64 default - - name: Create the Packages (Bash) run: conan install $CURA_CONAN_VERSION ${{ inputs.conan_args }} --build=missing --update -if cura_inst -g VirtualPythonEnv -o cura:enterprise=$ENTERPRISE -o cura:staging=$STAGING --json "cura_inst/conan_install_info.json" diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 57bace2fef..88d9d84e94 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -114,7 +114,9 @@ jobs: run: conan profile new default --detect --force - name: Get Conan configuration - run: conan config install https://github.com/Ultimaker/conan-config.git + run: | + conan config install https://github.com/Ultimaker/conan-config.git + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - name: Use Conan download cache (Powershell) run: conan config set storage.download_cache="C:\Users\runneradmin\.conan\conan_download_cache" diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index 06ec247ae4..0dbf3ba782 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -111,11 +111,7 @@ class ConvexHullDecorator(SceneNodeDecorator): # Parent can be None if node is just loaded. if self._isSingularOneAtATimeNode(): - hull = self.getConvexHullHeadFull() - if hull is None: - return None - hull = self._add2DAdhesionMargin(hull) - return hull + return self.getConvexHullHeadFull() return self._compute2DConvexHull() @@ -323,6 +319,7 @@ class ConvexHullDecorator(SceneNodeDecorator): def _compute2DConvexHeadFull(self) -> Optional[Polygon]: convex_hull = self._compute2DConvexHull() + convex_hull = self._add2DAdhesionMargin(convex_hull) if convex_hull: return convex_hull.getMinkowskiHull(self._getHeadAndFans()) return None diff --git a/packaging/MacOS/build_macos.py b/packaging/MacOS/build_macos.py index bde28afab0..eae9afceff 100644 --- a/packaging/MacOS/build_macos.py +++ b/packaging/MacOS/build_macos.py @@ -153,6 +153,6 @@ if __name__ == "__main__": app_name = f"{args.app_name}.app" if args.build_pkg: - create_pkg_installer(args.filename + ".pkg", args.dist_path, cura_version, app_name) + create_pkg_installer(f"{args.filename}.pkg", args.dist_path, cura_version, app_name) if args.build_dmg: - create_dmg(args.filename + ".dmg", args.dist_path, args.source_path, app_name) + create_dmg(f"{args.filename}.dmg", args.dist_path, args.source_path, app_name) diff --git a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py index e93473c25f..8c0c50d0b4 100644 --- a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py +++ b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py @@ -1,6 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import os import os.path from UM.Application import Application @@ -143,38 +144,44 @@ class RemovableDriveOutputDevice(OutputDevice): def _onFinished(self, job): if self._stream: - # Explicitly closing the stream flushes the write-buffer + error = job.getError() try: + # Explicitly closing the stream flushes the write-buffer self._stream.close() - self._stream = None - except: - Logger.logException("w", "An exception occurred while trying to write to removable drive.") - message = Message(catalog.i18nc("@info:status", "Could not save to removable drive {0}: {1}").format(self.getName(),str(job.getError())), - title = catalog.i18nc("@info:title", "Error"), - message_type = Message.MessageType.ERROR) + except Exception as e: + if not error: + # Only log new error if there was no previous one + error = e + + self._stream = None + self._writing = False + self.writeFinished.emit(self) + + if not error: + message = Message( + catalog.i18nc("@info:status", "Saved to Removable Drive {0} as {1}").format(self.getName(), + os.path.basename( + job.getFileName())), + title=catalog.i18nc("@info:title", "File Saved"), + message_type=Message.MessageType.POSITIVE) + message.addAction("eject", catalog.i18nc("@action:button", "Eject"), "eject", + catalog.i18nc("@action", "Eject removable device {0}").format(self.getName())) + message.actionTriggered.connect(self._onActionTriggered) + message.show() + self.writeSuccess.emit(self) + else: + try: + os.remove(job.getFileName()) + except Exception as e: + Logger.logException("e", "Exception when trying to remove incomplete exported file %s", + str(job.getFileName())) + message = Message(catalog.i18nc("@info:status", + "Could not save to removable drive {0}: {1}").format(self.getName(), + str(job.getError())), + title=catalog.i18nc("@info:title", "Error"), + message_type=Message.MessageType.ERROR) message.show() self.writeError.emit(self) - return - - self._writing = False - self.writeFinished.emit(self) - if job.getResult(): - message = Message(catalog.i18nc("@info:status", "Saved to Removable Drive {0} as {1}").format(self.getName(), os.path.basename(job.getFileName())), - title = catalog.i18nc("@info:title", "File Saved"), - message_type = Message.MessageType.POSITIVE) - message.addAction("eject", catalog.i18nc("@action:button", "Eject"), "eject", catalog.i18nc("@action", "Eject removable device {0}").format(self.getName())) - message.actionTriggered.connect(self._onActionTriggered) - message.show() - self.writeSuccess.emit(self) - else: - message = Message(catalog.i18nc("@info:status", - "Could not save to removable drive {0}: {1}").format(self.getName(), - str(job.getError())), - title = catalog.i18nc("@info:title", "Error"), - message_type = Message.MessageType.ERROR) - message.show() - self.writeError.emit(self) - job.getStream().close() def _onActionTriggered(self, message, action): if action == "eject": diff --git a/plugins/UM3NetworkPrinting/src/Messages/NewPrinterDetectedMessage.py b/plugins/UM3NetworkPrinting/src/Messages/NewPrinterDetectedMessage.py index d85ade9dce..76254547da 100644 --- a/plugins/UM3NetworkPrinting/src/Messages/NewPrinterDetectedMessage.py +++ b/plugins/UM3NetworkPrinting/src/Messages/NewPrinterDetectedMessage.py @@ -37,24 +37,13 @@ class NewPrinterDetectedMessage(Message): def finalize(self, new_devices_added, new_output_devices): self.setProgress(None) - num_devices_added = len(new_devices_added) - max_disp_devices = 3 - - if num_devices_added > max_disp_devices: - num_hidden = num_devices_added - max_disp_devices - device_name_list = ["
  • {} ({})
  • ".format(device.name, device.printerTypeName) for device in - new_output_devices[0: max_disp_devices]] - device_name_list.append( - "
  • " + self.i18n_catalog.i18ncp("info:{0} gets replaced by a number of printers", "... and {0} other", - "... and {0} others", num_hidden) + "
  • ") - device_names = "".join(device_name_list) - else: - device_names = "".join( - ["
  • {} ({})
  • ".format(device.name, device.printerTypeName) for device in new_devices_added]) if new_devices_added: - message_text = self.i18n_catalog.i18nc("info:status", - "Printers added from Digital Factory:") + f"" + device_names = "" + for device in new_devices_added: + device_names = device_names + "
  • {} ({})
  • ".format(device.name, device.printerTypeName) + message_title = self.i18n_catalog.i18nc("info:status", "Printers added from Digital Factory:") + message_text = f"{message_title}" self.setText(message_text) else: self.hide() diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 9c83431f54..ccd92c9ba3 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1664,16 +1664,28 @@ "small_skin_width": { "label": "Small Top/Bottom Width", - "description": "Small top/bottom regions are filled with walls instead of the default top/bottom pattern. This helps to avoids jerky motions.", + "description": "Small top/bottom regions are filled with walls instead of the default top/bottom pattern. This helps to avoids jerky motions. Off for the topmost (air-exposed) layer by default (see 'Small Top/Bottom On Surface').", "value": "skin_line_width * 2", "default_value": 1, "minimum_value": "0", + "maximum_value_warning": "skin_line_width * 3", "type": "float", "enabled": "(top_layers > 0 or bottom_layers > 0) and top_bottom_pattern != 'concentric'", "limit_to_extruder": "top_bottom_extruder_nr", "settable_per_mesh": true, "unit": "mm" }, + "small_skin_on_surface": + { + "label": "Small Top/Bottom On Surface", + "description": "Enable small (up to 'Small Top/Bottom Width') regions on the topmost skinned layer (exposed to air) to be filled with walls instead of the default pattern.", + "value": "False", + "default_value": false, + "type": "bool", + "enabled": "small_skin_width > 0 and top_layers > 0", + "limit_to_extruder": "top_bottom_extruder_nr", + "settable_per_mesh": true + }, "skin_no_small_gaps_heuristic": { "label": "No Skin in Z Gaps", diff --git a/resources/definitions/kingroon_kp3s_pro.def.json b/resources/definitions/kingroon_kp3s_pro.def.json index 79a4916680..bda5ac03b2 100644 --- a/resources/definitions/kingroon_kp3s_pro.def.json +++ b/resources/definitions/kingroon_kp3s_pro.def.json @@ -5,13 +5,14 @@ "metadata": { "visible": true, + "author": "willuhmjs", "platform": "kingroon_kp3s.stl", - "quality_definition": "kingroon_base", - "author": "willuhmjs" + "quality_definition": "kingroon_base" }, "overrides": { "machine_acceleration": { "value": 1000 }, + "machine_depth": { "default_value": 200 }, "machine_height": { "default_value": 200 }, "machine_max_acceleration_e": { "value": 1000 }, "machine_max_acceleration_x": { "value": 1000 }, @@ -23,7 +24,7 @@ "machine_max_feedrate_z": { "value": 4 }, "machine_max_jerk_xy": { "value": 15 }, "machine_max_jerk_z": { "value": 0.4 }, - "machine_name": { "default_value": "Kingroon KP3S" }, + "machine_name": { "default_value": "Kingroon KP3S Pro" }, "machine_steps_per_mm_e": { "value": 764 }, "machine_steps_per_mm_x": { "value": 160 }, "machine_steps_per_mm_y": { "value": 160 }, @@ -34,4 +35,4 @@ "retraction_speed": { "value": 40 }, "speed_z_hop": { "value": 4 } } -} +} \ No newline at end of file diff --git a/resources/definitions/tizyx_evy.def.json b/resources/definitions/tizyx_evy.def.json index 41b2b982be..7ae10f5f1d 100644 --- a/resources/definitions/tizyx_evy.def.json +++ b/resources/definitions/tizyx_evy.def.json @@ -57,7 +57,7 @@ [25, 49], [25, -49], [-25, -49], - [25, 49] + [-25, 49] ] }, "machine_heated_bed": { "default_value": true }, diff --git a/resources/definitions/tizyx_evy_dual.def.json b/resources/definitions/tizyx_evy_dual.def.json index 22046a16ff..7ef3aff2ab 100644 --- a/resources/definitions/tizyx_evy_dual.def.json +++ b/resources/definitions/tizyx_evy_dual.def.json @@ -54,7 +54,7 @@ [25, 49], [25, -49], [-25, -49], - [25, 49] + [-25, 49] ] }, "machine_heated_bed": { "default_value": true }, diff --git a/resources/definitions/tizyx_k25.def.json b/resources/definitions/tizyx_k25.def.json index 60005e8712..fbae8a657d 100644 --- a/resources/definitions/tizyx_k25.def.json +++ b/resources/definitions/tizyx_k25.def.json @@ -51,7 +51,7 @@ [25, 49], [25, -49], [-25, -49], - [25, 49] + [-25, 49] ] }, "machine_heated_bed": { "default_value": true }, diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 3b75c7699e..f945b1c11d 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -60,6 +60,7 @@ Item property alias showProfileFolder: showProfileFolderAction property alias documentation: documentationAction property alias showTroubleshooting: showTroubleShootingAction + property alias openSponsershipPage: openSponsershipPageAction property alias reportBug: reportBugAction property alias whatsNew: whatsNewAction property alias about: aboutAction @@ -90,6 +91,13 @@ Item text: catalog.i18nc("@action:inmenu", "Show Online Troubleshooting") } + Action + { + id: openSponsershipPageAction + onTriggered: Qt.openUrlExternally("https://ultimaker.com/software/ultimaker-cura/sponsor/") + text: catalog.i18nc("@action:inmenu", "Sponsor Cura") + } + Action { id: toggleFullScreenAction diff --git a/resources/qml/ApplicationSwitcher/ApplicationSwitcherPopup.qml b/resources/qml/ApplicationSwitcher/ApplicationSwitcherPopup.qml index f13ca28447..a42239056c 100644 --- a/resources/qml/ApplicationSwitcher/ApplicationSwitcherPopup.qml +++ b/resources/qml/ApplicationSwitcher/ApplicationSwitcherPopup.qml @@ -57,10 +57,10 @@ Popup permissionsRequired: [] }, { - displayName: "UltiMaker Academy", //Not translated, since it's a brand name. - thumbnail: UM.Theme.getIcon("Knowledge"), - description: catalog.i18nc("@tooltip:button", "Become a 3D printing expert with UltiMaker e-learning."), - link: "https://academy.ultimaker.com/?utm_source=cura&utm_medium=software&utm_campaign=switcher-academy", + displayName: catalog.i18nc("@label:button", "Sponsor Cura"), + thumbnail: UM.Theme.getIcon("Heart"), + description: catalog.i18nc("@tooltip:button", "Show your support for Cura with a donation."), + link: "https://ultimaker.com/software/ultimaker-cura/sponsor/", permissionsRequired: [] }, { diff --git a/resources/qml/Menus/HelpMenu.qml b/resources/qml/Menus/HelpMenu.qml index 4be25fdffe..6a57a99515 100644 --- a/resources/qml/Menus/HelpMenu.qml +++ b/resources/qml/Menus/HelpMenu.qml @@ -17,6 +17,7 @@ Cura.Menu Cura.MenuItem { action: Cura.Actions.showTroubleshooting} Cura.MenuItem { action: Cura.Actions.documentation } Cura.MenuItem { action: Cura.Actions.reportBug } + Cura.MenuItem { action: Cura.Actions.openSponsershipPage } Cura.MenuSeparator { } Cura.MenuItem { action: Cura.Actions.whatsNew } Cura.MenuItem { action: Cura.Actions.about } diff --git a/resources/qml/Widgets/ScrollableTextArea.qml b/resources/qml/Widgets/ScrollableTextArea.qml index 7d8f6b886d..3f0db28058 100644 --- a/resources/qml/Widgets/ScrollableTextArea.qml +++ b/resources/qml/Widgets/ScrollableTextArea.qml @@ -4,41 +4,11 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import UM 1.5 as UM +import UM 1.7 as UM import Cura 1.1 as Cura -// -// Cura-style TextArea with scrolls -// -Flickable +// Wrapper to UM.ScrollableTextArea which was originally placed here +UM.ScrollableTextArea { - id: scrollableTextAreaBase - property bool do_borders: true - property var back_color: UM.Theme.getColor("main_background") - property alias textArea: flickableTextArea - - ScrollBar.vertical: UM.ScrollBar {} - - TextArea.flickable: TextArea - { - id: flickableTextArea - - background: Rectangle //Providing the background color and border. - { - anchors.fill: parent - anchors.margins: -border.width - - color: scrollableTextAreaBase.back_color - border.color: UM.Theme.getColor("thick_lining") - border.width: scrollableTextAreaBase.do_borders ? UM.Theme.getSize("default_lining").width : 0 - } - - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - textFormat: TextEdit.PlainText - renderType: Text.NativeRendering - wrapMode: Text.Wrap - selectByMouse: true - } -} \ No newline at end of file +} diff --git a/resources/setting_visibility/advanced.cfg b/resources/setting_visibility/advanced.cfg index a744c8eae8..c3451d2c98 100644 --- a/resources/setting_visibility/advanced.cfg +++ b/resources/setting_visibility/advanced.cfg @@ -34,6 +34,8 @@ bottom_thickness bottom_layers ironing_enabled skin_monotonic +small_skin_width +small_skin_on_surface [infill] infill_extruder_nr diff --git a/resources/setting_visibility/expert.cfg b/resources/setting_visibility/expert.cfg index 9247ad1b9e..5388681e8f 100644 --- a/resources/setting_visibility/expert.cfg +++ b/resources/setting_visibility/expert.cfg @@ -60,6 +60,7 @@ skin_monotonic connect_skin_polygons skin_angles small_skin_width +small_skin_on_surface skin_no_small_gaps_heuristic skin_outline_count ironing_enabled diff --git a/resources/themes/cura-light/icons/default/Heart.svg b/resources/themes/cura-light/icons/default/Heart.svg new file mode 100644 index 0000000000..793dcbd6b5 --- /dev/null +++ b/resources/themes/cura-light/icons/default/Heart.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/variants/kingroon/kingroon_kp3s_pro_0.2.inst.cfg b/resources/variants/kingroon/kingroon_kp3s_pro_0.2.inst.cfg new file mode 100644 index 0000000000..4d0858a573 --- /dev/null +++ b/resources/variants/kingroon/kingroon_kp3s_pro_0.2.inst.cfg @@ -0,0 +1,13 @@ +[general] +definition = kingroon_kp3s_pro +name = 0.2mm Nozzle +version = 4 + +[metadata] +hardware_type = nozzle +setting_version = 22 +type = variant + +[values] +machine_nozzle_size = 0.2 + diff --git a/resources/variants/kingroon/kingroon_kp3s_pro_0.3.inst.cfg b/resources/variants/kingroon/kingroon_kp3s_pro_0.3.inst.cfg new file mode 100644 index 0000000000..e127a63c76 --- /dev/null +++ b/resources/variants/kingroon/kingroon_kp3s_pro_0.3.inst.cfg @@ -0,0 +1,13 @@ +[general] +definition = kingroon_kp3s_pro +name = 0.3mm Nozzle +version = 4 + +[metadata] +hardware_type = nozzle +setting_version = 22 +type = variant + +[values] +machine_nozzle_size = 0.3 + diff --git a/resources/variants/kingroon/kingroon_kp3s_pro_0.4.inst.cfg b/resources/variants/kingroon/kingroon_kp3s_pro_0.4.inst.cfg new file mode 100644 index 0000000000..3422da8b90 --- /dev/null +++ b/resources/variants/kingroon/kingroon_kp3s_pro_0.4.inst.cfg @@ -0,0 +1,13 @@ +[general] +definition = kingroon_kp3s_pro +name = 0.4mm Nozzle +version = 4 + +[metadata] +hardware_type = nozzle +setting_version = 22 +type = variant + +[values] +machine_nozzle_size = 0.4 + diff --git a/resources/variants/kingroon/kingroon_kp3s_pro_0.5.inst.cfg b/resources/variants/kingroon/kingroon_kp3s_pro_0.5.inst.cfg new file mode 100644 index 0000000000..744e7e441d --- /dev/null +++ b/resources/variants/kingroon/kingroon_kp3s_pro_0.5.inst.cfg @@ -0,0 +1,13 @@ +[general] +definition = kingroon_kp3s_pro +name = 0.5mm Nozzle +version = 4 + +[metadata] +hardware_type = nozzle +setting_version = 22 +type = variant + +[values] +machine_nozzle_size = 0.5 + diff --git a/resources/variants/kingroon/kingroon_kp3s_pro_0.6.inst.cfg b/resources/variants/kingroon/kingroon_kp3s_pro_0.6.inst.cfg new file mode 100644 index 0000000000..289eb3bd96 --- /dev/null +++ b/resources/variants/kingroon/kingroon_kp3s_pro_0.6.inst.cfg @@ -0,0 +1,13 @@ +[general] +definition = kingroon_kp3s_pro +name = 0.6mm Nozzle +version = 4 + +[metadata] +hardware_type = nozzle +setting_version = 22 +type = variant + +[values] +machine_nozzle_size = 0.6 + diff --git a/resources/variants/kingroon/kingroon_kp3s_pro_0.8.inst.cfg b/resources/variants/kingroon/kingroon_kp3s_pro_0.8.inst.cfg new file mode 100644 index 0000000000..68aa6dc7da --- /dev/null +++ b/resources/variants/kingroon/kingroon_kp3s_pro_0.8.inst.cfg @@ -0,0 +1,13 @@ +[general] +definition = kingroon_kp3s_pro +name = 0.8mm Nozzle +version = 4 + +[metadata] +hardware_type = nozzle +setting_version = 22 +type = variant + +[values] +machine_nozzle_size = 0.8 + diff --git a/resources/variants/kingroon/kingroon_kp3s_pro_1.0.inst.cfg b/resources/variants/kingroon/kingroon_kp3s_pro_1.0.inst.cfg new file mode 100644 index 0000000000..5e332ea26b --- /dev/null +++ b/resources/variants/kingroon/kingroon_kp3s_pro_1.0.inst.cfg @@ -0,0 +1,13 @@ +[general] +definition = kingroon_kp3s_pro +name = 1.0mm Nozzle +version = 4 + +[metadata] +hardware_type = nozzle +setting_version = 22 +type = variant + +[values] +machine_nozzle_size = 1.0 +