From ab6835e93beb82bd7005e6c31155bbc9d4f9da50 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 3 Jan 2020 08:51:03 +0100 Subject: [PATCH 01/40] Back-ported RF100 V1, V2, and XL resources files from [dok-net RF100](https://github.com/dok-net/RF100) to Renkforce COTS stock firmware (temperature, flow rate; build volume on V1). --- .../definitions/renkforce_rf100.def.json | 59 +++-- .../definitions/renkforce_rf100_v2.def.json | 234 ++++++++++++++++++ .../definitions/renkforce_rf100_xl.def.json | 222 +++++++++++++++++ .../renkforce_rf100_xl_extruder_0.def.json | 15 ++ 4 files changed, 514 insertions(+), 16 deletions(-) create mode 100644 resources/definitions/renkforce_rf100_v2.def.json create mode 100644 resources/definitions/renkforce_rf100_xl.def.json create mode 100644 resources/extruders/renkforce_rf100_xl_extruder_0.def.json diff --git a/resources/definitions/renkforce_rf100.def.json b/resources/definitions/renkforce_rf100.def.json index 2ff34a7519..0ef269886b 100644 --- a/resources/definitions/renkforce_rf100.def.json +++ b/resources/definitions/renkforce_rf100.def.json @@ -18,10 +18,10 @@ "default_value": "skirt" }, "bottom_thickness": { - "value": "0.5" + "value": "0.6" }, "brim_width": { - "value": "2.0" + "value": "3.0" }, "cool_fan_enabled": { "value": "True" @@ -47,11 +47,20 @@ "infill_before_walls": { "value": "True" }, + "infill_line_width": { + "value": "0.6" + }, "infill_overlap": { "value": "15.0" }, + "infill_sparse_density": { + "value": "26.0" + }, + "ironing_enabled": { + "value": "True" + }, "layer_0_z_overlap": { - "value": "0.22" + "value": "0.11" }, "layer_height_0": { "value": "0.3" @@ -60,11 +69,23 @@ "value": "100" }, "machine_end_gcode": { - "default_value": ";End GCode\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 E-5 X-20 Y-20 ;retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nG0 Z{machine_height} ;move the platform all the way down\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done" + "default_value": ";End GCode\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-2 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done" }, "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, + "machine_head_with_fans_polygon": + { + "default_value": [ + [-26, -27], + [38, -27], + [38, 55], + [-26, 55] + ] + }, + "gantry_height": { + "value": "8" + }, "machine_height": { "value": "100" }, @@ -72,7 +93,7 @@ "default_value": "Renkforce RF100" }, "machine_start_gcode": { - "default_value": ";Start GCode\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\n;Put printing message on LCD screen\nM117 Printing..." + "default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E4.0 ;extrude 4.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..." }, "machine_width": { "value": "100" @@ -90,7 +111,7 @@ "value": "True" }, "raft_airgap": { - "value": "0.22" + "value": "0.33" }, "raft_base_line_spacing": { "value": "3.0" @@ -111,22 +132,25 @@ "value": "0.27" }, "raft_margin": { - "value": "5.0" + "value": "6.0" + }, + "raft_speed": { + "value": "20.0" }, "raft_surface_layers": { - "value": "2.0" + "value": "2" }, "raft_surface_line_spacing": { - "value": "3.0" + "value": "0.4" }, "raft_surface_line_width": { "value": "0.4" }, "raft_surface_thickness": { - "value": "0.27" + "value": "0.1" }, "retraction_amount": { - "value": "2.0" + "value": "3.0" }, "retraction_combing": { "default_value": "all" @@ -134,7 +158,7 @@ "retraction_enable": { "value": "True" }, - "retraction_hop_enabled": { + "retraction_hop": { "value": "1.0" }, "retraction_min_travel": { @@ -185,6 +209,9 @@ "support_infill_rate": { "value": "15 if support_enable else 0 if support_tree_enable else 15" }, + "support_line_width": { + "value": "0.6" + }, "support_pattern": { "default_value": "lines" }, @@ -192,13 +219,13 @@ "default_value": "everywhere" }, "support_xy_distance": { - "value": "0.5" + "value": "0.7" }, "support_z_distance": { - "value": "0.1" + "value": "0.35" }, - "top_thickness": { - "value": "0.5" + "top_bottom_thickness": { + "value": "0.8" }, "wall_thickness": { "value": "0.8" diff --git a/resources/definitions/renkforce_rf100_v2.def.json b/resources/definitions/renkforce_rf100_v2.def.json new file mode 100644 index 0000000000..5467ff0ba8 --- /dev/null +++ b/resources/definitions/renkforce_rf100_v2.def.json @@ -0,0 +1,234 @@ +{ + "version": 2, + "name": "Renkforce RF100 V2", + "inherits": "fdmprinter", + "metadata": { + "author": "Simon Peter (based on RF100.ini by Conrad Electronic SE)", + "file_formats": "text/x-gcode", + "manufacturer": "Renkforce", + "visible": true, + "machine_extruder_trains": + { + "0": "renkforce_rf100_extruder_0" + } + }, + + "overrides": { + "adhesion_type": { + "default_value": "skirt" + }, + "bottom_thickness": { + "value": "0.6" + }, + "brim_width": { + "value": "3.0" + }, + "cool_fan_enabled": { + "value": "True" + }, + "cool_fan_full_at_height": { + "value": "0.5" + }, + "cool_fan_speed_max": { + "value": "100.0" + }, + "cool_fan_speed_min": { + "value": "100.0" + }, + "cool_lift_head": { + "value": "True" + }, + "cool_min_layer_time": { + "value": "5.0" + }, + "cool_min_speed": { + "value": "10.0" + }, + "infill_before_walls": { + "value": "True" + }, + "infill_line_width": { + "value": "0.6" + }, + "infill_overlap": { + "value": "15.0" + }, + "infill_sparse_density": { + "value": "26.0" + }, + "ironing_enabled": { + "value": "True" + }, + "layer_0_z_overlap": { + "value": "0.11" + }, + "layer_height_0": { + "value": "0.3" + }, + "machine_depth": { + "value": "120" + }, + "machine_end_gcode": { + "default_value": ";End GCode\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-2 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done" + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "machine_head_with_fans_polygon": + { + "default_value": [ + [-26, -27], + [38, -27], + [38, 55], + [-26, 55] + ] + }, + "gantry_height": { + "value": "8" + }, + "machine_height": { + "value": "120" + }, + "machine_name": { + "default_value": "Renkforce RF100 V2" + }, + "machine_start_gcode": { + "default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E4.0 ;extrude 4.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..." + }, + "machine_width": { + "value": "120" + }, + "material_bed_temperature": { + "enabled": false + }, + "material_flow": { + "value": "110" + }, + "material_print_temperature": { + "value": "210.0" + }, + "ooze_shield_enabled": { + "value": "True" + }, + "raft_airgap": { + "value": "0.33" + }, + "raft_base_line_spacing": { + "value": "3.0" + }, + "raft_base_line_width": { + "value": "1.0" + }, + "raft_base_thickness": { + "value": "0.3" + }, + "raft_interface_line_spacing": { + "value": "3.0" + }, + "raft_interface_line_width": { + "value": "0.4" + }, + "raft_interface_thickness": { + "value": "0.27" + }, + "raft_margin": { + "value": "6.0" + }, + "raft_speed": { + "value": "20.0" + }, + "raft_surface_layers": { + "value": "2" + }, + "raft_surface_line_spacing": { + "value": "0.4" + }, + "raft_surface_line_width": { + "value": "0.4" + }, + "raft_surface_thickness": { + "value": "0.1" + }, + "retraction_amount": { + "value": "3.0" + }, + "retraction_combing": { + "default_value": "all" + }, + "retraction_enable": { + "value": "True" + }, + "retraction_hop": { + "value": "1.0" + }, + "retraction_min_travel": { + "value": "1.5" + }, + "retraction_speed": { + "value": "40.0" + }, + "skin_overlap": { + "value": "15.0" + }, + "skirt_brim_minimal_length": { + "value": "150.0" + }, + "skirt_gap": { + "value": "3.0" + }, + "skirt_line_count": { + "value": "3" + }, + "speed_infill": { + "value": "50.0" + }, + "speed_layer_0": { + "value": "15.0" + }, + "speed_print": { + "value": "50.0" + }, + "speed_topbottom": { + "value": "30.0" + }, + "speed_travel": { + "value": "50.0" + }, + "speed_wall_0": { + "value": "25.0" + }, + "speed_wall_x": { + "value": "35.0" + }, + "support_angle": { + "value": "60.0" + }, + "support_enable": { + "value": "False" + }, + "support_infill_rate": { + "value": "15 if support_enable else 0 if support_tree_enable else 15" + }, + "support_line_width": { + "value": "0.6" + }, + "support_pattern": { + "default_value": "lines" + }, + "support_type": { + "default_value": "everywhere" + }, + "support_xy_distance": { + "value": "0.7" + }, + "support_z_distance": { + "value": "0.35" + }, + "top_bottom_thickness": { + "value": "0.8" + }, + "wall_thickness": { + "value": "0.8" + } + } +} diff --git a/resources/definitions/renkforce_rf100_xl.def.json b/resources/definitions/renkforce_rf100_xl.def.json new file mode 100644 index 0000000000..cf5bbcd006 --- /dev/null +++ b/resources/definitions/renkforce_rf100_xl.def.json @@ -0,0 +1,222 @@ +{ + "version": 2, + "name": "Renkforce RF100 XL", + "inherits": "fdmprinter", + "metadata": { + "author": "Simon Peter (based on RF100.ini by Conrad Electronic SE)", + "file_formats": "text/x-gcode", + "manufacturer": "Renkforce", + "visible": true, + "machine_extruder_trains": + { + "0": "renkforce_rf100_xl_extruder_0" + } + }, + + "overrides": { + "adhesion_type": { + "default_value": "skirt" + }, + "bottom_thickness": { + "value": "0.6" + }, + "brim_width": { + "value": "3.0" + }, + "cool_fan_enabled": { + "value": "True" + }, + "cool_fan_full_at_height": { + "value": "0.5" + }, + "cool_fan_speed_max": { + "value": "100.0" + }, + "cool_fan_speed_min": { + "value": "100.0" + }, + "cool_lift_head": { + "value": "True" + }, + "cool_min_layer_time": { + "value": "5.0" + }, + "cool_min_speed": { + "value": "10.0" + }, + "infill_before_walls": { + "value": "True" + }, + "infill_line_width": { + "value": "0.6" + }, + "infill_overlap": { + "value": "15.0" + }, + "infill_sparse_density": { + "value": "26.0" + }, + "ironing_enabled": { + "value": "True" + }, + "layer_0_z_overlap": { + "value": "0.11" + }, + "layer_height_0": { + "value": "0.3" + }, + "machine_depth": { + "value": "200" + }, + "machine_end_gcode": { + "default_value": ";End GCode\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-2 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done" + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "machine_heated_bed": { + "default_value": "true" + }, + "machine_height": { + "value": "200" + }, + "machine_name": { + "default_value": "Renkforce RF100 XL" + }, + "machine_start_gcode": { + "default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E4.0 ;extrude 4.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..." + }, + "machine_width": { + "value": "200" + }, + "material_bed_temperature": { + "value": "70" + }, + "material_print_temperature": { + "value": "210.0" + }, + "ooze_shield_enabled": { + "value": "True" + }, + "raft_airgap": { + "value": "0.33" + }, + "raft_base_line_spacing": { + "value": "3.0" + }, + "raft_base_line_width": { + "value": "1.0" + }, + "raft_base_thickness": { + "value": "0.3" + }, + "raft_interface_line_spacing": { + "value": "3.0" + }, + "raft_interface_line_width": { + "value": "0.4" + }, + "raft_interface_thickness": { + "value": "0.27" + }, + "raft_margin": { + "value": "6.0" + }, + "raft_speed": { + "value": "20.0" + }, + "raft_surface_layers": { + "value": "2" + }, + "raft_surface_line_spacing": { + "value": "0.4" + }, + "raft_surface_line_width": { + "value": "0.4" + }, + "raft_surface_thickness": { + "value": "0.1" + }, + "retraction_amount": { + "value": "3.0" + }, + "retraction_combing": { + "default_value": "all" + }, + "retraction_enable": { + "value": "True" + }, + "retraction_hop": { + "value": "1.0" + }, + "retraction_min_travel": { + "value": "1.5" + }, + "retraction_speed": { + "value": "40.0" + }, + "skin_overlap": { + "value": "15.0" + }, + "skirt_brim_minimal_length": { + "value": "150.0" + }, + "skirt_gap": { + "value": "3.0" + }, + "skirt_line_count": { + "value": "3" + }, + "speed_infill": { + "value": "50.0" + }, + "speed_layer_0": { + "value": "15.0" + }, + "speed_print": { + "value": "50.0" + }, + "speed_topbottom": { + "value": "30.0" + }, + "speed_travel": { + "value": "50.0" + }, + "speed_wall_0": { + "value": "25.0" + }, + "speed_wall_x": { + "value": "35.0" + }, + "support_angle": { + "value": "60.0" + }, + "support_enable": { + "value": "False" + }, + "support_infill_rate": { + "value": "15 if support_enable else 0 if support_tree_enable else 15" + }, + "support_line_width": { + "value": "0.6" + }, + "support_pattern": { + "default_value": "lines" + }, + "support_type": { + "default_value": "everywhere" + }, + "support_xy_distance": { + "value": "0.7" + }, + "support_z_distance": { + "value": "0.35" + }, + "top_bottom_thickness": { + "value": "0.8" + }, + "wall_thickness": { + "value": "0.8" + } + } +} diff --git a/resources/extruders/renkforce_rf100_xl_extruder_0.def.json b/resources/extruders/renkforce_rf100_xl_extruder_0.def.json new file mode 100644 index 0000000000..718a3738c4 --- /dev/null +++ b/resources/extruders/renkforce_rf100_xl_extruder_0.def.json @@ -0,0 +1,15 @@ +{ + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "renkforce_rf100_xl", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + } +} From a61207ca0488d45ea2c8cdf324f7e471906ca457 Mon Sep 17 00:00:00 2001 From: "Dirk O. Kaar" Date: Fri, 3 Jan 2020 15:45:31 +0100 Subject: [PATCH 02/40] Release v3.0.3 of [dok-net RF100](https://github.com/dok-net/RF100) brings less retraction stringing and much better qualitiy for small objects/layers. --- resources/definitions/renkforce_rf100.def.json | 13 +++++-------- resources/definitions/renkforce_rf100_v2.def.json | 13 +++++-------- resources/definitions/renkforce_rf100_xl.def.json | 13 +++++-------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/resources/definitions/renkforce_rf100.def.json b/resources/definitions/renkforce_rf100.def.json index 0ef269886b..33be020a95 100644 --- a/resources/definitions/renkforce_rf100.def.json +++ b/resources/definitions/renkforce_rf100.def.json @@ -39,10 +39,10 @@ "value": "True" }, "cool_min_layer_time": { - "value": "5.0" + "value": "1.0" }, "cool_min_speed": { - "value": "10.0" + "value": "5.0" }, "infill_before_walls": { "value": "True" @@ -69,7 +69,7 @@ "value": "100" }, "machine_end_gcode": { - "default_value": ";End GCode\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-2 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done" + "default_value": ";End GCode\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-4 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done" }, "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" @@ -93,7 +93,7 @@ "default_value": "Renkforce RF100" }, "machine_start_gcode": { - "default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E4.0 ;extrude 4.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..." + "default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E6.0 ;extrude 6.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..." }, "machine_width": { "value": "100" @@ -150,7 +150,7 @@ "value": "0.1" }, "retraction_amount": { - "value": "3.0" + "value": "5.0" }, "retraction_combing": { "default_value": "all" @@ -164,9 +164,6 @@ "retraction_min_travel": { "value": "1.5" }, - "retraction_speed": { - "value": "40.0" - }, "skin_overlap": { "value": "15.0" }, diff --git a/resources/definitions/renkforce_rf100_v2.def.json b/resources/definitions/renkforce_rf100_v2.def.json index 5467ff0ba8..c1dd9b003b 100644 --- a/resources/definitions/renkforce_rf100_v2.def.json +++ b/resources/definitions/renkforce_rf100_v2.def.json @@ -39,10 +39,10 @@ "value": "True" }, "cool_min_layer_time": { - "value": "5.0" + "value": "1.0" }, "cool_min_speed": { - "value": "10.0" + "value": "5.0" }, "infill_before_walls": { "value": "True" @@ -69,7 +69,7 @@ "value": "120" }, "machine_end_gcode": { - "default_value": ";End GCode\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-2 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done" + "default_value": ";End GCode\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-4 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done" }, "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" @@ -93,7 +93,7 @@ "default_value": "Renkforce RF100 V2" }, "machine_start_gcode": { - "default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E4.0 ;extrude 4.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..." + "default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E6.0 ;extrude 6.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..." }, "machine_width": { "value": "120" @@ -150,7 +150,7 @@ "value": "0.1" }, "retraction_amount": { - "value": "3.0" + "value": "5.0" }, "retraction_combing": { "default_value": "all" @@ -164,9 +164,6 @@ "retraction_min_travel": { "value": "1.5" }, - "retraction_speed": { - "value": "40.0" - }, "skin_overlap": { "value": "15.0" }, diff --git a/resources/definitions/renkforce_rf100_xl.def.json b/resources/definitions/renkforce_rf100_xl.def.json index cf5bbcd006..f7707913e2 100644 --- a/resources/definitions/renkforce_rf100_xl.def.json +++ b/resources/definitions/renkforce_rf100_xl.def.json @@ -39,10 +39,10 @@ "value": "True" }, "cool_min_layer_time": { - "value": "5.0" + "value": "1.0" }, "cool_min_speed": { - "value": "10.0" + "value": "5.0" }, "infill_before_walls": { "value": "True" @@ -69,7 +69,7 @@ "value": "200" }, "machine_end_gcode": { - "default_value": ";End GCode\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-2 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done" + "default_value": ";End GCode\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-4 F300 ;move Z up a bit and retract filament even more\nM104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG0 Z{machine_height} F1800 ;move the platform all the way down\nG28 X0 Y0 F1800 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning\nM117 Done" }, "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" @@ -84,7 +84,7 @@ "default_value": "Renkforce RF100 XL" }, "machine_start_gcode": { - "default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E4.0 ;extrude 4.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..." + "default_value": ";Sliced at: {day} {date} {time}\nG21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG1 Z5.0 F1800 ;move Z to 5mm\nG28 X0 Y0 F1800 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstop\nG92 E0 ;zero the extruded length\nG1 F200 E6.0 ;extrude 6.0mm of feed stock to build pressure\nG1 Z5.0 F300 ;move the platform down 5mm\nG92 E0 ;zero the extruded length again\nG1 F1800\n;Put printing message on LCD screen\nM117 Printing..." }, "machine_width": { "value": "200" @@ -138,7 +138,7 @@ "value": "0.1" }, "retraction_amount": { - "value": "3.0" + "value": "5.0" }, "retraction_combing": { "default_value": "all" @@ -152,9 +152,6 @@ "retraction_min_travel": { "value": "1.5" }, - "retraction_speed": { - "value": "40.0" - }, "skin_overlap": { "value": "15.0" }, From 1f2e079185d1493df13989b566c2ef9fe1187b6d Mon Sep 17 00:00:00 2001 From: Johnathan Chu Date: Mon, 6 Jan 2020 21:45:43 -0800 Subject: [PATCH 03/40] Update DisplayFilenameAndLayerOnLCD.py: - Fixed filename appearing in middle of text when scrolling is disabled and displaying max layer. - Fixed filename appearing twice when scrolling is enabled and displaying max layers. --- .../scripts/DisplayFilenameAndLayerOnLCD.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayFilenameAndLayerOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayFilenameAndLayerOnLCD.py index 001beecd3b..2bf687eb47 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayFilenameAndLayerOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayFilenameAndLayerOnLCD.py @@ -72,7 +72,7 @@ class DisplayFilenameAndLayerOnLCD(Script): lcd_text = "M117 Printing " + name + " - Layer " i = self.getSettingValueByKey("startNum") for layer in data: - display_text = lcd_text + str(i) + " " + name + display_text = lcd_text + str(i) layer_index = data.index(layer) lines = layer.split("\n") for line in lines: @@ -82,8 +82,13 @@ class DisplayFilenameAndLayerOnLCD(Script): if line.startswith(";LAYER:"): if self.getSettingValueByKey("maxlayer"): display_text = display_text + " of " + max_layer + if not self.getSettingValueByKey("scroll"): + display_text = display_text + " " + name else: - display_text = display_text + "!" + if not self.getSettingValueByKey("scroll"): + display_text = display_text + " " + name + "!" + else: + display_text = display_text + "!" line_index = lines.index(line) lines.insert(line_index + 1, display_text) i += 1 From 414b696b32cb9e3f5e5a326bdb33b7f7ac573dd8 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 14 Jan 2020 11:04:42 +0100 Subject: [PATCH 04/40] Use the resetWorkspace for new project This makes it a bit cleaner as we don't have to copy functionality CURA-6627 --- resources/qml/MainWindow/ApplicationMenu.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/MainWindow/ApplicationMenu.qml b/resources/qml/MainWindow/ApplicationMenu.qml index 30e44d7d3b..72a371e2f5 100644 --- a/resources/qml/MainWindow/ApplicationMenu.qml +++ b/resources/qml/MainWindow/ApplicationMenu.qml @@ -127,8 +127,8 @@ Item icon: StandardIcon.Question onYes: { - CuraApplication.deleteAll(); - Cura.Actions.resetProfile.trigger(); + CuraApplication.resetWorkspace() + Cura.Actions.resetProfile.trigger() UM.Controller.setActiveStage("PrepareStage") } } From b6d1429eb779b4724ea186d7d9eaee75e99054c3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 14 Jan 2020 15:27:30 +0100 Subject: [PATCH 05/40] Ensure that 3mf workspace reader loads aditional metadata CURA-6627 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 2e395c9efa..953980d0b7 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -4,7 +4,7 @@ from configparser import ConfigParser import zipfile import os -from typing import cast, Dict, List, Optional, Tuple +from typing import cast, Dict, List, Optional, Tuple, Any import xml.etree.ElementTree as ET @@ -732,7 +732,17 @@ class ThreeMFWorkspaceReader(WorkspaceReader): base_file_name = os.path.basename(file_name) self.setWorkspaceName(base_file_name) - return nodes + + return nodes, self._loadMetadata(file_name) + + def _loadMetadata(self, file_name) -> Dict[str, Dict[str, Any]]: + archive = zipfile.ZipFile(file_name, "r") + import json + try: + return json.loads(archive.open("Cura/plugin_metadata.json").read().decode("utf-8")) + except KeyError: + # The plugin_metadata.json file doesn't exist + return dict() def _processQualityChanges(self, global_stack): if self._machine_info.quality_changes_info is None: From 6ae3172287a20cd2e54b22a95997aa10c7bfe29a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 14 Jan 2020 15:40:38 +0100 Subject: [PATCH 06/40] Store metadata when writing the workspace --- plugins/3MFWriter/ThreeMFWorkspaceWriter.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index 33df0bfe90..953b0a57e6 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -73,11 +73,22 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter): version_config_parser.write(version_file_string) archive.writestr(version_file, version_file_string.getvalue()) + self._writePluginMetadataToArchive(archive) + # Close the archive & reset states. archive.close() mesh_writer.setStoreArchive(False) return True + def _writePluginMetadataToArchive(self, archive): + file_name = "Cura/plugin_metadata.json" + + file_in_archive = zipfile.ZipInfo(file_name) + # For some reason we have to set the compress type of each file as well (it doesn't keep the type of the entire archive) + file_in_archive.compress_type = zipfile.ZIP_DEFLATED + import json + archive.writestr(file_in_archive, json.dumps(Application.getInstance()._workspace_metadata_storage.getAllData())) + ## Helper function that writes ContainerStacks, InstanceContainers and DefinitionContainers to the archive. # \param container That follows the \type{ContainerInterface} to archive. # \param archive The archive to write to. From b8dbc1d1600c72f49ac7e9f781b0f5236afee201 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 16 Jan 2020 09:50:45 +0100 Subject: [PATCH 07/40] Store the data from each plugin in it's own folder CURA-6627 --- plugins/3MFWriter/ThreeMFWorkspaceWriter.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index 953b0a57e6..cd56e77ebd 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -81,13 +81,15 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter): return True def _writePluginMetadataToArchive(self, archive): - file_name = "Cura/plugin_metadata.json" + file_name_template = "%s/plugin_metadata.json" - file_in_archive = zipfile.ZipInfo(file_name) - # For some reason we have to set the compress type of each file as well (it doesn't keep the type of the entire archive) - file_in_archive.compress_type = zipfile.ZIP_DEFLATED - import json - archive.writestr(file_in_archive, json.dumps(Application.getInstance()._workspace_metadata_storage.getAllData())) + for plugin_id, metadata in Application.getInstance()._workspace_metadata_storage.getAllData().items(): + file_name = file_name_template % plugin_id + file_in_archive = zipfile.ZipInfo(file_name) + # We have to set the compress type of each file as well (it doesn't keep the type of the entire archive) + file_in_archive.compress_type = zipfile.ZIP_DEFLATED + import json + archive.writestr(file_in_archive, json.dumps(metadata, separators = (", ", ": "), indent = 4)) ## Helper function that writes ContainerStacks, InstanceContainers and DefinitionContainers to the archive. # \param container That follows the \type{ContainerInterface} to archive. From 4e0e0c033953e97325e37495cbe717866cfb6f95 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 16 Jan 2020 11:19:29 +0100 Subject: [PATCH 08/40] Add skipkeys flag to writing plugin metadata to workspace CURA-6627 --- plugins/3MFWriter/ThreeMFWorkspaceWriter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index cd56e77ebd..45d9db77f6 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -89,7 +89,7 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter): # We have to set the compress type of each file as well (it doesn't keep the type of the entire archive) file_in_archive.compress_type = zipfile.ZIP_DEFLATED import json - archive.writestr(file_in_archive, json.dumps(metadata, separators = (", ", ": "), indent = 4)) + archive.writestr(file_in_archive, json.dumps(metadata, separators = (", ", ": "), indent = 4, skipkeys = True)) ## Helper function that writes ContainerStacks, InstanceContainers and DefinitionContainers to the archive. # \param container That follows the \type{ContainerInterface} to archive. From 46050f9618afffce36701530010cd663c03ce41b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 16 Jan 2020 11:25:30 +0100 Subject: [PATCH 09/40] Use getter instead of protected attribute CURA-6627 --- plugins/3MFWriter/ThreeMFWorkspaceWriter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index 45d9db77f6..08918e1c12 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -83,7 +83,7 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter): def _writePluginMetadataToArchive(self, archive): file_name_template = "%s/plugin_metadata.json" - for plugin_id, metadata in Application.getInstance()._workspace_metadata_storage.getAllData().items(): + for plugin_id, metadata in Application.getInstance().getWorkspaceMetadataStorage().getAllData().items(): file_name = file_name_template % plugin_id file_in_archive = zipfile.ZipInfo(file_name) # We have to set the compress type of each file as well (it doesn't keep the type of the entire archive) From 712cebcdd2d11310199b62e5e2aa0a2ded712515 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 16 Jan 2020 11:43:07 +0100 Subject: [PATCH 10/40] Let 3mf workspace reader read from files per plugin CURA-6627 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 953980d0b7..7d4efa6e74 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -737,12 +737,20 @@ class ThreeMFWorkspaceReader(WorkspaceReader): def _loadMetadata(self, file_name) -> Dict[str, Dict[str, Any]]: archive = zipfile.ZipFile(file_name, "r") + + metadata_files = [name for name in archive.namelist() if name.endswith("plugin_metadata.json")] import json - try: - return json.loads(archive.open("Cura/plugin_metadata.json").read().decode("utf-8")) - except KeyError: - # The plugin_metadata.json file doesn't exist - return dict() + + result = dict() + + for metadata_file in metadata_files: + try: + plugin_id = metadata_file.split("/")[0] + result[plugin_id] = json.loads(archive.open("Cura/plugin_metadata.json").read().decode("utf-8")) + except Exception: + Logger.logException("w", "Unable to retrieve metadata for %s", metadata_file) + + return result def _processQualityChanges(self, global_stack): if self._machine_info.quality_changes_info is None: From bee641da5ac08477e0956f181dd80b01a0a1be68 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 20 Jan 2020 15:30:23 +0100 Subject: [PATCH 11/40] Remove local import of json CURA-7005 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 7d4efa6e74..e92b60976b 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -4,6 +4,7 @@ from configparser import ConfigParser import zipfile import os +import json from typing import cast, Dict, List, Optional, Tuple, Any import xml.etree.ElementTree as ET @@ -739,7 +740,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): archive = zipfile.ZipFile(file_name, "r") metadata_files = [name for name in archive.namelist() if name.endswith("plugin_metadata.json")] - import json result = dict() From 52ce10639932cc9da4c471864bdb315e6f33a295 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 20 Jan 2020 15:37:57 +0100 Subject: [PATCH 12/40] Change _loadMetadata to be static CURA-6627 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index e92b60976b..ba169f3dab 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -736,7 +736,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader): return nodes, self._loadMetadata(file_name) - def _loadMetadata(self, file_name) -> Dict[str, Dict[str, Any]]: + @staticmethod + def _loadMetadata(file_name: str) -> Dict[str, Dict[str, Any]]: archive = zipfile.ZipFile(file_name, "r") metadata_files = [name for name in archive.namelist() if name.endswith("plugin_metadata.json")] From 06ccd882e1395909cfa2d66be6310270bcfc94bb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 20 Jan 2020 16:03:56 +0100 Subject: [PATCH 13/40] Add missing typing CURA-6627 --- plugins/3MFWriter/ThreeMFWorkspaceWriter.py | 3 ++- plugins/3MFWriter/ThreeMFWriter.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index 08918e1c12..10a21f445b 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -80,7 +80,8 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter): mesh_writer.setStoreArchive(False) return True - def _writePluginMetadataToArchive(self, archive): + @staticmethod + def _writePluginMetadataToArchive(archive: zipfile.ZipFile) -> None: file_name_template = "%s/plugin_metadata.json" for plugin_id, metadata in Application.getInstance().getWorkspaceMetadataStorage().getAllData().items(): diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index 640aabd30c..9860804542 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -1,5 +1,6 @@ # Copyright (c) 2015 Ultimaker B.V. # Uranium is released under the terms of the LGPLv3 or higher. +from typing import Optional from UM.Mesh.MeshWriter import MeshWriter from UM.Math.Vector import Vector @@ -40,7 +41,7 @@ class ThreeMFWriter(MeshWriter): } self._unit_matrix_string = self._convertMatrixToString(Matrix()) - self._archive = None + self._archive = None # type: Optional[zipfile.ZipFile] self._store_archive = False def _convertMatrixToString(self, matrix): From 365c30fadb5923a0809a252b9925afce9251b6b8 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 21 Jan 2020 09:52:33 +0100 Subject: [PATCH 14/40] Added check to reevaluate dismissed incompatible packages (in case the package gets updated and has the correct SDK version) CURA-7090-addition --- .../Toolbox/src/CloudSync/CloudPackageChecker.py | 8 ++++++-- .../src/CloudSync/SubscribedPackagesModel.py | 13 ++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py index 78d13f34fe..2246051fe4 100644 --- a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py +++ b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py @@ -43,15 +43,19 @@ class CloudPackageChecker(QObject): def _handleCompatibilityData(self, json_data) -> None: user_subscribed_packages = [plugin["package_id"] for plugin in json_data] user_installed_packages = self._package_manager.getUserInstalledPackages() + + # We need to re-evaluate the dismissed packages + # (i.e. some package might got updated to the correct SDK version in the meantime, + # hence remove them from the Dismissed Incompatible list) + self._package_manager.reEvaluateDismissedPackages(json_data) user_dismissed_packages = self._package_manager.getDismissedPackages() if user_dismissed_packages: user_installed_packages += user_dismissed_packages # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace package_discrepancy = list(set(user_subscribed_packages).difference(user_installed_packages)) - self._model.setMetadata(json_data) self._model.addDiscrepancies(package_discrepancy) - self._model.initialize() + self._model.initialize(json_data) if not self._model.hasCompatiblePackages: return None diff --git a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py b/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py index 4a0f559748..833879a9fe 100644 --- a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py @@ -37,27 +37,22 @@ class SubscribedPackagesModel(ListModel): return True return False - # Sets the "is_compatible" to True for the given package, in memory - @pyqtSlot() def dismissPackage(self, package_id: str) -> None: package = self.find(key="package_id", value=package_id) - if package != -1: + if package != -1: # find() returns -1 if it doesn't finds what is looking for self.setProperty(package, property="is_dismissed", value=True) Logger.debug("Package {} has been dismissed".format(package_id)) - def setMetadata(self, data: List[Dict[str, List[Any]]]) -> None: - self._metadata = data - def addDiscrepancies(self, discrepancy: List[str]) -> None: self._discrepancies = discrepancy def getCompatiblePackages(self): - return [x for x in self._items if x["is_compatible"]] + return [package for package in self._items if package["is_compatible"]] - def initialize(self) -> None: + def initialize(self, json_data: List[Dict[str, List[Any]]]) -> None: self._items.clear() - for item in self._metadata: + for item in json_data: if item["package_id"] not in self._discrepancies: continue package = { From 8abaa600522dd21a9f2aa5e987ef0fd094e2cd17 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 21 Jan 2020 11:24:31 +0100 Subject: [PATCH 15/40] Passed the sdk_version to the reevalute() function CURA-7090 --- plugins/Toolbox/src/CloudSync/CloudPackageChecker.py | 8 +++++--- plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py index 2246051fe4..cadb3435fc 100644 --- a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py +++ b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py @@ -8,11 +8,12 @@ from UM import i18nCatalog from UM.Logger import Logger from UM.Message import Message from UM.Signal import Signal -from cura.CuraApplication import CuraApplication +from cura.CuraApplication import CuraApplication, ApplicationMetadata from ..CloudApiModel import CloudApiModel from .SubscribedPackagesModel import SubscribedPackagesModel from ..UltimakerCloudScope import UltimakerCloudScope +from typing import List, Dict, Any class CloudPackageChecker(QObject): def __init__(self, application: CuraApplication) -> None: @@ -25,6 +26,7 @@ class CloudPackageChecker(QObject): self._application.initializationFinished.connect(self._onAppInitialized) self._i18n_catalog = i18nCatalog("cura") + self._sdk_version = ApplicationMetadata.CuraSDKVersion # This is a plugin, so most of the components required are not ready when # this is initialized. Therefore, we wait until the application is ready. @@ -40,14 +42,14 @@ class CloudPackageChecker(QObject): if self._application.getCuraAPI().account.isLoggedIn: self._getUserPackages() - def _handleCompatibilityData(self, json_data) -> None: + def _handleCompatibilityData(self, json_data: List[Dict[str, List[Any]]]) -> None: user_subscribed_packages = [plugin["package_id"] for plugin in json_data] user_installed_packages = self._package_manager.getUserInstalledPackages() # We need to re-evaluate the dismissed packages # (i.e. some package might got updated to the correct SDK version in the meantime, # hence remove them from the Dismissed Incompatible list) - self._package_manager.reEvaluateDismissedPackages(json_data) + self._package_manager.reEvaluateDismissedPackages(json_data, self._sdk_version) user_dismissed_packages = self._package_manager.getDismissedPackages() if user_dismissed_packages: user_installed_packages += user_dismissed_packages diff --git a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py b/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py index 833879a9fe..270f0a2b01 100644 --- a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py @@ -47,7 +47,7 @@ class SubscribedPackagesModel(ListModel): def addDiscrepancies(self, discrepancy: List[str]) -> None: self._discrepancies = discrepancy - def getCompatiblePackages(self): + def getCompatiblePackages(self) -> List[str]: return [package for package in self._items if package["is_compatible"]] def initialize(self, json_data: List[Dict[str, List[Any]]]) -> None: From 1287ebdc519954339ac7bc622200b847fc5341f2 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Tue, 21 Jan 2020 14:43:02 +0100 Subject: [PATCH 16/40] Change backgroundColor and margin for licenseDialog CURA-7129 --- plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml b/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml index 3e7cdc9df8..5d29e70f97 100644 --- a/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml @@ -20,6 +20,8 @@ UM.Dialog minimumHeight: UM.Theme.getSize("license_window_minimum").height width: minimumWidth height: minimumHeight + backgroundColor: UM.Theme.getColor("main_background") + margin: screenScaleFactor * 10 Item { @@ -27,7 +29,6 @@ UM.Dialog UM.I18nCatalog{id: catalog; name: "cura"} - Label { id: licenseHeader From e7ed93c132ea05a238526bb3ab48308aa38b404b Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 21 Jan 2020 14:51:53 +0100 Subject: [PATCH 17/40] fixed typing CURA-7090 --- plugins/Toolbox/src/CloudSync/CloudPackageChecker.py | 2 +- plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py index cadb3435fc..f631a81fb2 100644 --- a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py +++ b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py @@ -42,7 +42,7 @@ class CloudPackageChecker(QObject): if self._application.getCuraAPI().account.isLoggedIn: self._getUserPackages() - def _handleCompatibilityData(self, json_data: List[Dict[str, List[Any]]]) -> None: + def _handleCompatibilityData(self, json_data: List[Dict[str, Any]]) -> None: user_subscribed_packages = [plugin["package_id"] for plugin in json_data] user_installed_packages = self._package_manager.getUserInstalledPackages() diff --git a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py b/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py index 270f0a2b01..fd5eae254d 100644 --- a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py @@ -47,10 +47,10 @@ class SubscribedPackagesModel(ListModel): def addDiscrepancies(self, discrepancy: List[str]) -> None: self._discrepancies = discrepancy - def getCompatiblePackages(self) -> List[str]: + def getCompatiblePackages(self) -> List[Dict[str, Any]]: return [package for package in self._items if package["is_compatible"]] - def initialize(self, json_data: List[Dict[str, List[Any]]]) -> None: + def initialize(self, json_data: List[Dict[str, Any]]) -> None: self._items.clear() for item in json_data: if item["package_id"] not in self._discrepancies: From 5c498de1e47f9d413dd007cc2d309017fa6ab982 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Tue, 21 Jan 2020 15:07:11 +0100 Subject: [PATCH 18/40] Use meaningful variable name && some TODOs for later. CURA-7090 --- plugins/Toolbox/src/CloudSync/CloudPackageChecker.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py index f631a81fb2..ee068e6115 100644 --- a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py +++ b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py @@ -42,24 +42,24 @@ class CloudPackageChecker(QObject): if self._application.getCuraAPI().account.isLoggedIn: self._getUserPackages() - def _handleCompatibilityData(self, json_data: List[Dict[str, Any]]) -> None: - user_subscribed_packages = [plugin["package_id"] for plugin in json_data] + def _handleCompatibilityData(self, subscribed_packages_payload: List[Dict[str, Any]]) -> None: + user_subscribed_packages = [plugin["package_id"] for plugin in subscribed_packages_payload] user_installed_packages = self._package_manager.getUserInstalledPackages() # We need to re-evaluate the dismissed packages # (i.e. some package might got updated to the correct SDK version in the meantime, # hence remove them from the Dismissed Incompatible list) - self._package_manager.reEvaluateDismissedPackages(json_data, self._sdk_version) + self._package_manager.reEvaluateDismissedPackages(subscribed_packages_payload, self._sdk_version) user_dismissed_packages = self._package_manager.getDismissedPackages() if user_dismissed_packages: user_installed_packages += user_dismissed_packages # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace package_discrepancy = list(set(user_subscribed_packages).difference(user_installed_packages)) - self._model.addDiscrepancies(package_discrepancy) - self._model.initialize(json_data) + self._model.addDiscrepancies(package_discrepancy) # TODO: Move these two lines below, under if package_discrepancy: + self._model.initialize(subscribed_packages_payload) - if not self._model.hasCompatiblePackages: + if not self._model.hasCompatiblePackages: # TODO: Remove these two lines... return None if package_discrepancy: From b3812a36300dc54a7201981e7665a4ce966a3127 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Tue, 21 Jan 2020 17:07:21 +0100 Subject: [PATCH 19/40] Add icon to licenseDialog (clowd flow) CURA-7129 --- .../qml/dialogs/ToolboxLicenseDialog.qml | 47 +++++++++++++++---- .../src/CloudSync/DownloadPresenter.py | 12 ++++- plugins/Toolbox/src/CloudSync/LicenseModel.py | 23 ++++++--- .../Toolbox/src/CloudSync/LicensePresenter.py | 10 ++-- .../Toolbox/src/CloudSync/SyncOrchestrator.py | 4 +- 5 files changed, 72 insertions(+), 24 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml b/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml index 5d29e70f97..dba4a5ba58 100644 --- a/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml @@ -5,6 +5,7 @@ import QtQuick 2.10 import QtQuick.Dialogs 1.1 import QtQuick.Window 2.2 import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 import QtQuick.Controls.Styles 1.4 // TODO: Switch to QtQuick.Controls 2.x and remove QtQuick.Controls.Styles @@ -23,7 +24,7 @@ UM.Dialog backgroundColor: UM.Theme.getColor("main_background") margin: screenScaleFactor * 10 - Item + ColumnLayout { anchors.fill: parent @@ -32,20 +33,48 @@ UM.Dialog Label { id: licenseHeader - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - text: licenseModel.headerText + Layout.fillWidth: true + text: catalog.i18nc("@label", "You need to accept the license to install the package") wrapMode: Text.Wrap renderType: Text.NativeRendering } + + Row { + id: packageRow + + anchors.left: parent.left + anchors.right: parent.right + height: childrenRect.height + + + Image + { + id: icon + width: 30 * screenScaleFactor + height: width + fillMode: Image.PreserveAspectFit + source: licenseModel.iconUrl || "../../images/logobot.svg" + mipmap: true + } + + Label + { + id: packageName + text: licenseModel.packageName + anchors.verticalCenter: icon.verticalCenter + height: contentHeight + wrapMode: Text.Wrap + renderType: Text.NativeRendering + } + + + } + TextArea { id: licenseText - anchors.top: licenseHeader.bottom - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right + Layout.fillWidth: true + Layout.fillHeight: true anchors.topMargin: UM.Theme.getSize("default_margin").height readOnly: true text: licenseModel.licenseText diff --git a/plugins/Toolbox/src/CloudSync/DownloadPresenter.py b/plugins/Toolbox/src/CloudSync/DownloadPresenter.py index f19cac047a..d79d031949 100644 --- a/plugins/Toolbox/src/CloudSync/DownloadPresenter.py +++ b/plugins/Toolbox/src/CloudSync/DownloadPresenter.py @@ -62,7 +62,8 @@ class DownloadPresenter: "received": 0, "total": 1, # make sure this is not considered done yet. Also divByZero-safe "file_written": None, - "request_data": request_data + "request_data": request_data, + "package_model": item } self._started = True @@ -128,7 +129,14 @@ class DownloadPresenter: if not item["file_written"]: return False - success_items = {package_id : value["file_written"] for package_id, value in self._progress.items()} + success_items = { + package_id: + { + "package_path": value["file_written"], + "icon_url": value["package_model"]["icon_url"] + } + for package_id, value in self._progress.items() + } error_items = [package_id for package_id in self._error] self._progress_message.hide() diff --git a/plugins/Toolbox/src/CloudSync/LicenseModel.py b/plugins/Toolbox/src/CloudSync/LicenseModel.py index c3b5ee5d31..66ea6ca5da 100644 --- a/plugins/Toolbox/src/CloudSync/LicenseModel.py +++ b/plugins/Toolbox/src/CloudSync/LicenseModel.py @@ -7,8 +7,9 @@ catalog = i18nCatalog("cura") # Model for the ToolboxLicenseDialog class LicenseModel(QObject): dialogTitleChanged = pyqtSignal() - headerChanged = pyqtSignal() + packageNameChanged = pyqtSignal() licenseTextChanged = pyqtSignal() + iconChanged = pyqtSignal() def __init__(self) -> None: super().__init__() @@ -16,21 +17,29 @@ class LicenseModel(QObject): self._current_page_idx = 0 self._page_count = 1 self._dialogTitle = "" - self._header_text = "" self._license_text = "" self._package_name = "" + self._icon_url = "" @pyqtProperty(str, notify=dialogTitleChanged) def dialogTitle(self) -> str: return self._dialogTitle - @pyqtProperty(str, notify=headerChanged) - def headerText(self) -> str: - return self._header_text + @pyqtProperty(str, notify=packageNameChanged) + def packageName(self) -> str: + return self._package_name def setPackageName(self, name: str) -> None: - self._header_text = name + ": " + catalog.i18nc("@label", "This plugin contains a license.\nYou need to accept this license to install this plugin.\nDo you agree with the terms below?") - self.headerChanged.emit() + self._package_name = name + self.packageNameChanged.emit() + + @pyqtProperty(str, notify=iconChanged) + def iconUrl(self) -> str: + return self._icon_url + + def setIconUrl(self, url: str): + self._icon_url = url + self.iconChanged.emit() @pyqtProperty(str, notify=licenseTextChanged) def licenseText(self) -> str: diff --git a/plugins/Toolbox/src/CloudSync/LicensePresenter.py b/plugins/Toolbox/src/CloudSync/LicensePresenter.py index cefe6f4037..5abf4a3b48 100644 --- a/plugins/Toolbox/src/CloudSync/LicensePresenter.py +++ b/plugins/Toolbox/src/CloudSync/LicensePresenter.py @@ -34,7 +34,7 @@ class LicensePresenter(QObject): ## Show a license dialog for multiple packages where users can read a license and accept or decline them # \param plugin_path: Root directory of the Toolbox plugin # \param packages: Dict[package id, file path] - def present(self, plugin_path: str, packages: Dict[str, str]) -> None: + def present(self, plugin_path: str, packages: Dict[str, Dict[str, str]]) -> None: path = os.path.join(plugin_path, self._compatibility_dialog_path) self._initState(packages) @@ -60,14 +60,15 @@ class LicensePresenter(QObject): self._package_models[self._current_package_idx]["accepted"] = False self._checkNextPage() - def _initState(self, packages: Dict[str, str]) -> None: + def _initState(self, packages: Dict[str, Dict[str, str]]) -> None: self._package_models = [ { "package_id" : package_id, - "package_path" : package_path, + "package_path" : item["package_path"], + "icon_url" : item["icon_url"], "accepted" : None #: None: no answer yet } - for package_id, package_path in packages.items() + for package_id, item in packages.items() ] def _presentCurrentPackage(self) -> None: @@ -80,6 +81,7 @@ class LicensePresenter(QObject): self._license_model.setCurrentPageIdx(self._current_package_idx) self._license_model.setPackageName(package_model["package_id"]) + self._license_model.setIconUrl(package_model["icon_url"]) self._license_model.setLicenseText(license_content) if self._dialog: self._dialog.open() # Does nothing if already open diff --git a/plugins/Toolbox/src/CloudSync/SyncOrchestrator.py b/plugins/Toolbox/src/CloudSync/SyncOrchestrator.py index e97bdbcbc4..4b8b3cf7f9 100644 --- a/plugins/Toolbox/src/CloudSync/SyncOrchestrator.py +++ b/plugins/Toolbox/src/CloudSync/SyncOrchestrator.py @@ -63,9 +63,9 @@ class SyncOrchestrator(Extension): self._download_presenter.download(mutations) ## Called when a set of packages have finished downloading - # \param success_items: Dict[package_id, file_path] + # \param success_items: Dict[package_id, Dict[str, str]] # \param error_items: List[package_id] - def _onDownloadFinished(self, success_items: Dict[str, str], error_items: List[str]) -> None: + def _onDownloadFinished(self, success_items: Dict[str, Dict[str, str]], error_items: List[str]) -> None: if error_items: message = i18n_catalog.i18nc("@info:generic", "{} plugins failed to download".format(len(error_items))) self._showErrorMessage(message) From 2f3cf3c493698834d9ee7cbe8c7f5c220c4610e2 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Tue, 21 Jan 2020 17:22:33 +0100 Subject: [PATCH 20/40] LicenseDialog layout tweaking CURA-7129 --- .../qml/dialogs/ToolboxLicenseDialog.qml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml b/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml index dba4a5ba58..67f850ca9e 100644 --- a/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml @@ -27,6 +27,7 @@ UM.Dialog ColumnLayout { anchors.fill: parent + spacing: UM.Theme.getSize("thick_margin").height UM.I18nCatalog{id: catalog; name: "cura"} @@ -45,22 +46,24 @@ UM.Dialog anchors.left: parent.left anchors.right: parent.right height: childrenRect.height - + spacing: UM.Theme.getSize("default_margin").width + leftPadding: UM.Theme.getSize("narrow_margin").width Image { - id: icon - width: 30 * screenScaleFactor - height: width - fillMode: Image.PreserveAspectFit - source: licenseModel.iconUrl || "../../images/logobot.svg" - mipmap: true + id: icon + width: 30 * screenScaleFactor + height: width + fillMode: Image.PreserveAspectFit + source: licenseModel.iconUrl || "../../images/logobot.svg" + mipmap: true } Label { id: packageName text: licenseModel.packageName + font.bold: true anchors.verticalCenter: icon.verticalCenter height: contentHeight wrapMode: Text.Wrap From 3b534ea986ae1e3b3161b39c1a037d197cc403ce Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Wed, 22 Jan 2020 10:54:05 +0100 Subject: [PATCH 21/40] Add icon to licenseDialog (Toolbox flow) CURA-7129 --- plugins/Toolbox/src/Toolbox.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index e0d04bed5b..4835651b99 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -154,10 +154,11 @@ class Toolbox(QObject, Extension): def getLicenseDialogPluginFileLocation(self) -> str: return self._license_dialog_plugin_file_location - def openLicenseDialog(self, plugin_name: str, license_content: str, plugin_file_location: str) -> None: + def openLicenseDialog(self, plugin_name: str, license_content: str, plugin_file_location: str, icon_url: str) -> None: # Set page 1/1 when opening the dialog for a single package self._license_model.setCurrentPageIdx(0) self._license_model.setPageCount(1) + self._license_model.setIconUrl(icon_url) self._license_model.setPackageName(plugin_name) self._license_model.setLicenseText(license_content) @@ -670,14 +671,17 @@ class Toolbox(QObject, Extension): return license_content = self._package_manager.getPackageLicense(file_path) + package_id = package_info["package_id"] if license_content is not None: - self.openLicenseDialog(package_info["package_id"], license_content, file_path) + # get the icon url for package_id, make sure the result is a string, never None + icon_url = next((x["icon_url"] for x in self.packagesModel.items if x["id"] == package_id), None) or "" + self.openLicenseDialog(package_id, license_content, file_path, icon_url) return - package_id = self.install(file_path) - if package_id != package_info["package_id"]: - Logger.error("Installed package {} does not match {}".format(package_id, package_info["package_id"])) - self.subscribe(package_id) + installed_id = self.install(file_path) + if installed_id != package_id: + Logger.error("Installed package {} does not match {}".format(installed_id, package_id)) + self.subscribe(installed_id) # Getter & Setters for Properties: # -------------------------------------------------------------------------- @@ -699,14 +703,14 @@ class Toolbox(QObject, Extension): def isDownloading(self) -> bool: return self._is_downloading - def setActivePackage(self, package: Dict[str, Any]) -> None: + def setActivePackage(self, package: QObject) -> None: if self._active_package != package: self._active_package = package self.activePackageChanged.emit() ## The active package is the package that is currently being downloaded @pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged) - def activePackage(self) -> Optional[Dict[str, Any]]: + def activePackage(self) -> Optional[QObject]: return self._active_package def setViewCategory(self, category: str = "plugin") -> None: From 95def2850d4419cf56b58edcf066ffac6397245f Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Wed, 22 Jan 2020 11:17:03 +0100 Subject: [PATCH 22/40] Make license decline button text context-dependant CURA-7129 --- .../resources/qml/dialogs/ToolboxLicenseDialog.qml | 4 ++-- plugins/Toolbox/src/CloudSync/LicenseModel.py | 14 +++++++++++++- plugins/Toolbox/src/CloudSync/LicensePresenter.py | 6 ++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml b/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml index 67f850ca9e..c37bb099d0 100644 --- a/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/ToolboxLicenseDialog.qml @@ -90,7 +90,7 @@ UM.Dialog leftPadding: UM.Theme.getSize("dialog_primary_button_padding").width rightPadding: UM.Theme.getSize("dialog_primary_button_padding").width - text: catalog.i18nc("@button", "Agree") + text: licenseModel.acceptButtonText onClicked: { handler.onLicenseAccepted() } } ] @@ -100,7 +100,7 @@ UM.Dialog Cura.SecondaryButton { id: declineButton - text: catalog.i18nc("@button", "Decline and remove from account") + text: licenseModel.declineButtonText onClicked: { handler.onLicenseDeclined() } } ] diff --git a/plugins/Toolbox/src/CloudSync/LicenseModel.py b/plugins/Toolbox/src/CloudSync/LicenseModel.py index 66ea6ca5da..bb62cb8abd 100644 --- a/plugins/Toolbox/src/CloudSync/LicenseModel.py +++ b/plugins/Toolbox/src/CloudSync/LicenseModel.py @@ -6,12 +6,15 @@ catalog = i18nCatalog("cura") # Model for the ToolboxLicenseDialog class LicenseModel(QObject): + DEFAULT_DECLINE_BUTTON_TEXT = catalog.i18nc("@button", "Decline") + ACCEPT_BUTTON_TEXT = catalog.i18nc("@button", "Agree") + dialogTitleChanged = pyqtSignal() packageNameChanged = pyqtSignal() licenseTextChanged = pyqtSignal() iconChanged = pyqtSignal() - def __init__(self) -> None: + def __init__(self, decline_button_text: str = DEFAULT_DECLINE_BUTTON_TEXT) -> None: super().__init__() self._current_page_idx = 0 @@ -20,6 +23,15 @@ class LicenseModel(QObject): self._license_text = "" self._package_name = "" self._icon_url = "" + self._decline_button_text = decline_button_text + + @pyqtProperty(str, constant = True) + def acceptButtonText(self): + return self.ACCEPT_BUTTON_TEXT + + @pyqtProperty(str, constant = True) + def declineButtonText(self): + return self._decline_button_text @pyqtProperty(str, notify=dialogTitleChanged) def dialogTitle(self) -> str: diff --git a/plugins/Toolbox/src/CloudSync/LicensePresenter.py b/plugins/Toolbox/src/CloudSync/LicensePresenter.py index 5abf4a3b48..0982bb4162 100644 --- a/plugins/Toolbox/src/CloudSync/LicensePresenter.py +++ b/plugins/Toolbox/src/CloudSync/LicensePresenter.py @@ -17,6 +17,7 @@ class LicensePresenter(QObject): def __init__(self, app: CuraApplication) -> None: super().__init__() + self._catalog = i18nCatalog("cura") self._dialog = None # type: Optional[QObject] self._package_manager = app.getPackageManager() # type: PackageManager # Emits List[Dict[str, [Any]] containing for example @@ -25,7 +26,8 @@ class LicensePresenter(QObject): self._current_package_idx = 0 self._package_models = [] # type: List[Dict] - self._license_model = LicenseModel() # type: LicenseModel + decline_button_text = self._catalog.i18nc("@button", "Decline and remove from account") + self._license_model = LicenseModel(decline_button_text=decline_button_text) # type: LicenseModel self._app = app @@ -42,7 +44,7 @@ class LicensePresenter(QObject): if self._dialog is None: context_properties = { - "catalog": i18nCatalog("cura"), + "catalog": self._catalog, "licenseModel": self._license_model, "handler": self } From 87fb0d7df3881b40d103323e2c4fb5769a1c03a8 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 23 Jan 2020 09:28:28 +0100 Subject: [PATCH 23/40] Fix incorrect read location for plugin metadata CURA-6627 --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index ba169f3dab..be90f02d37 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -747,7 +747,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): for metadata_file in metadata_files: try: plugin_id = metadata_file.split("/")[0] - result[plugin_id] = json.loads(archive.open("Cura/plugin_metadata.json").read().decode("utf-8")) + result[plugin_id] = json.loads(archive.open("%s/plugin_metadata.json" % plugin_id).read().decode("utf-8")) except Exception: Logger.logException("w", "Unable to retrieve metadata for %s", metadata_file) From a02e8d3b587da570f8bcc805924f32ae250c7e7e Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Fri, 24 Jan 2020 10:36:36 +0100 Subject: [PATCH 24/40] Use display_name for the package name in the license dialog Implemented for both CloudSync and Toolbox --- plugins/Toolbox/src/CloudSync/LicensePresenter.py | 3 ++- plugins/Toolbox/src/Toolbox.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/src/CloudSync/LicensePresenter.py b/plugins/Toolbox/src/CloudSync/LicensePresenter.py index 0982bb4162..74fc2a3099 100644 --- a/plugins/Toolbox/src/CloudSync/LicensePresenter.py +++ b/plugins/Toolbox/src/CloudSync/LicensePresenter.py @@ -75,6 +75,7 @@ class LicensePresenter(QObject): def _presentCurrentPackage(self) -> None: package_model = self._package_models[self._current_package_idx] + package_info = self._package_manager.getPackageInfo(package_model["package_path"]) license_content = self._package_manager.getPackageLicense(package_model["package_path"]) if license_content is None: # Implicitly accept when there is no license @@ -82,7 +83,7 @@ class LicensePresenter(QObject): return self._license_model.setCurrentPageIdx(self._current_package_idx) - self._license_model.setPackageName(package_model["package_id"]) + self._license_model.setPackageName(package_info["display_name"]) self._license_model.setIconUrl(package_model["icon_url"]) self._license_model.setLicenseText(license_content) if self._dialog: diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 4835651b99..72e1f59615 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -675,7 +675,7 @@ class Toolbox(QObject, Extension): if license_content is not None: # get the icon url for package_id, make sure the result is a string, never None icon_url = next((x["icon_url"] for x in self.packagesModel.items if x["id"] == package_id), None) or "" - self.openLicenseDialog(package_id, license_content, file_path, icon_url) + self.openLicenseDialog(package_info["display_name"], license_content, file_path, icon_url) return installed_id = self.install(file_path) From ae494110ba60e506d8d3aae02a40c18c2e758b3b Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Fri, 24 Jan 2020 11:28:36 +0100 Subject: [PATCH 25/40] Removed unnecessary line (because we still want to show the Compatability Dialog even if there are only Incompatible packages) CURA-7090 --- plugins/Toolbox/src/CloudSync/CloudPackageChecker.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py index ee068e6115..6e9409705d 100644 --- a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py +++ b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py @@ -59,9 +59,6 @@ class CloudPackageChecker(QObject): self._model.addDiscrepancies(package_discrepancy) # TODO: Move these two lines below, under if package_discrepancy: self._model.initialize(subscribed_packages_payload) - if not self._model.hasCompatiblePackages: # TODO: Remove these two lines... - return None - if package_discrepancy: self._handlePackageDiscrepancies() From 7ee8270b2e5431f645cad7c585e9a02f6988b731 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Fri, 24 Jan 2020 13:51:49 +0100 Subject: [PATCH 26/40] Added the mechanism to dismiss all Incompatible packages when the Compatibility Dialog is closed CURA-7090 --- plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py | 4 +++- plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py b/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py index f6b5622aad..78587117a0 100644 --- a/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py +++ b/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py @@ -36,5 +36,7 @@ class DiscrepanciesPresenter(QObject): def _onConfirmClicked(self, model: SubscribedPackagesModel) -> None: # For now, all compatible packages presented to the user should be installed. # Later, we might remove items for which the user unselected the package + if model.getIncompatiblePackages(): + self._package_manager.dismissAllIncompatiblePackages(model.getIncompatiblePackages()) model.setItems(model.getCompatiblePackages()) - self.packageMutations.emit(model) + self.packageMutations.emit(model) # #### proveri sho e ova??? diff --git a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py b/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py index fd5eae254d..dc684a11a7 100644 --- a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py @@ -50,9 +50,12 @@ class SubscribedPackagesModel(ListModel): def getCompatiblePackages(self) -> List[Dict[str, Any]]: return [package for package in self._items if package["is_compatible"]] - def initialize(self, json_data: List[Dict[str, Any]]) -> None: + def getIncompatiblePackages(self) -> List[str]: + return [package["package_id"] for package in self._items if not package["is_compatible"]] + + def initialize(self, subscribed_packages_payload: List[Dict[str, Any]]) -> None: self._items.clear() - for item in json_data: + for item in subscribed_packages_payload: if item["package_id"] not in self._discrepancies: continue package = { From 05e17843c6bd6a24a4ebb61bd6efe5c6914c637b Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Fri, 24 Jan 2020 13:56:59 +0100 Subject: [PATCH 27/40] Removed previous variants of the dismiss incompatible package functionality CURA-7090 --- .../qml/dialogs/CompatibilityDialog.qml | 20 ------------------- .../src/CloudSync/DiscrepanciesPresenter.py | 5 ----- .../src/CloudSync/SubscribedPackagesModel.py | 7 ------- 3 files changed, 32 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index 32b4da4823..84f69d8d4e 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -125,26 +125,6 @@ UM.Dialog{ color: UM.Theme.getColor("text") elide: Text.ElideRight } - UM.TooltipArea - { - width: childrenRect.width; - height: childrenRect.height; - text: catalog.i18nc("@info:tooltip", "Dismisses the package and won't be shown in this dialog anymore") - anchors.right: parent.right - anchors.verticalCenter: packageIcon.verticalCenter - Label - { - text: "(Dismiss)" - font: UM.Theme.getFont("small") - color: UM.Theme.getColor("text") - MouseArea - { - cursorShape: Qt.PointingHandCursor - anchors.fill: parent - onClicked: handler.dismissIncompatiblePackage(subscribedPackagesModel, model.package_id) - } - } - } } } } diff --git a/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py b/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py index 78587117a0..d5c6e38b85 100644 --- a/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py +++ b/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py @@ -28,11 +28,6 @@ class DiscrepanciesPresenter(QObject): assert self._dialog self._dialog.accepted.connect(lambda: self._onConfirmClicked(model)) - @pyqtSlot("QVariant", str) - def dismissIncompatiblePackage(self, model: SubscribedPackagesModel, package_id: str) -> None: - model.dismissPackage(package_id) # update the model to update the view - self._package_manager.dismissPackage(package_id) # adds this package_id as dismissed in the user config file - def _onConfirmClicked(self, model: SubscribedPackagesModel) -> None: # For now, all compatible packages presented to the user should be installed. # Later, we might remove items for which the user unselected the package diff --git a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py b/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py index dc684a11a7..614d397d91 100644 --- a/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py +++ b/plugins/Toolbox/src/CloudSync/SubscribedPackagesModel.py @@ -37,13 +37,6 @@ class SubscribedPackagesModel(ListModel): return True return False - @pyqtSlot() - def dismissPackage(self, package_id: str) -> None: - package = self.find(key="package_id", value=package_id) - if package != -1: # find() returns -1 if it doesn't finds what is looking for - self.setProperty(package, property="is_dismissed", value=True) - Logger.debug("Package {} has been dismissed".format(package_id)) - def addDiscrepancies(self, discrepancy: List[str]) -> None: self._discrepancies = discrepancy From bd18a539e26aca452ec606741337a8364889cec5 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 24 Jan 2020 14:04:27 +0100 Subject: [PATCH 28/40] Only update position of label when it's visible This prevents unneeded re-rendering --- resources/qml/Toolbar.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index c2a70143c3..ab32bff619 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -202,8 +202,9 @@ Item // dragging a tool handle. Rectangle { - x: -base.x + base.mouseX + UM.Theme.getSize("default_margin").width - y: -base.y + base.mouseY + UM.Theme.getSize("default_margin").height + id: toolInfo + x: visible ? -base.x + base.mouseX + UM.Theme.getSize("default_margin").width: 0 + y: visible ? -base.y + base.mouseY + UM.Theme.getSize("default_margin").height: 0 width: toolHint.width + UM.Theme.getSize("default_margin").width height: toolHint.height; From c97ce6fafbfe987a875b984c7489f0b58a86a6ad Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 24 Jan 2020 14:12:03 +0100 Subject: [PATCH 29/40] Make the sentry_env a bit smarter --- cura_app.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cura_app.py b/cura_app.py index cb97792662..5184a5ab8b 100755 --- a/cura_app.py +++ b/cura_app.py @@ -29,9 +29,13 @@ parser.add_argument("--debug", known_args = vars(parser.parse_known_args()[0]) if with_sentry_sdk: - sentry_env = "production" - if ApplicationMetadata.CuraVersion == "master": + sentry_env = "unknown" # Start off with a "IDK" + if hasattr(sys, "frozen"): + sentry_env = "production" # A frozen build is a "real" distribution. + elif ApplicationMetadata.CuraVersion == "master": sentry_env = "development" + elif "beta" in ApplicationMetadata.CuraVersion or "BETA" in ApplicationMetadata.CuraVersion: + sentry_env = "beta" try: if ApplicationMetadata.CuraVersion.split(".")[2] == "99": sentry_env = "nightly" From d82940534743982fb4d15bb9242acca3ac648a38 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Fri, 24 Jan 2020 14:54:12 +0100 Subject: [PATCH 30/40] Update plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py --- plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py b/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py index d5c6e38b85..3dc46f71de 100644 --- a/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py +++ b/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py @@ -34,4 +34,4 @@ class DiscrepanciesPresenter(QObject): if model.getIncompatiblePackages(): self._package_manager.dismissAllIncompatiblePackages(model.getIncompatiblePackages()) model.setItems(model.getCompatiblePackages()) - self.packageMutations.emit(model) # #### proveri sho e ova??? + self.packageMutations.emit(model) From 38c7d80f82b7a91ec59e57760419e0a53db7b336 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Fri, 24 Jan 2020 15:39:58 +0100 Subject: [PATCH 31/40] Refactored the onConfirmClicked function. Added the ability to auto-dismiss the incompatible packages. Changed the button text depending on the displayed list --- .../resources/qml/dialogs/CompatibilityDialog.qml | 4 +++- .../Toolbox/src/CloudSync/DiscrepanciesPresenter.py | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index 84f69d8d4e..73a1b7ec18 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -20,6 +20,8 @@ UM.Dialog{ maximumHeight: minimumHeight margin: 0 + property string buttonText: subscribedPackagesModel.hasIncompatiblePackages && !subscribedPackagesModel.hasCompatiblePackages ? "Dismiss" : "Next" + Rectangle { id: root @@ -138,7 +140,7 @@ UM.Dialog{ anchors.bottom: parent.bottom anchors.right: parent.right anchors.margins: UM.Theme.getSize("default_margin").height - text: catalog.i18nc("@button", "Next") + text: catalog.i18nc("@button", buttonText) onClicked: accept() leftPadding: UM.Theme.getSize("dialog_primary_button_padding").width rightPadding: UM.Theme.getSize("dialog_primary_button_padding").width diff --git a/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py b/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py index d5c6e38b85..950d6c57c1 100644 --- a/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py +++ b/plugins/Toolbox/src/CloudSync/DiscrepanciesPresenter.py @@ -29,9 +29,12 @@ class DiscrepanciesPresenter(QObject): self._dialog.accepted.connect(lambda: self._onConfirmClicked(model)) def _onConfirmClicked(self, model: SubscribedPackagesModel) -> None: - # For now, all compatible packages presented to the user should be installed. - # Later, we might remove items for which the user unselected the package + # If there are incompatible packages - automatically dismiss them if model.getIncompatiblePackages(): self._package_manager.dismissAllIncompatiblePackages(model.getIncompatiblePackages()) - model.setItems(model.getCompatiblePackages()) - self.packageMutations.emit(model) # #### proveri sho e ova??? + + # For now, all compatible packages presented to the user should be installed. + # Later, we might remove items for which the user unselected the package + if model.getCompatiblePackages(): + model.setItems(model.getCompatiblePackages()) + self.packageMutations.emit(model) # #### proveri sho e ova??? From 537891d80b9af4c6ba9110724db8e55784f6b589 Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Fri, 24 Jan 2020 16:05:35 +0100 Subject: [PATCH 32/40] Further improvements and refactors to the CloudPackageChecker --- .../src/CloudSync/CloudPackageChecker.py | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py index 6e9409705d..8b20fe93ad 100644 --- a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py +++ b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py @@ -32,7 +32,6 @@ class CloudPackageChecker(QObject): # this is initialized. Therefore, we wait until the application is ready. def _onAppInitialized(self) -> None: self._package_manager = self._application.getPackageManager() - # initial check self._fetchUserSubscribedPackages() # check again whenever the login state changes @@ -40,7 +39,33 @@ class CloudPackageChecker(QObject): def _fetchUserSubscribedPackages(self) -> None: if self._application.getCuraAPI().account.isLoggedIn: - self._getUserPackages() + self._getUserSubscribedPackages() + + def _getUserSubscribedPackages(self) -> None: + Logger.log("d", "Requesting subscribed packages metadata from server.") + url = CloudApiModel.api_url_user_packages + self._application.getHttpRequestManager().get(url, + callback = self._onUserPackagesRequestFinished, + error_callback = self._onUserPackagesRequestFinished, + scope = self._scope) + + def _onUserPackagesRequestFinished(self, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"] = None) -> None: + if error is not None or reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: + Logger.log("w", + "Requesting user packages failed, response code %s while trying to connect to %s", + reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url()) + return + + try: + json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) + # Check for errors: + if "errors" in json_data: + for error in json_data["errors"]: + Logger.log("e", "%s", error["title"]) + return + self._handleCompatibilityData(json_data["data"]) + except json.decoder.JSONDecodeError: + Logger.log("w", "Received invalid JSON for user subscribed packages from the Web Marketplace") def _handleCompatibilityData(self, subscribed_packages_payload: List[Dict[str, Any]]) -> None: user_subscribed_packages = [plugin["package_id"] for plugin in subscribed_packages_payload] @@ -53,13 +78,12 @@ class CloudPackageChecker(QObject): user_dismissed_packages = self._package_manager.getDismissedPackages() if user_dismissed_packages: user_installed_packages += user_dismissed_packages - # We check if there are packages installed in Cloud Marketplace but not in Cura marketplace + + # We check if there are packages installed in Web Marketplace but not in Cura marketplace package_discrepancy = list(set(user_subscribed_packages).difference(user_installed_packages)) - - self._model.addDiscrepancies(package_discrepancy) # TODO: Move these two lines below, under if package_discrepancy: - self._model.initialize(subscribed_packages_payload) - if package_discrepancy: + self._model.addDiscrepancies(package_discrepancy) + self._model.initialize(subscribed_packages_payload) self._handlePackageDiscrepancies() def _handlePackageDiscrepancies(self) -> None: @@ -79,35 +103,4 @@ class CloudPackageChecker(QObject): def _onSyncButtonClicked(self, sync_message: Message, sync_message_action: str) -> None: sync_message.hide() - self.discrepancies.emit(self._model) - - def _getUserPackages(self) -> None: - Logger.log("d", "Requesting subscribed packages metadata from server.") - url = CloudApiModel.api_url_user_packages - - self._application.getHttpRequestManager().get(url, - callback = self._onUserPackagesRequestFinished, - error_callback = self._onUserPackagesRequestFinished, - scope = self._scope) - - def _onUserPackagesRequestFinished(self, - reply: "QNetworkReply", - error: Optional["QNetworkReply.NetworkError"] = None) -> None: - if error is not None or reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: - Logger.log("w", - "Requesting user packages failed, response code %s while trying to connect to %s", - reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url()) - return - - try: - json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) - - # Check for errors: - if "errors" in json_data: - for error in json_data["errors"]: - Logger.log("e", "%s", error["title"]) - return - - self._handleCompatibilityData(json_data["data"]) - except json.decoder.JSONDecodeError: - Logger.log("w", "Received invalid JSON for user packages") + self.discrepancies.emit(self._model) \ No newline at end of file From db41a83aa6d555d31a3273ae408fdc3c8ba12159 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 24 Jan 2020 16:13:15 +0100 Subject: [PATCH 33/40] Fix mix of whitespace styles: Use spaces instead of tabs Contributes to issue CURA-7140. --- resources/definitions/renkforce_rf100.def.json | 8 ++++---- resources/definitions/renkforce_rf100_v2.def.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/definitions/renkforce_rf100.def.json b/resources/definitions/renkforce_rf100.def.json index 33be020a95..8ebd9c02fe 100644 --- a/resources/definitions/renkforce_rf100.def.json +++ b/resources/definitions/renkforce_rf100.def.json @@ -77,10 +77,10 @@ "machine_head_with_fans_polygon": { "default_value": [ - [-26, -27], - [38, -27], - [38, 55], - [-26, 55] + [-26, -27], + [38, -27], + [38, 55], + [-26, 55] ] }, "gantry_height": { diff --git a/resources/definitions/renkforce_rf100_v2.def.json b/resources/definitions/renkforce_rf100_v2.def.json index c1dd9b003b..bd012eff06 100644 --- a/resources/definitions/renkforce_rf100_v2.def.json +++ b/resources/definitions/renkforce_rf100_v2.def.json @@ -77,10 +77,10 @@ "machine_head_with_fans_polygon": { "default_value": [ - [-26, -27], - [38, -27], - [38, 55], - [-26, 55] + [-26, -27], + [38, -27], + [38, 55], + [-26, 55] ] }, "gantry_height": { From 9266c753c30868ca3b6badda41da1b0f6d9b9857 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Fri, 24 Jan 2020 17:12:00 +0100 Subject: [PATCH 34/40] Reset print_sequence when reactivating extruder 2 The setting print_sequence was not being resetted to all at once whenever the 2nd extruder was reactivated. This commit fixes that by explicitly resetting print_sequence when reenabling an extruder, if it has been changed by the user. CURA-6914 --- cura/Settings/MachineManager.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 31ea691e76..f78a21aaad 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -747,6 +747,11 @@ class MachineManager(QObject): result = [] # type: List[str] for setting_instance in container.findInstances(): setting_key = setting_instance.definition.key + if setting_key == "print_sequence": + old_value = container.getProperty(setting_key, "value") + Logger.log("d", "Reset setting [%s] in [%s] because its old value [%s] is no longer valid", setting_key, container, old_value) + result.append(setting_key) + continue if not self._global_container_stack.getProperty(setting_key, "type") in ("extruder", "optional_extruder"): continue From bcec433f53740043f389184101e154d1f885b332 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Fri, 24 Jan 2020 17:30:46 +0100 Subject: [PATCH 35/40] Update description of the Print Sequence setting Update description to clearly state that Print Sequence is not possible if more than one extruders are enabled. CURA-6914 --- 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 b5d423c28b..22eecd8f58 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -5984,7 +5984,7 @@ "print_sequence": { "label": "Print Sequence", - "description": "Whether to print all models one layer at a time or to wait for one model to finish, before moving on to the next. One at a time mode is only possible if all models are separated in such a way that the whole print head can move in between and all models are lower than the distance between the nozzle and the X/Y axes.", + "description": "Whether to print all models one layer at a time or to wait for one model to finish, before moving on to the next. One at a time mode is possible if a) only one extruder is enabled and b) all models are separated in such a way that the whole print head can move in between and all models are lower than the distance between the nozzle and the X/Y axes. ", "type": "enum", "options": { From aa2c612925fc15066a07393af456208130d6797d Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 27 Jan 2020 13:25:29 +0100 Subject: [PATCH 36/40] PR review changes --- plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml | 4 ++-- plugins/Toolbox/src/CloudSync/CloudPackageChecker.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index 73a1b7ec18..7c0b50c655 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -20,7 +20,7 @@ UM.Dialog{ maximumHeight: minimumHeight margin: 0 - property string buttonText: subscribedPackagesModel.hasIncompatiblePackages && !subscribedPackagesModel.hasCompatiblePackages ? "Dismiss" : "Next" + property string actionButtonText: subscribedPackagesModel.hasIncompatiblePackages && !subscribedPackagesModel.hasCompatiblePackages ? "Dismiss" : "Next" Rectangle { @@ -140,7 +140,7 @@ UM.Dialog{ anchors.bottom: parent.bottom anchors.right: parent.right anchors.margins: UM.Theme.getSize("default_margin").height - text: catalog.i18nc("@button", buttonText) + text: catalog.i18nc("@button", actionButtonText) onClicked: accept() leftPadding: UM.Theme.getSize("dialog_primary_button_padding").width rightPadding: UM.Theme.getSize("dialog_primary_button_padding").width diff --git a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py index 8b20fe93ad..a14341a4b7 100644 --- a/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py +++ b/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py @@ -42,7 +42,7 @@ class CloudPackageChecker(QObject): self._getUserSubscribedPackages() def _getUserSubscribedPackages(self) -> None: - Logger.log("d", "Requesting subscribed packages metadata from server.") + Logger.debug("Requesting subscribed packages metadata from server.") url = CloudApiModel.api_url_user_packages self._application.getHttpRequestManager().get(url, callback = self._onUserPackagesRequestFinished, From a08bea1376ae098d9ee17cca515b6c5c4387ec6a Mon Sep 17 00:00:00 2001 From: Dimitriovski Date: Mon, 27 Jan 2020 14:00:47 +0100 Subject: [PATCH 37/40] String translate improvements CURA-7090 --- plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml index 7c0b50c655..f0df441f1d 100644 --- a/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml +++ b/plugins/Toolbox/resources/qml/dialogs/CompatibilityDialog.qml @@ -20,7 +20,7 @@ UM.Dialog{ maximumHeight: minimumHeight margin: 0 - property string actionButtonText: subscribedPackagesModel.hasIncompatiblePackages && !subscribedPackagesModel.hasCompatiblePackages ? "Dismiss" : "Next" + property string actionButtonText: subscribedPackagesModel.hasIncompatiblePackages && !subscribedPackagesModel.hasCompatiblePackages ? catalog.i18nc("@button", "Dismiss") : catalog.i18nc("@button", "Next") Rectangle { @@ -140,7 +140,7 @@ UM.Dialog{ anchors.bottom: parent.bottom anchors.right: parent.right anchors.margins: UM.Theme.getSize("default_margin").height - text: catalog.i18nc("@button", actionButtonText) + text: actionButtonText onClicked: accept() leftPadding: UM.Theme.getSize("dialog_primary_button_padding").width rightPadding: UM.Theme.getSize("dialog_primary_button_padding").width From a720939e64949adf5d304f23ece7c28e7bfb4f9f Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Mon, 27 Jan 2020 14:21:51 +0100 Subject: [PATCH 38/40] Fix max layer to correspond to starting layer When the starting layer was set to 0 the max layer was not adjusted to be max_layer-1. This commit makes the extra adjustment. --- .../scripts/DisplayFilenameAndLayerOnLCD.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/PostProcessingPlugin/scripts/DisplayFilenameAndLayerOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayFilenameAndLayerOnLCD.py index 2bf687eb47..cbd131f17e 100644 --- a/plugins/PostProcessingPlugin/scripts/DisplayFilenameAndLayerOnLCD.py +++ b/plugins/PostProcessingPlugin/scripts/DisplayFilenameAndLayerOnLCD.py @@ -79,6 +79,8 @@ class DisplayFilenameAndLayerOnLCD(Script): if line.startswith(";LAYER_COUNT:"): max_layer = line max_layer = max_layer.split(":")[1] + if self.getSettingValueByKey("startNum") == 0: + max_layer = str(int(max_layer) - 1) if line.startswith(";LAYER:"): if self.getSettingValueByKey("maxlayer"): display_text = display_text + " of " + max_layer From a706b42782ac6c118aa5efef23269ba96eb43234 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 27 Jan 2020 16:12:26 +0100 Subject: [PATCH 39/40] Disable create & import on materialpage if machine does not support materials --- resources/qml/Preferences/Materials/MaterialsPage.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/Preferences/Materials/MaterialsPage.qml b/resources/qml/Preferences/Materials/MaterialsPage.qml index d635b2b721..791d6685de 100644 --- a/resources/qml/Preferences/Materials/MaterialsPage.qml +++ b/resources/qml/Preferences/Materials/MaterialsPage.qml @@ -125,6 +125,7 @@ Item id: createMenuButton text: catalog.i18nc("@action:button", "Create") iconName: "list-add" + enabled: Cura.MachineManager.activeMachine.hasMaterials onClicked: { forceActiveFocus(); @@ -174,7 +175,7 @@ Item forceActiveFocus(); importMaterialDialog.open(); } - visible: true + enabled: Cura.MachineManager.activeMachine.hasMaterials } // Export button From 305bce5a785247900d2d5a91718074375cdbc3ca Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 27 Jan 2020 16:28:08 +0100 Subject: [PATCH 40/40] Enforce Cura API to be a singleton If it's not used that way we want to know about it and fail. If plug-ins use it this way, the plug-in won't get initialised so it'll be as if the plug-in is disabled or that function doesn't work. Contributes to issue CURA-7135. --- cura/API/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cura/API/__init__.py b/cura/API/__init__.py index b3e702263a..26c9a4c829 100644 --- a/cura/API/__init__.py +++ b/cura/API/__init__.py @@ -28,11 +28,12 @@ class CuraAPI(QObject): # The main reason for this is that we want to prevent consumers of API to have a dependency on CuraApplication. # Since the API is intended to be used by plugins, the cura application should have already created this. def __new__(cls, application: Optional["CuraApplication"] = None): - if cls.__instance is None: - if application is None: - raise Exception("Upon first time creation, the application must be set.") - cls.__instance = super(CuraAPI, cls).__new__(cls) - cls._application = application + if cls.__instance is not None: + raise RuntimeError("Tried to create singleton '{class_name}' more than once.".format(class_name = CuraAPI.__name__)) + if application is None: + raise RuntimeError("Upon first time creation, the application must be set.") + cls.__instance = super(CuraAPI, cls).__new__(cls) + cls._application = application return cls.__instance def __init__(self, application: Optional["CuraApplication"] = None) -> None: