From 64a5349018d5fbf57abdefe3fc57b63403c1c2c5 Mon Sep 17 00:00:00 2001 From: StefanBruens Date: Thu, 20 Feb 2020 02:29:24 +0100 Subject: [PATCH 01/87] Avoid crash caused by KDE qqc2-desktop-style Workaround/fix for #5488 Also see https://bugs.kde.org/show_bug.cgi?id=417900 --- cura_app.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cura_app.py b/cura_app.py index 6149eeeb9a..3c2620bb38 100755 --- a/cura_app.py +++ b/cura_app.py @@ -207,5 +207,11 @@ if Platform.isLinux() and getattr(sys, "frozen", False): import trimesh.exchange.load os.environ["LD_LIBRARY_PATH"] = old_env +# WORKAROUND: CURA-5488 +# When using the KDE qqc2-desktop-style, the UI layout is completely broken, and +# even worse, it crashes when switching to the "Preview" pane. +if Platform.isLinux(): + os.environ["QT_QUICK_CONTROLS_STYLE"] = "material" + app = CuraApplication() app.run() From 09066f2744cd11852131b6678b2e141713c72deb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 2 Sep 2020 14:19:38 +0200 Subject: [PATCH 02/87] Don't mark children nodes in a group below the buildplate Fixes #8268 --- cura/BuildVolume.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 373f708389..81a1eed6ad 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -272,7 +272,7 @@ class BuildVolume(SceneNode): continue # If the entire node is below the build plate, still mark it as outside. node_bounding_box = node.getBoundingBox() - if node_bounding_box and node_bounding_box.top < 0: + if node_bounding_box and node_bounding_box.top < 0 and not node.getParent().callDecoration("isGroup"): node.setOutsideBuildArea(True) continue # Mark the node as outside build volume if the set extruder is disabled From 5a14ef2a56d96b3dbc697c949d0fb13834ac8c59 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 9 Nov 2020 14:14:32 +0100 Subject: [PATCH 03/87] Simplify and speed up the extending of the qualities list --- cura/Machines/MaterialNode.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cura/Machines/MaterialNode.py b/cura/Machines/MaterialNode.py index c78c6aff03..6820c7d740 100644 --- a/cura/Machines/MaterialNode.py +++ b/cura/Machines/MaterialNode.py @@ -88,8 +88,10 @@ class MaterialNode(ContainerNode): variant = self.variant.variant_name) else: qualities_any_material = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.variant.machine.quality_definition) - for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type): - qualities.extend((quality for quality in qualities_any_material if quality.get("material") == material_metadata["base_file"])) + + all_material_base_files = [material_metadata["base_file"] for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type)] + + qualities.extend((quality for quality in qualities_any_material if quality.get("material") in all_material_base_files)) if not qualities: # No quality profiles found. Go by GUID then. my_guid = self.guid From 01648a329e225795b7fb7a4c74a0755da82df69b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 9 Nov 2020 15:28:13 +0100 Subject: [PATCH 04/87] Convert list of all base_files into set Co-authored-by: Ghostkeeper --- cura/Machines/MaterialNode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Machines/MaterialNode.py b/cura/Machines/MaterialNode.py index 6820c7d740..18db7d982d 100644 --- a/cura/Machines/MaterialNode.py +++ b/cura/Machines/MaterialNode.py @@ -89,7 +89,7 @@ class MaterialNode(ContainerNode): else: qualities_any_material = container_registry.findInstanceContainersMetadata(type = "quality", definition = self.variant.machine.quality_definition) - all_material_base_files = [material_metadata["base_file"] for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type)] + all_material_base_files = {material_metadata["base_file"] for material_metadata in container_registry.findInstanceContainersMetadata(type = "material", material = my_material_type)} qualities.extend((quality for quality in qualities_any_material if quality.get("material") in all_material_base_files)) From 394fa8f2f294c292633f828a6ec8c97148a75ac2 Mon Sep 17 00:00:00 2001 From: Lucas Teske Date: Tue, 17 Nov 2020 17:05:15 -0300 Subject: [PATCH 05/87] Add Z-Hop and filament retraction to Timelapse So less string occurs --- .../PostProcessingPlugin/scripts/TimeLapse.py | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/TimeLapse.py b/plugins/PostProcessingPlugin/scripts/TimeLapse.py index 41fd4a5805..778ec0f7b2 100644 --- a/plugins/PostProcessingPlugin/scripts/TimeLapse.py +++ b/plugins/PostProcessingPlugin/scripts/TimeLapse.py @@ -66,6 +66,22 @@ class TimeLapse(Script): "type": "float", "default_value": 9000, "enabled": "park_print_head" + }, + "retract": + { + "label": "Retraction Distance", + "description": "Filament retraction distance for camera trigger.", + "unit": "mm", + "type": "int", + "default_value": 1 + }, + "zhop": + { + "label": "Z-Hop when parking", + "description": "Z-hop length before parking", + "unit": "mm", + "type": "float", + "default_value": 0.8 } } }""" @@ -77,9 +93,12 @@ class TimeLapse(Script): y_park = self.getSettingValueByKey("head_park_y") trigger_command = self.getSettingValueByKey("trigger_command") pause_length = self.getSettingValueByKey("pause_length") + retract = int(self.getSettingValueByKey("retract")) + zhop = self.getSettingValueByKey("zhop") gcode_to_append = ";TimeLapse Begin\n" last_x = 0 last_y = 0 + last_z = 0 if park_print_head: gcode_to_append += self.putValue(G=1, F=feed_rate, @@ -90,16 +109,35 @@ class TimeLapse(Script): for idx, layer in enumerate(data): for line in layer.split("\n"): - if self.getValue(line, "G") in {0, 1}: # Track X,Y location. + if self.getValue(line, "G") in {0, 1}: # Track X,Y,Z location. last_x = self.getValue(line, "X", last_x) last_y = self.getValue(line, "Y", last_y) + last_z = self.getValue(line, "Z", last_z) # Check that a layer is being printed lines = layer.split("\n") for line in lines: if ";LAYER:" in line: + if retract != 0: # Retract the filament so no stringing happens + layer += self.putValue(M=83) + " ;Extrude Relative\n" + layer += self.putValue(G=1, E=-retract, F=3000) + " ;Retract filament\n" + layer += self.putValue(M=82) + " ;Extrude Absolute\n" + layer += self.putValue(M=400) + " ;Wait for moves to finish\n" # Wait to fully retract before hopping + + if zhop != 0: + layer += self.putValue(G=1, Z=last_z+zhop, F=3000) + " ;Z-Hop\n" + layer += gcode_to_append - layer += "G0 X%s Y%s\n" % (last_x, last_y) + if zhop != 0: + layer += self.putValue(G=0, X=last_x, Y=last_y, Z=last_z) + "; Restore position \n" + else: + layer += self.putValue(G=0, X=last_x, Y=last_y) + "; Restore position \n" + + if retract != 0: + layer += self.putValue(M=400) + " ;Wait for moves to finish\n" + layer += self.putValue(M=83) + " ;Extrude Relative\n" + layer += self.putValue(G=1, E=retract, F=3000) + " ;Retract filament\n" + layer += self.putValue(M=82) + " ;Extrude Absolute\n" data[idx] = layer break From 67b27a53d30767aec38a18a59d1b2837f884b104 Mon Sep 17 00:00:00 2001 From: Michael Graf Date: Fri, 20 Nov 2020 18:53:20 +0100 Subject: [PATCH 06/87] ai3m use material temperatures Makes Anycubic i3 Mega use bed and print temperature from material. --- .../quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg | 3 --- .../quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg | 3 --- .../quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg | 3 --- 3 files changed, 9 deletions(-) diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg index 27bf2ce7dd..e91ffacd62 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg @@ -28,10 +28,7 @@ jerk_print = 8 jerk_travel = 10 layer_height = 0.3 layer_height_0 = 0.3 -material_bed_temperature = 60 material_diameter = 1.75 -material_print_temperature = 200 -material_print_temperature_layer_0 = 0 retract_at_layer_change = False retraction_amount = 6 retraction_hop = 0.075 diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg index 6005152107..adafd850ef 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg @@ -28,10 +28,7 @@ jerk_print = 8 jerk_travel = 10 layer_height = 0.1 layer_height_0 = 0.1 -material_bed_temperature = 60 material_diameter = 1.75 -material_print_temperature = 200 -material_print_temperature_layer_0 = 0 retract_at_layer_change = False retraction_amount = 6 retraction_hop = 0.075 diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg index b2c0309dc9..5ea655190c 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg @@ -28,10 +28,7 @@ jerk_print = 8 jerk_travel = 10 layer_height = 0.2 layer_height_0 = 0.2 -material_bed_temperature = 60 material_diameter = 1.75 -material_print_temperature = 200 -material_print_temperature_layer_0 = 0 retract_at_layer_change = False retraction_amount = 6 retraction_hop = 0.075 From 69dc54b76334dfe2d08c8f68bcc321a46a3e8800 Mon Sep 17 00:00:00 2001 From: Michael Graf Date: Fri, 20 Nov 2020 19:10:30 +0100 Subject: [PATCH 07/87] Makes Anycubic i3 Mega use fan speed from material. Sets max speed to 100% for small layers as the stock fan is not very strong. --- .../quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg | 3 +-- .../quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg | 3 +-- .../quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg index e91ffacd62..60c4210209 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg @@ -17,8 +17,7 @@ acceleration_travel = 3000 adhesion_type = skirt brim_width = 4.0 cool_fan_full_at_height = 0.5 -cool_fan_speed = 100 -cool_fan_speed_0 = 100 +cool_fan_speed_max = 100 infill_overlap = 15 infill_pattern = zigzag infill_sparse_density = 25 diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg index adafd850ef..209e2008d1 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg @@ -17,8 +17,7 @@ acceleration_travel = 3000 adhesion_type = skirt brim_width = 4.0 cool_fan_full_at_height = 0.5 -cool_fan_speed = 100 -cool_fan_speed_0 = 100 +cool_fan_speed_max = 100 infill_overlap = 15 infill_pattern = zigzag infill_sparse_density = 25 diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg index 5ea655190c..e0e4d0743b 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg @@ -17,8 +17,7 @@ acceleration_travel = 3000 adhesion_type = skirt brim_width = 4.0 cool_fan_full_at_height = 0.5 -cool_fan_speed = 100 -cool_fan_speed_0 = 100 +cool_fan_speed_max = 100 infill_overlap = 15 infill_pattern = zigzag infill_sparse_density = 25 From 0a21cff2340c59e0a98721b53797936d1a4ae4d4 Mon Sep 17 00:00:00 2001 From: Lucas Teske Date: Wed, 25 Nov 2020 19:33:24 -0300 Subject: [PATCH 08/87] Update plugins/PostProcessingPlugin/scripts/TimeLapse.py Co-authored-by: Jaime van Kessel --- plugins/PostProcessingPlugin/scripts/TimeLapse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/TimeLapse.py b/plugins/PostProcessingPlugin/scripts/TimeLapse.py index 778ec0f7b2..7f320b51d7 100644 --- a/plugins/PostProcessingPlugin/scripts/TimeLapse.py +++ b/plugins/PostProcessingPlugin/scripts/TimeLapse.py @@ -77,7 +77,7 @@ class TimeLapse(Script): }, "zhop": { - "label": "Z-Hop when parking", + "label": "Z-Hop Height When Parking", "description": "Z-hop length before parking", "unit": "mm", "type": "float", From 62e4baa746854230748fe27d027897cf9a4fcfa7 Mon Sep 17 00:00:00 2001 From: Lucas Teske Date: Wed, 25 Nov 2020 19:33:30 -0300 Subject: [PATCH 09/87] Update plugins/PostProcessingPlugin/scripts/TimeLapse.py Co-authored-by: Jaime van Kessel --- plugins/PostProcessingPlugin/scripts/TimeLapse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/TimeLapse.py b/plugins/PostProcessingPlugin/scripts/TimeLapse.py index 7f320b51d7..8a555fdbeb 100644 --- a/plugins/PostProcessingPlugin/scripts/TimeLapse.py +++ b/plugins/PostProcessingPlugin/scripts/TimeLapse.py @@ -73,7 +73,7 @@ class TimeLapse(Script): "description": "Filament retraction distance for camera trigger.", "unit": "mm", "type": "int", - "default_value": 1 + "default_value": 0 }, "zhop": { From c4d9edca69713a71189c2213470a19244f4f8b05 Mon Sep 17 00:00:00 2001 From: Lucas Teske Date: Wed, 25 Nov 2020 19:33:37 -0300 Subject: [PATCH 10/87] Update plugins/PostProcessingPlugin/scripts/TimeLapse.py Co-authored-by: Jaime van Kessel --- plugins/PostProcessingPlugin/scripts/TimeLapse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/TimeLapse.py b/plugins/PostProcessingPlugin/scripts/TimeLapse.py index 8a555fdbeb..210199e087 100644 --- a/plugins/PostProcessingPlugin/scripts/TimeLapse.py +++ b/plugins/PostProcessingPlugin/scripts/TimeLapse.py @@ -81,7 +81,7 @@ class TimeLapse(Script): "description": "Z-hop length before parking", "unit": "mm", "type": "float", - "default_value": 0.8 + "default_value": 0 } } }""" From a4fd701f4b51612e34acf45eb362003a848d09bd Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 26 Nov 2020 17:29:50 +0100 Subject: [PATCH 11/87] Correct description of Mesh Processing Rank to indicate correct order Two problems with this description: - It claims that meshes with lower rank override meshes with higher rank. It's the other way around. - It mentions 'rank' in one sentence and then 'order' in the next. I've corrected this to use the term 'rank' in both sentences for clarity. This is also the term used for the setting label, to be consistent. Found in issue #8821. --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index b4d6f4a44a..d34ce63224 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -6161,7 +6161,7 @@ "infill_mesh_order": { "label": "Mesh Processing Rank", - "description": "Determines the priority of this mesh when considering multiple overlapping infill meshes. Areas where multiple infill meshes overlap will take on the settings of the mesh with the lowest rank. An infill mesh with a higher order will modify the infill of infill meshes with lower order and normal meshes.", + "description": "Determines the priority of this mesh when considering multiple overlapping infill meshes. Areas where multiple infill meshes overlap will take on the settings of the mesh with the highest rank. An infill mesh with a higher rank will modify the infill of infill meshes with lower rank and normal meshes.", "default_value": 0, "value": "1 if infill_mesh else 0", "minimum_value_warning": "1", From 00bc1bfcdc960d4bad62adc4b1615ca4cc6b3cee Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 27 Nov 2020 10:46:33 +0100 Subject: [PATCH 12/87] Add extra cast to author model Contributes to #8817 --- plugins/Toolbox/src/AuthorsModel.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/src/AuthorsModel.py b/plugins/Toolbox/src/AuthorsModel.py index 9a8e7f5dfe..04c8ed3a40 100644 --- a/plugins/Toolbox/src/AuthorsModel.py +++ b/plugins/Toolbox/src/AuthorsModel.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import re -from typing import Dict, List, Optional, Union +from typing import Dict, List, Optional, Union, cast from PyQt5.QtCore import Qt, pyqtProperty @@ -68,7 +68,7 @@ class AuthorsModel(ListModel): # Execute all filters. filtered_items = list(items) - filtered_items.sort(key = lambda k: k["name"]) + filtered_items.sort(key = lambda k: cast(str, k["name"])) self.setItems(filtered_items) def setFilter(self, filter_dict: Dict[str, str]) -> None: From 4df5fbc2153ac7c95aab80345b4011968f3bf71e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 27 Nov 2020 12:10:47 +0100 Subject: [PATCH 13/87] Make skirt/brim line width and flow visible for draft/ooze shields The draft and ooze shield use the skirt/brim settings to print. So these settings should be adjustable if the draft or ooze shield is enabled. It's a bit weird UX-wise, but it's correct. The UX problem will be tackled later. Apparently this was already done for the speed/acceleration/jerk settings so no change necessary there. Contributes to issue #8808. --- resources/definitions/fdmprinter.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index d34ce63224..c17e0da9d8 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -873,7 +873,7 @@ "default_value": 0.4, "type": "float", "value": "line_width", - "enabled": "resolveOrValue('adhesion_type') == 'skirt' or resolveOrValue('adhesion_type') == 'brim' or resolveOrValue('prime_tower_brim_enable')", + "enabled": "resolveOrValue('adhesion_type') == 'skirt' or resolveOrValue('adhesion_type') == 'brim' or resolveOrValue('prime_tower_brim_enable') or resolveOrValue('draft_shield_enabled') or resolveOrValue('ooze_shield_enabled')", "settable_per_mesh": false, "settable_per_extruder": true }, @@ -2614,7 +2614,7 @@ "minimum_value": "5", "minimum_value_warning": "50", "maximum_value_warning": "150", - "enabled": "resolveOrValue('adhesion_type') == 'skirt' or resolveOrValue('adhesion_type') == 'brim'", + "enabled": "resolveOrValue('adhesion_type') == 'skirt' or resolveOrValue('adhesion_type') == 'brim' or resolveOrValue('draft_shield_enabled') or resolveOrValue('ooze_shield_enabled')", "settable_per_mesh": false, "settable_per_extruder": true }, From 590e46cef3367f70a73735b6555b4dc8e3c2208d Mon Sep 17 00:00:00 2001 From: Or Bin Date: Sat, 28 Nov 2020 22:45:58 +0200 Subject: [PATCH 14/87] Fixed typo: positionning -> positioning --- resources/definitions/SV01.def.json | 4 ++-- resources/definitions/anycubic_mega_zero.def.json | 2 +- resources/definitions/biqu_base.def.json | 2 +- resources/definitions/creality_base.def.json | 2 +- resources/definitions/creality_ender5.def.json | 4 ++-- resources/definitions/flyingbear_base.def.json | 4 ++-- resources/definitions/lotmaxx_sc60.def.json | 2 +- resources/definitions/twotrees_bluer.def.json | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/resources/definitions/SV01.def.json b/resources/definitions/SV01.def.json index 02347a8e3b..439ced7d38 100644 --- a/resources/definitions/SV01.def.json +++ b/resources/definitions/SV01.def.json @@ -65,6 +65,6 @@ "retraction_speed": { "default_value": 50}, "adhesion_type": { "value": "'skirt'" }, "machine_start_gcode": { "default_value": "M201 X500.00 Y500.00 Z100.00 E5000.00 ;Setup machine max acceleration\nM203 X500.00 Y500.00 Z10.00 E50.00 ;Setup machine max feedrate\nM204 P500.00 R1000.00 T500.00 ;Setup Print/Retract/Travel acceleration\nM205 X8.00 Y8.00 Z0.40 E5.00 ;Setup Jerk\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\n" }, - "machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X0 Y240 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" } + "machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X0 Y240 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" } } -} \ No newline at end of file +} diff --git a/resources/definitions/anycubic_mega_zero.def.json b/resources/definitions/anycubic_mega_zero.def.json index a17fddc4b4..b0c3132858 100644 --- a/resources/definitions/anycubic_mega_zero.def.json +++ b/resources/definitions/anycubic_mega_zero.def.json @@ -59,7 +59,7 @@ }, "machine_end_gcode": { - "default_value": "M117 Cooling down...\nM104 S0 ; turn off extruder\nM84 ; disable motors\nM107 ; Fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 ;move X to min endstops, so the head is out of the way\nG90 ;Absolute positionning\nG1 Y200 F3000 ;Present print\nM84 ;steppers off\nM300 P300 S4000\nM117 Finished.\n" + "default_value": "M117 Cooling down...\nM104 S0 ; turn off extruder\nM84 ; disable motors\nM107 ; Fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 ;move X to min endstops, so the head is out of the way\nG90 ;Absolute positioning\nG1 Y200 F3000 ;Present print\nM84 ;steppers off\nM300 P300 S4000\nM117 Finished.\n" }, "machine_max_feedrate_x": { "value": 500 }, "machine_max_feedrate_y": { "value": 500 }, diff --git a/resources/definitions/biqu_base.def.json b/resources/definitions/biqu_base.def.json index 1077b864b8..6beaa3b7a8 100755 --- a/resources/definitions/biqu_base.def.json +++ b/resources/definitions/biqu_base.def.json @@ -25,7 +25,7 @@ "overrides": { "machine_name": { "default_value": "BIQU Base Printer" }, "machine_start_gcode": { "default_value": "M201 X500.00 Y500.00 Z100.00 E5000.00 ;Setup machine max acceleration\nM203 X500.00 Y500.00 Z10.00 E50.00 ;Setup machine max feedrate\nM204 P500.00 R1000.00 T500.00 ;Setup Print/Retract/Travel acceleration\nM205 X8.00 Y8.00 Z0.40 E5.00 ;Setup Jerk\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\n" }, - "machine_end_gcode": { "default_value": " ;BIQU Default End Gcode\nG91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract a bit more and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z by 10mm\nG90 ;Return to absolute positionning\n\nG1 X0 Y{machine_depth} ;TaDaaaa\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }, + "machine_end_gcode": { "default_value": " ;BIQU Default End Gcode\nG91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract a bit more and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z by 10mm\nG90 ;Return to absolute positioning\n\nG1 X0 Y{machine_depth} ;TaDaaaa\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }, "machine_max_feedrate_x": { "value": 500 }, "machine_max_feedrate_y": { "value": 500 }, diff --git a/resources/definitions/creality_base.def.json b/resources/definitions/creality_base.def.json index b56d2b7c06..892a0f9276 100644 --- a/resources/definitions/creality_base.def.json +++ b/resources/definitions/creality_base.def.json @@ -125,7 +125,7 @@ "overrides": { "machine_name": { "default_value": "Creawsome Base Printer" }, "machine_start_gcode": { "default_value": "M201 X500.00 Y500.00 Z100.00 E5000.00 ;Setup machine max acceleration\nM203 X500.00 Y500.00 Z10.00 E50.00 ;Setup machine max feedrate\nM204 P500.00 R1000.00 T500.00 ;Setup Print/Retract/Travel acceleration\nM205 X8.00 Y8.00 Z0.40 E5.00 ;Setup Jerk\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\n" }, - "machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }, + "machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }, "machine_max_feedrate_x": { "value": 500 }, "machine_max_feedrate_y": { "value": 500 }, diff --git a/resources/definitions/creality_ender5.def.json b/resources/definitions/creality_ender5.def.json index 1b4be4d71f..896f532c81 100644 --- a/resources/definitions/creality_ender5.def.json +++ b/resources/definitions/creality_ender5.def.json @@ -4,7 +4,7 @@ "inherits": "creality_base", "overrides": { "machine_name": { "default_value": "Creality Ender-5" }, - "machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y0 ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }, + "machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\n\nG1 X0 Y0 ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }, "machine_width": { "default_value": 220 }, "machine_depth": { "default_value": 220 }, "machine_height": { "default_value": 300 }, @@ -25,4 +25,4 @@ "quality_definition": "creality_base", "visible": true } -} \ No newline at end of file +} diff --git a/resources/definitions/flyingbear_base.def.json b/resources/definitions/flyingbear_base.def.json index 79a0b6ea89..7205e25be4 100644 --- a/resources/definitions/flyingbear_base.def.json +++ b/resources/definitions/flyingbear_base.def.json @@ -152,7 +152,7 @@ "machine_start_gcode": { "default_value": "M220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\n\nG28 ;Home\n\n;Code for nozzle cleaning and flow normalization\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\nG1 X10.4 Y20 Z0.28 F5000.0\nG1 X10.4 Y170.0 Z0.28 F1500.0 E15\nG1 X10.1 Y170.0 Z0.28 F5000.0\nG1 X10.1 Y40 Z0.28 F1500.0 E30\n\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up" }, - "machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract the filament\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG28 X0 Y0 ;Home X and Y\n\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z" }, + "machine_end_gcode": { "default_value": "G91 ;Relative positioning\nG1 E-2 F2700 ;Retract the filament\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\n\nG28 X0 Y0 ;Home X and Y\n\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z" }, "machine_heated_bed": { "default_value": true }, "machine_shape": { "default_value": "rectangular" }, @@ -256,4 +256,4 @@ "adaptive_layer_height_variation": { "value": 0.04 }, "adaptive_layer_height_variation_step": { "value": 0.04 } } -} \ No newline at end of file +} diff --git a/resources/definitions/lotmaxx_sc60.def.json b/resources/definitions/lotmaxx_sc60.def.json index abbf68d75a..a18e197757 100644 --- a/resources/definitions/lotmaxx_sc60.def.json +++ b/resources/definitions/lotmaxx_sc60.def.json @@ -20,7 +20,7 @@ "default_value":"G28 ;Home\nG92 E0 ;Reset Extruder\nG1 Z4.0 F3000 ;Move Z Axis up\nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z2.0 F3000 ;Move Z Axis up\n" }, "machine_end_gcode":{ - "default_value":"G91 ;Relative positionning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" + "default_value":"G91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\n\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\n\nM84 X Y E ;Disable all steppers but Z\n" }, "acceleration_print":{"value":1000}, "acceleration_travel":{"value":1000}, diff --git a/resources/definitions/twotrees_bluer.def.json b/resources/definitions/twotrees_bluer.def.json index a272527e6e..8cf7d804cf 100644 --- a/resources/definitions/twotrees_bluer.def.json +++ b/resources/definitions/twotrees_bluer.def.json @@ -30,7 +30,7 @@ ] }, "machine_start_gcode": { "default_value": "; Two Trees Bluer Custom Start G-code\nG28 ;Home\nG92 E0 ;Reset Extruder\nG1 Z4.0 F3000 ;Move Z Axis up\nG1 E10 F1500 ;Purge a bit\nG1 X10.1 Y20 Z0.2 F5000.0 ;Move to start position\nG1 X10.1 Y200.0 Z0.2 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.2 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.2 F1500.0 E20 ;Draw the second line\nG92 E0 ;Reset Extruder\nG1 Z3.0 X20 Y20 F3000 ;Move Z Axis up\nG1 E3 F2700 ;Purge a bit" }, - "machine_end_gcode": { "default_value": "; Two Trees Bluer Custom End G-code\nG91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positionning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" }, + "machine_end_gcode": { "default_value": "; Two Trees Bluer Custom End G-code\nG91 ;Relative positioning\nG1 E-2 F2700 ;Retract a bit\nG1 E-2 Z0.2 F2400 ;Retract and raise Z\nG1 X5 Y5 F3000 ;Wipe out\nG1 Z10 ;Raise Z more\nG90 ;Absolute positioning\nG1 X0 Y{machine_depth} ;Present print\nM106 S0 ;Turn-off fan\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM84 X Y E ;Disable all steppers but Z" }, "gantry_height": { "value": 25 } } } From f18c274825c69aac2de62dcee647e7f75f9e076f Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Wed, 2 Dec 2020 16:30:16 +0100 Subject: [PATCH 15/87] Remove duplicate settings from quality profiles After https://github.com/Ultimaker/Cura/pull/8684 raft_airgap and speed_layer_0 existed twice in some of the quality profiles. This commit fixes that by removing the old (calculated) values. --- .../ultimaker_s3/um_s3_aa0.25_Nylon_Normal_Quality.inst.cfg | 3 +-- .../ultimaker_s3/um_s3_aa0.4_ABS_Draft_Print.inst.cfg | 3 +-- .../ultimaker_s3/um_s3_aa0.4_ABS_Fast_Print.inst.cfg | 3 +-- .../ultimaker_s3/um_s3_aa0.4_ABS_High_Quality.inst.cfg | 3 +-- .../ultimaker_s3/um_s3_aa0.4_ABS_Normal_Quality.inst.cfg | 3 +-- .../ultimaker_s3/um_s3_aa0.4_Nylon_Draft_Print.inst.cfg | 6 ++---- .../ultimaker_s3/um_s3_aa0.4_Nylon_Fast_Print.inst.cfg | 6 ++---- .../ultimaker_s3/um_s3_aa0.4_Nylon_High_Quality.inst.cfg | 6 ++---- .../ultimaker_s3/um_s3_aa0.4_Nylon_Normal_Quality.inst.cfg | 6 ++---- .../ultimaker_s3/um_s3_aa0.4_PLA_Draft_Print.inst.cfg | 3 +-- .../ultimaker_s3/um_s3_aa0.4_PLA_Fast_Print.inst.cfg | 3 +-- .../ultimaker_s3/um_s3_aa0.4_PLA_High_Quality.inst.cfg | 3 +-- .../ultimaker_s3/um_s3_aa0.4_PLA_Normal_Quality.inst.cfg | 3 +-- .../ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg | 3 +-- .../ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg | 3 +-- .../ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg | 3 +-- .../ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg | 3 +-- .../ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg | 6 ++---- .../ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg | 6 ++---- .../ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg | 6 ++---- .../ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg | 6 ++---- .../ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg | 3 +-- .../ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg | 3 +-- .../ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg | 3 +-- .../ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg | 3 +-- 25 files changed, 33 insertions(+), 66 deletions(-) diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.25_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.25_Nylon_Normal_Quality.inst.cfg index 08a27aa3b4..0cef0f11da 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.25_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.25_Nylon_Normal_Quality.inst.cfg @@ -19,7 +19,7 @@ machine_nozzle_cool_down_speed = 0.9 machine_nozzle_heat_up_speed = 1.4 ooze_shield_angle = 40 raft_acceleration = =acceleration_layer_0 -raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_airgap = 0.4 raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 3) raft_jerk = =jerk_layer_0 raft_margin = 10 @@ -33,6 +33,5 @@ switch_extruder_prime_speed = 30 switch_extruder_retraction_amount = 30 switch_extruder_retraction_speeds = 40 wall_line_width_x = =wall_line_width -raft_airgap = 0.4 raft_surface_speed = 45 speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Draft_Print.inst.cfg index 9a8ed386c0..3ed16d3420 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Draft_Print.inst.cfg @@ -20,7 +20,7 @@ material_final_print_temperature = =material_print_temperature - 20 prime_tower_enable = False skin_overlap = 20 speed_print = 60 -speed_layer_0 = =math.ceil(speed_print * 20 / 60) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 35 / 60) speed_wall = =math.ceil(speed_print * 45 / 60) speed_wall_0 = =math.ceil(speed_wall * 35 / 45) @@ -30,4 +30,3 @@ infill_line_width = =round(line_width * 0.4 / 0.35, 2) speed_infill = =math.ceil(speed_print * 50 / 60) raft_airgap = 0.15 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Fast_Print.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Fast_Print.inst.cfg index 615e8cc487..75619973a8 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Fast_Print.inst.cfg @@ -20,7 +20,7 @@ material_initial_print_temperature = =material_print_temperature - 15 material_final_print_temperature = =material_print_temperature - 20 prime_tower_enable = False speed_print = 60 -speed_layer_0 = =math.ceil(speed_print * 20 / 60) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 30 / 60) speed_wall = =math.ceil(speed_print * 40 / 60) speed_wall_0 = =math.ceil(speed_wall * 30 / 40) @@ -29,4 +29,3 @@ infill_line_width = =round(line_width * 0.4 / 0.35, 2) speed_infill = =math.ceil(speed_print * 45 / 60) raft_airgap = 0.15 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_High_Quality.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_High_Quality.inst.cfg index 10149781cb..5edfda7541 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_High_Quality.inst.cfg @@ -20,7 +20,7 @@ material_initial_print_temperature = =material_print_temperature - 15 material_final_print_temperature = =material_print_temperature - 20 prime_tower_enable = False speed_print = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 50) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 30 / 50) speed_wall = =math.ceil(speed_print * 30 / 50) @@ -28,4 +28,3 @@ infill_line_width = =round(line_width * 0.4 / 0.35, 2) speed_infill = =math.ceil(speed_print * 40 / 50) raft_airgap = 0.15 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Normal_Quality.inst.cfg index 137a316f14..6aba500231 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_ABS_Normal_Quality.inst.cfg @@ -19,7 +19,7 @@ material_initial_print_temperature = =material_print_temperature - 15 material_final_print_temperature = =material_print_temperature - 20 prime_tower_enable = False speed_print = 55 -speed_layer_0 = =math.ceil(speed_print * 20 / 55) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 30 / 55) speed_wall = =math.ceil(speed_print * 30 / 55) @@ -27,4 +27,3 @@ infill_line_width = =round(line_width * 0.4 / 0.35, 2) speed_infill = =math.ceil(speed_print * 40 / 55) raft_airgap = 0.15 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Draft_Print.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Draft_Print.inst.cfg index f98d5c5d70..4949817547 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Draft_Print.inst.cfg @@ -23,13 +23,13 @@ material_final_print_temperature = =material_print_temperature - 10 material_standby_temperature = 100 ooze_shield_angle = 40 raft_acceleration = =acceleration_layer_0 -raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_airgap = 0.4 raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 2) raft_jerk = =jerk_layer_0 raft_margin = 10 raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) skin_overlap = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 switch_extruder_prime_speed = 30 switch_extruder_retraction_amount = 30 switch_extruder_retraction_speeds = 40 @@ -37,6 +37,4 @@ wall_line_width_x = =wall_line_width jerk_travel = 50 -raft_airgap = 0.4 raft_surface_speed = 45 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Fast_Print.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Fast_Print.inst.cfg index 9e4d40bcf3..026783cf17 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Fast_Print.inst.cfg @@ -23,13 +23,13 @@ material_final_print_temperature = =material_print_temperature - 10 material_standby_temperature = 100 ooze_shield_angle = 40 raft_acceleration = =acceleration_layer_0 -raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_airgap = 0.4 raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 2) raft_jerk = =jerk_layer_0 raft_margin = 10 raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) skin_overlap = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 switch_extruder_prime_speed = 30 switch_extruder_retraction_amount = 30 switch_extruder_retraction_speeds = 40 @@ -37,6 +37,4 @@ wall_line_width_x = =wall_line_width jerk_travel = 50 -raft_airgap = 0.4 raft_surface_speed = 45 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_High_Quality.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_High_Quality.inst.cfg index 755127b66f..f23ad38956 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_High_Quality.inst.cfg @@ -22,13 +22,13 @@ material_final_print_temperature = =material_print_temperature - 10 material_standby_temperature = 100 ooze_shield_angle = 40 raft_acceleration = =acceleration_layer_0 -raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_airgap = 0.4 raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 2) raft_jerk = =jerk_layer_0 raft_margin = 10 raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) skin_overlap = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 switch_extruder_prime_speed = 30 switch_extruder_retraction_amount = 30 switch_extruder_retraction_speeds = 40 @@ -36,6 +36,4 @@ wall_line_width_x = =wall_line_width jerk_travel = 50 -raft_airgap = 0.4 raft_surface_speed = 45 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Normal_Quality.inst.cfg index 26f0b52f91..b97c0b9ffe 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_Nylon_Normal_Quality.inst.cfg @@ -22,13 +22,13 @@ material_final_print_temperature = =material_print_temperature - 10 material_standby_temperature = 100 ooze_shield_angle = 40 raft_acceleration = =acceleration_layer_0 -raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_airgap = 0.4 raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 2) raft_jerk = =jerk_layer_0 raft_margin = 10 raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) skin_overlap = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 switch_extruder_prime_speed = 30 switch_extruder_retraction_amount = 30 switch_extruder_retraction_speeds = 40 @@ -36,6 +36,4 @@ wall_line_width_x = =wall_line_width jerk_travel = 50 -raft_airgap = 0.4 raft_surface_speed = 45 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Draft_Print.inst.cfg index c5613e0f49..81be2ebb6d 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Draft_Print.inst.cfg @@ -20,7 +20,7 @@ material_print_temperature = =default_material_print_temperature + 5 material_standby_temperature = 100 prime_tower_enable = False skin_overlap = 20 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 40 / 70) speed_wall = =math.ceil(speed_print * 55 / 70) speed_wall_0 = =math.ceil(speed_wall * 45 / 50) @@ -35,4 +35,3 @@ acceleration_wall = 2000 acceleration_wall_0 = 2000 raft_airgap = 0.25 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Fast_Print.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Fast_Print.inst.cfg index 17967f923c..e57fc51db1 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Fast_Print.inst.cfg @@ -19,7 +19,7 @@ machine_nozzle_heat_up_speed = 1.6 material_standby_temperature = 100 prime_tower_enable = False speed_print = 70 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 35 / 70) speed_wall = =math.ceil(speed_print * 45 / 70) speed_wall_0 = =math.ceil(speed_wall * 35 / 70) @@ -31,4 +31,3 @@ infill_line_width = =round(line_width * 0.42 / 0.35, 2) layer_height_0 = 0.2 raft_airgap = 0.25 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_High_Quality.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_High_Quality.inst.cfg index 1c271f6af1..5abeff6589 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_High_Quality.inst.cfg @@ -22,7 +22,7 @@ material_standby_temperature = 100 prime_tower_enable = False skin_overlap = 10 speed_print = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 50) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 35 / 50) speed_wall = =math.ceil(speed_print * 35 / 50) top_bottom_thickness = 1 @@ -33,4 +33,3 @@ infill_line_width = =round(line_width * 0.42 / 0.35, 2) layer_height_0 = 0.2 raft_airgap = 0.25 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Normal_Quality.inst.cfg index 0aae33c115..56ab049b3b 100644 --- a/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s3/um_s3_aa0.4_PLA_Normal_Quality.inst.cfg @@ -20,7 +20,7 @@ machine_nozzle_heat_up_speed = 1.6 material_standby_temperature = 100 prime_tower_enable = False skin_overlap = 10 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 top_bottom_thickness = 1 wall_thickness = 1 @@ -29,4 +29,3 @@ infill_line_width = =round(line_width * 0.42 / 0.35, 2) layer_height_0 = 0.2 raft_airgap = 0.25 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg index 193e6de18b..5689aaff8b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg @@ -20,7 +20,7 @@ material_final_print_temperature = =material_print_temperature - 20 prime_tower_enable = False skin_overlap = 20 speed_print = 60 -speed_layer_0 = =math.ceil(speed_print * 20 / 60) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 35 / 60) speed_wall = =math.ceil(speed_print * 45 / 60) speed_wall_0 = =math.ceil(speed_wall * 35 / 45) @@ -30,4 +30,3 @@ infill_line_width = =round(line_width * 0.4 / 0.35, 2) speed_infill = =math.ceil(speed_print * 50 / 60) raft_airgap = 0.15 -speed_layer_0 = 10 \ No newline at end of file diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg index 1aae14fe6b..854858ee5b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg @@ -20,7 +20,7 @@ material_initial_print_temperature = =material_print_temperature - 15 material_final_print_temperature = =material_print_temperature - 20 prime_tower_enable = False speed_print = 60 -speed_layer_0 = =math.ceil(speed_print * 20 / 60) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 30 / 60) speed_wall = =math.ceil(speed_print * 40 / 60) speed_wall_0 = =math.ceil(speed_wall * 30 / 40) @@ -29,4 +29,3 @@ infill_line_width = =round(line_width * 0.4 / 0.35, 2) speed_infill = =math.ceil(speed_print * 45 / 60) raft_airgap = 0.15 -speed_layer_0 = 10 \ No newline at end of file diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg index 1c57587309..bb75703f87 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg @@ -20,7 +20,7 @@ material_initial_print_temperature = =material_print_temperature - 15 material_final_print_temperature = =material_print_temperature - 20 prime_tower_enable = False speed_print = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 50) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 30 / 50) speed_wall = =math.ceil(speed_print * 30 / 50) @@ -28,4 +28,3 @@ infill_line_width = =round(line_width * 0.4 / 0.35, 2) speed_infill = =math.ceil(speed_print * 40 / 50) raft_airgap = 0.15 -speed_layer_0 = 10 \ No newline at end of file diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg index ec2819cf93..4c850c4904 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg @@ -19,11 +19,10 @@ material_initial_print_temperature = =material_print_temperature - 15 material_final_print_temperature = =material_print_temperature - 20 prime_tower_enable = False speed_print = 55 -speed_layer_0 = =math.ceil(speed_print * 20 / 55) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 30 / 55) speed_wall = =math.ceil(speed_print * 30 / 55) infill_line_width = =round(line_width * 0.4 / 0.35, 2) speed_infill = =math.ceil(speed_print * 40 / 55) raft_airgap = 0.15 -speed_layer_0 = 10 \ No newline at end of file diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg index 0f1f2c872b..bd42d349c5 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg @@ -23,19 +23,17 @@ material_final_print_temperature = =material_print_temperature - 10 material_standby_temperature = 100 ooze_shield_angle = 40 raft_acceleration = =acceleration_layer_0 -raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_airgap = 0.4 raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 2) raft_jerk = =jerk_layer_0 raft_margin = 10 raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) skin_overlap = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 switch_extruder_prime_speed = 30 switch_extruder_retraction_amount = 30 switch_extruder_retraction_speeds = 40 wall_line_width_x = =wall_line_width jerk_travel = 50 -raft_airgap = 0.4 raft_surface_speed = 45 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg index d3e3e43830..9fe2328c88 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg @@ -23,19 +23,17 @@ material_final_print_temperature = =material_print_temperature - 10 material_standby_temperature = 100 ooze_shield_angle = 40 raft_acceleration = =acceleration_layer_0 -raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_airgap = 0.4 raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 2) raft_jerk = =jerk_layer_0 raft_margin = 10 raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) skin_overlap = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 switch_extruder_prime_speed = 30 switch_extruder_retraction_amount = 30 switch_extruder_retraction_speeds = 40 wall_line_width_x = =wall_line_width jerk_travel = 50 -raft_airgap = 0.4 raft_surface_speed = 45 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg index 4a838d3b02..6bfd398d12 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg @@ -22,19 +22,17 @@ material_final_print_temperature = =material_print_temperature - 10 material_standby_temperature = 100 ooze_shield_angle = 40 raft_acceleration = =acceleration_layer_0 -raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_airgap = 0.4 raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 2) raft_jerk = =jerk_layer_0 raft_margin = 10 raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) skin_overlap = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 switch_extruder_prime_speed = 30 switch_extruder_retraction_amount = 30 switch_extruder_retraction_speeds = 40 wall_line_width_x = =wall_line_width jerk_travel = 50 -raft_airgap = 0.4 raft_surface_speed = 45 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg index 8c86a63b0b..7832217f95 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg @@ -22,19 +22,17 @@ material_final_print_temperature = =material_print_temperature - 10 material_standby_temperature = 100 ooze_shield_angle = 40 raft_acceleration = =acceleration_layer_0 -raft_airgap = =round(layer_height_0 * 0.85, 2) +raft_airgap = 0.4 raft_interface_thickness = =round(machine_nozzle_size * 0.3 / 0.4, 2) raft_jerk = =jerk_layer_0 raft_margin = 10 raft_surface_thickness = =round(machine_nozzle_size * 0.2 / 0.4, 2) skin_overlap = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 switch_extruder_prime_speed = 30 switch_extruder_retraction_amount = 30 switch_extruder_retraction_speeds = 40 wall_line_width_x = =wall_line_width jerk_travel = 50 -raft_airgap = 0.4 raft_surface_speed = 45 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg index 25235ba336..dd674e9a6b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg @@ -21,7 +21,7 @@ material_standby_temperature = 100 prime_tower_enable = False skin_edge_support_thickness = =0.8 if infill_sparse_density < 30 else 0 skin_overlap = 20 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 40 / 70) speed_wall = =math.ceil(speed_print * 55 / 70) speed_wall_0 = =math.ceil(speed_wall * 45 / 50) @@ -35,4 +35,3 @@ layer_height_0 = 0.2 acceleration_wall = 2000 acceleration_wall_0 = 2000 raft_airgap = 0.25 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg index f9ed0bc62a..d743e3fc31 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg @@ -19,7 +19,7 @@ machine_nozzle_heat_up_speed = 1.6 material_standby_temperature = 100 prime_tower_enable = False speed_print = 70 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 35 / 70) speed_wall = =math.ceil(speed_print * 45 / 70) speed_wall_0 = =math.ceil(speed_wall * 35 / 70) @@ -30,4 +30,3 @@ jerk_travel = 50 infill_line_width = =round(line_width * 0.42 / 0.35, 2) layer_height_0 = 0.2 raft_airgap = 0.25 -speed_layer_0 = 10 \ No newline at end of file diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg index 23042d0f23..4c69195362 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg @@ -22,7 +22,7 @@ material_standby_temperature = 100 prime_tower_enable = False skin_overlap = 10 speed_print = 50 -speed_layer_0 = =math.ceil(speed_print * 20 / 50) +speed_layer_0 = 10 speed_topbottom = =math.ceil(speed_print * 35 / 50) speed_wall = =math.ceil(speed_print * 35 / 50) top_bottom_thickness = 1 @@ -32,4 +32,3 @@ jerk_travel = 50 infill_line_width = =round(line_width * 0.42 / 0.35, 2) layer_height_0 = 0.2 raft_airgap = 0.25 -speed_layer_0 = 10 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg index c0414898af..462e2b37bd 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg @@ -20,7 +20,7 @@ machine_nozzle_heat_up_speed = 1.6 material_standby_temperature = 100 prime_tower_enable = False skin_overlap = 10 -speed_layer_0 = =math.ceil(speed_print * 20 / 70) +speed_layer_0 = 10 top_bottom_thickness = 1 wall_thickness = 1 @@ -28,4 +28,3 @@ jerk_travel = 50 infill_line_width = =round(line_width * 0.42 / 0.35, 2) layer_height_0 = 0.2 raft_airgap = 0.25 -speed_layer_0 = 10 From 0313b29dcf95bec65cbfdd721fde198154dceaa6 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 7 Dec 2020 16:04:21 +0100 Subject: [PATCH 16/87] Fix validator for floats in Machine Settings dialog The DoubleValidator depends on the system locale, requiring users with certain locales to enter floats with a comma instead of a dot (though confusingly floats are always represented with a decimal a dot). Instead of configuring the DoubleValidator with a locale that only accepts decimal dots, this commit uses a RegExpValidator, like other numeric fields in Cura does. --- .../MachineSettingsExtruderTab.qml | 1 + .../NumericTextFieldWithUnit.qml | 22 ++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/plugins/MachineSettingsAction/MachineSettingsExtruderTab.qml b/plugins/MachineSettingsAction/MachineSettingsExtruderTab.qml index 902388b669..293e7386b2 100644 --- a/plugins/MachineSettingsAction/MachineSettingsExtruderTab.qml +++ b/plugins/MachineSettingsAction/MachineSettingsExtruderTab.qml @@ -137,6 +137,7 @@ Item labelWidth: base.labelWidth controlWidth: base.controlWidth unitText: "" + decimals: 0 forceUpdateOnChangeFunction: forceUpdateFunction } } diff --git a/resources/qml/MachineSettings/NumericTextFieldWithUnit.qml b/resources/qml/MachineSettings/NumericTextFieldWithUnit.qml index 031ef5241a..32e0e6dcaa 100644 --- a/resources/qml/MachineSettings/NumericTextFieldWithUnit.qml +++ b/resources/qml/MachineSettings/NumericTextFieldWithUnit.qml @@ -156,12 +156,24 @@ UM.TooltipArea const value = propertyProvider.properties.value return value ? value : "" } - validator: DoubleValidator + property string validatorString: { - bottom: numericTextFieldWithUnit.minimum - top: numericTextFieldWithUnit.maximum - decimals: numericTextFieldWithUnit.decimals - notation: DoubleValidator.StandardNotation + var digits = Math.min(8, 1 + Math.floor( + Math.log(Math.max(Math.abs(numericTextFieldWithUnit.maximum), Math.abs(numericTextFieldWithUnit.minimum)))/Math.log(10) + )) + var minus = numericTextFieldWithUnit.minimum < 0 ? "-?" : "" + if (numericTextFieldWithUnit.decimals == 0) + { + return "^%0\\d{1,%1}$".arg(minus).arg(digits) + } + else + { + return "^%0\\d{0,%1}[.,]?\\d{0,%2}$".arg(minus).arg(digits).arg(numericTextFieldWithUnit.decimals) + } + } + validator: RegExpValidator + { + regExp: new RegExp(textFieldWithUnit.validatorString) } //Enforce actual minimum and maximum values. From 7096f64ca2628c1d5b3c260c8d791fb5de21e682 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 7 Dec 2020 16:45:28 +0100 Subject: [PATCH 17/87] Fix crash when extruder could not be found Fixes #8891 --- cura/BuildVolume.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 325c924cc4..6bda2d94e3 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -344,7 +344,12 @@ class BuildVolume(SceneNode): # Mark the node as outside build volume if the set extruder is disabled extruder_position = node.callDecoration("getActiveExtruderPosition") - if not self._global_container_stack.extruderList[int(extruder_position)].isEnabled: + try: + if not self._global_container_stack.extruderList[int(extruder_position)].isEnabled: + node.setOutsideBuildArea(True) + return + except IndexError: + # If the extruder doesn't exist, also mark it as unprintable. node.setOutsideBuildArea(True) return From 000d7e90f511f1baae5b70bbaebe17aed6f8224c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 8 Dec 2020 12:18:23 +0100 Subject: [PATCH 18/87] Fix support density when using tree support on engineering materials These formulas were using a setting that didn't exist any more. Not sure how this got through QA. Oh well, this is what they are intending. This formula is a little bit weird in how it works with support meshes if support is disabled, but it's more or less as good as it gets. The formula is mirrored from how other Ultimaker printers write it down, but slightly simplified with the same outcome. --- .../ultimaker2_plus_connect/um2pc_cpep_0.4_draft.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_cpep_0.4_normal.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_cpep_0.6_draft.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_cpep_0.6_normal.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_cpep_0.8_draft.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_cpep_0.8_normal.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_nylon_0.25_high.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_nylon_0.25_normal.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_nylon_0.4_fast.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_nylon_0.4_normal.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_nylon_0.6_fast.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_nylon_0.6_normal.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_nylon_0.8_draft.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_nylon_0.8_normal.inst.cfg | 2 +- .../quality/ultimaker2_plus_connect/um2pc_pc_0.25_high.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_pc_0.25_normal.inst.cfg | 2 +- .../quality/ultimaker2_plus_connect/um2pc_pc_0.4_fast.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_pc_0.4_normal.inst.cfg | 2 +- .../quality/ultimaker2_plus_connect/um2pc_pc_0.6_fast.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_pc_0.6_normal.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_pc_0.8_normal.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_tpu_0.25_high.inst.cfg | 2 +- .../ultimaker2_plus_connect/um2pc_tpu_0.4_normal.inst.cfg | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.4_draft.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.4_draft.inst.cfg index ed6f5a0c5e..0d39ffadac 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.4_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.4_draft.inst.cfg @@ -34,7 +34,7 @@ speed_wall_0 = =math.ceil(speed_print * 20 / 25) speed_wall_x = =speed_print support_angle = 45 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_pattern = lines support_z_distance = 0.26 top_bottom_thickness = 1.5 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.4_normal.inst.cfg index 2aebe1e918..7589c59364 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.4_normal.inst.cfg @@ -34,7 +34,7 @@ speed_wall_0 = =math.ceil(speed_print * 20 / 35) speed_wall_x = =math.ceil(speed_print * 30 / 35) support_angle = 45 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_pattern = lines support_z_distance = 0.26 top_bottom_thickness = 1.5 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.6_draft.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.6_draft.inst.cfg index 78ffe10ce9..91f932df98 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.6_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.6_draft.inst.cfg @@ -36,7 +36,7 @@ speed_wall_0 = =math.ceil(speed_print * 20 / 25) speed_wall_x = =speed_print support_angle = 45 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_line_distance = 2.85 support_pattern = lines support_xy_distance = 0.6 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.6_normal.inst.cfg index 2099ea7cbf..d7989f9f3b 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.6_normal.inst.cfg @@ -36,7 +36,7 @@ speed_wall_0 = =math.ceil(speed_print * 30 / 35) speed_wall_x = =speed_print support_angle = 45 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_line_distance = 2.85 support_pattern = lines support_xy_distance = 0.6 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.8_draft.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.8_draft.inst.cfg index bf4be03a20..8d7471ccd1 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.8_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.8_draft.inst.cfg @@ -33,7 +33,7 @@ speed_wall_0 = =math.ceil(speed_print * 20 / 25) speed_wall_x = =speed_print support_angle = 45 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_pattern = lines support_z_distance = 0.26 top_bottom_thickness = 1.2 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.8_normal.inst.cfg index f1dd92db53..8a3ff137f1 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_cpep_0.8_normal.inst.cfg @@ -33,7 +33,7 @@ speed_wall_0 = =math.ceil(speed_print * 20 / 30) speed_wall_x = =speed_print support_angle = 45 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_pattern = lines support_z_distance = 0.26 top_bottom_thickness = 1.2 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.25_high.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.25_high.inst.cfg index 2fd638f0e9..7026181d38 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.25_high.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.25_high.inst.cfg @@ -34,7 +34,7 @@ speed_travel = 150 speed_wall_0 = =math.ceil(speed_print * 20 / 40) speed_wall_x = =speed_print support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_pattern = lines support_xy_distance = 0.6 support_z_distance = =layer_height * 2 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.25_normal.inst.cfg index d7a64e12c8..d11a53640d 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.25_normal.inst.cfg @@ -34,7 +34,7 @@ speed_travel = 150 speed_wall_0 = =math.ceil(speed_print * 20 / 40) speed_wall_x = =speed_print support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_pattern = lines support_xy_distance = 0.6 support_z_distance = =layer_height * 2 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.4_fast.inst.cfg index d62a4e79ee..5497712600 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.4_fast.inst.cfg @@ -33,7 +33,7 @@ speed_travel = 150 speed_wall = =math.ceil(speed_print * 40 / 45) support_angle = 45 support_enable = True -support_infill_rate = =25 if support_enable else 0 if support_tree_enable else 25 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 25 support_pattern = lines support_xy_distance = 0.6 support_z_distance = =layer_height * 2 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.4_normal.inst.cfg index a6161c2a07..b966c9132a 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.4_normal.inst.cfg @@ -32,7 +32,7 @@ speed_travel = 150 speed_wall = =math.ceil(speed_print * 40 / 45) support_angle = 45 support_enable = True -support_infill_rate = =25 if support_enable else 0 if support_tree_enable else 25 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 25 support_pattern = lines support_xy_distance = 0.6 support_z_distance = =layer_height * 2 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.6_fast.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.6_fast.inst.cfg index dbb90918b8..55a067f3aa 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.6_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.6_fast.inst.cfg @@ -37,7 +37,7 @@ speed_wall_x = =math.ceil(speed_print * 40 / 55) support_angle = 45 support_bottom_distance = 0.55 support_enable = True -support_infill_rate = =25 if support_enable else 0 if support_tree_enable else 25 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 25 support_pattern = lines support_top_distance = 0.55 support_xy_distance = 0.7 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.6_normal.inst.cfg index 172ef69c01..744985a7fc 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.6_normal.inst.cfg @@ -36,7 +36,7 @@ speed_wall_0 = =math.ceil(speed_print * 15 / 55) speed_wall_x = =math.ceil(speed_print * 40 / 55) support_angle = 45 support_enable = True -support_infill_rate = =25 if support_enable else 0 if support_tree_enable else 25 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 25 support_pattern = lines support_xy_distance = 0.7 support_z_distance = =layer_height * 2 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.8_draft.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.8_draft.inst.cfg index c85bacb89e..f9510aa268 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.8_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.8_draft.inst.cfg @@ -36,7 +36,7 @@ speed_wall_x = =math.ceil(speed_print * 40 / 55) support_angle = 45 support_bottom_distance = 0.65 support_enable = True -support_infill_rate = =25 if support_enable else 0 if support_tree_enable else 25 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 25 support_pattern = lines support_top_distance = 0.5 support_xy_distance = 0.75 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.8_normal.inst.cfg index 153f206b19..548f61df37 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_nylon_0.8_normal.inst.cfg @@ -36,7 +36,7 @@ speed_wall_x = =math.ceil(speed_print * 40 / 55) support_angle = 45 support_bottom_distance = 0.65 support_enable = True -support_infill_rate = =25 if support_enable else 0 if support_tree_enable else 25 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 25 support_pattern = lines support_top_distance = 0.5 support_xy_distance = 0.75 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.25_high.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.25_high.inst.cfg index 062e7f97b5..43e6ffd774 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.25_high.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.25_high.inst.cfg @@ -30,7 +30,7 @@ raft_surface_line_width = 0.2 speed_layer_0 = =round(speed_print * 30 / 30) speed_print = 30 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_pattern = lines support_z_distance = 0.19 wall_thickness = 0.88 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.25_normal.inst.cfg index 20ab28f97b..cf67cdf35c 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.25_normal.inst.cfg @@ -30,7 +30,7 @@ raft_surface_line_width = 0.2 speed_layer_0 = =round(speed_print * 30 / 30) speed_print = 30 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_pattern = lines support_z_distance = 0.19 wall_thickness = 0.88 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.4_fast.inst.cfg index 249bfb9053..3c3b01cf4b 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.4_fast.inst.cfg @@ -31,7 +31,7 @@ speed_wall_0 = =math.ceil(speed_print * 20 / 45) speed_wall_x = =math.ceil(speed_print * 30 / 45) support_angle = 45 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_pattern = lines support_z_distance = 0.19 wall_thickness = 1.2 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.4_normal.inst.cfg index 86a2dabb6f..2ef73c0ba6 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.4_normal.inst.cfg @@ -31,7 +31,7 @@ speed_wall_0 = =math.ceil(speed_print * 20 / 45) speed_wall_x = =math.ceil(speed_print * 30 / 45) support_angle = 45 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_pattern = lines support_z_distance = 0.19 wall_thickness = 1.2 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.6_fast.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.6_fast.inst.cfg index 95bbbff2d3..ed02c4d22a 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.6_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.6_fast.inst.cfg @@ -35,7 +35,7 @@ speed_wall_0 = =math.ceil(speed_print * 30 / 45) speed_wall_x = =math.ceil(speed_print * 40 / 45) support_angle = 45 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_line_distance = 3.5333 support_pattern = lines support_z_distance = 0.21 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.6_normal.inst.cfg index c4f451d972..75edf9330b 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.6_normal.inst.cfg @@ -35,7 +35,7 @@ speed_wall_0 = =math.ceil(speed_print * 30 / 45) speed_wall_x = =math.ceil(speed_print * 40 / 45) support_angle = 45 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 20 support_line_distance = 3.5333 support_pattern = lines support_z_distance = 0.21 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.8_normal.inst.cfg index c9fe800c7b..72ef057e7e 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_pc_0.8_normal.inst.cfg @@ -30,7 +30,7 @@ speed_layer_0 = =round(speed_print * 30 / 40) speed_print = 40 support_angle = 45 support_enable = True -support_infill_rate = =20 if support_enable else 0 if support_tree_enable else 20 +support_infill_rate = =0 if support_structure == 'tree' else 20 support_pattern = lines support_z_distance = 0.26 top_bottom_thickness = 1.2 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_tpu_0.25_high.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_tpu_0.25_high.inst.cfg index f03f8ca03d..0948aa2af5 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_tpu_0.25_high.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_tpu_0.25_high.inst.cfg @@ -35,7 +35,7 @@ speed_wall_0 = =math.ceil(speed_print * 15 / 40) speed_wall_x = =math.ceil(speed_print * 38 / 40) support_angle = 45 support_enable = True -support_infill_rate = =25 if support_enable else 0 if support_tree_enable else 25 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 25 support_xy_distance = 0.6 support_z_distance = =layer_height * 2 top_bottom_thickness = 1.2 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_tpu_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_tpu_0.4_normal.inst.cfg index 40e8daa71f..c14808ccde 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_tpu_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_tpu_0.4_normal.inst.cfg @@ -33,7 +33,7 @@ speed_wall_0 = =math.ceil(speed_print * 20 / 40) speed_wall_x = =math.ceil(speed_print * 35 / 40) support_angle = 45 support_enable = True -support_infill_rate = =25 if support_enable else 0 if support_tree_enable else 25 +support_infill_rate = =0 if support_enable and support_structure == 'tree' else 25 support_xy_distance = 0.65 support_z_distance = =layer_height * 2 top_bottom_thickness = 1.2 From caa9916cf00ec1c8daf15f1837da3a78e93c5a3c Mon Sep 17 00:00:00 2001 From: StefanBruens Date: Tue, 8 Dec 2020 14:01:18 +0100 Subject: [PATCH 19/87] Fix bug reference --- cura_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura_app.py b/cura_app.py index 3c2620bb38..ad5f7b759d 100755 --- a/cura_app.py +++ b/cura_app.py @@ -207,7 +207,7 @@ if Platform.isLinux() and getattr(sys, "frozen", False): import trimesh.exchange.load os.environ["LD_LIBRARY_PATH"] = old_env -# WORKAROUND: CURA-5488 +# WORKAROUND: Cura#5488 # When using the KDE qqc2-desktop-style, the UI layout is completely broken, and # even worse, it crashes when switching to the "Preview" pane. if Platform.isLinux(): From bfe62514e2b8933df22ac361c88b5252ecfae5a2 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 8 Dec 2020 14:14:14 +0100 Subject: [PATCH 20/87] Allow steps per mm to be float You could have 333.333 steps per mm, for instance. Not common, but it's possible. Fixes #8889. --- resources/definitions/fdmprinter.def.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index c17e0da9d8..63c7b25b0b 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -652,7 +652,7 @@ { "label": "Steps per Millimeter (X)", "description": "How many steps of the stepper motor will result in one millimeter of movement in the X direction.", - "type": "int", + "type": "float", "default_value": 50, "minimum_value": "0.0000001", "settable_per_mesh": false, @@ -662,7 +662,7 @@ { "label": "Steps per Millimeter (Y)", "description": "How many steps of the stepper motor will result in one millimeter of movement in the Y direction.", - "type": "int", + "type": "float", "default_value": 50, "minimum_value": "0.0000001", "settable_per_mesh": false, @@ -672,7 +672,7 @@ { "label": "Steps per Millimeter (Z)", "description": "How many steps of the stepper motor will result in one millimeter of movement in the Z direction.", - "type": "int", + "type": "float", "default_value": 50, "minimum_value": "0.0000001", "settable_per_mesh": false, @@ -682,7 +682,7 @@ { "label": "Steps per Millimeter (E)", "description": "How many steps of the stepper motors will result in one millimeter of extrusion.", - "type": "int", + "type": "float", "default_value": 1600, "minimum_value": "0.0000001", "settable_per_mesh": false, From 8023bee35a952f0ca9b5dce7d2b6aa0faebef9ec Mon Sep 17 00:00:00 2001 From: StefanBruens Date: Tue, 8 Dec 2020 14:53:48 +0100 Subject: [PATCH 21/87] Use 'default' theme instead of 'material' Using 'material' causes some layout issues, while 'default' shows no such issues and also fixes the preview pane crashes. --- cura_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura_app.py b/cura_app.py index ad5f7b759d..4469098666 100755 --- a/cura_app.py +++ b/cura_app.py @@ -211,7 +211,7 @@ if Platform.isLinux() and getattr(sys, "frozen", False): # When using the KDE qqc2-desktop-style, the UI layout is completely broken, and # even worse, it crashes when switching to the "Preview" pane. if Platform.isLinux(): - os.environ["QT_QUICK_CONTROLS_STYLE"] = "material" + os.environ["QT_QUICK_CONTROLS_STYLE"] = "default" app = CuraApplication() app.run() From d53568c5dbe08bcc10dd633313bd752dcc4ce672 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 9 Dec 2020 12:16:56 +0100 Subject: [PATCH 22/87] Fix linear-scaling height map thickness Previously it was using this 'peak_height' which is the total height of the model in scale_vector.y. However it was then multiplying the height map with that scale vector and adding the base height again a second time. So the scaling part was too thick and included the base height, and the total height of the mesh was also too big. I also reduced an unnecessary re-calculation of the height_from_base parameter. And as a result we don't need the peak_height variable at all any more. Fixes #8902. --- plugins/ImageReader/ImageReader.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/plugins/ImageReader/ImageReader.py b/plugins/ImageReader/ImageReader.py index 1bab15e9d6..4a32ed71f1 100644 --- a/plugins/ImageReader/ImageReader.py +++ b/plugins/ImageReader/ImageReader.py @@ -52,11 +52,8 @@ class ImageReader(MeshReader): def _generateSceneNode(self, file_name, xz_size, height_from_base, base_height, blur_iterations, max_size, lighter_is_higher, use_transparency_model, transmittance_1mm): scene_node = SceneNode() - mesh = MeshBuilder() - img = QImage(file_name) - if img.isNull(): Logger.log("e", "Image is corrupt.") return None @@ -70,11 +67,10 @@ class ImageReader(MeshReader): height_from_base = max(height_from_base, 0) base_height = max(base_height, 0) - peak_height = base_height + height_from_base xz_size = max(xz_size, 1) - scale_vector = Vector(xz_size, peak_height, xz_size) + scale_vector = Vector(xz_size, height_from_base, xz_size) if width > height: scale_vector = scale_vector.set(z=scale_vector.z * aspect) @@ -132,7 +128,7 @@ class ImageReader(MeshReader): if use_transparency_model: divisor = 1.0 / math.log(transmittance_1mm / 100.0) # log-base doesn't matter here. Precompute this value for faster computation of each pixel. - min_luminance = (transmittance_1mm / 100.0) ** (peak_height - base_height) + min_luminance = (transmittance_1mm / 100.0) ** height_from_base for (y, x) in numpy.ndindex(height_data.shape): mapped_luminance = min_luminance + (1.0 - min_luminance) * height_data[y, x] height_data[y, x] = base_height + divisor * math.log(mapped_luminance) # use same base as a couple lines above this From 2dce2118f5deb20802819992bfc91549a6e5a1db Mon Sep 17 00:00:00 2001 From: Eryone <40707672+Eryone@users.noreply.github.com> Date: Thu, 10 Dec 2020 20:25:00 +0800 Subject: [PATCH 23/87] Added the eryone_er20 3d printer --- resources/definitions/eryone_er20.def.json | 92 ++++++++++++++++++ .../extruders/eryone_er20_extruder_0.def.json | 27 +++++ resources/meshes/eryone_er20_plateform.stl | Bin 0 -> 39684 bytes .../eryone_er20/eryone_er20_draft.inst.cfg | 66 +++++++++++++ .../eryone_er20/eryone_er20_high.inst.cfg | 79 +++++++++++++++ .../eryone_er20/eryone_er20_normal.inst.cfg | 66 +++++++++++++ 6 files changed, 330 insertions(+) create mode 100644 resources/definitions/eryone_er20.def.json create mode 100644 resources/extruders/eryone_er20_extruder_0.def.json create mode 100644 resources/meshes/eryone_er20_plateform.stl create mode 100644 resources/quality/eryone_er20/eryone_er20_draft.inst.cfg create mode 100644 resources/quality/eryone_er20/eryone_er20_high.inst.cfg create mode 100644 resources/quality/eryone_er20/eryone_er20_normal.inst.cfg diff --git a/resources/definitions/eryone_er20.def.json b/resources/definitions/eryone_er20.def.json new file mode 100644 index 0000000000..f16de9011e --- /dev/null +++ b/resources/definitions/eryone_er20.def.json @@ -0,0 +1,92 @@ +{ + "version": 2, + "name": "Eryone ER20", + "inherits": "fdmprinter", + "metadata": + { + "visible": true, + "author": "Tom", + "manufacturer": "Eryone", + "file_formats": "text/x-gcode", + "platform": "eryone_er20_plateform.stl", + "has_materials": true, + "has_machine_quality": true, + "preferred_quality_type": "high", + "machine_extruder_trains": + { + "0": "eryone_er20_extruder_0" + } + }, + + "overrides": + { + "machine_name": + { + "default_value": "Eryone ER20" + }, + "machine_heated_bed": + { + "default_value": true + }, + "machine_width": + { + "default_value": 250 + }, + "machine_height": + { + "default_value": 200 + }, + "machine_depth": + { + "default_value": 220 + }, + "machine_center_is_zero": + { + "default_value": false + }, + "machine_head_with_fans_polygon": + { + "default_value": [ + [-10, -10], + [-10, 10], + [10, 10], + [10, -10] + ] + }, + "gantry_height": + { + "value": "0" + }, + "machine_gcode_flavor": + { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "machine_start_gcode": + { + "default_value": "G90 ; use absolute coordinates\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling/ABL\nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0.0\nG1 Y-2.0 X150 F2400G1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\nG1 X150 F1000\nG1 Z0.2 F720\nG1 X80.0 E8.0 F900\nG1 X20.0 E10.0 F700\nG92 E0.0\nM221 S95 ; set flow\n" + }, + "machine_end_gcode": + { + "default_value": "M104 S0 ; turn off extruder\nM140 S0 ; turn off bed\nM84 ; disable motors\nM107\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 ;Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y180 F2000\nM84 ;steppers off\nG90\nM300 P300 S4000" + } + } +} + + + + + + + + + + + + + + + + + + + diff --git a/resources/extruders/eryone_er20_extruder_0.def.json b/resources/extruders/eryone_er20_extruder_0.def.json new file mode 100644 index 0000000000..9b1c1fa435 --- /dev/null +++ b/resources/extruders/eryone_er20_extruder_0.def.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "eryone_er20", + "position": "0" + }, + + "overrides": { + "extruder_nr": { + "default_value": 0 + }, + "machine_nozzle_size": { + "default_value": 0.4 + }, + "material_diameter": { + "default_value": 1.75 + }, + "machine_nozzle_offset_x": { + "default_value": -10.0 + }, + "machine_nozzle_offset_y": { + "default_value": 8.0 + } + } +} diff --git a/resources/meshes/eryone_er20_plateform.stl b/resources/meshes/eryone_er20_plateform.stl new file mode 100644 index 0000000000000000000000000000000000000000..a181d0f37eead1213a91a1d48f3d53b46a408753 GIT binary patch literal 39684 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 literal 0 HcmV?d00001 diff --git a/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg b/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg new file mode 100644 index 0000000000..b44797eae6 --- /dev/null +++ b/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg @@ -0,0 +1,66 @@ +[general] +version = 4 +name = Draft +definition = eryone_er20 + +[metadata] +setting_version = 15 +type = quality +quality_type = draft +weight = -2 +global_quality = True + +[values] +acceleration_enabled = True +acceleration_print = 1500 +acceleration_travel = 3000 +adhesion_type = brim +brim_width = 4.0 +cool_fan_full_at_height = 0.5 +cool_fan_speed = 100 +cool_fan_speed_0 = 100 +infill_overlap = 15 +infill_pattern = zigzag +infill_sparse_density = 25 +initial_layer_line_width_factor = 105 +jerk_enabled = True +jerk_print = 10 +jerk_travel = 12 +layer_height = 0.3 +layer_height_0 = 0.3 +material_bed_temperature = 60 +material_diameter = 1.75 +default_material_print_temperature = 205 +material_print_temperature = 205 +material_print_temperature_layer_0 = 205 +material_initial_print_temperature = 205 +material_final_print_temperature = 205 +retract_at_layer_change = False +retraction_amount = 6 +retraction_hop = 0.075 +retraction_hop_enabled = True +retraction_hop_only_when_collides = True +retraction_min_travel = 1.5 +retraction_speed = 55 +skirt_brim_speed = 40 +skirt_gap = 5 +skirt_line_count = 3 +speed_infill = =speed_print +speed_print = 80 +speed_support = 60 +speed_topbottom = =math.ceil(speed_print * 30 / 60) +speed_travel = 150 +speed_wall = =speed_print +speed_wall_x = =speed_print +support_angle = 50 +support_enable = True +support_interface_enable = True +support_pattern = triangles +support_roof_enable = True +support_type = everywhere +support_use_towers = False +support_z_distance = 0.3 +support_xy_distance = 0.7 +support_xy_distance_overhang = 0.2 +top_bottom_thickness = 1.2 +wall_thickness = 1.2 diff --git a/resources/quality/eryone_er20/eryone_er20_high.inst.cfg b/resources/quality/eryone_er20/eryone_er20_high.inst.cfg new file mode 100644 index 0000000000..53fbe8a8d8 --- /dev/null +++ b/resources/quality/eryone_er20/eryone_er20_high.inst.cfg @@ -0,0 +1,79 @@ +[general] +version = 4 +name = High +definition = eryone_er20 + +[metadata] +setting_version = 15 +type = quality +quality_type = high +weight = 1 +global_quality = True + +[values] +acceleration_enabled = True +acceleration_print = 500 +acceleration_travel = 3000 +adhesion_type = brim +brim_width = 6.0 +cool_fan_full_at_height = 0.5 +cool_fan_speed = 100 +cool_fan_speed_0 = 100 +infill_overlap = 25 +infill_pattern = zigzag +infill_sparse_density = 25 +initial_layer_line_width_factor = 105 +jerk_enabled = True +jerk_print = 8 +jerk_travel = 10 +layer_height = 0.15 +layer_height_0 = 0.2 +line_width = 0.42 +material_bed_temperature = 60 +material_diameter = 1.75 +default_material_print_temperature = 205 +material_print_temperature = 205 +material_print_temperature_layer_0 = 205 +material_initial_print_temperature = 205 +material_final_print_temperature = 205 +optimize_wall_printing_order = True +outer_inset_first = True +retract_at_layer_change = False +retraction_amount = 6 +retraction_hop = 0.075 +retraction_hop_enabled = True +retraction_hop_only_when_collides = True +retraction_min_travel = 1.5 +retraction_speed = 55 +retraction_combing = noskin +skin_overlap = 10 +skirt_brim_speed = 40 +skirt_gap = 5 +skirt_line_count = 3 +speed_infill = =speed_print +speed_print = 50 +speed_support = 30 +speed_topbottom = =math.ceil(speed_print * 20 / 50) +speed_travel = 150 +speed_wall = =speed_print +speed_wall_x = =speed_print +support_angle = 50 +support_enable = True +support_interface_enable = True +support_pattern = triangles +support_roof_enable = True +support_type = everywhere +support_use_towers = False +support_z_distance = 0.3 +support_xy_distance = 0.7 +support_xy_distance_overhang = 0.2 +smooth_spiralized_contours = False +top_bottom_thickness = 1.2 +travel_retract_before_outer_wall = True +wall_thickness = 1.2 +z_seam_corner = z_seam_corner_weighted + + + + + diff --git a/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg b/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg new file mode 100644 index 0000000000..64764745ec --- /dev/null +++ b/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg @@ -0,0 +1,66 @@ +[general] +version = 4 +name = Normal +definition = eryone_er20 + +[metadata] +setting_version = 15 +type = quality +quality_type = normal +weight = 0 +global_quality = True + +[values] +acceleration_enabled = True +acceleration_print = 1000 +acceleration_travel = 3000 +adhesion_type = brim +brim_width = 5 +cool_fan_full_at_height = 0.5 +cool_fan_speed = 100 +cool_fan_speed_0 = 100 +infill_overlap = 15 +infill_pattern = zigzag +infill_sparse_density = 25 +initial_layer_line_width_factor = 105 +jerk_enabled = True +jerk_print = 8 +jerk_travel = 10 +layer_height = 0.2 +layer_height_0 = 0.2 +material_bed_temperature = 60 +material_diameter = 1.75 +default_material_print_temperature = 205 +material_print_temperature = 205 +material_print_temperature_layer_0 = 205 +material_initial_print_temperature = 205 +material_final_print_temperature = 205 +retract_at_layer_change = False +retraction_amount = 6 +retraction_hop = 0.075 +retraction_hop_enabled = True +retraction_hop_only_when_collides = True +retraction_min_travel = 1.5 +retraction_speed = 55 +skirt_brim_speed = 40 +skirt_gap = 5 +skirt_line_count = 3 +speed_infill = =speed_print +speed_print = 50 +speed_support = 30 +speed_topbottom = =math.ceil(speed_print * 20 / 50) +speed_travel = 150 +speed_wall = =speed_print +speed_wall_x = =speed_print +support_angle = 50 +support_enable = True +support_interface_enable = True +support_pattern = triangles +support_roof_enable = True +support_type = everywhere +support_use_towers = False +support_z_distance = 0.3 +support_xy_distance = 0.7 +support_xy_distance_overhang = 0.2 +top_bottom_thickness = 1.2 +wall_thickness = 1.2 From 04418853e6940282a1b22c477b05f252a6a459a5 Mon Sep 17 00:00:00 2001 From: Eryone <40707672+Eryone@users.noreply.github.com> Date: Thu, 10 Dec 2020 20:56:25 +0800 Subject: [PATCH 24/87] --- resources/definitions/eryone_er20.def.json | 55 +++++----------------- 1 file changed, 13 insertions(+), 42 deletions(-) diff --git a/resources/definitions/eryone_er20.def.json b/resources/definitions/eryone_er20.def.json index f16de9011e..fc61574f06 100644 --- a/resources/definitions/eryone_er20.def.json +++ b/resources/definitions/eryone_er20.def.json @@ -5,11 +5,10 @@ "metadata": { "visible": true, - "author": "Tom", + "author": "Eryone3d", "manufacturer": "Eryone", "file_formats": "text/x-gcode", "platform": "eryone_er20_plateform.stl", - "has_materials": true, "has_machine_quality": true, "preferred_quality_type": "high", "machine_extruder_trains": @@ -18,34 +17,15 @@ } }, - "overrides": - { - "machine_name": - { - "default_value": "Eryone ER20" - }, - "machine_heated_bed": - { - "default_value": true - }, - "machine_width": - { - "default_value": 250 - }, - "machine_height": - { - "default_value": 200 - }, - "machine_depth": - { - "default_value": 220 - }, - "machine_center_is_zero": - { - "default_value": false - }, - "machine_head_with_fans_polygon": - { + "overrides":{ + "machine_name":{ "default_value": "Eryone ER20" }, + "machine_heated_bed":{ "default_value": true }, + "machine_width":{ "default_value": 250 }, + "machine_height":{ "default_value": 200 }, + "machine_depth":{ "default_value": 220 }, + "machine_center_is_zero":{ "default_value": false }, + "machine_gcode_flavor":{ "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_head_with_fans_polygon": { "default_value": [ [-10, -10], [-10, 10], @@ -53,20 +33,11 @@ [10, -10] ] }, - "gantry_height": - { - "value": "0" - }, - "machine_gcode_flavor": - { - "default_value": "RepRap (Marlin/Sprinter)" - }, - "machine_start_gcode": - { + "gantry_height":{ "value": "0" }, + "machine_start_gcode":{ "default_value": "G90 ; use absolute coordinates\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling/ABL\nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0.0\nG1 Y-2.0 X150 F2400G1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\nG1 X150 F1000\nG1 Z0.2 F720\nG1 X80.0 E8.0 F900\nG1 X20.0 E10.0 F700\nG92 E0.0\nM221 S95 ; set flow\n" }, - "machine_end_gcode": - { + "machine_end_gcode":{ "default_value": "M104 S0 ; turn off extruder\nM140 S0 ; turn off bed\nM84 ; disable motors\nM107\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 ;Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y180 F2000\nM84 ;steppers off\nG90\nM300 P300 S4000" } } From af5200afd32aed79c19f23d413e5982ff99e3eb1 Mon Sep 17 00:00:00 2001 From: Eryone <40707672+Eryone@users.noreply.github.com> Date: Thu, 10 Dec 2020 20:59:29 +0800 Subject: [PATCH 25/87] Update eryone_er20.def.json Delete the space tab --- resources/definitions/eryone_er20.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/eryone_er20.def.json b/resources/definitions/eryone_er20.def.json index fc61574f06..c9aac47d94 100644 --- a/resources/definitions/eryone_er20.def.json +++ b/resources/definitions/eryone_er20.def.json @@ -24,8 +24,8 @@ "machine_height":{ "default_value": 200 }, "machine_depth":{ "default_value": 220 }, "machine_center_is_zero":{ "default_value": false }, - "machine_gcode_flavor":{ "default_value": "RepRap (Marlin/Sprinter)" }, - "machine_head_with_fans_polygon": { + "machine_gcode_flavor":{ "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_head_with_fans_polygon": { "default_value": [ [-10, -10], [-10, 10], From 008fc3ffd17828099d3828651e4b8d2b7562f54a Mon Sep 17 00:00:00 2001 From: Eryone <40707672+Eryone@users.noreply.github.com> Date: Thu, 10 Dec 2020 21:10:16 +0800 Subject: [PATCH 26/87] Delete the space tab --- resources/definitions/eryone_er20.def.json | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/resources/definitions/eryone_er20.def.json b/resources/definitions/eryone_er20.def.json index c9aac47d94..66182790ef 100644 --- a/resources/definitions/eryone_er20.def.json +++ b/resources/definitions/eryone_er20.def.json @@ -2,8 +2,7 @@ "version": 2, "name": "Eryone ER20", "inherits": "fdmprinter", - "metadata": - { + "metadata": { "visible": true, "author": "Eryone3d", "manufacturer": "Eryone", @@ -17,15 +16,15 @@ } }, - "overrides":{ - "machine_name":{ "default_value": "Eryone ER20" }, - "machine_heated_bed":{ "default_value": true }, - "machine_width":{ "default_value": 250 }, - "machine_height":{ "default_value": 200 }, - "machine_depth":{ "default_value": 220 }, - "machine_center_is_zero":{ "default_value": false }, - "machine_gcode_flavor":{ "default_value": "RepRap (Marlin/Sprinter)" }, - "machine_head_with_fans_polygon": { + "overrides": { + "machine_name": { "default_value": "Eryone ER20" }, + "machine_heated_bed": { "default_value": true }, + "machine_width": { "default_value": 250 }, + "machine_height": { "default_value": 200 }, + "machine_depth": { "default_value": 220 }, + "machine_center_is_zero": { "default_value": false }, + "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_head_with_fans_polygon": { "default_value": [ [-10, -10], [-10, 10], @@ -34,10 +33,10 @@ ] }, "gantry_height":{ "value": "0" }, - "machine_start_gcode":{ + "machine_start_gcode": { "default_value": "G90 ; use absolute coordinates\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling/ABL\nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0.0\nG1 Y-2.0 X150 F2400G1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\nG1 X150 F1000\nG1 Z0.2 F720\nG1 X80.0 E8.0 F900\nG1 X20.0 E10.0 F700\nG92 E0.0\nM221 S95 ; set flow\n" }, - "machine_end_gcode":{ + "machine_end_gcode": { "default_value": "M104 S0 ; turn off extruder\nM140 S0 ; turn off bed\nM84 ; disable motors\nM107\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 ;Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y180 F2000\nM84 ;steppers off\nG90\nM300 P300 S4000" } } From ba309f3cac8c8f059880fcd3d4a6c5484a408d65 Mon Sep 17 00:00:00 2001 From: Eryone <40707672+Eryone@users.noreply.github.com> Date: Thu, 10 Dec 2020 21:37:36 +0800 Subject: [PATCH 27/87] Update the setting version "15" to "16" --- resources/quality/eryone_er20/eryone_er20_draft.inst.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg b/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg index b44797eae6..008ed519a7 100644 --- a/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg +++ b/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = eryone_er20 [metadata] -setting_version = 15 +setting_version = 16 type = quality quality_type = draft weight = -2 From 894ec4232e915ac78a9088aaed934da937b21c31 Mon Sep 17 00:00:00 2001 From: Eryone <40707672+Eryone@users.noreply.github.com> Date: Thu, 10 Dec 2020 21:38:03 +0800 Subject: [PATCH 28/87] Update the setting version "15" to "16" --- resources/quality/eryone_er20/eryone_er20_high.inst.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/quality/eryone_er20/eryone_er20_high.inst.cfg b/resources/quality/eryone_er20/eryone_er20_high.inst.cfg index 53fbe8a8d8..d504bb5acd 100644 --- a/resources/quality/eryone_er20/eryone_er20_high.inst.cfg +++ b/resources/quality/eryone_er20/eryone_er20_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = eryone_er20 [metadata] -setting_version = 15 +setting_version = 16 type = quality quality_type = high weight = 1 From 42ef3fea313c69743a5837b4405d6eaf194108d5 Mon Sep 17 00:00:00 2001 From: Eryone <40707672+Eryone@users.noreply.github.com> Date: Thu, 10 Dec 2020 21:38:28 +0800 Subject: [PATCH 29/87] Update the setting version "15" to "16" --- resources/quality/eryone_er20/eryone_er20_normal.inst.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg b/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg index 64764745ec..33f7a4b619 100644 --- a/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg +++ b/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = eryone_er20 [metadata] -setting_version = 15 +setting_version = 16 type = quality quality_type = normal weight = 0 From 5e7eb2fc93d39bb42acff91ef5d1edd82ad482c6 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 11 Dec 2020 17:18:23 +0100 Subject: [PATCH 30/87] No need for leading 0 in load-image num-fields. fixes #8906 --- plugins/ImageReader/ConfigUI.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ImageReader/ConfigUI.qml b/plugins/ImageReader/ConfigUI.qml index a7dbc5f748..a3dceed50d 100644 --- a/plugins/ImageReader/ConfigUI.qml +++ b/plugins/ImageReader/ConfigUI.qml @@ -43,7 +43,7 @@ UM.Dialog TextField { id: peak_height objectName: "Peak_Height" - validator: RegExpValidator {regExp: /^\d{1,3}([\,|\.]\d*)?$/} + validator: RegExpValidator {regExp: /^\d{0,3}([\,|\.]\d*)?$/} width: 180 * screenScaleFactor onTextChanged: { manager.onPeakHeightChanged(text) } } @@ -66,7 +66,7 @@ UM.Dialog TextField { id: base_height objectName: "Base_Height" - validator: RegExpValidator {regExp: /^\d{1,3}([\,|\.]\d*)?$/} + validator: RegExpValidator {regExp: /^\d{0,3}([\,|\.]\d*)?$/} width: 180 * screenScaleFactor onTextChanged: { manager.onBaseHeightChanged(text) } } From 35e3665a7822a81014ebf80578bffeda4f3f7a04 Mon Sep 17 00:00:00 2001 From: RPI News <44128563+mobilegmYT@users.noreply.github.com> Date: Mon, 14 Dec 2020 21:27:18 -0700 Subject: [PATCH 31/87] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ba2b3c277..a108994cb7 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Dependencies Build scripts ------------- -Please checkout [cura-build](https://github.com/Ultimaker/cura-build) for detailed building instructions. +Please check out [cura-build](https://github.com/Ultimaker/cura-build) for detailed building instructions. Running from Source ------------- From 430550452f80310435ba9859993f82456a24b7b8 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Tue, 15 Dec 2020 18:31:15 +0100 Subject: [PATCH 32/87] Add a new file menu option when there are multiple file providers CURA-7868 --- resources/qml/Menus/FileMenu.qml | 5 +++ resources/qml/Menus/OpenFilesMenu.qml | 45 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 resources/qml/Menus/OpenFilesMenu.qml diff --git a/resources/qml/Menus/FileMenu.qml b/resources/qml/Menus/FileMenu.qml index b9845678d6..24c081502a 100644 --- a/resources/qml/Menus/FileMenu.qml +++ b/resources/qml/Menus/FileMenu.qml @@ -22,6 +22,11 @@ Menu { id: openMenu action: Cura.Actions.open + visible: CuraApplication.fileProviders.length > 0 // DEBUG: It's > 0 so that both options are visible for debugging purposes + } + + OpenFilesMenu { + visible: CuraApplication.fileProviders.length > 0 // DEBUG: It's > 0 so that both options are visible for debugging purposes } RecentFilesMenu { } diff --git a/resources/qml/Menus/OpenFilesMenu.qml b/resources/qml/Menus/OpenFilesMenu.qml new file mode 100644 index 0000000000..a3afa9a598 --- /dev/null +++ b/resources/qml/Menus/OpenFilesMenu.qml @@ -0,0 +1,45 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.6 as UM +import Cura 1.0 as Cura + +import "../Dialogs" + +Menu +{ + id: menu + title: catalog.i18nc("@title:menu menubar:file", "Open File(s)...") + iconName: "document-open-recent"; + + + Instantiator + { + id: fileProviders + model: UM.FileProviderModel { } + MenuItem + { + text: + { + return model.displayText; + } + onTriggered: + { + if (model.index == 0) // The 0th element is the "From Disk" option, which should activate the open local file dialog + { + Cura.Actions.open.trigger() + } + else + { + fileProviders.model.trigger(model.name); + } + } + shortcut: model.shortcut + } + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) + } +} From 49effbcd806f0cc3b99a226bc58bcc47614fb2d6 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 17 Dec 2020 10:22:29 +0100 Subject: [PATCH 33/87] Disable survey button --- plugins/CuraDrive/src/qml/pages/BackupsPage.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml index c337294744..bb17cea973 100644 --- a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml +++ b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml @@ -69,7 +69,7 @@ Item BackupListFooter { id: backupListFooter - showInfoButton: backupList.model.length > 4 + } } } From df414909403642dbbe90a5fc55398851151dc009 Mon Sep 17 00:00:00 2001 From: Eryone <40707672+Eryone@users.noreply.github.com> Date: Thu, 17 Dec 2020 18:06:20 +0800 Subject: [PATCH 34/87] Improved the setting --- resources/definitions/eryone_er20.def.json | 212 +++++++++++++++++- .../eryone_er20/eryone_er20_draft.inst.cfg | 48 ---- .../eryone_er20/eryone_er20_high.inst.cfg | 57 +---- .../eryone_er20/eryone_er20_normal.inst.cfg | 54 +---- 4 files changed, 212 insertions(+), 159 deletions(-) diff --git a/resources/definitions/eryone_er20.def.json b/resources/definitions/eryone_er20.def.json index 66182790ef..b1349edc0c 100644 --- a/resources/definitions/eryone_er20.def.json +++ b/resources/definitions/eryone_er20.def.json @@ -17,13 +17,27 @@ }, "overrides": { - "machine_name": { "default_value": "Eryone ER20" }, - "machine_heated_bed": { "default_value": true }, - "machine_width": { "default_value": 250 }, - "machine_height": { "default_value": 200 }, - "machine_depth": { "default_value": 220 }, - "machine_center_is_zero": { "default_value": false }, - "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_name": { + "default_value": "Eryone ER20" + }, + "machine_heated_bed": { + "default_value": true + }, + "machine_width": { + "default_value": 250 + }, + "machine_height": { + "default_value": 200 + }, + "machine_depth": { + "default_value": 220 + }, + "machine_center_is_zero": { + "default_value": false + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, "machine_head_with_fans_polygon": { "default_value": [ [-10, -10], @@ -38,6 +52,190 @@ }, "machine_end_gcode": { "default_value": "M104 S0 ; turn off extruder\nM140 S0 ; turn off bed\nM84 ; disable motors\nM107\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 ;X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\nG28 X0 ;Y0 ;move X/Y to min endstops, so the head is out of the way\nG1 Y180 F2000\nM84 ;steppers off\nG90\nM300 P300 S4000" + }, + "material_print_temperature": { + "value": 205 + }, + "material_print_temperature_layer_0": { + "value": 215 + }, + "material_initial_print_temperature": { + "value": 205 + }, + "material_final_print_temperature": { + "value": 205 + }, + "acceleration_enabled": { + "value": true + }, + "acceleration_travel": { + "value": 1800 + }, + "adhesion_type": { + "value": "'brim'" + }, + "brim_width": { + "value": 5 + }, + "cool_fan_full_at_height": { + "value": 0.5 + }, + "cool_fan_speed": { + "value": 100 + }, + "cool_fan_speed_0": { + "value": 100 + }, + "infill_overlap": { + "value": "25 if infill_sparse_density < 95 and infill_pattern != 'concentric' else 0", + "maximum_value_warning": 100, + "minimum_value_warning": -50 + }, + "infill_pattern": { + "value": "'grid'" + }, + "infill_sparse_density": { + "value": 20 + }, + "initial_layer_line_width_factor": { + "value": 105 + }, + "infill_before_walls": { + "value": false + }, + "material_bed_temperature": { + "value": 60 + }, + "material_bed_temperature_layer_0": { + "value": 65 + }, + "optimize_wall_printing_order": { + "default_value": true + }, + "retract_at_layer_change": { + "value": true + }, + "retraction_amount": { + "default_value": 4 + }, + "retraction_hop": { + "value": 0.075 + }, + "retraction_hop_enabled": { + "value": false + }, + "retraction_hop_only_when_collides": { + "value": true + }, + "retraction_min_travel": { + "value": 1.5 + }, + "retraction_speed": { + "default_value": 85, + "maximum_value_warning": 100 + }, + "retraction_retract_speed": { + "maximum_value_warning": 130 + }, + "retraction_prime_speed": { + "value": "math.ceil(retraction_speed * 0.4)", + "maximum_value_warning": 130 + }, + "retraction_combing": { + "value": "'noskin'" + }, + "skin_overlap": { + "value": 10 + }, + "skirt_brim_speed": { + "value": 40 + }, + "skirt_gap": { + "value": 5 + }, + "skirt_line_count": { + "value": 3 + }, + "speed_infill": { + "value": "speed_print" + }, + "speed_topbottom": { + "value": "math.ceil(speed_print * 20 / 50)" + }, + "speed_travel": { + "value": "150" + }, + "speed_layer_0": { + "value": "20" + }, + "speed_wall": { + "value": "speed_print" + }, + "speed_wall_0": { + "value": "math.ceil(speed_print * 30 / 50)" + }, + "speed_wall_x": { + "value": "speed_print" + }, + "support_angle": { + "value": 50 + }, + "support_enable": { + "default_value": false + }, + "support_interface_enable": { + "value": true + }, + "support_pattern": { + "value": "'triangles'" + }, + "support_roof_enable": { + "value": true + }, + "support_type": { + "value": "'everywhere'" + }, + "support_use_towers": { + "value": false + }, + "support_z_distance": { + "value": 0.3 + }, + "support_xy_distance": { + "value": 0.7 + }, + "support_xy_distance_overhang": { + "value": 0.2 + }, + "smooth_spiralized_contours": { + "value": false + }, + "travel_retract_before_outer_wall": { + "value": true + }, + "wall_line_count": { + "value": 3 + }, + "wall_thickness": { + "value": "1.2" + }, + "bottom_layers": { + "value": "4" + }, + "bottom_thickness":{ + "value": "layer_height * 4" + }, + "top_thickness":{ + "value": "layer_height * 5" + }, + "top_layers": { + "value": "5" + }, + "z_seam_type": { + "value": "'shortest'" + }, + "z_seam_corner": { + "value": "'z_seam_corner_inner'" } } } diff --git a/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg b/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg index 008ed519a7..6ba6af5aec 100644 --- a/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg +++ b/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg @@ -11,56 +11,8 @@ weight = -2 global_quality = True [values] -acceleration_enabled = True acceleration_print = 1500 -acceleration_travel = 3000 -adhesion_type = brim -brim_width = 4.0 -cool_fan_full_at_height = 0.5 -cool_fan_speed = 100 -cool_fan_speed_0 = 100 -infill_overlap = 15 -infill_pattern = zigzag -infill_sparse_density = 25 -initial_layer_line_width_factor = 105 -jerk_enabled = True -jerk_print = 10 -jerk_travel = 12 layer_height = 0.3 layer_height_0 = 0.3 -material_bed_temperature = 60 -material_diameter = 1.75 -default_material_print_temperature = 205 -material_print_temperature = 205 -material_print_temperature_layer_0 = 205 -material_initial_print_temperature = 205 -material_final_print_temperature = 205 -retract_at_layer_change = False -retraction_amount = 6 -retraction_hop = 0.075 -retraction_hop_enabled = True -retraction_hop_only_when_collides = True -retraction_min_travel = 1.5 -retraction_speed = 55 -skirt_brim_speed = 40 -skirt_gap = 5 -skirt_line_count = 3 -speed_infill = =speed_print speed_print = 80 speed_support = 60 -speed_topbottom = =math.ceil(speed_print * 30 / 60) -speed_travel = 150 -speed_wall = =speed_print -speed_wall_x = =speed_print -support_angle = 50 -support_enable = True -support_interface_enable = True -support_pattern = triangles -support_roof_enable = True -support_type = everywhere -support_use_towers = False -support_z_distance = 0.3 -support_xy_distance = 0.7 -support_xy_distance_overhang = 0.2 -top_bottom_thickness = 1.2 -wall_thickness = 1.2 diff --git a/resources/quality/eryone_er20/eryone_er20_high.inst.cfg b/resources/quality/eryone_er20/eryone_er20_high.inst.cfg index d504bb5acd..04822040a5 100644 --- a/resources/quality/eryone_er20/eryone_er20_high.inst.cfg +++ b/resources/quality/eryone_er20/eryone_er20_high.inst.cfg @@ -11,67 +11,12 @@ weight = 1 global_quality = True [values] -acceleration_enabled = True acceleration_print = 500 -acceleration_travel = 3000 -adhesion_type = brim -brim_width = 6.0 -cool_fan_full_at_height = 0.5 -cool_fan_speed = 100 -cool_fan_speed_0 = 100 -infill_overlap = 25 -infill_pattern = zigzag -infill_sparse_density = 25 -initial_layer_line_width_factor = 105 -jerk_enabled = True -jerk_print = 8 -jerk_travel = 10 layer_height = 0.15 layer_height_0 = 0.2 -line_width = 0.42 -material_bed_temperature = 60 -material_diameter = 1.75 -default_material_print_temperature = 205 -material_print_temperature = 205 -material_print_temperature_layer_0 = 205 -material_initial_print_temperature = 205 -material_final_print_temperature = 205 -optimize_wall_printing_order = True -outer_inset_first = True -retract_at_layer_change = False -retraction_amount = 6 -retraction_hop = 0.075 -retraction_hop_enabled = True -retraction_hop_only_when_collides = True -retraction_min_travel = 1.5 -retraction_speed = 55 -retraction_combing = noskin -skin_overlap = 10 -skirt_brim_speed = 40 -skirt_gap = 5 -skirt_line_count = 3 -speed_infill = =speed_print speed_print = 50 speed_support = 30 -speed_topbottom = =math.ceil(speed_print * 20 / 50) -speed_travel = 150 -speed_wall = =speed_print -speed_wall_x = =speed_print -support_angle = 50 -support_enable = True -support_interface_enable = True -support_pattern = triangles -support_roof_enable = True -support_type = everywhere -support_use_towers = False -support_z_distance = 0.3 -support_xy_distance = 0.7 -support_xy_distance_overhang = 0.2 -smooth_spiralized_contours = False -top_bottom_thickness = 1.2 -travel_retract_before_outer_wall = True -wall_thickness = 1.2 -z_seam_corner = z_seam_corner_weighted + diff --git a/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg b/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg index 33f7a4b619..1c29936cc0 100644 --- a/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg +++ b/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg @@ -11,56 +11,14 @@ weight = 0 global_quality = True [values] -acceleration_enabled = True acceleration_print = 1000 -acceleration_travel = 3000 -adhesion_type = brim -brim_width = 5 -cool_fan_full_at_height = 0.5 -cool_fan_speed = 100 -cool_fan_speed_0 = 100 -infill_overlap = 15 -infill_pattern = zigzag -infill_sparse_density = 25 -initial_layer_line_width_factor = 105 -jerk_enabled = True -jerk_print = 8 -jerk_travel = 10 layer_height = 0.2 layer_height_0 = 0.2 -material_bed_temperature = 60 -material_diameter = 1.75 -default_material_print_temperature = 205 -material_print_temperature = 205 -material_print_temperature_layer_0 = 205 -material_initial_print_temperature = 205 -material_final_print_temperature = 205 -retract_at_layer_change = False -retraction_amount = 6 -retraction_hop = 0.075 -retraction_hop_enabled = True -retraction_hop_only_when_collides = True -retraction_min_travel = 1.5 -retraction_speed = 55 -skirt_brim_speed = 40 -skirt_gap = 5 -skirt_line_count = 3 -speed_infill = =speed_print speed_print = 50 speed_support = 30 -speed_topbottom = =math.ceil(speed_print * 20 / 50) -speed_travel = 150 -speed_wall = =speed_print -speed_wall_x = =speed_print -support_angle = 50 -support_enable = True -support_interface_enable = True -support_pattern = triangles -support_roof_enable = True -support_type = everywhere -support_use_towers = False -support_z_distance = 0.3 -support_xy_distance = 0.7 -support_xy_distance_overhang = 0.2 -top_bottom_thickness = 1.2 -wall_thickness = 1.2 + + + + + + From b6ba0eac3533daba7f2a2d653ff8582564d13857 Mon Sep 17 00:00:00 2001 From: Eryone <40707672+Eryone@users.noreply.github.com> Date: Thu, 17 Dec 2020 18:16:07 +0800 Subject: [PATCH 35/87] DelDelete the space tab Deleted the space tab --- resources/definitions/eryone_er20.def.json | 19 ------------------- .../eryone_er20/eryone_er20_draft.inst.cfg | 2 +- .../eryone_er20/eryone_er20_high.inst.cfg | 8 +------- .../eryone_er20/eryone_er20_normal.inst.cfg | 8 +------- 4 files changed, 3 insertions(+), 34 deletions(-) diff --git a/resources/definitions/eryone_er20.def.json b/resources/definitions/eryone_er20.def.json index b1349edc0c..236ef63188 100644 --- a/resources/definitions/eryone_er20.def.json +++ b/resources/definitions/eryone_er20.def.json @@ -239,22 +239,3 @@ } } } - - - - - - - - - - - - - - - - - - - diff --git a/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg b/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg index 6ba6af5aec..64e4f2b180 100644 --- a/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg +++ b/resources/quality/eryone_er20/eryone_er20_draft.inst.cfg @@ -15,4 +15,4 @@ acceleration_print = 1500 layer_height = 0.3 layer_height_0 = 0.3 speed_print = 80 -speed_support = 60 +speed_support = 60 \ No newline at end of file diff --git a/resources/quality/eryone_er20/eryone_er20_high.inst.cfg b/resources/quality/eryone_er20/eryone_er20_high.inst.cfg index 04822040a5..db15abbeb4 100644 --- a/resources/quality/eryone_er20/eryone_er20_high.inst.cfg +++ b/resources/quality/eryone_er20/eryone_er20_high.inst.cfg @@ -15,10 +15,4 @@ acceleration_print = 500 layer_height = 0.15 layer_height_0 = 0.2 speed_print = 50 -speed_support = 30 - - - - - - +speed_support = 30 \ No newline at end of file diff --git a/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg b/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg index 1c29936cc0..ca29834126 100644 --- a/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg +++ b/resources/quality/eryone_er20/eryone_er20_normal.inst.cfg @@ -15,10 +15,4 @@ acceleration_print = 1000 layer_height = 0.2 layer_height_0 = 0.2 speed_print = 50 -speed_support = 30 - - - - - - +speed_support = 30 \ No newline at end of file From 037b5d5b44014dfead2a4a6fe336fe88da3b9dbc Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 18 Dec 2020 11:31:55 +0100 Subject: [PATCH 36/87] Ensure that the skirt_brim_line_width is selected from right extruder This fixes CURA-7835 and Fixes #8716 --- cura/BuildVolume.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 6bda2d94e3..fd953a838a 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -1068,7 +1068,14 @@ class BuildVolume(SceneNode): adhesion_type = adhesion_override if adhesion_type is None: adhesion_type = container_stack.getProperty("adhesion_type", "value") - skirt_brim_line_width = self._global_container_stack.getProperty("skirt_brim_line_width", "value") + + # Skirt_brim_line_width is a bit of an odd one out. The primary bit of the skirt/brim is printed + # with the adhesion extruder, but it also prints one extra line by all other extruders. As such, the + # setting does *not* have a limit_to_extruder setting (which means that we can't ask the global extruder what + # the value is. + adhesion_extruder = self._global_container_stack.getProperty("adhesion_extruder_nr", "value") + skirt_brim_line_width = self._global_container_stack.extruderList[int(adhesion_extruder)].getProperty("skirt_brim_line_width", "value") + initial_layer_line_width_factor = self._global_container_stack.getProperty("initial_layer_line_width_factor", "value") # Use brim width if brim is enabled OR the prime tower has a brim. if adhesion_type == "brim": From cd76326fed2f8d6328dda9e66dbc7f43d53d6762 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 18 Dec 2020 11:52:53 +0100 Subject: [PATCH 37/87] Fix tests CURA-7835 --- tests/TestBuildVolume.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/TestBuildVolume.py b/tests/TestBuildVolume.py index c5d59b64d7..293b8e0270 100644 --- a/tests/TestBuildVolume.py +++ b/tests/TestBuildVolume.py @@ -66,9 +66,12 @@ class TestCalculateBedAdhesionSize: return properties.get(args[2]) def createAndSetGlobalStack(self, build_volume): - mocked_stack = MagicMock() + mocked_stack = MagicMock(name = "mocked_stack") mocked_stack.getProperty = MagicMock(side_effect=self.getPropertySideEffect) + mocked_extruder = MagicMock(name = "mocked_extruder") + mocked_extruder.getProperty = MagicMock(side_effect=self.getPropertySideEffect) + mocked_stack.extruderList = [mocked_extruder] build_volume._global_container_stack = mocked_stack def test_noGlobalStack(self, build_volume: BuildVolume): @@ -90,6 +93,7 @@ class TestCalculateBedAdhesionSize: self.createAndSetGlobalStack(build_volume) patched_dictionary = self.setting_property_dict.copy() patched_dictionary.update(setting_dict) + patched_dictionary.update({"adhesion_extruder_nr": {"value": 0}}) with patch.dict(self.setting_property_dict, patched_dictionary): assert build_volume._calculateBedAdhesionSize([]) == result From 561620336eab48a0554e4447e0e5ae1f10d1d602 Mon Sep 17 00:00:00 2001 From: StijnArntz <41779876+StijnArntz@users.noreply.github.com> Date: Fri, 18 Dec 2020 14:08:41 +0100 Subject: [PATCH 38/87] Update CPE settings for UM2C Combing settings for 0.4nozzle --- .../quality/ultimaker2_plus_connect/um2pc_cpe_0.4_fast.inst.cfg | 1 + .../quality/ultimaker2_plus_connect/um2pc_cpe_0.4_high.inst.cfg | 1 + .../ultimaker2_plus_connect/um2pc_cpe_0.4_normal.inst.cfg | 1 + 3 files changed, 3 insertions(+) diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_fast.inst.cfg index a45f1d3e6c..228b0fb853 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_fast.inst.cfg @@ -27,3 +27,4 @@ speed_wall_0 = =math.ceil(speed_print * 30 / 45) speed_topbottom = =math.ceil(speed_print * 30 / 45) speed_wall_x = =math.ceil(speed_print * 40 / 45) speed_infill = =math.ceil(speed_print * 45 / 45) +retraction_combing_max_distance = 50 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_high.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_high.inst.cfg index bcbc535fe0..b4f7f13a42 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_high.inst.cfg @@ -24,3 +24,4 @@ top_bottom_thickness = 0.72 wall_thickness = 1.05 speed_topbottom = =math.ceil(speed_print * 15 / 45) speed_infill = =math.ceil(speed_print * 45 / 45) +retraction_combing_max_distance = 50 diff --git a/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_normal.inst.cfg index 71f2916a3a..6d417fe0f1 100644 --- a/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus_connect/um2pc_cpe_0.4_normal.inst.cfg @@ -22,3 +22,4 @@ speed_print = 45 speed_wall = =math.ceil(speed_print * 30 / 45) top_bottom_thickness = 0.8 wall_thickness = 1.05 +retraction_combing_max_distance = 50 From 43615a57b61c2be31a439025f4bf6279b45a63a0 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Fri, 18 Dec 2020 14:34:29 +0100 Subject: [PATCH 39/87] Change client scope in the Account CURA-7868 --- cura/API/Account.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/API/Account.py b/cura/API/Account.py index 15bccb71e1..d5ef2bfcb9 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -81,7 +81,8 @@ class Account(QObject): CLIENT_ID="um----------------------------ultimaker_cura", CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download " "packages.rating.read packages.rating.write connect.cluster.read connect.cluster.write " - "cura.printjob.read cura.printjob.write cura.mesh.read cura.mesh.write", + "library.project.read library.project.write cura.printjob.read cura.printjob.write " + "cura.mesh.read cura.mesh.write", AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data", AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root), AUTH_FAILED_REDIRECT="{}/app/auth-error".format(self._oauth_root) From 19a8bd63ab55d095f82c63026784085b3bec6355 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 18 Dec 2020 15:31:48 +0100 Subject: [PATCH 40/87] Remove unused variable CURA-6682 --- plugins/Toolbox/src/Toolbox.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 98054c26e9..60c8f9107e 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -46,8 +46,6 @@ class Toolbox(QObject, Extension): self._application = application # type: CuraApplication - self._sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int] - # Network: self._download_request_data = None # type: Optional[HttpRequestData] self._download_progress = 0 # type: float From 5b43c94c0b6e916d9ce8afe531db0315c7845ba4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 21 Dec 2020 13:15:32 +0100 Subject: [PATCH 41/87] Mark setting as disabled in perobject setting panel if it's enabled property requires it This is a bit different from the rest, since hiding would mean that the setting can't be removed. CURA-7569 --- plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index e834372ae9..cf3e182096 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -237,7 +237,7 @@ Item id: settingLoader width: UM.Theme.getSize("setting").width height: UM.Theme.getSize("section").height - + enabled: provider.properties.enabled === "True" property var definition: model property var settingDefinitionsModel: addedSettingsModel property var propertyProvider: provider From 9efe5dd5e29f36732148731c389c8bb280ab2402 Mon Sep 17 00:00:00 2001 From: FusedForm <69219710+FusedForm@users.noreply.github.com> Date: Tue, 22 Dec 2020 11:27:15 -0500 Subject: [PATCH 42/87] Small parameter update for printing improvement (#8961) * Add files via upload * Add files via upload * Add files via upload * Update fusedform_300_draft_abs.inst.cfg * Add files via upload * Delete fusedform_300_draft_abs.inst.cfg * Delete fusedform_300_draft_pla.inst.cfg * Deletion of old files * changes in STD quality profiles * Changes in mini quality files * Changes in FF600 quality files * changes in machine definition name * Changes in definition files * extruder and definition linking * ff300 inherits and extruder def * Update fusedform_300.def.json * category deprecation in definitions * inherits * ff300 extruder definition machine * extruder machine definitions * Fused form FF600plus machine profiles * PETG profiles * Update fusedform_300.def.json * changes in start gcode * machine configuration dynamics on definitions * speed adjustments * dynamics modification in petg and flex quality * Further adjustments on dynamics definitiosn * Modification of all files under inherits data structure. * Quality setting version 15 to 16 * These changes are for limiting quality types in the materials defined * Quality Modification under Global and Material specific structure Global quality structures and material specific definitions under the global structures * Addition of double extruder machine definitions * Machine definitions update according to structure * Doppia Base definitions erased, Doppia definitions updated to use a single base definition. * extruder 1 base definition fix * extruder train overrides * extruder modifications * Update fusedform_300_doppia.def.json * Base settings and extruder definitions * retraction modification to avoid filament damage * REdefinition test of second extruder train in doppia * Update fusedform_300_doppia.def.json * Update fusedform_base_extruder_1.def.json * Update fusedform_300_doppia.def.json * Update fusedform_300_doppia.def.json * Update fusedform_300_doppia.def.json * Update fusedform_base_extruder_1.def.json * Update fusedform_300_doppia.def.json * Update fusedform_600_doppia.def.json * Update fusedform_600_doppia.def.json * Additional updates in Doppia definitions * Creation of Doppia base definition, doppia extruders. Update in doppia machine definitions * correction in quality definitions to pull base definitions from a single base * more corrections * small parameter update Small changes in printing parameters to optimize retraction and other settings. --- resources/definitions/fusedform_300_doppia.def.json | 2 +- resources/definitions/fusedform_600plus_doppia.def.json | 3 +++ resources/definitions/fusedform_base.def.json | 2 +- resources/definitions/fusedform_doppia_base.def.json | 6 ++++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/resources/definitions/fusedform_300_doppia.def.json b/resources/definitions/fusedform_300_doppia.def.json index ed8fa719a6..6b13bc02ea 100644 --- a/resources/definitions/fusedform_300_doppia.def.json +++ b/resources/definitions/fusedform_300_doppia.def.json @@ -10,7 +10,7 @@ "overrides": { "machine_extruder_count": { "value": 2 }, "machine_name": { "default_value": "FF300 Doppia" }, - "machine_width": { "default_value": 320 }, + "machine_width": { "default_value": 360 }, "machine_depth": { "default_value": 300 }, "machine_height": { "default_value": 320 }, "machine_max_feedrate_x": { "default_value": 100 }, diff --git a/resources/definitions/fusedform_600plus_doppia.def.json b/resources/definitions/fusedform_600plus_doppia.def.json index 18959e8084..de3889c1a8 100644 --- a/resources/definitions/fusedform_600plus_doppia.def.json +++ b/resources/definitions/fusedform_600plus_doppia.def.json @@ -25,6 +25,9 @@ "machine_max_jerk_z": { "default_value": 0.3 }, "machine_max_jerk_e": { "default_value": 5 }, "acceleration_travel": {"value":900} + + + } } diff --git a/resources/definitions/fusedform_base.def.json b/resources/definitions/fusedform_base.def.json index b68ab1a449..24b841f704 100644 --- a/resources/definitions/fusedform_base.def.json +++ b/resources/definitions/fusedform_base.def.json @@ -54,7 +54,7 @@ "retraction_amount": { "default_value": 4 }, "retraction_speed": { "default_value": 70}, "retraction_min_travel": {"value":5 }, - "retraction_count_max": {"value":10 }, + "retraction_count_max": {"default_value":10 }, "retraction_extrusion_window": {"value":4 }, "retraction_hop": {"default_value":0.2}, "retraction_hop_enabled": {"value":true}, diff --git a/resources/definitions/fusedform_doppia_base.def.json b/resources/definitions/fusedform_doppia_base.def.json index 7513ea9fb4..ddee568e28 100644 --- a/resources/definitions/fusedform_doppia_base.def.json +++ b/resources/definitions/fusedform_doppia_base.def.json @@ -54,7 +54,7 @@ "retraction_amount": { "default_value": 4 }, "retraction_speed": { "default_value": 70}, "retraction_min_travel": {"value":5 }, - "retraction_count_max": {"value":10 }, + "retraction_count_max": {"default_value":10 }, "retraction_extrusion_window": {"value":4 }, "retraction_hop": {"default_value":0.2}, "retraction_hop_enabled": {"value":true}, @@ -77,7 +77,9 @@ "support_xy_distance": {"value": 0.5}, "support_z_distance": {"value": 0.3 }, + "adhesion_type": {"default_value":"skirt"}, - "adhesion_type": {"default_value":"skirt"} + "switch_extruder_retraction_amount": { "value": 6 }, + "switch_extruder_retraction_speeds": { "value": 60 } } } From 71994eaaf90ca6302cbbc860ebb1366bf710b8fe Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Wed, 23 Dec 2020 17:13:14 +0100 Subject: [PATCH 43/87] Change the Open File(s) option according to the file providers count When there is only one file provider (i.e. the local file provider), the Open File(s) will be a simple item in the File menu. When there are more than one file providers, the Open File(s) will become a submenu in the File menu, which will contain all the file providers as submenu items. CURA-7868 --- cura/CuraApplication.py | 11 +++++++++++ resources/qml/Menus/FileMenu.qml | 18 +++++++++++++++--- resources/qml/Menus/OpenFilesMenu.qml | 11 +++++------ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index f0c69d5a61..ee347e7a4d 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -30,6 +30,7 @@ from UM.Operations.SetTransformOperation import SetTransformOperation from UM.Platform import Platform from UM.PluginError import PluginNotFoundError from UM.Preferences import Preferences +from UM.Qt.Bindings.FileProviderModel import FileProviderModel from UM.Qt.QtApplication import QtApplication # The class we're inheriting from. from UM.Resources import Resources from UM.Scene.Camera import Camera @@ -822,6 +823,9 @@ class CuraApplication(QtApplication): self._add_printer_pages_model_without_cancel.initialize(cancellable = False) self._whats_new_pages_model.initialize() + # Initialize the FileProviderModel + self._file_provider_model.initialize(self._onFileProviderEnabledChanged) + # Detect in which mode to run and execute that mode if self._is_headless: self.runWithoutGUI() @@ -1051,6 +1055,13 @@ class CuraApplication(QtApplication): self._simple_mode_settings_manager = SimpleModeSettingsManager() return self._simple_mode_settings_manager + @pyqtSlot(result = QObject) + def getFileProviderModel(self) -> FileProviderModel: + return self._file_provider_model + + def _onFileProviderEnabledChanged(self): + self._file_provider_model.update() + def event(self, event): """Handle Qt events""" diff --git a/resources/qml/Menus/FileMenu.qml b/resources/qml/Menus/FileMenu.qml index 24c081502a..a1bd246763 100644 --- a/resources/qml/Menus/FileMenu.qml +++ b/resources/qml/Menus/FileMenu.qml @@ -22,11 +22,23 @@ Menu { id: openMenu action: Cura.Actions.open - visible: CuraApplication.fileProviders.length > 0 // DEBUG: It's > 0 so that both options are visible for debugging purposes + visible: (CuraApplication.getFileProviderModel().count == 1) } - OpenFilesMenu { - visible: CuraApplication.fileProviders.length > 0 // DEBUG: It's > 0 so that both options are visible for debugging purposes + OpenFilesMenu + { + id: openFilesMenu + visible: (CuraApplication.getFileProviderModel().count > 1) + } + + Connections + { + target: CuraApplication.getFileProviderModel() + onItemsChanged: + { + openMenu.visible = (CuraApplication.getFileProviderModel().count == 1) // 1 because the open local files menu should always exist in the model + openFilesMenu.visible = (CuraApplication.getFileProviderModel().count > 1) + } } RecentFilesMenu { } diff --git a/resources/qml/Menus/OpenFilesMenu.qml b/resources/qml/Menus/OpenFilesMenu.qml index a3afa9a598..226ea70680 100644 --- a/resources/qml/Menus/OpenFilesMenu.qml +++ b/resources/qml/Menus/OpenFilesMenu.qml @@ -11,15 +11,14 @@ import "../Dialogs" Menu { - id: menu + id: openFilesMenu title: catalog.i18nc("@title:menu menubar:file", "Open File(s)...") iconName: "document-open-recent"; - Instantiator { id: fileProviders - model: UM.FileProviderModel { } + model: CuraApplication.getFileProviderModel() MenuItem { text: @@ -34,12 +33,12 @@ Menu } else { - fileProviders.model.trigger(model.name); + CuraApplication.getFileProviderModel().trigger(model.name); } } shortcut: model.shortcut } - onObjectAdded: menu.insertItem(index, object) - onObjectRemoved: menu.removeItem(object) + onObjectAdded: openFilesMenu.insertItem(index, object) + onObjectRemoved: openFilesMenu.removeItem(object) } } From ba9e10cfd80623909eaa4217add4ba3665d2bee2 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 24 Dec 2020 16:08:12 +0100 Subject: [PATCH 44/87] Round temperatures when displaying them These are long lines for some screens. It'll save a bit of space. No PID controller can really control down to fractional degrees. Fan speed and flow rates are already integer settings. For speeds and retraction lenghts the fractions can be important. Done for #8967. --- plugins/PostProcessingPlugin/scripts/ChangeAtZ.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py index b4036f7ff2..a23e52b750 100644 --- a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py +++ b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py @@ -774,15 +774,15 @@ class ChangeAtZProcessor: # looking for wait for bed temp if "bedTemp" in values: - codes.append("BedTemp: " + str(values["bedTemp"])) + codes.append("BedTemp: " + str(round(values["bedTemp"]))) # set our extruder one temp (if specified) if "extruderOne" in values: - codes.append("Extruder 1 Temp: " + str(values["extruderOne"])) + codes.append("Extruder 1 Temp: " + str(round(values["extruderOne"]))) # set our extruder two temp (if specified) if "extruderTwo" in values: - codes.append("Extruder 2 Temp: " + str(values["extruderTwo"])) + codes.append("Extruder 2 Temp: " + str(round(values["extruderTwo"]))) # set global flow rate if "flowrate" in values: From cf7b0d9768e5e922a7abea42da7fd93244f1e96a Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Tue, 29 Dec 2020 09:35:29 +1100 Subject: [PATCH 45/87] docs: fix simple typo, thier -> their There is a small typo in docs/How_to_use_the_flame_graph_profiler.md. Should read `their` rather than `thier`. --- docs/How_to_use_the_flame_graph_profiler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/How_to_use_the_flame_graph_profiler.md b/docs/How_to_use_the_flame_graph_profiler.md index b1cdaf358e..b40a86bb24 100644 --- a/docs/How_to_use_the_flame_graph_profiler.md +++ b/docs/How_to_use_the_flame_graph_profiler.md @@ -27,7 +27,7 @@ Note: The profiler front-end itself is quite "heavy" (ok, not optimised). It run What the Profiler Sees ---------------------- -The profiler doesn't capture every function call in Cura. It hooks into a number of important systems which give a good picture of activity without too much run time overhead. The most important system is Uranium's signal mechanism and PyQt5 slots. Functions which are called via the signal mechanism are recorded and thier names appear in the results. PyQt5 slots appear in the results with the prefix `[SLOT]`. +The profiler doesn't capture every function call in Cura. It hooks into a number of important systems which give a good picture of activity without too much run time overhead. The most important system is Uranium's signal mechanism and PyQt5 slots. Functions which are called via the signal mechanism are recorded and their names appear in the results. PyQt5 slots appear in the results with the prefix `[SLOT]`. Note that not all slots are captured. Only those slots which belong to classes which use the `pyqtSlot` decorator from the `UM.FlameProfiler` module. From 47723e982341737443b41956e975b2303342fb0e Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Sun, 3 Jan 2021 13:22:51 +0100 Subject: [PATCH 46/87] Fix definition of machine_gcode_flavor setting in PauseAtHeight --- plugins/PostProcessingPlugin/scripts/PauseAtHeight.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py index fc7bfec60a..69a67990fe 100644 --- a/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py +++ b/plugins/PostProcessingPlugin/scripts/PauseAtHeight.py @@ -182,8 +182,7 @@ class PauseAtHeight(Script): "Repetier": "Repetier" }, "default_value": "RepRap (Marlin/Sprinter)", - "enabled": false, - "default_value": "" + "enabled": false }, "custom_gcode_before_pause": { From 70550594cd113bee4af662c1dd980738ed3dbe0d Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Mon, 4 Jan 2021 15:07:53 +0100 Subject: [PATCH 47/87] Connect the visibility of the components through their properties As Ghostkeeper suspected correctly in the review comment https://github.com/Ultimaker/Cura/pull/9012#discussion_r549707433 the binding wasn't working because the model was being retrieved using a function (CuraApplication.getFileProviderModel()). Separating this model into a variable allows us to properly bind the "visible" properties of the menu items with the count property of the model without a problem. CURA-7868 --- resources/qml/Menus/FileMenu.qml | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/resources/qml/Menus/FileMenu.qml b/resources/qml/Menus/FileMenu.qml index a1bd246763..94fc2358e1 100644 --- a/resources/qml/Menus/FileMenu.qml +++ b/resources/qml/Menus/FileMenu.qml @@ -11,6 +11,7 @@ Menu { id: base title: catalog.i18nc("@title:menu menubar:toplevel", "&File") + property var fileProviderModel: CuraApplication.getFileProviderModel() MenuItem { @@ -22,23 +23,13 @@ Menu { id: openMenu action: Cura.Actions.open - visible: (CuraApplication.getFileProviderModel().count == 1) + visible: (base.fileProviderModel.count == 1) } OpenFilesMenu { id: openFilesMenu - visible: (CuraApplication.getFileProviderModel().count > 1) - } - - Connections - { - target: CuraApplication.getFileProviderModel() - onItemsChanged: - { - openMenu.visible = (CuraApplication.getFileProviderModel().count == 1) // 1 because the open local files menu should always exist in the model - openFilesMenu.visible = (CuraApplication.getFileProviderModel().count > 1) - } + visible: (base.fileProviderModel.count > 1) } RecentFilesMenu { } From e5038ab46dc6b5d43a3a9d575f74bbf3b21ee6f0 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Mon, 4 Jan 2021 15:12:53 +0100 Subject: [PATCH 48/87] Update year in the copyright comment. CURA-7868 --- resources/qml/Menus/OpenFilesMenu.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/OpenFilesMenu.qml b/resources/qml/Menus/OpenFilesMenu.qml index 226ea70680..60fb507b34 100644 --- a/resources/qml/Menus/OpenFilesMenu.qml +++ b/resources/qml/Menus/OpenFilesMenu.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Ultimaker B.V. +// Copyright (c) 2020 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 From 4b375ce2fe70815ba2e07024c132b2bd11e1c226 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jan 2021 15:19:17 +0100 Subject: [PATCH 49/87] Remove unused hasMesh signal This is logic that shouldn't be in QML anyway. It's not used by anything at this point. Let's remove it. Contributes to issue CURA-7868. --- resources/qml/Cura.qml | 11 +---------- .../Dialogs/AskOpenAsProjectOrModelsDialog.qml | 8 +------- .../Dialogs/OpenFilesIncludingProjectsDialog.qml | 16 ++++++++-------- resources/qml/Menus/RecentFilesMenu.qml | 10 ++-------- 4 files changed, 12 insertions(+), 33 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 9f24d91caf..bb7b5ac19c 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Ultimaker B.V. +// Copyright (c) 2021 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 @@ -149,15 +149,6 @@ UM.MainWindow id: backgroundItem anchors.fill: parent - signal hasMesh(string name) //this signal sends the filebase name so it can be used for the JobSpecs.qml - function getMeshName(path) - { - //takes the path the complete path of the meshname and returns only the filebase - var fileName = path.slice(path.lastIndexOf("/") + 1) - var fileBase = fileName.slice(0, fileName.indexOf(".")) - return fileBase - } - //DeleteSelection on the keypress backspace event Keys.onPressed: { diff --git a/resources/qml/Dialogs/AskOpenAsProjectOrModelsDialog.qml b/resources/qml/Dialogs/AskOpenAsProjectOrModelsDialog.qml index 8cdaeea5fa..68f58616f1 100644 --- a/resources/qml/Dialogs/AskOpenAsProjectOrModelsDialog.qml +++ b/resources/qml/Dialogs/AskOpenAsProjectOrModelsDialog.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Ultimaker B.V. +// Copyright (c) 2021 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -32,30 +32,24 @@ UM.Dialog // load the entire project function loadProjectFile() { - // update preference if (rememberChoiceCheckBox.checked) { UM.Preferences.setValue("cura/choice_on_open_project", "open_as_project") } UM.WorkspaceFileHandler.readLocalFile(base.fileUrl) - var meshName = backgroundItem.getMeshName(base.fileUrl.toString()) - backgroundItem.hasMesh(decodeURIComponent(meshName)) base.hide() } // load the project file as separated models function loadModelFiles() { - // update preference if (rememberChoiceCheckBox.checked) { UM.Preferences.setValue("cura/choice_on_open_project", "open_as_model") } CuraApplication.readLocalFile(base.fileUrl, "open_as_model") - var meshName = backgroundItem.getMeshName(base.fileUrl.toString()) - backgroundItem.hasMesh(decodeURIComponent(meshName)) base.hide() } diff --git a/resources/qml/Dialogs/OpenFilesIncludingProjectsDialog.qml b/resources/qml/Dialogs/OpenFilesIncludingProjectsDialog.qml index 187578f12c..62cded866c 100644 --- a/resources/qml/Dialogs/OpenFilesIncludingProjectsDialog.qml +++ b/resources/qml/Dialogs/OpenFilesIncludingProjectsDialog.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Ultimaker B.V. +// Copyright (c) 2021 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -33,9 +33,6 @@ UM.Dialog function loadProjectFile(projectFile) { UM.WorkspaceFileHandler.readLocalFile(projectFile); - - var meshName = backgroundItem.getMeshName(projectFile.toString()); - backgroundItem.hasMesh(decodeURIComponent(meshName)); } function loadModelFiles(fileUrls) @@ -44,9 +41,6 @@ UM.Dialog { CuraApplication.readLocalFile(fileUrls[i], "open_as_model"); } - - var meshName = backgroundItem.getMeshName(fileUrls[0].toString()); - backgroundItem.hasMesh(decodeURIComponent(meshName)); } Column @@ -108,5 +102,11 @@ UM.Dialog } } } + + UM.I18nCatalog + { + id: catalog + name: "cura" + } } -} +} \ No newline at end of file diff --git a/resources/qml/Menus/RecentFilesMenu.qml b/resources/qml/Menus/RecentFilesMenu.qml index 9de523280c..de6d2e3817 100644 --- a/resources/qml/Menus/RecentFilesMenu.qml +++ b/resources/qml/Menus/RecentFilesMenu.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Ultimaker B.V. +// Copyright (c) 2021 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -27,13 +27,7 @@ Menu var path = decodeURIComponent(modelData.toString()) return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1); } - onTriggered: - { - CuraApplication.readLocalFile(modelData); - - var meshName = backgroundItem.getMeshName(modelData.toString()) - backgroundItem.hasMesh(decodeURIComponent(meshName)) - } + onTriggered: CuraApplication.readLocalFile(modelData) } onObjectAdded: menu.insertItem(index, object) onObjectRemoved: menu.removeItem(object) From 00de7497a4c2986cf8fd13be8f598a0f615f3d63 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jan 2021 15:48:51 +0100 Subject: [PATCH 50/87] Move open dialogue to separate file provider plug-in We can now define plug-ins that specify where to open files from. This is one of the places where you can open files. This breaks the main button to open files in the interface. It needs to be redirected to trigger the plug-in to show the open file dialogue. Contributest o issue CURA-7868. --- .../LocalFileProvider/LocalFileProvider.py | 47 +++++ plugins/LocalFileProvider/OpenLocalFile.qml | 171 ++++++++++++++++++ plugins/LocalFileProvider/__init__.py | 11 ++ plugins/LocalFileProvider/plugin.json | 8 + resources/qml/Cura.qml | 147 --------------- 5 files changed, 237 insertions(+), 147 deletions(-) create mode 100644 plugins/LocalFileProvider/LocalFileProvider.py create mode 100644 plugins/LocalFileProvider/OpenLocalFile.qml create mode 100644 plugins/LocalFileProvider/__init__.py create mode 100644 plugins/LocalFileProvider/plugin.json diff --git a/plugins/LocalFileProvider/LocalFileProvider.py b/plugins/LocalFileProvider/LocalFileProvider.py new file mode 100644 index 0000000000..7917b649c2 --- /dev/null +++ b/plugins/LocalFileProvider/LocalFileProvider.py @@ -0,0 +1,47 @@ +# Copyright (c) 2021 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import os.path + +from UM.FileProvider import FileProvider # The plug-in type we're going to implement. +from UM.i18n import i18nCatalog +from UM.Logger import Logger +from UM.PluginRegistry import PluginRegistry # To get resources from the plug-in folder. +from cura.CuraApplication import CuraApplication # To create QML elements. + +i18n_catalog = i18nCatalog("cura") + + +class LocalFileProvider(FileProvider): + """ + Allows the user to open files from their local file system. + + These files will then be interpreted through the file handlers. + """ + + def __init__(self): + super().__init__() + self.menu_item_display_text = i18n_catalog.i18nc("@menu Open files from local disk", "Local disk") + self.shortcut = "Ctrl+O" + + self._dialog = None # Lazy-load this QML element. + + def _load_file_dialog(self): + """ + Loads the file dialog QML element into the QML context so that it can be shown. + :return: + """ + plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) + if plugin_path is None: + plugin_path = os.path.dirname(__file__) + path = os.path.join(plugin_path, "OpenLocalFile.qml") + self._dialog = CuraApplication.getInstance().createQmlComponent(path) + if self._dialog is None: + Logger.log("e", "Unable to create open file dialogue.") + + def run(self): + if self._dialog is None: + self._load_file_dialog() + if self._dialog is None: + return # Will already have logged an error in _load_file_dialog. + self._dialog.show() \ No newline at end of file diff --git a/plugins/LocalFileProvider/OpenLocalFile.qml b/plugins/LocalFileProvider/OpenLocalFile.qml new file mode 100644 index 0000000000..f78fe4b991 --- /dev/null +++ b/plugins/LocalFileProvider/OpenLocalFile.qml @@ -0,0 +1,171 @@ +// Copyright (c) 2021 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Dialogs 1.2 + +import UM 1.3 as UM +import Cura 1.1 as Cura + +Item +{ + id: base + + function show() + { + openDialog.visible = true; + } + + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + FileDialog + { + id: openDialog; + + //: File open dialog title + title: catalog.i18nc("@title:window","Open file(s)") + modality: Qt.WindowModal + selectMultiple: true + nameFilters: UM.MeshFileHandler.supportedReadFileTypes; + folder: + { + //Because several implementations of the file dialog only update the folder when it is explicitly set. + folder = CuraApplication.getDefaultPath("dialog_load_path"); + return CuraApplication.getDefaultPath("dialog_load_path"); + } + onAccepted: + { + // Because several implementations of the file dialog only update the folder + // when it is explicitly set. + var f = folder; + folder = f; + + CuraApplication.setDefaultPath("dialog_load_path", folder); + + handleOpenFileUrls(fileUrls); + } + + // Yeah... I know... it is a mess to put all those things here. + // There are lots of user interactions in this part of the logic, such as showing a warning dialog here and there, + // etc. This means it will come back and forth from time to time between QML and Python. So, separating the logic + // and view here may require more effort but make things more difficult to understand. + function handleOpenFileUrls(fileUrlList) + { + // look for valid project files + var projectFileUrlList = []; + var hasGcode = false; + var nonGcodeFileList = []; + for (var i in fileUrlList) + { + var endsWithG = /\.g$/; + var endsWithGcode = /\.gcode$/; + if (endsWithG.test(fileUrlList[i]) || endsWithGcode.test(fileUrlList[i])) + { + continue; + } + else if (CuraApplication.checkIsValidProjectFile(fileUrlList[i])) + { + projectFileUrlList.push(fileUrlList[i]); + } + nonGcodeFileList.push(fileUrlList[i]); + } + hasGcode = nonGcodeFileList.length < fileUrlList.length; + + // show a warning if selected multiple files together with Gcode + var hasProjectFile = projectFileUrlList.length > 0; + var selectedMultipleFiles = fileUrlList.length > 1; + if (selectedMultipleFiles && hasGcode) + { + infoMultipleFilesWithGcodeDialog.selectedMultipleFiles = selectedMultipleFiles; + infoMultipleFilesWithGcodeDialog.hasProjectFile = hasProjectFile; + infoMultipleFilesWithGcodeDialog.fileUrls = nonGcodeFileList.slice(); + infoMultipleFilesWithGcodeDialog.projectFileUrlList = projectFileUrlList.slice(); + infoMultipleFilesWithGcodeDialog.open(); + } + else + { + handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList); + } + } + + function handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList) + { + // we only allow opening one project file + if (selectedMultipleFiles && hasProjectFile) + { + openFilesIncludingProjectsDialog.fileUrls = fileUrlList.slice(); + openFilesIncludingProjectsDialog.show(); + return; + } + + if (hasProjectFile) + { + var projectFile = projectFileUrlList[0]; + + // check preference + var choice = UM.Preferences.getValue("cura/choice_on_open_project"); + if (choice == "open_as_project") + { + openFilesIncludingProjectsDialog.loadProjectFile(projectFile); + } + else if (choice == "open_as_model") + { + openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice()); + } + else // always ask + { + // ask whether to open as project or as models + askOpenAsProjectOrModelsDialog.fileUrl = projectFile; + askOpenAsProjectOrModelsDialog.show(); + } + } + else + { + openFilesIncludingProjectsDialog.loadModelFiles(fileUrlList.slice()); + } + } + } + + MessageDialog + { + id: infoMultipleFilesWithGcodeDialog + title: catalog.i18nc("@title:window", "Open File(s)") + icon: StandardIcon.Information + standardButtons: StandardButton.Ok + text: catalog.i18nc("@text:window", "We have found one or more G-Code files within the files you have selected. You can only open one G-Code file at a time. If you want to open a G-Code file, please just select only one.") + + property var selectedMultipleFiles + property var hasProjectFile + property var fileUrls + property var projectFileUrlList + + onAccepted: + { + openDialog.handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList); + } + } + + Cura.AskOpenAsProjectOrModelsDialog + { + id: askOpenAsProjectOrModelsDialog + } + + Connections + { + target: CuraApplication + onOpenProjectFile: + { + askOpenAsProjectOrModelsDialog.fileUrl = project_file; + askOpenAsProjectOrModelsDialog.show(); + } + } + + Cura.OpenFilesIncludingProjectsDialog + { + id: openFilesIncludingProjectsDialog + } +} \ No newline at end of file diff --git a/plugins/LocalFileProvider/__init__.py b/plugins/LocalFileProvider/__init__.py new file mode 100644 index 0000000000..63845d7820 --- /dev/null +++ b/plugins/LocalFileProvider/__init__.py @@ -0,0 +1,11 @@ +# Copyright (c) 2021 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from . import LocalFileProvider + + +def getMetaData(): + return {} + +def register(app): + return { "file_provider": LocalFileProvider.LocalFileProvider() } diff --git a/plugins/LocalFileProvider/plugin.json b/plugins/LocalFileProvider/plugin.json new file mode 100644 index 0000000000..cd4d77eb98 --- /dev/null +++ b/plugins/LocalFileProvider/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Local File Provider", + "description": "Enables opening files from the local file system.", + "author": "Ultimaker B.V.", + "version": "1.0.0", + "api": "7.4.0", + "i18n-catalog": "cura" +} \ No newline at end of file diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index bb7b5ac19c..ea615b3c17 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -619,114 +619,6 @@ UM.MainWindow onTriggered: base.exitFullscreen() } - FileDialog - { - id: openDialog; - - //: File open dialog title - title: catalog.i18nc("@title:window","Open file(s)") - modality: Qt.WindowModal - selectMultiple: true - nameFilters: UM.MeshFileHandler.supportedReadFileTypes; - folder: - { - //Because several implementations of the file dialog only update the folder when it is explicitly set. - folder = CuraApplication.getDefaultPath("dialog_load_path"); - return CuraApplication.getDefaultPath("dialog_load_path"); - } - onAccepted: - { - // Because several implementations of the file dialog only update the folder - // when it is explicitly set. - var f = folder; - folder = f; - - CuraApplication.setDefaultPath("dialog_load_path", folder); - - handleOpenFileUrls(fileUrls); - } - - // Yeah... I know... it is a mess to put all those things here. - // There are lots of user interactions in this part of the logic, such as showing a warning dialog here and there, - // etc. This means it will come back and forth from time to time between QML and Python. So, separating the logic - // and view here may require more effort but make things more difficult to understand. - function handleOpenFileUrls(fileUrlList) - { - // look for valid project files - var projectFileUrlList = []; - var hasGcode = false; - var nonGcodeFileList = []; - for (var i in fileUrlList) - { - var endsWithG = /\.g$/; - var endsWithGcode = /\.gcode$/; - if (endsWithG.test(fileUrlList[i]) || endsWithGcode.test(fileUrlList[i])) - { - continue; - } - else if (CuraApplication.checkIsValidProjectFile(fileUrlList[i])) - { - projectFileUrlList.push(fileUrlList[i]); - } - nonGcodeFileList.push(fileUrlList[i]); - } - hasGcode = nonGcodeFileList.length < fileUrlList.length; - - // show a warning if selected multiple files together with Gcode - var hasProjectFile = projectFileUrlList.length > 0; - var selectedMultipleFiles = fileUrlList.length > 1; - if (selectedMultipleFiles && hasGcode) - { - infoMultipleFilesWithGcodeDialog.selectedMultipleFiles = selectedMultipleFiles; - infoMultipleFilesWithGcodeDialog.hasProjectFile = hasProjectFile; - infoMultipleFilesWithGcodeDialog.fileUrls = nonGcodeFileList.slice(); - infoMultipleFilesWithGcodeDialog.projectFileUrlList = projectFileUrlList.slice(); - infoMultipleFilesWithGcodeDialog.open(); - } - else - { - handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList); - } - } - - function handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList) - { - // we only allow opening one project file - if (selectedMultipleFiles && hasProjectFile) - { - openFilesIncludingProjectsDialog.fileUrls = fileUrlList.slice(); - openFilesIncludingProjectsDialog.show(); - return; - } - - if (hasProjectFile) - { - var projectFile = projectFileUrlList[0]; - - // check preference - var choice = UM.Preferences.getValue("cura/choice_on_open_project"); - if (choice == "open_as_project") - { - openFilesIncludingProjectsDialog.loadProjectFile(projectFile); - } - else if (choice == "open_as_model") - { - openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice()); - } - else // always ask - { - // ask whether to open as project or as models - askOpenAsProjectOrModelsDialog.fileUrl = projectFile; - askOpenAsProjectOrModelsDialog.show(); - } - } - else - { - openFilesIncludingProjectsDialog.loadModelFiles(fileUrlList.slice()); - } - } - } - MessageDialog { id: packageInstallDialog @@ -735,51 +627,12 @@ UM.MainWindow modality: Qt.ApplicationModal } - MessageDialog - { - id: infoMultipleFilesWithGcodeDialog - title: catalog.i18nc("@title:window", "Open File(s)") - icon: StandardIcon.Information - standardButtons: StandardButton.Ok - text: catalog.i18nc("@text:window", "We have found one or more G-Code files within the files you have selected. You can only open one G-Code file at a time. If you want to open a G-Code file, please just select only one.") - - property var selectedMultipleFiles - property var hasProjectFile - property var fileUrls - property var projectFileUrlList - - onAccepted: - { - openDialog.handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList); - } - } - Connections { target: Cura.Actions.open onTriggered: openDialog.open() } - OpenFilesIncludingProjectsDialog - { - id: openFilesIncludingProjectsDialog - } - - AskOpenAsProjectOrModelsDialog - { - id: askOpenAsProjectOrModelsDialog - } - - Connections - { - target: CuraApplication - onOpenProjectFile: - { - askOpenAsProjectOrModelsDialog.fileUrl = project_file; - askOpenAsProjectOrModelsDialog.show(); - } - } - Connections { target: Cura.Actions.showProfileFolder From de80461954fc91d32c95e1a56ef88f5c7f69796f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jan 2021 15:54:18 +0100 Subject: [PATCH 51/87] Remove open action, fixing ambiguous overload of Ctrl+O The action was no longer in the menu, but the hotkey still functioned. Then there were two actions for Ctrl+O, which was ambiguous to Qt. Contributes to issue CURA-7868. --- resources/qml/Actions.qml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index c62b0cb89a..5ad5337e78 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2018 Ultimaker B.V. +// Copyright (c) 2021 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. pragma Singleton @@ -12,7 +12,6 @@ import Cura 1.0 as Cura Item { property alias newProject: newProjectAction; - property alias open: openAction; property alias quit: quitAction; property alias undo: undoAction; @@ -413,14 +412,6 @@ Item onTriggered: CuraApplication.resetAll(); } - Action - { - id: openAction; - text: catalog.i18nc("@action:inmenu menubar:file","&Open File(s)..."); - iconName: "document-open"; - shortcut: StandardKey.Open; - } - Action { id: newProjectAction From b266904d76542216b5bdbdf6c2b628b784bd7d8f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jan 2021 17:45:16 +0100 Subject: [PATCH 52/87] Replace other references to the open action with the file provider model This adds a function 'triggerFirst' to the file provider that triggers the first file provider in the model. That should then be the local file provider, but if the plug-in is disabled for some reason it would use another plug-in. Contributes to issue CURA-7868. --- plugins/PrepareStage/PrepareMenu.qml | 4 ++-- resources/qml/Cura.qml | 6 ------ resources/qml/Menus/OpenFilesMenu.qml | 19 +++---------------- 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 87d7c5f35c..d1bb47cb35 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2018 Ultimaker B.V. +// Copyright (c) 2021 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 @@ -96,7 +96,7 @@ Item id: openFileButton height: UM.Theme.getSize("stage_menu").height width: UM.Theme.getSize("stage_menu").height - onClicked: Cura.Actions.open.trigger() + onClicked: CuraApplication.getFileProviderModel().triggerFirst() hoverEnabled: true contentItem: Item diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index ea615b3c17..1a42f693a9 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -627,12 +627,6 @@ UM.MainWindow modality: Qt.ApplicationModal } - Connections - { - target: Cura.Actions.open - onTriggered: openDialog.open() - } - Connections { target: Cura.Actions.showProfileFolder diff --git a/resources/qml/Menus/OpenFilesMenu.qml b/resources/qml/Menus/OpenFilesMenu.qml index 60fb507b34..20d6fe453c 100644 --- a/resources/qml/Menus/OpenFilesMenu.qml +++ b/resources/qml/Menus/OpenFilesMenu.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Ultimaker B.V. +// Copyright (c) 2021 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -21,21 +21,8 @@ Menu model: CuraApplication.getFileProviderModel() MenuItem { - text: - { - return model.displayText; - } - onTriggered: - { - if (model.index == 0) // The 0th element is the "From Disk" option, which should activate the open local file dialog - { - Cura.Actions.open.trigger() - } - else - { - CuraApplication.getFileProviderModel().trigger(model.name); - } - } + text: model.displayText + onTriggered: CuraApplication.getFileProviderModel().trigger(model.name) shortcut: model.shortcut } onObjectAdded: openFilesMenu.insertItem(index, object) From 5f6b3b52c1848416c98f7d276fe02f08d067f675 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jan 2021 18:10:50 +0100 Subject: [PATCH 53/87] Don't trigger open menu upon starting Cura That would be very annoying. Contributes to issue CURA-7868. --- resources/qml/Menus/FileMenu.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/FileMenu.qml b/resources/qml/Menus/FileMenu.qml index 94fc2358e1..918b96c727 100644 --- a/resources/qml/Menus/FileMenu.qml +++ b/resources/qml/Menus/FileMenu.qml @@ -22,7 +22,7 @@ Menu MenuItem { id: openMenu - action: Cura.Actions.open + onTriggered: CuraApplication.getFileProviderModel().triggerFirst() visible: (base.fileProviderModel.count == 1) } From dc02038513090941be5d665a8013e6ded8f2103a Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Tue, 5 Jan 2021 14:47:46 +0100 Subject: [PATCH 54/87] Add a Cura-themed, re-usable TableView component CURA-7868 --- resources/qml/TableView.qml | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 resources/qml/TableView.qml diff --git a/resources/qml/TableView.qml b/resources/qml/TableView.qml new file mode 100644 index 0000000000..4d9ca75f92 --- /dev/null +++ b/resources/qml/TableView.qml @@ -0,0 +1,72 @@ +// Copyright (C) 2021 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Window 2.2 +import QtQuick.Controls 1.4 as OldControls // TableView doesn't exist in the QtQuick Controls 2.x in 5.10, so use the old one +import QtQuick.Controls 2.3 +import QtQuick.Controls.Styles 1.4 + +import UM 1.2 as UM +import Cura 1.6 as Cura + + +OldControls.TableView +{ + id: tableView + + itemDelegate: Item + { + height: tableCellLabel.implicitHeight + UM.Theme.getSize("thin_margin").height + + Label + { + id: tableCellLabel + color: UM.Theme.getColor("text") + elide: Text.ElideRight + text: styleData.value + anchors.fill: parent + anchors.leftMargin: 10 * screenScaleFactor + verticalAlignment: Text.AlignVCenter + } + } + + rowDelegate: Rectangle + { + color: styleData.selected ? UM.Theme.getColor("toolbar_button_hover") : UM.Theme.getColor("main_background") + } + + // Use the old styling technique since it's the only way to make the scrollbars themed in the TableView + style: TableViewStyle + { + backgroundColor: UM.Theme.getColor("main_background") + + handle: Rectangle + { + // both implicit width and height have to be set, since the handle is used by both the horizontal and the vertical scrollbars + implicitWidth: UM.Theme.getSize("scrollbar").width + implicitHeight: UM.Theme.getSize("scrollbar").width // + radius: width / 2 + color: UM.Theme.getColor(styleData.pressed ? "scrollbar_handle_down" : styleData.hovered ? "scrollbar_handle_hover" : "scrollbar_handle") + } + + scrollBarBackground: Rectangle + { + // both implicit width and height have to be set, since the handle is used by both the horizontal and the vertical scrollbars + implicitWidth: UM.Theme.getSize("scrollbar").width + implicitHeight: UM.Theme.getSize("scrollbar").width + color: UM.Theme.getColor("main_background") + } + + corner: Rectangle // The little rectangle between the vertical and horizontal scrollbars + { + color: UM.Theme.getColor("main_background") + } + + // Override the control arrows + incrementControl: Item { } + decrementControl: Item { } + } +} + + From adcdf7bad9b202c6bad8130615cd7b07bc8db21f Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Tue, 5 Jan 2021 14:54:28 +0100 Subject: [PATCH 55/87] Remove unnecessary QML imports CURA-7868 --- resources/qml/TableView.qml | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/resources/qml/TableView.qml b/resources/qml/TableView.qml index 4d9ca75f92..b2ebee51c9 100644 --- a/resources/qml/TableView.qml +++ b/resources/qml/TableView.qml @@ -2,19 +2,15 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.10 -import QtQuick.Window 2.2 import QtQuick.Controls 1.4 as OldControls // TableView doesn't exist in the QtQuick Controls 2.x in 5.10, so use the old one import QtQuick.Controls 2.3 import QtQuick.Controls.Styles 1.4 import UM 1.2 as UM -import Cura 1.6 as Cura OldControls.TableView { - id: tableView - itemDelegate: Item { height: tableCellLabel.implicitHeight + UM.Theme.getSize("thin_margin").height @@ -43,22 +39,23 @@ OldControls.TableView handle: Rectangle { - // both implicit width and height have to be set, since the handle is used by both the horizontal and the vertical scrollbars + // Both implicit width and height have to be set, since the handle is used by both the horizontal and the vertical scrollbars implicitWidth: UM.Theme.getSize("scrollbar").width - implicitHeight: UM.Theme.getSize("scrollbar").width // + implicitHeight: UM.Theme.getSize("scrollbar").width radius: width / 2 - color: UM.Theme.getColor(styleData.pressed ? "scrollbar_handle_down" : styleData.hovered ? "scrollbar_handle_hover" : "scrollbar_handle") + color: UM.Theme.getColor(styleData.pressed ? "scrollbar_handle_down" : (styleData.hovered ? "scrollbar_handle_hover" : "scrollbar_handle")) } scrollBarBackground: Rectangle { - // both implicit width and height have to be set, since the handle is used by both the horizontal and the vertical scrollbars + // Both implicit width and height have to be set, since the handle is used by both the horizontal and the vertical scrollbars implicitWidth: UM.Theme.getSize("scrollbar").width implicitHeight: UM.Theme.getSize("scrollbar").width color: UM.Theme.getColor("main_background") } - corner: Rectangle // The little rectangle between the vertical and horizontal scrollbars + // The little rectangle between the vertical and horizontal scrollbars + corner: Rectangle { color: UM.Theme.getColor("main_background") } @@ -67,6 +64,4 @@ OldControls.TableView incrementControl: Item { } decrementControl: Item { } } -} - - +} \ No newline at end of file From 606ec587fe627945108d93dd23b1e39417d51904 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Wed, 6 Jan 2021 11:51:29 +0100 Subject: [PATCH 56/87] Change the row height in the table view CURA-7868 --- resources/qml/TableView.qml | 5 +++-- resources/themes/cura-light/theme.json | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/qml/TableView.qml b/resources/qml/TableView.qml index b2ebee51c9..dd80304b83 100644 --- a/resources/qml/TableView.qml +++ b/resources/qml/TableView.qml @@ -13,7 +13,7 @@ OldControls.TableView { itemDelegate: Item { - height: tableCellLabel.implicitHeight + UM.Theme.getSize("thin_margin").height + height: tableCellLabel.implicitHeight Label { @@ -29,7 +29,8 @@ OldControls.TableView rowDelegate: Rectangle { - color: styleData.selected ? UM.Theme.getColor("toolbar_button_hover") : UM.Theme.getColor("main_background") + color: styleData.selected ? UM.Theme.getColor("secondary") : UM.Theme.getColor("main_background") + height: UM.Theme.getSize("table_row").height } // Use the old styling technique since it's the only way to make the scrollbars themed in the TableView diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 7bb8156458..e897969a5e 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -630,6 +630,8 @@ "monitor_external_link_icon": [1.16, 1.16], "monitor_column": [18.0, 1.0], "monitor_progress_bar": [16.5, 1.0], - "monitor_margin": [1.5, 1.5] + "monitor_margin": [1.5, 1.5], + + "table_row": [2.0, 2.0] } } From 0788df4797cfc85c26399de773a57f3a0f1231f5 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 6 Jan 2021 14:11:15 +0100 Subject: [PATCH 57/87] Add log entry upon exporting container Quite a salient moment in the log. According to our log levels definitions this must be info since it's the direct result of a user interaction. --- cura/Settings/ContainerManager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 08fdf707cf..48d4cb3cbc 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -221,6 +221,7 @@ class ContainerManager(QObject): except OSError: return {"status": "error", "message": "Unable to write to this location.", "path": file_url} + Logger.info("Successfully exported container to {path}".format(path = file_url)) return {"status": "success", "message": "Successfully exported container", "path": file_url} @pyqtSlot(QUrl, result = "QVariantMap") From 96c4d66029011df9f70ac54ba023d3be53ea67d2 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Thu, 7 Jan 2021 16:35:40 +0100 Subject: [PATCH 58/87] Revert making the open file dialog a separate LocalFileProvider plugin This reverts commits 00de7497a4c2986cf8fd13be8f598a0f615f3d63 to 5f6b3b52c1848416c98f7d276fe02f08d067f675 CURA-7868 --- .../LocalFileProvider/LocalFileProvider.py | 47 ----- plugins/LocalFileProvider/OpenLocalFile.qml | 171 ------------------ plugins/LocalFileProvider/__init__.py | 11 -- plugins/LocalFileProvider/plugin.json | 8 - plugins/PrepareStage/PrepareMenu.qml | 4 +- resources/qml/Actions.qml | 11 +- resources/qml/Cura.qml | 153 ++++++++++++++++ resources/qml/Menus/FileMenu.qml | 2 +- resources/qml/Menus/OpenFilesMenu.qml | 19 +- 9 files changed, 182 insertions(+), 244 deletions(-) delete mode 100644 plugins/LocalFileProvider/LocalFileProvider.py delete mode 100644 plugins/LocalFileProvider/OpenLocalFile.qml delete mode 100644 plugins/LocalFileProvider/__init__.py delete mode 100644 plugins/LocalFileProvider/plugin.json diff --git a/plugins/LocalFileProvider/LocalFileProvider.py b/plugins/LocalFileProvider/LocalFileProvider.py deleted file mode 100644 index 7917b649c2..0000000000 --- a/plugins/LocalFileProvider/LocalFileProvider.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2021 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -import os.path - -from UM.FileProvider import FileProvider # The plug-in type we're going to implement. -from UM.i18n import i18nCatalog -from UM.Logger import Logger -from UM.PluginRegistry import PluginRegistry # To get resources from the plug-in folder. -from cura.CuraApplication import CuraApplication # To create QML elements. - -i18n_catalog = i18nCatalog("cura") - - -class LocalFileProvider(FileProvider): - """ - Allows the user to open files from their local file system. - - These files will then be interpreted through the file handlers. - """ - - def __init__(self): - super().__init__() - self.menu_item_display_text = i18n_catalog.i18nc("@menu Open files from local disk", "Local disk") - self.shortcut = "Ctrl+O" - - self._dialog = None # Lazy-load this QML element. - - def _load_file_dialog(self): - """ - Loads the file dialog QML element into the QML context so that it can be shown. - :return: - """ - plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) - if plugin_path is None: - plugin_path = os.path.dirname(__file__) - path = os.path.join(plugin_path, "OpenLocalFile.qml") - self._dialog = CuraApplication.getInstance().createQmlComponent(path) - if self._dialog is None: - Logger.log("e", "Unable to create open file dialogue.") - - def run(self): - if self._dialog is None: - self._load_file_dialog() - if self._dialog is None: - return # Will already have logged an error in _load_file_dialog. - self._dialog.show() \ No newline at end of file diff --git a/plugins/LocalFileProvider/OpenLocalFile.qml b/plugins/LocalFileProvider/OpenLocalFile.qml deleted file mode 100644 index f78fe4b991..0000000000 --- a/plugins/LocalFileProvider/OpenLocalFile.qml +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2021 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Dialogs 1.2 - -import UM 1.3 as UM -import Cura 1.1 as Cura - -Item -{ - id: base - - function show() - { - openDialog.visible = true; - } - - UM.I18nCatalog - { - id: catalog - name: "cura" - } - - FileDialog - { - id: openDialog; - - //: File open dialog title - title: catalog.i18nc("@title:window","Open file(s)") - modality: Qt.WindowModal - selectMultiple: true - nameFilters: UM.MeshFileHandler.supportedReadFileTypes; - folder: - { - //Because several implementations of the file dialog only update the folder when it is explicitly set. - folder = CuraApplication.getDefaultPath("dialog_load_path"); - return CuraApplication.getDefaultPath("dialog_load_path"); - } - onAccepted: - { - // Because several implementations of the file dialog only update the folder - // when it is explicitly set. - var f = folder; - folder = f; - - CuraApplication.setDefaultPath("dialog_load_path", folder); - - handleOpenFileUrls(fileUrls); - } - - // Yeah... I know... it is a mess to put all those things here. - // There are lots of user interactions in this part of the logic, such as showing a warning dialog here and there, - // etc. This means it will come back and forth from time to time between QML and Python. So, separating the logic - // and view here may require more effort but make things more difficult to understand. - function handleOpenFileUrls(fileUrlList) - { - // look for valid project files - var projectFileUrlList = []; - var hasGcode = false; - var nonGcodeFileList = []; - for (var i in fileUrlList) - { - var endsWithG = /\.g$/; - var endsWithGcode = /\.gcode$/; - if (endsWithG.test(fileUrlList[i]) || endsWithGcode.test(fileUrlList[i])) - { - continue; - } - else if (CuraApplication.checkIsValidProjectFile(fileUrlList[i])) - { - projectFileUrlList.push(fileUrlList[i]); - } - nonGcodeFileList.push(fileUrlList[i]); - } - hasGcode = nonGcodeFileList.length < fileUrlList.length; - - // show a warning if selected multiple files together with Gcode - var hasProjectFile = projectFileUrlList.length > 0; - var selectedMultipleFiles = fileUrlList.length > 1; - if (selectedMultipleFiles && hasGcode) - { - infoMultipleFilesWithGcodeDialog.selectedMultipleFiles = selectedMultipleFiles; - infoMultipleFilesWithGcodeDialog.hasProjectFile = hasProjectFile; - infoMultipleFilesWithGcodeDialog.fileUrls = nonGcodeFileList.slice(); - infoMultipleFilesWithGcodeDialog.projectFileUrlList = projectFileUrlList.slice(); - infoMultipleFilesWithGcodeDialog.open(); - } - else - { - handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList); - } - } - - function handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList) - { - // we only allow opening one project file - if (selectedMultipleFiles && hasProjectFile) - { - openFilesIncludingProjectsDialog.fileUrls = fileUrlList.slice(); - openFilesIncludingProjectsDialog.show(); - return; - } - - if (hasProjectFile) - { - var projectFile = projectFileUrlList[0]; - - // check preference - var choice = UM.Preferences.getValue("cura/choice_on_open_project"); - if (choice == "open_as_project") - { - openFilesIncludingProjectsDialog.loadProjectFile(projectFile); - } - else if (choice == "open_as_model") - { - openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice()); - } - else // always ask - { - // ask whether to open as project or as models - askOpenAsProjectOrModelsDialog.fileUrl = projectFile; - askOpenAsProjectOrModelsDialog.show(); - } - } - else - { - openFilesIncludingProjectsDialog.loadModelFiles(fileUrlList.slice()); - } - } - } - - MessageDialog - { - id: infoMultipleFilesWithGcodeDialog - title: catalog.i18nc("@title:window", "Open File(s)") - icon: StandardIcon.Information - standardButtons: StandardButton.Ok - text: catalog.i18nc("@text:window", "We have found one or more G-Code files within the files you have selected. You can only open one G-Code file at a time. If you want to open a G-Code file, please just select only one.") - - property var selectedMultipleFiles - property var hasProjectFile - property var fileUrls - property var projectFileUrlList - - onAccepted: - { - openDialog.handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList); - } - } - - Cura.AskOpenAsProjectOrModelsDialog - { - id: askOpenAsProjectOrModelsDialog - } - - Connections - { - target: CuraApplication - onOpenProjectFile: - { - askOpenAsProjectOrModelsDialog.fileUrl = project_file; - askOpenAsProjectOrModelsDialog.show(); - } - } - - Cura.OpenFilesIncludingProjectsDialog - { - id: openFilesIncludingProjectsDialog - } -} \ No newline at end of file diff --git a/plugins/LocalFileProvider/__init__.py b/plugins/LocalFileProvider/__init__.py deleted file mode 100644 index 63845d7820..0000000000 --- a/plugins/LocalFileProvider/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) 2021 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -from . import LocalFileProvider - - -def getMetaData(): - return {} - -def register(app): - return { "file_provider": LocalFileProvider.LocalFileProvider() } diff --git a/plugins/LocalFileProvider/plugin.json b/plugins/LocalFileProvider/plugin.json deleted file mode 100644 index cd4d77eb98..0000000000 --- a/plugins/LocalFileProvider/plugin.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "Local File Provider", - "description": "Enables opening files from the local file system.", - "author": "Ultimaker B.V.", - "version": "1.0.0", - "api": "7.4.0", - "i18n-catalog": "cura" -} \ No newline at end of file diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index d1bb47cb35..87d7c5f35c 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Ultimaker B.V. +// Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 @@ -96,7 +96,7 @@ Item id: openFileButton height: UM.Theme.getSize("stage_menu").height width: UM.Theme.getSize("stage_menu").height - onClicked: CuraApplication.getFileProviderModel().triggerFirst() + onClicked: Cura.Actions.open.trigger() hoverEnabled: true contentItem: Item diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 5ad5337e78..c62b0cb89a 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Ultimaker B.V. +// Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. pragma Singleton @@ -12,6 +12,7 @@ import Cura 1.0 as Cura Item { property alias newProject: newProjectAction; + property alias open: openAction; property alias quit: quitAction; property alias undo: undoAction; @@ -412,6 +413,14 @@ Item onTriggered: CuraApplication.resetAll(); } + Action + { + id: openAction; + text: catalog.i18nc("@action:inmenu menubar:file","&Open File(s)..."); + iconName: "document-open"; + shortcut: StandardKey.Open; + } + Action { id: newProjectAction diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 1a42f693a9..bb7b5ac19c 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -619,6 +619,114 @@ UM.MainWindow onTriggered: base.exitFullscreen() } + FileDialog + { + id: openDialog; + + //: File open dialog title + title: catalog.i18nc("@title:window","Open file(s)") + modality: Qt.WindowModal + selectMultiple: true + nameFilters: UM.MeshFileHandler.supportedReadFileTypes; + folder: + { + //Because several implementations of the file dialog only update the folder when it is explicitly set. + folder = CuraApplication.getDefaultPath("dialog_load_path"); + return CuraApplication.getDefaultPath("dialog_load_path"); + } + onAccepted: + { + // Because several implementations of the file dialog only update the folder + // when it is explicitly set. + var f = folder; + folder = f; + + CuraApplication.setDefaultPath("dialog_load_path", folder); + + handleOpenFileUrls(fileUrls); + } + + // Yeah... I know... it is a mess to put all those things here. + // There are lots of user interactions in this part of the logic, such as showing a warning dialog here and there, + // etc. This means it will come back and forth from time to time between QML and Python. So, separating the logic + // and view here may require more effort but make things more difficult to understand. + function handleOpenFileUrls(fileUrlList) + { + // look for valid project files + var projectFileUrlList = []; + var hasGcode = false; + var nonGcodeFileList = []; + for (var i in fileUrlList) + { + var endsWithG = /\.g$/; + var endsWithGcode = /\.gcode$/; + if (endsWithG.test(fileUrlList[i]) || endsWithGcode.test(fileUrlList[i])) + { + continue; + } + else if (CuraApplication.checkIsValidProjectFile(fileUrlList[i])) + { + projectFileUrlList.push(fileUrlList[i]); + } + nonGcodeFileList.push(fileUrlList[i]); + } + hasGcode = nonGcodeFileList.length < fileUrlList.length; + + // show a warning if selected multiple files together with Gcode + var hasProjectFile = projectFileUrlList.length > 0; + var selectedMultipleFiles = fileUrlList.length > 1; + if (selectedMultipleFiles && hasGcode) + { + infoMultipleFilesWithGcodeDialog.selectedMultipleFiles = selectedMultipleFiles; + infoMultipleFilesWithGcodeDialog.hasProjectFile = hasProjectFile; + infoMultipleFilesWithGcodeDialog.fileUrls = nonGcodeFileList.slice(); + infoMultipleFilesWithGcodeDialog.projectFileUrlList = projectFileUrlList.slice(); + infoMultipleFilesWithGcodeDialog.open(); + } + else + { + handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList); + } + } + + function handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList) + { + // we only allow opening one project file + if (selectedMultipleFiles && hasProjectFile) + { + openFilesIncludingProjectsDialog.fileUrls = fileUrlList.slice(); + openFilesIncludingProjectsDialog.show(); + return; + } + + if (hasProjectFile) + { + var projectFile = projectFileUrlList[0]; + + // check preference + var choice = UM.Preferences.getValue("cura/choice_on_open_project"); + if (choice == "open_as_project") + { + openFilesIncludingProjectsDialog.loadProjectFile(projectFile); + } + else if (choice == "open_as_model") + { + openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice()); + } + else // always ask + { + // ask whether to open as project or as models + askOpenAsProjectOrModelsDialog.fileUrl = projectFile; + askOpenAsProjectOrModelsDialog.show(); + } + } + else + { + openFilesIncludingProjectsDialog.loadModelFiles(fileUrlList.slice()); + } + } + } + MessageDialog { id: packageInstallDialog @@ -627,6 +735,51 @@ UM.MainWindow modality: Qt.ApplicationModal } + MessageDialog + { + id: infoMultipleFilesWithGcodeDialog + title: catalog.i18nc("@title:window", "Open File(s)") + icon: StandardIcon.Information + standardButtons: StandardButton.Ok + text: catalog.i18nc("@text:window", "We have found one or more G-Code files within the files you have selected. You can only open one G-Code file at a time. If you want to open a G-Code file, please just select only one.") + + property var selectedMultipleFiles + property var hasProjectFile + property var fileUrls + property var projectFileUrlList + + onAccepted: + { + openDialog.handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList); + } + } + + Connections + { + target: Cura.Actions.open + onTriggered: openDialog.open() + } + + OpenFilesIncludingProjectsDialog + { + id: openFilesIncludingProjectsDialog + } + + AskOpenAsProjectOrModelsDialog + { + id: askOpenAsProjectOrModelsDialog + } + + Connections + { + target: CuraApplication + onOpenProjectFile: + { + askOpenAsProjectOrModelsDialog.fileUrl = project_file; + askOpenAsProjectOrModelsDialog.show(); + } + } + Connections { target: Cura.Actions.showProfileFolder diff --git a/resources/qml/Menus/FileMenu.qml b/resources/qml/Menus/FileMenu.qml index 918b96c727..94fc2358e1 100644 --- a/resources/qml/Menus/FileMenu.qml +++ b/resources/qml/Menus/FileMenu.qml @@ -22,7 +22,7 @@ Menu MenuItem { id: openMenu - onTriggered: CuraApplication.getFileProviderModel().triggerFirst() + action: Cura.Actions.open visible: (base.fileProviderModel.count == 1) } diff --git a/resources/qml/Menus/OpenFilesMenu.qml b/resources/qml/Menus/OpenFilesMenu.qml index 20d6fe453c..60fb507b34 100644 --- a/resources/qml/Menus/OpenFilesMenu.qml +++ b/resources/qml/Menus/OpenFilesMenu.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Ultimaker B.V. +// Copyright (c) 2020 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -21,8 +21,21 @@ Menu model: CuraApplication.getFileProviderModel() MenuItem { - text: model.displayText - onTriggered: CuraApplication.getFileProviderModel().trigger(model.name) + text: + { + return model.displayText; + } + onTriggered: + { + if (model.index == 0) // The 0th element is the "From Disk" option, which should activate the open local file dialog + { + Cura.Actions.open.trigger() + } + else + { + CuraApplication.getFileProviderModel().trigger(model.name); + } + } shortcut: model.shortcut } onObjectAdded: openFilesMenu.insertItem(index, object) From aa0a08500e959f29ae02dc31e901646e139407a0 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 8 Jan 2021 13:44:23 +0100 Subject: [PATCH 59/87] Temproray files shouldn't be in recent file list. CURA-7864 --- cura/CuraApplication.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index ee347e7a4d..e1afb8a88f 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1735,7 +1735,7 @@ class CuraApplication(QtApplication): @pyqtSlot(QUrl, str) @pyqtSlot(QUrl) - def readLocalFile(self, file: QUrl, project_mode: Optional[str] = None): + def readLocalFile(self, file: QUrl, project_mode: Optional[str] = None, add_to_recent_files: str = True): """Open a local file :param project_mode: How to handle project files. Either None(default): Follow user preference, "open_as_model" @@ -1760,7 +1760,7 @@ class CuraApplication(QtApplication): if is_project_file and project_mode == "open_as_project": # open as project immediately without presenting a dialog workspace_handler = self.getWorkspaceFileHandler() - workspace_handler.readLocalFile(file) + workspace_handler.readLocalFile(file, add_to_recent_files = add_to_recent_files) return if is_project_file and project_mode == "always_ask": @@ -1801,7 +1801,7 @@ class CuraApplication(QtApplication): if extension in self._non_sliceable_extensions: self.deleteAll(only_selectable = False) - job = ReadMeshJob(f) + job = ReadMeshJob(f, add_to_recent_files = add_to_recent_files) job.finished.connect(self._readMeshFinished) job.start() From 13d45e3a55d72742ac220177248637820b288578 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 8 Jan 2021 13:51:03 +0100 Subject: [PATCH 60/87] Typing typo. CURA-7864 --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index e1afb8a88f..98e2e923d0 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1735,7 +1735,7 @@ class CuraApplication(QtApplication): @pyqtSlot(QUrl, str) @pyqtSlot(QUrl) - def readLocalFile(self, file: QUrl, project_mode: Optional[str] = None, add_to_recent_files: str = True): + def readLocalFile(self, file: QUrl, project_mode: Optional[str] = None, add_to_recent_files: bool = True): """Open a local file :param project_mode: How to handle project files. Either None(default): Follow user preference, "open_as_model" From 352fef3efa700b53f308a095c5faa85520d9a4c1 Mon Sep 17 00:00:00 2001 From: Philip Lorenz Date: Sun, 10 Jan 2021 20:21:40 +0100 Subject: [PATCH 61/87] Adapt to Python 3.9 API changes Python 3.9 now made the TreeBuilder.start() `attrs` parameter ([1]) mandatory on all implementations. Adapt the plugin accordingly. [1] https://bugs.python.org/issue39495 --- .../XmlMaterialProfile/XmlMaterialProfile.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 70e702d0bf..ce0bb06d8d 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -151,7 +151,7 @@ class XmlMaterialProfile(InstanceContainer): "version": self.CurrentFdmMaterialVersion}) ## Begin Metadata Block - builder.start("metadata") # type: ignore + builder.start("metadata", {}) # type: ignore metadata = copy.deepcopy(self.getMetaData()) # setting_version is derived from the "version" tag in the schema, so don't serialize it into a file @@ -165,21 +165,21 @@ class XmlMaterialProfile(InstanceContainer): properties = metadata.pop("properties", {}) ## Begin Name Block - builder.start("name") # type: ignore + builder.start("name", {}) # type: ignore - builder.start("brand") # type: ignore + builder.start("brand", {}) # type: ignore builder.data(metadata.pop("brand", "")) builder.end("brand") - builder.start("material") # type: ignore + builder.start("material", {}) # type: ignore builder.data(metadata.pop("material", "")) builder.end("material") - builder.start("color") # type: ignore + builder.start("color", {}) # type: ignore builder.data(metadata.pop("color_name", "")) builder.end("color") - builder.start("label") # type: ignore + builder.start("label", {}) # type: ignore builder.data(self.getName()) builder.end("label") @@ -190,7 +190,7 @@ class XmlMaterialProfile(InstanceContainer): key_to_use = key if key in self._metadata_tags_that_have_cura_namespace: key_to_use = "cura:" + key_to_use - builder.start(key_to_use) # type: ignore + builder.start(key_to_use, {}) # type: ignore if value is not None: #Nones get handled well by the builder. #Otherwise the builder always expects a string. #Deserialize expects the stringified version. @@ -202,10 +202,10 @@ class XmlMaterialProfile(InstanceContainer): ## End Metadata Block ## Begin Properties Block - builder.start("properties") # type: ignore + builder.start("properties", {}) # type: ignore for key, value in properties.items(): - builder.start(key) # type: ignore + builder.start(key, {}) # type: ignore builder.data(value) builder.end(key) @@ -213,7 +213,7 @@ class XmlMaterialProfile(InstanceContainer): ## End Properties Block ## Begin Settings Block - builder.start("settings") # type: ignore + builder.start("settings", {}) # type: ignore if self.getMetaDataEntry("definition") == "fdmprinter": for instance in self.findInstances(): @@ -258,7 +258,7 @@ class XmlMaterialProfile(InstanceContainer): product = product_name break - builder.start("machine") # type: ignore + builder.start("machine", {}) # type: ignore builder.start("machine_identifier", { "manufacturer": container.getMetaDataEntry("machine_manufacturer", definition_metadata.get("manufacturer", "Unknown")), From a2e5ce66806a46bf0d2ab66784ae8ec50c96352c Mon Sep 17 00:00:00 2001 From: cataclism <69798041+cataclism@users.noreply.github.com> Date: Mon, 11 Jan 2021 09:07:35 +0100 Subject: [PATCH 62/87] Adding the MINGDA D2 (#8872) * Added the configuration files for the Artillery Printers Added the configuration files for the Artillery Printers * Changed version file * Updated versions, removed machine_head_polygon * fixed version in some files * Added generic PLA,PETG,ABS,TPU * Update resources/definitions/artillery_base.def.json Co-authored-by: Konstantinos Karmas * Update resources/definitions/artillery_base.def.json Co-authored-by: Konstantinos Karmas * 1st batch of update * Apply suggestions from code review Co-authored-by: Konstantinos Karmas * 2nd batch of changes * Update resources/quality/artillery/PETG/artillery_0.2_PETG_super.inst.cfg Co-authored-by: Konstantinos Karmas * Update resources/quality/artillery/PETG/artillery_0.2_PETG_ultra.inst.cfg Co-authored-by: Konstantinos Karmas * Update resources/quality/artillery/PETG/artillery_0.3_PETG_low.inst.cfg Co-authored-by: Konstantinos Karmas * Update resources/quality/artillery/PETG/artillery_0.3_PETG_adaptive.inst.cfg Co-authored-by: Konstantinos Karmas * Added the MINDA definition and the MINGDA D2 * Update mingda_d2.def.json Inserted artillery genius plate for the moment * Update mingda_d2.def.json Updated machine dimension * Added the mesh for the D2 & updated the offsets * Update resources/definitions/mingda_base.def.json Co-authored-by: Konstantinos Karmas * Apply suggestions from code review Co-authored-by: Konstantinos Karmas * Apply suggestions from code review Co-authored-by: Konstantinos Karmas Co-authored-by: Konstantinos Karmas --- resources/definitions/mingda_base.def.json | 264 ++++++++++++++++++ resources/definitions/mingda_d2.def.json | 19 ++ .../extruders/mingda_base_extruder_0.def.json | 16 ++ resources/meshes/mingda_d2_base.stl | Bin 0 -> 270284 bytes .../mingda/ABS/mingda_0.2_ABS_super.inst.cfg | 14 + .../mingda/ABS/mingda_0.2_ABS_ultra.inst.cfg | 14 + .../ABS/mingda_0.3_ABS_adaptive.inst.cfg | 14 + .../mingda/ABS/mingda_0.3_ABS_low.inst.cfg | 14 + .../ABS/mingda_0.3_ABS_standard.inst.cfg | 14 + .../mingda/ABS/mingda_0.3_ABS_super.inst.cfg | 14 + .../ABS/mingda_0.4_ABS_adaptive.inst.cfg | 14 + .../mingda/ABS/mingda_0.4_ABS_low.inst.cfg | 14 + .../ABS/mingda_0.4_ABS_standard.inst.cfg | 14 + .../mingda/ABS/mingda_0.4_ABS_super.inst.cfg | 14 + .../ABS/mingda_0.5_ABS_adaptive.inst.cfg | 14 + .../mingda/ABS/mingda_0.5_ABS_low.inst.cfg | 14 + .../ABS/mingda_0.5_ABS_standard.inst.cfg | 14 + .../mingda/ABS/mingda_0.5_ABS_super.inst.cfg | 14 + .../ABS/mingda_0.6_ABS_standard.inst.cfg | 14 + .../mingda/ABS/mingda_0.8_ABS_draft.inst.cfg | 14 + .../mingda/ABS/mingda_1.0_ABS_draft.inst.cfg | 14 + .../PETG/mingda_0.2_PETG_super.inst.cfg | 15 + .../PETG/mingda_0.2_PETG_ultra.inst.cfg | 15 + .../PETG/mingda_0.3_PETG_adaptive.inst.cfg | 15 + .../mingda/PETG/mingda_0.3_PETG_low.inst.cfg | 15 + .../PETG/mingda_0.3_PETG_standard.inst.cfg | 15 + .../PETG/mingda_0.3_PETG_super.inst.cfg | 15 + .../PETG/mingda_0.4_PETG_adaptive.inst.cfg | 15 + .../mingda/PETG/mingda_0.4_PETG_low.inst.cfg | 15 + .../PETG/mingda_0.4_PETG_standard.inst.cfg | 15 + .../PETG/mingda_0.4_PETG_super.inst.cfg | 15 + .../PETG/mingda_0.5_PETG_adaptive.inst.cfg | 15 + .../mingda/PETG/mingda_0.5_PETG_low.inst.cfg | 15 + .../PETG/mingda_0.5_PETG_standard.inst.cfg | 15 + .../PETG/mingda_0.5_PETG_super.inst.cfg | 15 + .../PETG/mingda_0.6_PETG_standard.inst.cfg | 15 + .../PETG/mingda_0.8_PETG_draft.inst.cfg | 15 + .../PETG/mingda_1.0_PETG_draft.inst.cfg | 15 + .../mingda/PLA/mingda_0.2_PLA_super.inst.cfg | 13 + .../mingda/PLA/mingda_0.2_PLA_ultra.inst.cfg | 13 + .../PLA/mingda_0.3_PLA_adaptive.inst.cfg | 12 + .../mingda/PLA/mingda_0.3_PLA_low.inst.cfg | 13 + .../PLA/mingda_0.3_PLA_standard.inst.cfg | 13 + .../mingda/PLA/mingda_0.3_PLA_super.inst.cfg | 13 + .../PLA/mingda_0.4_PLA_adaptive.inst.cfg | 13 + .../mingda/PLA/mingda_0.4_PLA_low.inst.cfg | 13 + .../PLA/mingda_0.4_PLA_standard.inst.cfg | 13 + .../mingda/PLA/mingda_0.4_PLA_super.inst.cfg | 13 + .../PLA/mingda_0.5_PLA_adaptive.inst.cfg | 13 + .../mingda/PLA/mingda_0.5_PLA_low.inst.cfg | 13 + .../PLA/mingda_0.5_PLA_standard.inst.cfg | 13 + .../mingda/PLA/mingda_0.5_PLA_super.inst.cfg | 13 + .../mingda/PLA/mingda_0.6_PLA_draft.inst.cfg | 13 + .../mingda/PLA/mingda_0.6_PLA_low.inst.cfg | 13 + .../PLA/mingda_0.6_PLA_standard.inst.cfg | 13 + .../mingda/PLA/mingda_0.8_PLA_draft.inst.cfg | 13 + .../mingda/PLA/mingda_1.0_PLA_draft.inst.cfg | 13 + .../TPU/mingda_0.3_TPU_adaptive.inst.cfg | 13 + .../TPU/mingda_0.3_TPU_standard.inst.cfg | 13 + .../mingda/TPU/mingda_0.3_TPU_super.inst.cfg | 13 + .../TPU/mingda_0.4_TPU_adaptive.inst.cfg | 13 + .../TPU/mingda_0.4_TPU_standard.inst.cfg | 13 + .../mingda/TPU/mingda_0.4_TPU_super.inst.cfg | 13 + .../TPU/mingda_0.5_TPU_adaptive.inst.cfg | 13 + .../TPU/mingda_0.5_TPU_standard.inst.cfg | 13 + .../mingda/TPU/mingda_0.5_TPU_super.inst.cfg | 13 + .../TPU/mingda_0.6_TPU_standard.inst.cfg | 13 + .../mingda/TPU/mingda_0.8_TPU_draft.inst.cfg | 13 + .../mingda/TPU/mingda_1.0_TPU_draft.inst.cfg | 13 + .../mingda/mingda_global_adaptive.inst.cfg | 19 ++ .../mingda/mingda_global_draft.inst.cfg | 18 ++ .../quality/mingda/mingda_global_low.inst.cfg | 18 ++ .../mingda/mingda_global_standard.inst.cfg | 18 ++ .../mingda/mingda_global_super.inst.cfg | 18 ++ .../mingda/mingda_global_ultra.inst.cfg | 18 ++ resources/variants/mingda_base_0.2.inst.cfg | 12 + resources/variants/mingda_base_0.3.inst.cfg | 12 + resources/variants/mingda_base_0.4.inst.cfg | 12 + resources/variants/mingda_base_0.6.inst.cfg | 12 + resources/variants/mingda_base_0.8.inst.cfg | 12 + resources/variants/mingda_base_1.0.inst.cfg | 12 + resources/variants/mingda_d2_0.2.inst.cfg | 12 + resources/variants/mingda_d2_0.3.inst.cfg | 12 + resources/variants/mingda_d2_0.4.inst.cfg | 12 + resources/variants/mingda_d2_0.5.inst.cfg | 12 + resources/variants/mingda_d2_0.6.inst.cfg | 12 + resources/variants/mingda_d2_0.8.inst.cfg | 12 + resources/variants/mingda_d2_1.0.inst.cfg | 12 + 88 files changed, 1459 insertions(+) create mode 100644 resources/definitions/mingda_base.def.json create mode 100644 resources/definitions/mingda_d2.def.json create mode 100644 resources/extruders/mingda_base_extruder_0.def.json create mode 100644 resources/meshes/mingda_d2_base.stl create mode 100644 resources/quality/mingda/ABS/mingda_0.2_ABS_super.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.2_ABS_ultra.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.3_ABS_adaptive.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.3_ABS_low.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.3_ABS_standard.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.3_ABS_super.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.4_ABS_adaptive.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.4_ABS_low.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.4_ABS_standard.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.4_ABS_super.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.5_ABS_adaptive.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.5_ABS_low.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.5_ABS_standard.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.5_ABS_super.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.6_ABS_standard.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_0.8_ABS_draft.inst.cfg create mode 100644 resources/quality/mingda/ABS/mingda_1.0_ABS_draft.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.2_PETG_super.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.2_PETG_ultra.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.3_PETG_adaptive.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.3_PETG_low.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.3_PETG_standard.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.3_PETG_super.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.4_PETG_adaptive.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.4_PETG_low.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.4_PETG_standard.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.4_PETG_super.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.5_PETG_adaptive.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.5_PETG_low.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.5_PETG_standard.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.5_PETG_super.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.6_PETG_standard.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_0.8_PETG_draft.inst.cfg create mode 100644 resources/quality/mingda/PETG/mingda_1.0_PETG_draft.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.2_PLA_super.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.2_PLA_ultra.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.3_PLA_adaptive.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.3_PLA_low.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.3_PLA_standard.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.3_PLA_super.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.4_PLA_adaptive.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.4_PLA_low.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.4_PLA_standard.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.4_PLA_super.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.5_PLA_adaptive.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.5_PLA_low.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.5_PLA_standard.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.5_PLA_super.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.6_PLA_draft.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.6_PLA_low.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.6_PLA_standard.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_0.8_PLA_draft.inst.cfg create mode 100644 resources/quality/mingda/PLA/mingda_1.0_PLA_draft.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_0.3_TPU_adaptive.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_0.3_TPU_standard.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_0.3_TPU_super.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_0.4_TPU_adaptive.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_0.4_TPU_standard.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_0.4_TPU_super.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_0.5_TPU_adaptive.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_0.5_TPU_standard.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_0.5_TPU_super.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_0.6_TPU_standard.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_0.8_TPU_draft.inst.cfg create mode 100644 resources/quality/mingda/TPU/mingda_1.0_TPU_draft.inst.cfg create mode 100644 resources/quality/mingda/mingda_global_adaptive.inst.cfg create mode 100644 resources/quality/mingda/mingda_global_draft.inst.cfg create mode 100644 resources/quality/mingda/mingda_global_low.inst.cfg create mode 100644 resources/quality/mingda/mingda_global_standard.inst.cfg create mode 100644 resources/quality/mingda/mingda_global_super.inst.cfg create mode 100644 resources/quality/mingda/mingda_global_ultra.inst.cfg create mode 100644 resources/variants/mingda_base_0.2.inst.cfg create mode 100644 resources/variants/mingda_base_0.3.inst.cfg create mode 100644 resources/variants/mingda_base_0.4.inst.cfg create mode 100644 resources/variants/mingda_base_0.6.inst.cfg create mode 100644 resources/variants/mingda_base_0.8.inst.cfg create mode 100644 resources/variants/mingda_base_1.0.inst.cfg create mode 100644 resources/variants/mingda_d2_0.2.inst.cfg create mode 100644 resources/variants/mingda_d2_0.3.inst.cfg create mode 100644 resources/variants/mingda_d2_0.4.inst.cfg create mode 100644 resources/variants/mingda_d2_0.5.inst.cfg create mode 100644 resources/variants/mingda_d2_0.6.inst.cfg create mode 100644 resources/variants/mingda_d2_0.8.inst.cfg create mode 100644 resources/variants/mingda_d2_1.0.inst.cfg diff --git a/resources/definitions/mingda_base.def.json b/resources/definitions/mingda_base.def.json new file mode 100644 index 0000000000..a7daa76d97 --- /dev/null +++ b/resources/definitions/mingda_base.def.json @@ -0,0 +1,264 @@ +{ + "name": "MINGDA Base Printer", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": false, + "author": "cataclism", + "manufacturer": "MINGDA", + "file_formats": "text/x-gcode", + "first_start_actions": ["MachineSettingsAction"], + + "machine_extruder_trains": { + "0": "mingda_base_extruder_0" + }, + + "has_materials": true, + "has_variants": true, + "has_machine_quality": true, + "variants_name": "Nozzle Size", + + "preferred_variant_name": "0.4mm Nozzle", + "preferred_quality_type": "standard", + "preferred_material": "generic_pla", + "exclude_materials": [ + "Vertex_Delta_ABS", + "Vertex_Delta_PET", + "Vertex_Delta_PLA", + "Vertex_Delta_TPU", + "chromatik_pla", + "dsm_arnitel2045_175", + "dsm_novamid1070_175", + "fabtotum_abs", + "fabtotum_nylon", + "fabtotum_pla", + "fabtotum_tpu", + "fiberlogy_hd_pla", + "filo3d_pla", + "filo3d_pla_green", + "filo3d_pla_red", + "generic_bam", + "generic_cffcpe", + "generic_cffpa", + "generic_cpe", + "generic_cpe_plus", + "generic_gffcpe", + "generic_gffpa", + "generic_hips", + "generic_nylon", + "generic_pc", + "generic_pp", + "generic_pva", + "generic_tough_pla", + "imade3d_petg_green", + "imade3d_petg_pink", + "imade3d_pla_green", + "imade3d_pla_pink", + "innofill_innoflex60_175", + "octofiber_pla", + "polyflex_pla", + "polymax_pla", + "polyplus_pla", + "polywood_pla", + "structur3d_dap100silicone", + "tizyx_abs", + "tizyx_pla", + "tizyx_pla_bois", + "ultimaker_abs_black", + "ultimaker_abs_blue", + "ultimaker_abs_green", + "ultimaker_abs_grey", + "ultimaker_abs_orange", + "ultimaker_abs_pearl-gold", + "ultimaker_abs_red", + "ultimaker_abs_silver-metallic", + "ultimaker_abs_white", + "ultimaker_abs_yellow", + "ultimaker_bam", + "ultimaker_cpe_black", + "ultimaker_cpe_blue", + "ultimaker_cpe_dark-grey", + "ultimaker_cpe_green", + "ultimaker_cpe_light-grey", + "ultimaker_cpe_plus_black", + "ultimaker_cpe_plus_transparent", + "ultimaker_cpe_plus_white", + "ultimaker_cpe_red", + "ultimaker_cpe_transparent", + "ultimaker_cpe_white", + "ultimaker_cpe_yellow", + "ultimaker_nylon_black", + "ultimaker_nylon_transparent", + "ultimaker_pc_black", + "ultimaker_pc_transparent", + "ultimaker_pc_white", + "ultimaker_pla_black", + "ultimaker_pla_blue", + "ultimaker_pla_green", + "ultimaker_pla_magenta", + "ultimaker_pla_orange", + "ultimaker_pla_pearl-white", + "ultimaker_pla_red", + "ultimaker_pla_silver-metallic", + "ultimaker_pla_transparent", + "ultimaker_pla_white", + "ultimaker_pla_yellow", + "ultimaker_pp_transparent", + "ultimaker_pva", + "ultimaker_tough_pla_black", + "ultimaker_tough_pla_green", + "ultimaker_tough_pla_red", + "ultimaker_tough_pla_white", + "ultimaker_tpu_black", + "ultimaker_tpu_blue", + "ultimaker_tpu_red", + "ultimaker_tpu_white", + "verbatim_bvoh_175", + "zyyx_pro_flex", + "zyyx_pro_pla" + ] + }, + "overrides": { + "machine_name": { "default_value": "MINGDA Base Printer" }, + "machine_start_gcode": { "default_value": "G28 ; home all axes\n M117 Purge extruder\n G92 E0 ; reset extruder\n G1 Z1.0 F3000 ; move z up little to prevent scratching of surface\n G1 X2 Y20 Z0.3 F5000.0 ; move to start-line position\n G1 X2 Y200.0 Z0.3 F1500.0 E15 ; draw 1st line\n G1 X2 Y200.0 Z0.4 F5000.0 ; move to side a little\n G1 X2 Y20 Z0.4 F1500.0 E30 ; draw 2nd line\n G92 E0 ; reset extruder\n G1 Z1.0 F3000 ; move z up little to prevent scratching of surface"}, + "machine_end_gcode": { "default_value": "G91; relative positioning\n G1 Z1.0 F3000 ; move z up little to prevent scratching of print\n G90; absolute positioning\n G1 X0 Y200 F1000 ; prepare for part removal\n M104 S0; turn off extruder\n M140 S0 ; turn off bed\n G1 X0 Y300 F1000 ; prepare for part removal\n M84 ; disable motors\n M106 S0 ; turn off fan" }, + + "machine_max_feedrate_x": { "value": 500 }, + "machine_max_feedrate_y": { "value": 500 }, + "machine_max_feedrate_z": { "value": 10 }, + "machine_max_feedrate_e": { "value": 50 }, + + "machine_max_acceleration_x": { "value": 500 }, + "machine_max_acceleration_y": { "value": 500 }, + "machine_max_acceleration_z": { "value": 100 }, + "machine_max_acceleration_e": { "value": 5000 }, + "machine_acceleration": { "value": 500 }, + + "machine_max_jerk_xy": { "value": 10 }, + "machine_max_jerk_z": { "value": 0.4 }, + "machine_max_jerk_e": { "value": 5 }, + + "machine_heated_bed": { "default_value": true }, + + "material_diameter": { "default_value": 1.75 }, + + "acceleration_print": { "value": 500 }, + "acceleration_travel": { "value": 500 }, + "acceleration_travel_layer_0": { "value": "acceleration_travel" }, + "acceleration_roofing": { "enabled": "acceleration_enabled and roofing_layer_count > 0 and top_layers > 0" }, + + "jerk_print": { "value": 8 }, + "jerk_travel": { "value": "jerk_print" }, + "jerk_travel_layer_0": { "value": "jerk_travel" }, + + "acceleration_enabled": { "value": false }, + "jerk_enabled": { "value": false }, + + "speed_print": { "value": 60.0 } , + "speed_infill": { "value": "speed_print" }, + "speed_wall": { "value": "speed_print / 2" }, + "speed_wall_0": { "value": "speed_wall" }, + "speed_wall_x": { "value": "speed_wall" }, + "speed_topbottom": { "value": "speed_print / 2" }, + "speed_roofing": { "value": "speed_topbottom" }, + "speed_travel": { "value": "150.0 if speed_print < 60 else 250.0 if speed_print > 100 else speed_print * 2.5" }, + "speed_layer_0": { "value": 20.0 }, + "speed_print_layer_0": { "value": "speed_layer_0" }, + "speed_travel_layer_0": { "value": "100 if speed_layer_0 < 20 else 150 if speed_layer_0 > 30 else speed_layer_0 * 5" }, + "speed_prime_tower": { "value": "speed_topbottom" }, + "speed_support": { "value": "speed_wall_0" }, + "speed_support_interface": { "value": "speed_topbottom" }, + "speed_z_hop": { "value": 5 }, + + "skirt_brim_speed": { "value": "speed_layer_0" }, + + "line_width": { "value": "machine_nozzle_size * 1.1" }, + + "optimize_wall_printing_order": { "value": true }, + + "material_initial_print_temperature": { "value": "material_print_temperature" }, + "material_final_print_temperature": { "value": "material_print_temperature" }, + "material_flow": { "value": 100 }, + "travel_compensate_overlapping_walls_0_enabled": { "value": false }, + + "z_seam_type": { "value": "'back'" }, + "z_seam_corner": { "value": "'z_seam_corner_none'" }, + + "infill_sparse_density": { "value": "15" }, + "infill_pattern": { "value": "'lines' if infill_sparse_density > 50 else 'cubic'" }, + "infill_before_walls": { "value": false }, + "infill_overlap": { "value": 30.0 }, + "skin_overlap": { "value": 10.0 }, + "infill_wipe_dist": { "value": 0.0 }, + "wall_0_wipe_dist": { "value": 0.0 }, + + "fill_perimeter_gaps": { "value": "'everywhere'" }, + "fill_outline_gaps": { "value": false }, + "filter_out_tiny_gaps": { "value": false }, + + "retraction_speed": { + "maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')", + "maximum_value": 200 + }, + "retraction_retract_speed": { + "maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')", + "maximum_value": 200 + }, + "retraction_prime_speed": { + "maximum_value_warning": "machine_max_feedrate_e if retraction_enable else float('inf')", + "maximum_value": 200 + }, + + "retraction_hop_enabled": { "value": true }, + "retraction_hop": { "value": "layer_height*2" }, + "retraction_combing": { "value": "'off' if retraction_hop_enabled else 'infill'" }, + "retraction_combing_max_distance": { "value": 30 }, + "travel_avoid_other_parts": { "value": true }, + "travel_avoid_supports": { "value": true }, + "travel_retract_before_outer_wall": { "value": true }, + + "retraction_amount": { "value": 2 }, + "retraction_enable": { "value": true }, + "retraction_count_max": { "value": 100 }, + "retraction_extrusion_window": { "value": 10 }, + "retraction_min_travel": { "value": 1.5 }, + + "cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" }, + "cool_fan_enabled": { "value": true }, + "cool_min_layer_time": { "value": 10 }, + + "adhesion_type": { "value": "'none' if support_enable else 'skirt'" }, + "brim_replaces_support": { "value": false }, + "skirt_gap": { "value": 10.0 }, + "skirt_line_count": { "value": 4 }, + + "adaptive_layer_height_variation": { "value": 0.04 }, + "adaptive_layer_height_variation_step": { "value": 0.04 }, + + "meshfix_maximum_resolution": { "value": "0.05" }, + "meshfix_maximum_travel_resolution": { "value": "meshfix_maximum_resolution" }, + + "support_angle": { "value": "math.floor(math.degrees(math.atan(line_width / 2.0 / layer_height)))" }, + "support_pattern": { "value": "'zigzag'" }, + "support_infill_rate": { "value": "0 if support_enable and support_structure == 'tree' else 20" }, + "support_use_towers": { "value": false }, + "support_xy_distance": { "value": "wall_line_width_0 * 2" }, + "support_xy_distance_overhang": { "value": "wall_line_width_0" }, + "support_z_distance": { "value": "layer_height if layer_height >= 0.16 else layer_height * 2" }, + "support_xy_overrides_z": { "value": "'xy_overrides_z'" }, + "support_wall_count": { "value": 1 }, + "support_brim_enable": { "value": true }, + "support_brim_width": { "value": 4 }, + + "support_interface_enable": { "value": true }, + "support_interface_height": { "value": "layer_height * 4" }, + "support_interface_density": { "value": 33.333 }, + "support_interface_pattern": { "value": "'grid'" }, + "support_interface_skip_height": { "value": 0.2 }, + "minimum_support_area": { "value": 2 }, + "minimum_interface_area": { "value": 10 }, + "top_bottom_thickness": {"value": "layer_height_0 + layer_height * 3" }, + "wall_thickness": {"value": "line_width * 2" } + + } +} diff --git a/resources/definitions/mingda_d2.def.json b/resources/definitions/mingda_d2.def.json new file mode 100644 index 0000000000..a20ff53db1 --- /dev/null +++ b/resources/definitions/mingda_d2.def.json @@ -0,0 +1,19 @@ +{ + "name": "MINGDA D2", + "version": 2, + "inherits": "mingda_base", + "overrides": { + "machine_name": { "default_value": "MINGDA D2" }, + "machine_width": { "default_value": 230 }, + "machine_depth": { "default_value": 230 }, + "machine_height": { "default_value": 260 }, + "gantry_height": { "value": 25 } + + }, + "metadata": { + "quality_definition": "mingda_base", + "visible": true, + "platform": "mingda_d2_base.stl", + "platform_offset": [ -205, -77, 65] + } +} diff --git a/resources/extruders/mingda_base_extruder_0.def.json b/resources/extruders/mingda_base_extruder_0.def.json new file mode 100644 index 0000000000..034f6ce45f --- /dev/null +++ b/resources/extruders/mingda_base_extruder_0.def.json @@ -0,0 +1,16 @@ +{ + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "mingda_base", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + + } +} diff --git a/resources/meshes/mingda_d2_base.stl b/resources/meshes/mingda_d2_base.stl new file mode 100644 index 0000000000000000000000000000000000000000..025900f243662db101ff16b284c421d0e80654f4 GIT binary patch literal 270284 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 Date: Mon, 11 Jan 2021 02:15:17 -0600 Subject: [PATCH 63/87] Added Maker Made 300x Printer (#8901) * Add files via upload * Add files via upload * Update maker_made_300x.def.json * Update maker_made_300x_extruder_0.json * Update maker_made_300x_extruder_0.json * Update maker_made_300x.def.json * Update maker_made_300x.def.json * Update maker_made_300x.def.json * Update resources/extruders/maker_made_300x_extruder_0.json Co-authored-by: Konstantinos Karmas * Update resources/definitions/maker_made_300x.def.json Co-authored-by: Konstantinos Karmas * Update resources/definitions/maker_made_300x.def.json Co-authored-by: Konstantinos Karmas * Update maker_made_300x.def.json * Rename maker_made_300x_extruder_0.json to maker_made_300x_extruder_0.def.json Co-authored-by: Konstantinos Karmas --- .../definitions/maker_made_300x.def.json | 150 ++++++++++++++++++ .../maker_made_300x_extruder_0.def.json | 15 ++ 2 files changed, 165 insertions(+) create mode 100644 resources/definitions/maker_made_300x.def.json create mode 100644 resources/extruders/maker_made_300x_extruder_0.def.json diff --git a/resources/definitions/maker_made_300x.def.json b/resources/definitions/maker_made_300x.def.json new file mode 100644 index 0000000000..9651aaf5f3 --- /dev/null +++ b/resources/definitions/maker_made_300x.def.json @@ -0,0 +1,150 @@ +{ + "version": 2, + "name": "Maker Made 300x", + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "DragonJe", + "manufacturer": "Maker Made", + "file_formats": "text/x-gcode", + "platform_offset": [0, 0, 0], + "has_materials": true, + "has_variants": false, + "preferred_quality_type": "normal", + "has_machine_quality": false, + "preferred_material": "generic_pla", + "machine_extruder_trains": + { + "0": "maker_made_300x_extruder_0" + } + }, + + "overrides": { + "machine_name": {"default_value": "Maker Made 300x"}, + "machine_width": {"default_value": 300}, + "machine_height": {"default_value": 400}, + "machine_depth": {"default_value": 300}, + "machine_head_with_fans_polygon": {"default_value": [[-30, 34],[-30, -32],[30, -32],[30, 34]]}, + "gantry_height": {"value": "30"}, + "machine_heated_bed": {"default_value": true}, + "material_diameter": {"default_value": 1.75}, + "machine_gcode_flavor": {"default_value": " RepRap (Marlin/Sprinter)"}, + "machine_start_gcode": {"default_value": "G28 ;Home\n G29 ;Auto Level\n G92 E0 ;Reset Extruder\n G1 Z5.0 F3000 ;Move Z Axis up\n G1 X25 Y295.0 Z0.28 F3000.0 ;Move to extrude\n G1 X250 Y295.0 Z0.28 F1500.0 E15 ;Draw the first line\n G1 X25 Y290.0 Z0.28 F3000.0 ;Move to side a little\n G1 X250 Y290.0 Z0.28 F1500.0 E30 ;Draw the second line\n G92 E0 ;Reset Extruder\n G1 Z5.0 F3000 ;Move Z Axis up" }, + "machine_end_gcode": {"default_value": "M104 S0\n M140 S0\n ;Retract the filament\n G92 E1\n G1 E-1 F300\n G28 X0 Y0\n G1 Y300 F3000 ;Move bed forward\n M84" }, + + "layer_height": {"value": 0.16}, + "layer_height_0": {"value": 0.32}, + "line_width": {"value": 0.4}, + "wall_line_width_0": {"value": 0.4}, + "initial_layer_line_width_factor": {"value": 100}, + "wall_thickness": {"value": 0.8}, + "wall_0_wipe_dist": {"value": 0.2}, + "roofing_layer_count": {"value": 1}, + "top_bottom_thickness": {"value": 0.6}, + "top_thickness": {"value": 0.8}, + "top_layers": {"value": 5}, + "bottom_thickness": {"value": 0.6}, + "bottom_layers": {"value": 3}, + "top_bottom_pattern": {"value": "'lines'" }, + "top_bottom_pattern_0": {"value": "'lines'" }, + "wall_0_inset": {"value": 0}, + "optimize_wall_printing_order": {"value": false }, + "outer_inset_first": {"value": false }, + "alternate_extra_perimeter": {"value": false }, + "travel_compensate_overlapping_walls_enabled": {"value": true }, + "travel_compensate_overlapping_walls_0_enabled": {"value": true }, + "travel_compensate_overlapping_walls_x_enabled": {"value": true }, + "wall_min_flow": {"value": 0}, + "fill_perimeter_gaps": {"value": "'everywhere'" }, + "filter_out_tiny_gaps": {"value": true }, + "fill_outline_gaps": {"value": true }, + "xy_offset": {"value": 0}, + "skin_no_small_gaps_heuristic": {"value": true }, + "skin_outline_count": {"value": 1}, + "ironing_enabled": {"value": false }, + "infill_sparse_density": {"value": 20 }, + "zig_zaggify_infill": {"value": false }, + "infill_multiplier": {"value": 1}, + "infill_wall_line_count": {"value": 0}, + "infill_overlap": {"value": 10}, + "skin_overlap": {"value": 5}, + "infill_wipe_dist": {"value": 0.1}, + "gradual_infill_steps": {"value": 0}, + "infill_before_walls": {"value": false }, + "infill_support_enabled": {"value": false }, + "max_skin_angle_for_expansion": {"value": 90}, + "default_material_print_temperature": {"value": 220}, + "material_print_temperature": {"value": 220}, + "material_print_temperature_layer_0": {"value": 220}, + "material_initial_print_temperature": {"value": 220}, + "material_final_print_temperature": {"value": 220}, + "default_material_bed_temperature": {"value": 50}, + "material_bed_temperature": {"value": 50}, + "material_flow": {"value": 100}, + "retraction_enable": {"value": true }, + "retract_at_layer_change": {"value": false }, + "retraction_amount": {"value": 5}, + "retraction_speed": {"value": 45}, + "retraction_extra_prime_amount": {"value": 0}, + "retraction_min_travel": {"value": 0.8}, + "retraction_count_max": {"value": 90}, + "retraction_extrusion_window": {"value": 5}, + "limit_support_retractions": {"value": true }, + "switch_extruder_retraction_amount": {"value": 16}, + "switch_extruder_retraction_speeds": {"value": 20}, + "speed_print": {"value": 50}, + "speed_travel": {"value": 150}, + "speed_layer_0": {"value": 10}, + "speed_travel_layer_0": {"value": 50}, + "machine_max_feedrate_z": {"value": 0}, + "speed_slowdown_layers": {"value": 2}, + "speed_equalize_flow_enabled": {"value": false }, + "acceleration_enabled": {"value": false }, + "acceleration_roofing": {"value": 3000 }, + "jerk_enabled": {"value": false }, + "retraction_combing": {"value": "'within infill'" }, + "travel_retract_before_outer_wall": {"value": false }, + "travel_avoid_other_parts": {"value": true }, + "retraction_hop_enabled": {"value": false }, + "cool_fan_enabled": {"value": true }, + "cool_fan_speed": {"value": 100}, + "cool_fan_speed_0": {"value": 0}, + "cool_fan_full_at_height": {"value": 0.32 }, + "cool_lift_head": {"value": false }, + "support_enable": {"value": true }, + "support_type": {"value": "'everywhere'" }, + "support_angle": {"value": "50"}, + "support_pattern": {"value": "'grid'"}, + "support_wall_count": {"value": 0}, + "zig_zaggify_support": {"value": false }, + "support_infill_rate": {"value": "15 if support_enable else 0"}, + "support_brim_enable": {"value": true }, + "support_brim_line_count": {"value": 5}, + "support_z_distance": {"value": 0.2}, + "support_xy_distance": {"value": 0.7}, + "support_xy_distance_overhang": {"value": 0.2}, + "support_bottom_stair_step_height": {"value": 0.3}, + "support_bottom_stair_step_width": {"value": 5.0}, + "support_join_distance": {"value": 2.0}, + "support_offset": {"value": 0.2}, + "gradual_support_infill_steps": {"value": 0}, + "support_roof_enable": {"value": true }, + "support_bottom_enable": {"value": false }, + "support_roof_height": {"value": 0.45}, + "support_roof_density": {"value": 45}, + "support_roof_pattern": {"value": "'lines'" }, + "support_fan_enable": {"value": false }, + "support_use_towers": {"value": true }, + "support_tower_diameter": {"value": 3}, + "support_tower_roof_angle": {"value": "65"}, + "adhesion_type": {"value": "'skirt'"}, + "skirt_line_count": {"value": 2}, + "skirt_gap": {"value": 3}, + "meshfix_union_all": {"value": true }, + "meshfix_union_all_remove_holes": {"value": false }, + "meshfix_extensive_stitching": {"value": false }, + "meshfix_keep_open_polygons": {"value": false }, + "multiple_mesh_overlap": {"value": "0.16"}, + "carve_multiple_volumes": {"value": false } + } +} diff --git a/resources/extruders/maker_made_300x_extruder_0.def.json b/resources/extruders/maker_made_300x_extruder_0.def.json new file mode 100644 index 0000000000..a35c47b395 --- /dev/null +++ b/resources/extruders/maker_made_300x_extruder_0.def.json @@ -0,0 +1,15 @@ +{ + "name": "Extruder 1", + "version": 2, + "inherits": "fdmextruder", + "metadata": { + "machine": "maker_made_300x", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + } +} From 3282ad7a4169cd76f160a84781d30603ed9c1d7e Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Mon, 11 Jan 2021 12:52:23 +0100 Subject: [PATCH 64/87] Don't add temporary files in the recent files list if F5 is pressed CURA-7864 --- cura/CuraApplication.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 98e2e923d0..76d48f9682 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -3,6 +3,7 @@ import os import sys +import tempfile import time from typing import cast, TYPE_CHECKING, Optional, Callable, List, Any, Dict @@ -1477,7 +1478,8 @@ class CuraApplication(QtApplication): for file_name, nodes in objects_in_filename.items(): for node in nodes: - job = ReadMeshJob(file_name) + file_path = os.path.normpath(os.path.dirname(file_name)) + job = ReadMeshJob(file_name, add_to_recent_files = file_path != tempfile.gettempdir()) # Don't add temp files to the recent files list job._node = node # type: ignore job.finished.connect(self._reloadMeshFinished) if has_merged_nodes: From fd3c9854406e9a655d7076307bf91d8ca4840a1f Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Mon, 11 Jan 2021 17:50:26 +0100 Subject: [PATCH 65/87] Fix ambiguous Ctrl+O shortcut not opening the local file dialog Ctrl+O was assigned as a shortcut in two places: 1. To the "File->Open File(s)" menu item, which is visible when only the local file provider is enabled (i.e. the DF file provider is disabled) 2. To the "File->Open File(s)->From Disk" menu item, which is visible when there are more than one file providers enabled. This was creating an ambiguous shortcut, thus never opening the local file dialog. This is now fixed by disabling the shortcuts when the respective items are not visible. CURA-7868 --- resources/qml/Actions.qml | 6 +++++- resources/qml/Menus/OpenFilesMenu.qml | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index c62b0cb89a..78c4958598 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -416,9 +416,13 @@ Item Action { id: openAction; + property var fileProviderModel: CuraApplication.getFileProviderModel() + text: catalog.i18nc("@action:inmenu menubar:file","&Open File(s)..."); iconName: "document-open"; - shortcut: StandardKey.Open; + // Unassign the shortcut when there are more than one file providers, since then the file provider's shortcut is + // enabled instead, and Ctrl+O is assigned to the local file provider + shortcut: fileProviderModel.count == 1 ? StandardKey.Open : ""; } Action diff --git a/resources/qml/Menus/OpenFilesMenu.qml b/resources/qml/Menus/OpenFilesMenu.qml index 60fb507b34..3c2b64ee62 100644 --- a/resources/qml/Menus/OpenFilesMenu.qml +++ b/resources/qml/Menus/OpenFilesMenu.qml @@ -36,7 +36,9 @@ Menu CuraApplication.getFileProviderModel().trigger(model.name); } } - shortcut: model.shortcut + // Unassign the shortcuts when the submenu is invisible (i.e. when there is only one file provider) to avoid ambiguous shortcuts. + // When there is a signle file provider, the openAction is assigned with the Ctrl+O shortcut instead. + shortcut: openFilesMenu.visible ? model.shortcut : "" } onObjectAdded: openFilesMenu.insertItem(index, object) onObjectRemoved: openFilesMenu.removeItem(object) From 263e1ae3a781dcb2e3991250a8b9c9b8d536e97e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 12 Jan 2021 11:18:42 +0100 Subject: [PATCH 66/87] Show 'connect top/bottom polygons' also when initial layer bottom pattern is concentric Fixes CURA-7962. Fixes #9106. --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 63c7b25b0b..bb64e7f0dc 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1230,7 +1230,7 @@ "description": "Connect top/bottom skin paths where they run next to each other. For the concentric pattern enabling this setting greatly reduces the travel time, but because the connections can happen midway over infill this feature can reduce the top surface quality.", "type": "bool", "default_value": false, - "enabled": "((top_layers > 0 or bottom_layers > 0) and top_bottom_pattern == 'concentric') or (roofing_layer_count > 0 and roofing_pattern == 'concentric')", + "enabled": "((top_layers > 0 or bottom_layers > 0) and top_bottom_pattern == 'concentric') or (initial_bottom_layers > 0 and top_bottom_pattern_0 == 'concentric') or (roofing_layer_count > 0 and roofing_pattern == 'concentric')", "limit_to_extruder": "top_bottom_extruder_nr", "settable_per_mesh": true }, From a45af1da25a475c874aec843dda384bda5814af3 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Fri, 15 Jan 2021 09:50:23 +0100 Subject: [PATCH 67/87] Add save projects submenu CURA-7866 --- resources/qml/Menus/FileMenu.qml | 14 ++++++- resources/qml/Menus/SaveProjectMenu.qml | 53 +++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 resources/qml/Menus/SaveProjectMenu.qml diff --git a/resources/qml/Menus/FileMenu.qml b/resources/qml/Menus/FileMenu.qml index 94fc2358e1..5ea1c9bc06 100644 --- a/resources/qml/Menus/FileMenu.qml +++ b/resources/qml/Menus/FileMenu.qml @@ -4,7 +4,7 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 -import UM 1.2 as UM +import UM 1.6 as UM import Cura 1.0 as Cura Menu @@ -37,8 +37,9 @@ Menu MenuItem { id: saveWorkspaceMenu - shortcut: StandardKey.Save + shortcut: visible ? StandardKey.Save : "" text: catalog.i18nc("@title:menu menubar:file", "&Save Project...") + visible: saveProjectMenu.model.count == 1 onTriggered: { var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" }; @@ -54,6 +55,15 @@ Menu } } + UM.ProjectOutputDevicesModel { id: projectOutputDevicesModel } + + SaveProjectMenu + { + id: saveProjectMenu + model: projectOutputDevicesModel + visible: model.count > 1 + } + MenuSeparator { } MenuItem diff --git a/resources/qml/Menus/SaveProjectMenu.qml b/resources/qml/Menus/SaveProjectMenu.qml new file mode 100644 index 0000000000..51894674f0 --- /dev/null +++ b/resources/qml/Menus/SaveProjectMenu.qml @@ -0,0 +1,53 @@ +// Copyright (c) 2021 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.6 as UM +import Cura 1.1 as Cura + +import "../Dialogs" + +Menu +{ + id: saveProjectMenu + title: catalog.i18nc("@title:menu menubar:file", "Save project...") + property alias model: projectOutputDevices.model + + Instantiator + { + id: projectOutputDevices + MenuItem + { + text: model.name + onTriggered: + { + var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" }; + if (UM.Preferences.getValue("cura/dialog_on_project_save")) + { + saveWorkspaceDialog.deviceId = model.id + saveWorkspaceDialog.args = args + saveWorkspaceDialog.open() + } + else + { + UM.OutputDeviceManager.requestWriteToDevice(model.id, PrintInformation.jobName, args) + } + } + // Unassign the shortcuts when the submenu is invisible (i.e. when there is only one file provider) to avoid ambiguous shortcuts. + // When there is a signle file provider, the openAction is assigned with the Ctrl+O shortcut instead. + shortcut: saveProjectMenu.visible ? model.shortcut : "" + } + onObjectAdded: saveProjectMenu.insertItem(index, object) + onObjectRemoved: saveProjectMenu.removeItem(object) + } + + WorkspaceSummaryDialog + { + id: saveWorkspaceDialog + property var args + property var deviceId + onYes: UM.OutputDeviceManager.requestWriteToDevice(deviceId, PrintInformation.jobName, args) + } +} From 5d9affc69074e754ec07c8734cb85326029ae558 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 15 Jan 2021 15:21:57 +0100 Subject: [PATCH 68/87] Drop objects to buildplate that have been scaled Otherwise they could be floating very high above the buildplate. CURA-7855 --- cura/CuraApplication.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 76d48f9682..7302066550 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1918,6 +1918,11 @@ class CuraApplication(QtApplication): arrange(nodes_to_arrange, self.getBuildVolume(), fixed_nodes) except: Logger.logException("e", "Failed to arrange the models") + + # Ensure that we don't have any weird floaty objects (CURA-7855) + for node in nodes_to_arrange: + node.translate(Vector(0, -node.getBoundingBox().bottom, 0), SceneNode.TransformSpace.World) + self.fileCompleted.emit(file_name) def addNonSliceableExtension(self, extension): From 2774ec7bdf8b9c98beb9a244f1a2b90b57ddec1f Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Fri, 15 Jan 2021 17:42:42 +0100 Subject: [PATCH 69/87] Remove unnecessary kwargs CURA-7865 --- resources/qml/Menus/SaveProjectMenu.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/SaveProjectMenu.qml b/resources/qml/Menus/SaveProjectMenu.qml index 51894674f0..f855af5120 100644 --- a/resources/qml/Menus/SaveProjectMenu.qml +++ b/resources/qml/Menus/SaveProjectMenu.qml @@ -23,7 +23,7 @@ Menu text: model.name onTriggered: { - var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" }; + var args = { "file_type": "workspace" }; if (UM.Preferences.getValue("cura/dialog_on_project_save")) { saveWorkspaceDialog.deviceId = model.id From fc718892d94226b29f97ae44801177586c9d61d0 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Mon, 18 Jan 2021 12:10:15 +0100 Subject: [PATCH 70/87] Add default text in the ComboBox The Cura.ComboBox component can now display a default text when there are no items in its model and another text when there is no item selected. CURA-7865 --- resources/qml/Widgets/ComboBox.qml | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/resources/qml/Widgets/ComboBox.qml b/resources/qml/Widgets/ComboBox.qml index d4c526e265..26324d7773 100644 --- a/resources/qml/Widgets/ComboBox.qml +++ b/resources/qml/Widgets/ComboBox.qml @@ -15,6 +15,18 @@ ComboBox { id: control + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + property var defaultTextOnEmptyModel: catalog.i18nc("@label", "No items to select from") // Text displayed in the combobox when the model is empty + property var defaultTextOnEmptyIndex: "" // Text displayed in the combobox when the model has items but no item is selected + enabled: model.count > 0 + + onVisibleChanged: { popup.close() } + states: [ State { @@ -67,11 +79,22 @@ ComboBox anchors.verticalCenter: parent.verticalCenter anchors.right: downArrow.left - text: control.currentText + text: + { + if (control.model.count == 0) + { + return control.defaultTextOnEmptyModel != "" ? control.defaultTextOnEmptyModel : control.defaultTextOnEmptyIndex + } + else + { + return control.currentIndex == -1 ? control.defaultTextOnEmptyIndex : control.currentText + } + } + textFormat: Text.PlainText renderType: Text.NativeRendering font: UM.Theme.getFont("default") - color: UM.Theme.getColor("setting_control_text") + color: control.currentIndex == -1 ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text") elide: Text.ElideRight verticalAlignment: Text.AlignVCenter } @@ -81,6 +104,7 @@ ComboBox y: control.height - UM.Theme.getSize("default_lining").height width: control.width implicitHeight: contentItem.implicitHeight + 2 * UM.Theme.getSize("default_lining").width + bottomMargin: UM.Theme.getSize("default_margin").height padding: UM.Theme.getSize("default_lining").width contentItem: ListView @@ -133,7 +157,7 @@ ComboBox text: delegateItem.text textFormat: Text.PlainText renderType: Text.NativeRendering - color: control.contentItem.color + color: UM.Theme.getColor("setting_control_text") font: UM.Theme.getFont("default") elide: Text.ElideRight verticalAlignment: Text.AlignVCenter From 770dcd1cf0ba69e98afa0f7fb7242adc6392c45f Mon Sep 17 00:00:00 2001 From: maihde Date: Sun, 17 Jan 2021 18:43:19 -0500 Subject: [PATCH 71/87] feat: add snapmaker 2.0 definitions --- resources/definitions/snapmaker2.def.json | 77 +++++++++++++++++++ .../definitions/snapmaker2_A150.def.json | 39 ++++++++++ .../definitions/snapmaker2_A250.def.json | 39 ++++++++++ .../definitions/snapmaker2_A350.def.json | 39 ++++++++++ .../extruders/snapmaker_extruder_0.def.json | 20 +++++ .../snapmaker2/snapmaker2_fast.inst.cfg | 66 ++++++++++++++++ .../snapmaker2/snapmaker2_high.inst.cfg | 65 ++++++++++++++++ .../snapmaker2/snapmaker2_normal.inst.cfg | 66 ++++++++++++++++ 8 files changed, 411 insertions(+) create mode 100644 resources/definitions/snapmaker2.def.json create mode 100644 resources/definitions/snapmaker2_A150.def.json create mode 100644 resources/definitions/snapmaker2_A250.def.json create mode 100644 resources/definitions/snapmaker2_A350.def.json create mode 100644 resources/extruders/snapmaker_extruder_0.def.json create mode 100644 resources/quality/snapmaker2/snapmaker2_fast.inst.cfg create mode 100644 resources/quality/snapmaker2/snapmaker2_high.inst.cfg create mode 100644 resources/quality/snapmaker2/snapmaker2_normal.inst.cfg diff --git a/resources/definitions/snapmaker2.def.json b/resources/definitions/snapmaker2.def.json new file mode 100644 index 0000000000..e4ad7e19df --- /dev/null +++ b/resources/definitions/snapmaker2.def.json @@ -0,0 +1,77 @@ +{ + "version": 2, + "name": "Snapmaker 2", + "inherits": "fdmprinter", + "metadata": { + "visible": false, + "manufacturer": "Snapmaker", + "file_formats": "text/x-gcode", + "machine_extruder_trains": { + "0": "snapmaker_extruder_0" + }, + "has_materials": true, + "has_machine_quality": true, + "preferred_quality_type": "normal", + "preferred_material": "generic_pla", + "exclude_materials": [ ] + }, + "overrides": { + "machine_name": { + "default_value": "Snapmaker" + }, + "machine_buildplate_type": { + "default_value": "aluminum" + }, + "machine_heated_bed": { + "default_value": true + }, + "machine_start_gcode": { + "default_value": "M104 S{material_print_temperature} ;Set Hotend Temperature\nM140 S{material_bed_temperature} ;Set Bed Temperature\nG28 ;home\nG90 ;absolute positioning\nG1 X-10 Y-10 F3000 ;Move to corner \nG1 Z0 F1800 ;Go to zero offset\nM109 S{material_print_temperature} ;Wait for Hotend Temperature\nM190 S{material_bed_temperature} ;Wait for Bed Temperature\nG92 E0 ;Zero set extruder position\nG1 E20 F200 ;Feed filament to clear nozzle\nG92 E0 ;Zero set extruder position" + }, + "machine_end_gcode": { + "default_value": "M104 S0 ;Extruder heater off\nM140 S0 ;Heated bed heater off\nG90 ;absolute positioning\nG92 E0 ;Retract the filament\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z{machine_width} E-1 F3000 ;move Z up a bit and retract filament even more\nG1 X0 F3000 ;move X to min endstops, so the head is out of the way\nG1 Y{machine_depth} F3000 ;so the head is out of the way and Plate is moved forward" + }, + "machine_nozzle_size": { + "default_value": 0.4 + }, + "material_diameter": { + "default_value": 1.75 + }, + "machine_max_acceleration_x": { + "default_value": 1000 + }, + "machine_max_acceleration_y": { + "default_value": 1000 + }, + "machine_max_acceleration_z": { + "default_value": 1000 + }, + "machine_max_acceleration_e": { + "default_value": 1000 + }, + "machine_acceleration": { + "default_value": 1000 + }, + "material_print_temp_prepend": { + "default_value": false + }, + "material_bed_temp_prepend": { + "default_value": false + }, + "default_material_print_temperature": { + "default_value": 205 + }, + "retraction_enable": { + "default_value": true + }, + "retraction_amount": { + "default_value": 5 + }, + "retraction_speed": { + "default_value": 60 + }, + "retract_at_layer_change": { + "default_value": false + } + } +} diff --git a/resources/definitions/snapmaker2_A150.def.json b/resources/definitions/snapmaker2_A150.def.json new file mode 100644 index 0000000000..8baea05016 --- /dev/null +++ b/resources/definitions/snapmaker2_A150.def.json @@ -0,0 +1,39 @@ +{ + "version": 2, + "name": "Snapmaker 2 A150", + "inherits": "snapmaker2", + "metadata": { + "visible": true, + "manufacturer": "Snapmaker", + "file_formats": "text/x-gcode", + "machine_extruder_trains": { + "0": "snapmaker_extruder_0" + }, + "quality_definition": "snapmaker2" + }, + "overrides": { + "machine_name": { + "default_value": "Snapmaker A150" + }, + "machine_width": { + "default_value": 160 + }, + "machine_depth": { + "default_value": 160 + }, + "machine_height": { + "default_value": 145 + }, + "machine_head_with_fans_polygon": { + "default_value": [ + [-67, 22], + [-67, -25], + [25.5, 22], + [25.5, -25] + ] + }, + "gantry_height": { + "value": 27 + } + } +} diff --git a/resources/definitions/snapmaker2_A250.def.json b/resources/definitions/snapmaker2_A250.def.json new file mode 100644 index 0000000000..a61d52c46f --- /dev/null +++ b/resources/definitions/snapmaker2_A250.def.json @@ -0,0 +1,39 @@ +{ + "version": 2, + "name": "Snapmaker 2 A250", + "inherits": "snapmaker2", + "metadata": { + "visible": true, + "manufacturer": "Snapmaker", + "file_formats": "text/x-gcode", + "machine_extruder_trains": { + "0": "snapmaker_extruder_0" + }, + "quality_definition": "snapmaker2" + }, + "overrides": { + "machine_name": { + "default_value": "Snapmaker A250" + }, + "machine_width": { + "default_value": 230 + }, + "machine_depth": { + "default_value": 250 + }, + "machine_height": { + "default_value": 235 + }, + "machine_head_with_fans_polygon": { + "default_value": [ + [-67, 22], + [-67, -25], + [25.5, 22], + [25.5, -25] + ] + }, + "gantry_height": { + "value": 27 + } + } +} diff --git a/resources/definitions/snapmaker2_A350.def.json b/resources/definitions/snapmaker2_A350.def.json new file mode 100644 index 0000000000..944e9ebc3b --- /dev/null +++ b/resources/definitions/snapmaker2_A350.def.json @@ -0,0 +1,39 @@ +{ + "version": 2, + "name": "Snapmaker 2 A350", + "inherits": "snapmaker2", + "metadata": { + "visible": true, + "manufacturer": "Snapmaker", + "file_formats": "text/x-gcode", + "machine_extruder_trains": { + "0": "snapmaker_extruder_0" + }, + "quality_definition": "snapmaker2" + }, + "overrides": { + "machine_name": { + "default_value": "Snapmaker A350" + }, + "machine_width": { + "default_value": 320 + }, + "machine_depth": { + "default_value": 350 + }, + "machine_height": { + "default_value": 330 + }, + "machine_head_with_fans_polygon": { + "default_value": [ + [-67, 22], + [-67, -25], + [25.5, 22], + [25.5, -25] + ] + }, + "gantry_height": { + "value": 27 + } + } +} diff --git a/resources/extruders/snapmaker_extruder_0.def.json b/resources/extruders/snapmaker_extruder_0.def.json new file mode 100644 index 0000000000..c9b69703a7 --- /dev/null +++ b/resources/extruders/snapmaker_extruder_0.def.json @@ -0,0 +1,20 @@ +{ + "name": "Extruder 1", + "version": 2, + "inherits": "fdmextruder", + "metadata": { + "machine": "snapmaker2", + "position": "0" + }, + "overrides": { + "extruder_nr": { + "default_value": 0 + }, + "machine_nozzle_size": { + "default_value": 0.4 + }, + "material_diameter": { + "default_value": 1.75 + } + } +} diff --git a/resources/quality/snapmaker2/snapmaker2_fast.inst.cfg b/resources/quality/snapmaker2/snapmaker2_fast.inst.cfg new file mode 100644 index 0000000000..a2249ac1cb --- /dev/null +++ b/resources/quality/snapmaker2/snapmaker2_fast.inst.cfg @@ -0,0 +1,66 @@ +[general] +version = 4 +name = Fast +definition = snapmaker2 + +[metadata] +setting_version = 16 +type = quality +quality_type = fast +weight = -2 +global_quality = True + +[values] +layer_height = 0.24 +layer_height_0 = 0.2 +initial_layer_line_width_factor = 100 + +wall_thickness = 0.8 +wall_line_count = 2 +top_thickness = 0.8 +top_layers = 4 +bottom_thickness = 0.8 +bottom_layers = 4 +outer_inset_first = False +skin_outline_count = 0 + +; infill_line_distance = 8 +infill_sparse_density = 15 +infill_sparse_thickness = 0.24 +skin_preshrink = 0.8 +top_skin_preshrink = 0.8 +bottom_skin_preshrink = 0.8 +expand_skins_expand_distance = 0.8 +top_skin_expand_distance = 0.8 +bottom_skin_expand_distance = 0.8 + +speed_travel = 80 +speed_topbottom = 30 +speed_wall_x = 25 +speed_wall_0 = 20 +speed_wall = 40 +speed_infill = 60 +speed_print = 60 +speed_print_layer_0 = 18 +speed_travel_layer_0 = 24 +skirt_brim_speed = 18 + +retraction_hop = 1 +retraction_hop_enabled = False + +magic_spiralize = False +magic_mesh_surface_mode = normal + +adhesion_type = skirt +skirt_line_count = 1 +brim_width = 8 +brim_line_count = 20 +raft_margin = 15 + +support_enable = False +support_type = everywhere +support_pattern = zigzag +support_angle = 50 +support_infill_rate = 15 +support_line_distance = 2.66 +support_initial_layer_line_distance = 2.66 \ No newline at end of file diff --git a/resources/quality/snapmaker2/snapmaker2_high.inst.cfg b/resources/quality/snapmaker2/snapmaker2_high.inst.cfg new file mode 100644 index 0000000000..7dc84e0378 --- /dev/null +++ b/resources/quality/snapmaker2/snapmaker2_high.inst.cfg @@ -0,0 +1,65 @@ +[general] +version = 4 +name = High +definition = snapmaker2 + +[metadata] +setting_version = 16 +type = quality +quality_type = high +weight = 1 +global_quality = True + +[values] +layer_height = 0.08 +layer_height_0 = 0.15 +initial_layer_line_width_factor = 100 + +wall_thickness = 1.2 +wall_line_count = 3 +top_thickness = 0.8 +top_layers = 10 +bottom_thickness = 0.8 +bottom_layers = 10 +outer_inset_first = False +skin_outline_count = 1 + +; infill_line_distance = 8 +infill_sparse_density = 15 +infill_sparse_thickness = 0.08 +skin_preshrink = 1.2 +top_skin_preshrink = 1.2 +bottom_skin_preshrink = 1.2 +expand_skins_expand_distance = 1.2 +top_skin_expand_distance = 1.2 +bottom_skin_expand_distance = 1.2 + +speed_travel = 60 +speed_topbottom = 20 +speed_wall_x = 15 +speed_wall_0 = 10 +speed_wall = 30 +speed_infill = 40 +speed_print = 40 +speed_print_layer_0 = 18 +speed_travel_layer_0 = 24 +skirt_brim_speed = 18 + +retraction_hop = 1 +retraction_hop_enabled = False + +magic_spiralize = False +magic_mesh_surface_mode = normal + +adhesion_type = skirt +skirt_line_count = 1 +brim_width = 8 +brim_line_count = 20 +raft_margin = 15 + +support_enable = False +support_type = everywhere +support_pattern = zigzag +support_angle = 50 +support_infill_rate = 15 +support_line_distance = 2.66 diff --git a/resources/quality/snapmaker2/snapmaker2_normal.inst.cfg b/resources/quality/snapmaker2/snapmaker2_normal.inst.cfg new file mode 100644 index 0000000000..5680725817 --- /dev/null +++ b/resources/quality/snapmaker2/snapmaker2_normal.inst.cfg @@ -0,0 +1,66 @@ +[general] +version = 4 +name = Normal +definition = snapmaker2 + +[metadata] +setting_version = 16 +type = quality +quality_type = normal +weight = 0 +global_quality = True + +[values] +layer_height = 0.16 +layer_height_0 = 0.2 +initial_layer_line_width_factor = 100 + +wall_thickness = 1.2 +wall_line_count = 3 +top_thickness = 0.8 +top_layers = 5 +bottom_thickness = 0.8 +bottom_layers = 5 +outer_inset_first = False +skin_outline_count = 0 + +; infill_line_distance = 8 +infill_sparse_density = 15 +infill_sparse_thickness = 0.16 +skin_preshrink = 1.2 +top_skin_preshrink = 1.2 +bottom_skin_preshrink = 1.2 +expand_skins_expand_distance = 1.2 +top_skin_expand_distance = 1.2 +bottom_skin_expand_distance = 1.2 + +speed_travel = 70 +speed_topbottom = 25 +speed_wall_x = 20 +speed_wall_0 = 15 +speed_wall = 30 +speed_infill = 50 +speed_print = 50 +speed_print_layer_0 = 18 +speed_travel_layer_0 = 24 +skirt_brim_speed = 18 + +retraction_hop = 1 +retraction_hop_enabled = False + +magic_spiralize = False +magic_mesh_surface_mode = normal + +adhesion_type = skirt +skirt_line_count = 1 +brim_width = 8 +brim_line_count = 20 +raft_margin = 15 + +support_enable = False +support_type = everywhere +support_pattern = zigzag +support_angle = 50 +support_infill_rate = 15 +support_line_distance = 2.66 +support_initial_layer_line_distance = 2.66 \ No newline at end of file From f1e152955c716977cdd6554f79b17b279cb6aaf7 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Mon, 18 Jan 2021 18:00:05 +0100 Subject: [PATCH 72/87] Correct the Menu title CURA-7865 --- resources/qml/Menus/SaveProjectMenu.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/SaveProjectMenu.qml b/resources/qml/Menus/SaveProjectMenu.qml index f855af5120..35c511344b 100644 --- a/resources/qml/Menus/SaveProjectMenu.qml +++ b/resources/qml/Menus/SaveProjectMenu.qml @@ -12,7 +12,7 @@ import "../Dialogs" Menu { id: saveProjectMenu - title: catalog.i18nc("@title:menu menubar:file", "Save project...") + title: catalog.i18nc("@title:menu menubar:file", "Save Project...") property alias model: projectOutputDevices.model Instantiator From 0a7dc34a9c5dde1f71f1fb3977b444ea4de745e4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 18 Jan 2021 18:52:01 +0100 Subject: [PATCH 73/87] Update start g-code for BIBO2 Dual This is a correction supplied by braden0202 in the issue thread #8977. --- resources/definitions/bibo2_dual.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/bibo2_dual.def.json b/resources/definitions/bibo2_dual.def.json index b9b1164e9d..8c6dc4ec76 100644 --- a/resources/definitions/bibo2_dual.def.json +++ b/resources/definitions/bibo2_dual.def.json @@ -71,7 +71,7 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z2.0 F400 ;move the platform down 15mm\nT0\nG92 E0\nG28\nG1 Y0 F1200 E0\nG92 E0\nM117 BIBO Printing..." + "default_value": "M104 T0 165\nM104 T1 165\nM109 T{initial_extruder_nr} S{material_print_temperature_layer_0, initial_extruder_nr}\nG21 ;metric values\nG90 ;absolute positioning\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z2.0 F400 ;move the platform down 2mm\nT0\nG92 E0\nG28\nG1 Y0 F1200 E0\nG92 E0\nT{initial_extruder_nr}\nM117 BIBO Printing..." }, "machine_end_gcode": { "default_value": ";End GCode\nM104 T0 S0 ;extruder heater off\nM104 T1 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91\nG1 Z1 F100 ;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-2 X-20 Y-20 F300 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" From 640e038ce71bb4d4f637891941b324c73c4f1bf7 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Jan 2021 11:05:55 +0100 Subject: [PATCH 74/87] Catch OSError when authorizing connection Fixes Sentry issue CURA-1GW. --- cura/OAuth2/AuthorizationHelpers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/OAuth2/AuthorizationHelpers.py b/cura/OAuth2/AuthorizationHelpers.py index f7fe6958a1..2534cdb808 100644 --- a/cura/OAuth2/AuthorizationHelpers.py +++ b/cura/OAuth2/AuthorizationHelpers.py @@ -69,7 +69,9 @@ class AuthorizationHelpers: try: return self.parseTokenResponse(requests.post(self._token_url, data = data)) # type: ignore except requests.exceptions.ConnectionError: - return AuthenticationResponse(success=False, err_message="Unable to connect to remote server") + return AuthenticationResponse(success = False, err_message = "Unable to connect to remote server") + except OSError as e: + return AuthenticationResponse(success = False, err_message = "Operating system is unable to set up a secure connection: {err}".format(err = str(e))) @staticmethod def parseTokenResponse(token_response: requests.models.Response) -> "AuthenticationResponse": From 1b8463ba30f76154d80811e44d931b2160ae2d44 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Jan 2021 14:09:46 +0100 Subject: [PATCH 75/87] Catch TypeError when parsing setting version Someone had a list in the setting version, which then crashed Cura. --- cura/Settings/CuraContainerRegistry.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 24b7436bad..00a0eedbf0 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -400,7 +400,9 @@ class CuraContainerRegistry(ContainerRegistry): try: if int(metadata["setting_version"]) != cura.CuraApplication.CuraApplication.SettingVersion: return False - except ValueError: #Not parsable as int. + except ValueError: # Not parsable as int. + return False + except TypeError: # Expecting string input here, not e.g. list or anything. return False return True From a9c8c634420c05c980d090127d49d2609f9eb51c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Jan 2021 14:49:47 +0100 Subject: [PATCH 76/87] Catch environment errors removing temporary package files Could be removed or not having access rights. Fixes Sentry issue CURA-1K6. --- plugins/Toolbox/src/CloudSync/SyncOrchestrator.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/src/CloudSync/SyncOrchestrator.py b/plugins/Toolbox/src/CloudSync/SyncOrchestrator.py index 5693b82ded..a85c13f639 100644 --- a/plugins/Toolbox/src/CloudSync/SyncOrchestrator.py +++ b/plugins/Toolbox/src/CloudSync/SyncOrchestrator.py @@ -1,3 +1,6 @@ +# Copyright (c) 2021 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + import os from typing import List, Dict, Any, cast @@ -96,7 +99,10 @@ class SyncOrchestrator(Extension): else: self._cloud_api.unsubscribe(item["package_id"]) # delete temp file - os.remove(item["package_path"]) + try: + os.remove(item["package_path"]) + except EnvironmentError as e: # File was already removed, no access rights, etc. + Logger.error("Can't delete temporary package file: {err}".format(err = str(e))) if has_changes: self._restart_presenter.present() From 4380cdbd86df87f547e0ba6acfbd0491d8d5eb0b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Jan 2021 16:31:31 +0100 Subject: [PATCH 77/87] Don't crash on malformed JSON structures JSON dictionaries need to have a certain structure such as strings as keys. If that's not correct it gives a TypeError. Don't crash on those; give a similar error message as when the values in the JSON are invalid. --- cura/OAuth2/AuthorizationService.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/OAuth2/AuthorizationService.py b/cura/OAuth2/AuthorizationService.py index 9a5c81ae55..a96b05be5f 100644 --- a/cura/OAuth2/AuthorizationService.py +++ b/cura/OAuth2/AuthorizationService.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Ultimaker B.V. +# Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json @@ -241,7 +241,7 @@ class AuthorizationService: self._unable_to_get_data_message = Message(i18n_catalog.i18nc("@info", "Unable to reach the Ultimaker account server."), title = i18n_catalog.i18nc("@info:title", "Warning")) self._unable_to_get_data_message.show() - except ValueError: + except (ValueError, TypeError): Logger.logException("w", "Could not load auth data from preferences") def _storeAuthData(self, auth_data: Optional[AuthenticationResponse] = None) -> None: From b7e613a271c6294c91c1bcb861487aeee878ad72 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Tue, 19 Jan 2021 21:39:16 +0100 Subject: [PATCH 78/87] Remove file_type argument from the saveWorkspaceDialog Because we also want to export ufp files, which are not workspace files. CURA-7865 --- resources/qml/Menus/SaveProjectMenu.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/SaveProjectMenu.qml b/resources/qml/Menus/SaveProjectMenu.qml index 35c511344b..100e6f2ea4 100644 --- a/resources/qml/Menus/SaveProjectMenu.qml +++ b/resources/qml/Menus/SaveProjectMenu.qml @@ -23,7 +23,7 @@ Menu text: model.name onTriggered: { - var args = { "file_type": "workspace" }; + var args = {}; if (UM.Preferences.getValue("cura/dialog_on_project_save")) { saveWorkspaceDialog.deviceId = model.id @@ -35,8 +35,8 @@ Menu UM.OutputDeviceManager.requestWriteToDevice(model.id, PrintInformation.jobName, args) } } - // Unassign the shortcuts when the submenu is invisible (i.e. when there is only one file provider) to avoid ambiguous shortcuts. - // When there is a signle file provider, the openAction is assigned with the Ctrl+O shortcut instead. + // Unassign the shortcuts when the submenu is invisible (i.e. when there is only one project output device) to avoid ambiguous shortcuts. + // When there is only the LocalFileOutputDevice, the Ctrl+S shortcut is assigned to the saveWorkspaceMenu MenuItem shortcut: saveProjectMenu.visible ? model.shortcut : "" } onObjectAdded: saveProjectMenu.insertItem(index, object) From a72a58cca194c605d8894ba7f04cebeb359da991 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Wed, 20 Jan 2021 11:23:48 +0100 Subject: [PATCH 79/87] Comment out the generation of the snapshot While generating UFP files from outside the main thread, the snapshot generation crashes Cura due to the OpenGL context. To avoid that, for the time being, we comment out the generation of the snapshot. CURA-7865 --- plugins/UFPWriter/UFPWriter.py | 36 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index 6179872b2d..08c2f3252f 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -72,24 +72,24 @@ class UFPWriter(MeshWriter): gcode.write(gcode_textio.getvalue().encode("UTF-8")) archive.addRelation(virtual_path = "/3D/model.gcode", relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode") - self._createSnapshot() - - # Store the thumbnail. - if self._snapshot: - archive.addContentType(extension = "png", mime_type = "image/png") - thumbnail = archive.getStream("/Metadata/thumbnail.png") - - thumbnail_buffer = QBuffer() - thumbnail_buffer.open(QBuffer.ReadWrite) - thumbnail_image = self._snapshot - thumbnail_image.save(thumbnail_buffer, "PNG") - - thumbnail.write(thumbnail_buffer.data()) - archive.addRelation(virtual_path = "/Metadata/thumbnail.png", - relation_type = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail", - origin = "/3D/model.gcode") - else: - Logger.log("d", "Thumbnail not created, cannot save it") + # self._createSnapshot() + # + # # Store the thumbnail. + # if self._snapshot: + # archive.addContentType(extension = "png", mime_type = "image/png") + # thumbnail = archive.getStream("/Metadata/thumbnail.png") + # + # thumbnail_buffer = QBuffer() + # thumbnail_buffer.open(QBuffer.ReadWrite) + # thumbnail_image = self._snapshot + # thumbnail_image.save(thumbnail_buffer, "PNG") + # + # thumbnail.write(thumbnail_buffer.data()) + # archive.addRelation(virtual_path = "/Metadata/thumbnail.png", + # relation_type = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail", + # origin = "/3D/model.gcode") + # else: + # Logger.log("d", "Thumbnail not created, cannot save it") # Store the material. application = CuraApplication.getInstance() From 32df06c2801c78cd8375f45a975625805bb7da41 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Wed, 20 Jan 2021 12:51:13 +0100 Subject: [PATCH 80/87] Add TODO comment to explain the commenting out of the snapshot CURA-7865 --- plugins/UFPWriter/UFPWriter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index 08c2f3252f..af208209e4 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -72,6 +72,7 @@ class UFPWriter(MeshWriter): gcode.write(gcode_textio.getvalue().encode("UTF-8")) archive.addRelation(virtual_path = "/3D/model.gcode", relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode") + # TODO temporarily commented out, as is causes a crash whenever the UFPWriter is called outside of the main thread # self._createSnapshot() # # # Store the thumbnail. From cdedb56a9ae6db5479dc060b9254a0307b69ab55 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Wed, 20 Jan 2021 21:13:36 +0100 Subject: [PATCH 81/87] Restore arguments in the SaveProjectMenu Or else the LocalFileOutputDevice will output a mesh 3mf file and not a Cura 3mf project when being called from the 'File -> Save Project -> To Disk' submenu item. CURA-7865 --- resources/qml/Menus/SaveProjectMenu.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/SaveProjectMenu.qml b/resources/qml/Menus/SaveProjectMenu.qml index 100e6f2ea4..dd17324e58 100644 --- a/resources/qml/Menus/SaveProjectMenu.qml +++ b/resources/qml/Menus/SaveProjectMenu.qml @@ -23,7 +23,7 @@ Menu text: model.name onTriggered: { - var args = {}; + var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" }; if (UM.Preferences.getValue("cura/dialog_on_project_save")) { saveWorkspaceDialog.deviceId = model.id From 4fc0612806bf361c7e85bb43db87577feb71caf9 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 20 Jan 2021 20:15:33 +0100 Subject: [PATCH 82/87] Make a snapshot on slice instead of write. In some cases, UFP-writing is going to be done when the OpenGL-context is off the main window. This doesn't work. That unfortunately also goes for this commit, but it's a work in progress. --- .../CuraEngineBackend/CuraEngineBackend.py | 24 +++++++- plugins/UFPWriter/UFPWriter.py | 55 ++++++++----------- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 4b196f7b5d..7a88618a7e 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import argparse #To run the engine in debug mode if the front-end is in debug mode. @@ -9,6 +9,8 @@ import sys from time import time from typing import Any, cast, Dict, List, Optional, Set, TYPE_CHECKING +from PyQt5.QtGui import QImage + from UM.Backend.Backend import Backend, BackendState from UM.Scene.SceneNode import SceneNode from UM.Signal import Signal @@ -24,6 +26,8 @@ from UM.Tool import Tool #For typing. from cura.CuraApplication import CuraApplication from cura.Settings.ExtruderManager import ExtruderManager +from cura.Snapshot import Snapshot +from cura.Utils.Threading import call_on_qt_thread from .ProcessSlicedLayersJob import ProcessSlicedLayersJob from .StartSliceJob import StartSliceJob, StartJobResult @@ -153,6 +157,8 @@ class CuraEngineBackend(QObject, Backend): self.determineAutoSlicing() application.getPreferences().preferenceChanged.connect(self._onPreferencesChanged) + self._snapshot = None #type: Optional[QImage] + application.initializationFinished.connect(self.initialize) def initialize(self) -> None: @@ -241,9 +247,24 @@ class CuraEngineBackend(QObject, Backend): self.markSliceAll() self.slice() + @call_on_qt_thread # must be called from the main thread because of OpenGL + def _createSnapshot(self) -> None: + self._snapshot = None + Logger.log("i", "Creating thumbnail image (just before slice)...") + try: + self._snapshot = Snapshot.snapshot(width = 300, height = 300) + except: + Logger.logException("w", "Failed to create snapshot image") + self._snapshot = None # Failing to create thumbnail should not fail creation of UFP + + def getLatestSnapShot(self) -> Optional[QImage]: + return self._snapshot + def slice(self) -> None: """Perform a slice of the scene.""" + self._createSnapshot() + Logger.log("i", "Starting to slice...") self._slice_start_time = time() if not self._build_plates_to_be_sliced: @@ -331,7 +352,6 @@ class CuraEngineBackend(QObject, Backend): def _onStartSliceCompleted(self, job: StartSliceJob) -> None: """Event handler to call when the job to initiate the slicing process is - completed. When the start slice job is successfully completed, it will be happily diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index af208209e4..b7941c425f 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -7,19 +7,20 @@ from Charon.VirtualFile import VirtualFile # To open UFP files. from Charon.OpenMode import OpenMode # To indicate that we want to write to UFP files. from io import StringIO # For converting g-code to bytes. +from PyQt5.QtCore import QBuffer + from UM.Logger import Logger from UM.Mesh.MeshWriter import MeshWriter # The writer we need to implement. from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType from UM.PluginRegistry import PluginRegistry # To get the g-code writer. -from PyQt5.QtCore import QBuffer from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.SceneNode import SceneNode from cura.CuraApplication import CuraApplication -from cura.Snapshot import Snapshot from cura.Utils.Threading import call_on_qt_thread from UM.i18n import i18nCatalog +from plugins.CuraEngineBackend.CuraEngineBackend import CuraEngineBackend METADATA_OBJECTS_PATH = "metadata/objects" @@ -38,17 +39,6 @@ class UFPWriter(MeshWriter): ) ) - self._snapshot = None - - def _createSnapshot(self, *args): - # must be called from the main thread because of OpenGL - Logger.log("d", "Creating thumbnail image...") - try: - self._snapshot = Snapshot.snapshot(width = 300, height = 300) - except Exception: - Logger.logException("w", "Failed to create snapshot image") - self._snapshot = None # Failing to create thumbnail should not fail creation of UFP - # This needs to be called on the main thread (Qt thread) because the serialization of material containers can # trigger loading other containers. Because those loaded containers are QtObjects, they must be created on the # Qt thread. The File read/write operations right now are executed on separated threads because they are scheduled @@ -72,25 +62,26 @@ class UFPWriter(MeshWriter): gcode.write(gcode_textio.getvalue().encode("UTF-8")) archive.addRelation(virtual_path = "/3D/model.gcode", relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode") - # TODO temporarily commented out, as is causes a crash whenever the UFPWriter is called outside of the main thread - # self._createSnapshot() - # - # # Store the thumbnail. - # if self._snapshot: - # archive.addContentType(extension = "png", mime_type = "image/png") - # thumbnail = archive.getStream("/Metadata/thumbnail.png") - # - # thumbnail_buffer = QBuffer() - # thumbnail_buffer.open(QBuffer.ReadWrite) - # thumbnail_image = self._snapshot - # thumbnail_image.save(thumbnail_buffer, "PNG") - # - # thumbnail.write(thumbnail_buffer.data()) - # archive.addRelation(virtual_path = "/Metadata/thumbnail.png", - # relation_type = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail", - # origin = "/3D/model.gcode") - # else: - # Logger.log("d", "Thumbnail not created, cannot save it") + snapshot = None + backend = CuraApplication.getInstance().getBackend() + if isinstance(backend, CuraEngineBackend): + snapshot = backend.getLatestSnapshot() + + # Store the thumbnail. + if snapshot: + archive.addContentType(extension = "png", mime_type = "image/png") + thumbnail = archive.getStream("/Metadata/thumbnail.png") + + thumbnail_buffer = QBuffer() + thumbnail_buffer.open(QBuffer.ReadWrite) + snapshot.save(thumbnail_buffer, "PNG") + + thumbnail.write(thumbnail_buffer.data()) + archive.addRelation(virtual_path = "/Metadata/thumbnail.png", + relation_type = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail", + origin = "/3D/model.gcode") + else: + Logger.log("w", "Thumbnail not created, cannot save it") # Store the material. application = CuraApplication.getInstance() From f4ef3b44e386238781cd0705edd9124d3eac5183 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 21 Jan 2021 08:55:45 +0100 Subject: [PATCH 83/87] Properly retrieve snapshot (now made on slice). Circumvents the snapshot/thumbnail not working when the focus is not on the main window, even if the thread is main. This was an issue when writing a file to Digital Factory becasue that workflow uses a modal window. Thanks for the idea Jelle! --- plugins/CuraEngineBackend/CuraEngineBackend.py | 2 +- plugins/UFPWriter/UFPWriter.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 7a88618a7e..eed9d56741 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -257,7 +257,7 @@ class CuraEngineBackend(QObject, Backend): Logger.logException("w", "Failed to create snapshot image") self._snapshot = None # Failing to create thumbnail should not fail creation of UFP - def getLatestSnapShot(self) -> Optional[QImage]: + def getLatestSnapshot(self) -> Optional[QImage]: return self._snapshot def slice(self) -> None: diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index b7941c425f..fe4d39059c 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -62,12 +62,9 @@ class UFPWriter(MeshWriter): gcode.write(gcode_textio.getvalue().encode("UTF-8")) archive.addRelation(virtual_path = "/3D/model.gcode", relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode") - snapshot = None + # Attempt to store the thumbnail, if any: backend = CuraApplication.getInstance().getBackend() - if isinstance(backend, CuraEngineBackend): - snapshot = backend.getLatestSnapshot() - - # Store the thumbnail. + snapshot = None if getattr(backend, "getLatestSnapshot", None) is None else backend.getLatestSnapshot() if snapshot: archive.addContentType(extension = "png", mime_type = "image/png") thumbnail = archive.getStream("/Metadata/thumbnail.png") From 9144f38814b6534268b91003cd8b206a7087d289 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 21 Jan 2021 09:04:39 +0100 Subject: [PATCH 84/87] Fix wrong import. --- plugins/UFPWriter/UFPWriter.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index fe4d39059c..f9b86be651 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020 Ultimaker B.V. +# Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from typing import cast, List, Dict @@ -20,7 +20,6 @@ from cura.CuraApplication import CuraApplication from cura.Utils.Threading import call_on_qt_thread from UM.i18n import i18nCatalog -from plugins.CuraEngineBackend.CuraEngineBackend import CuraEngineBackend METADATA_OBJECTS_PATH = "metadata/objects" From f28b90a1a4ebf13543ab4bb87b8864e389ff68a2 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 22 Jan 2021 11:04:14 +0100 Subject: [PATCH 85/87] Make Line Type the default selection for layer view colour scheme Implements CURA-7983. Fixes #9154. --- plugins/SimulationView/SimulationView.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 349426d463..75b1352845 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -104,7 +104,7 @@ class SimulationView(CuraView): Application.getInstance().getPreferences().addPreference("view/only_show_top_layers", False) Application.getInstance().getPreferences().addPreference("view/force_layer_view_compatibility_mode", False) - Application.getInstance().getPreferences().addPreference("layerview/layer_view_type", 0) + Application.getInstance().getPreferences().addPreference("layerview/layer_view_type", 1) # Default to "Line Type". Application.getInstance().getPreferences().addPreference("layerview/extruder_opacities", "") Application.getInstance().getPreferences().addPreference("layerview/show_travel_moves", False) From 63deea4721204d010a68e4800a5d287e5da4a492 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 22 Jan 2021 13:52:27 +0100 Subject: [PATCH 86/87] Fix model to use for comboboxes to check if they are empty This was checking against the wrong model, it seems. Contributes to issue CURA-7865. --- resources/qml/Widgets/ComboBox.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Widgets/ComboBox.qml b/resources/qml/Widgets/ComboBox.qml index 26324d7773..7eb366f0a3 100644 --- a/resources/qml/Widgets/ComboBox.qml +++ b/resources/qml/Widgets/ComboBox.qml @@ -23,7 +23,7 @@ ComboBox property var defaultTextOnEmptyModel: catalog.i18nc("@label", "No items to select from") // Text displayed in the combobox when the model is empty property var defaultTextOnEmptyIndex: "" // Text displayed in the combobox when the model has items but no item is selected - enabled: model.count > 0 + enabled: delegateModel.count > 0 onVisibleChanged: { popup.close() } @@ -81,7 +81,7 @@ ComboBox text: { - if (control.model.count == 0) + if (control.delegateModel.count == 0) { return control.defaultTextOnEmptyModel != "" ? control.defaultTextOnEmptyModel : control.defaultTextOnEmptyIndex } From e681a6d32d5be1e9d23cdf8962306c885f9c03b8 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 22 Jan 2021 18:07:59 +0100 Subject: [PATCH 87/87] Fix camera not found on pre-slice snapshot/thumbnail. --- cura/Snapshot.py | 4 ++-- plugins/UM3NetworkPrinting/src/ExportFileJob.py | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cura/Snapshot.py b/cura/Snapshot.py index 6f12aa88ba..c4c1ce000e 100644 --- a/cura/Snapshot.py +++ b/cura/Snapshot.py @@ -42,8 +42,8 @@ class Snapshot: """ scene = Application.getInstance().getController().getScene() - active_camera = scene.getActiveCamera() - render_width, render_height = active_camera.getWindowSize() + active_camera = scene.getActiveCamera() or scene.findCamera("3d") + render_width, render_height = (width, height) if active_camera is None else active_camera.getWindowSize() render_width = int(render_width) render_height = int(render_height) preview_pass = PreviewPass(render_width, render_height) diff --git a/plugins/UM3NetworkPrinting/src/ExportFileJob.py b/plugins/UM3NetworkPrinting/src/ExportFileJob.py index 6fde08cc5f..12f5a28877 100644 --- a/plugins/UM3NetworkPrinting/src/ExportFileJob.py +++ b/plugins/UM3NetworkPrinting/src/ExportFileJob.py @@ -1,3 +1,6 @@ +# Copyright (c) 2021 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + from typing import List, Optional from UM.FileHandler.FileHandler import FileHandler