From 7e9054616358f237d8c0615ff739dd7e43e48b31 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 29 Jun 2018 10:34:15 +0200 Subject: [PATCH 01/54] Fix firmware upload on Windows... by correctly creating a local path from a url-encoded path Fixes #3731 and #3987 --- plugins/USBPrinting/USBPrinterOutputDevice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 00eb2f0b25..926cd9001d 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -15,7 +15,7 @@ from cura.PrinterOutput.GenericOutputController import GenericOutputController from .AutoDetectBaudJob import AutoDetectBaudJob from .avr_isp import stk500v2, intelHex -from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty +from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty, QUrl from serial import Serial, SerialException, SerialTimeoutException from threading import Thread, Event @@ -128,7 +128,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): @pyqtSlot(str) def updateFirmware(self, file): # the file path is qurl encoded. - self._firmware_location = file.replace("file://", "") + self._firmware_location = QUrl(file).toLocalFile() self.showFirmwareInterface() self.setFirmwareUpdateState(FirmwareUpdateState.updating) self._update_firmware_thread.start() From 65ada30c9c45687f6fe860af7ecc74889978a3a1 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 16 Jul 2018 13:26:51 +0200 Subject: [PATCH 02/54] Fix flashing "default" firmware The url is not QUrl encoded if the "default" firmware is flashed --- plugins/USBPrinting/USBPrinterOutputDevice.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 926cd9001d..6a97a3c163 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -127,8 +127,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice): @pyqtSlot(str) def updateFirmware(self, file): - # the file path is qurl encoded. - self._firmware_location = QUrl(file).toLocalFile() + # the file path could be qurl encoded. + if file[:7] == "file://": + self._firmware_location = QUrl(file).toLocalFile() + else: + self._firmware_location = file self.showFirmwareInterface() self.setFirmwareUpdateState(FirmwareUpdateState.updating) self._update_firmware_thread.start() From 494fcb98b833a8505b037bd5b37bbb3ed45de9d1 Mon Sep 17 00:00:00 2001 From: milkok Date: Tue, 24 Jul 2018 17:17:40 +0800 Subject: [PATCH 03/54] Create winbo_mini2.def.json --- resources/definitions/winbo_mini2.def.json | 138 +++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 resources/definitions/winbo_mini2.def.json diff --git a/resources/definitions/winbo_mini2.def.json b/resources/definitions/winbo_mini2.def.json new file mode 100644 index 0000000000..d166b7847e --- /dev/null +++ b/resources/definitions/winbo_mini2.def.json @@ -0,0 +1,138 @@ +{ + "version": 2, + "name": "Winbo FDM-Mini2", + "inherits": "fdmprinter", + "metadata": { + "author": "Winbo", + "manufacturer": "Winbo Smart Tech Co., Ltd.", + "category": "Other", + "visible": true, + "file_formats": "text/x-gcode", + "supports_usb_connection": true, + "machine_extruder_trains": + { + "0": "winbo_mini2_extruder" + } + }, + + "overrides": { + "machine_name": { "default_value": "Winbo FDM-Mini2" }, + "machine_width": { "default_value": 205 }, + "machine_depth": { "default_value": 155 }, + "machine_height": { "default_value": 205 }, + "machine_heated_bed": { "default_value": false }, + "material_bed_temp_wait": { "default_value": false }, + "machine_filament_park_distance": { "value": "machine_heat_zone_length" }, + "machine_nozzle_heat_up_speed": { "default_value": 1.4 }, + "machine_nozzle_cool_down_speed": { "default_value": 0.8 }, + "machine_head_with_fans_polygon": + { + "default_value": + [ + [ -52, 30 ],[ -52, -40 ],[ 13, -40 ],[ 13, 30 ] + ] + }, + "machine_gcode_flavor": { "default_value": "Marlin" }, + "machine_max_feedrate_x": { "default_value": 250 }, + "machine_max_feedrate_y": { "default_value": 200 }, + "machine_max_feedrate_z": { "default_value": 40 }, + "machine_acceleration": { "default_value": 3000 }, + "gantry_height": { "default_value": 75 }, + "machine_extruder_count": { "default_value": 1 }, + "machine_start_gcode": { "default_value": "G21\nG90\nM82\nM107\nG28 X0 Y0\nG28 Z0\nG1 F1000 Z3\nG1 F4000 X0\nG1 F4000 Y0\nG1 F1000 Z0.2\nG92 E0\nG1 F1000 X30 E8\nG92 E0\nM117 Printing." }, + "machine_end_gcode": { "default_value": "M104 S0\nM140 S0\nG92 E2\nG1 E0 F200\nG28 X0 Y0\nM84 X Y E" }, + "material_diameter": { "default_value": 1.75 }, + "prime_blob_enable": { "enabled": true }, + "acceleration_enabled": { "value": "True" }, + "acceleration_layer_0": { "value": "acceleration_topbottom" }, + "acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" }, + "acceleration_print": { "value": "2000" }, + "acceleration_travel": { "value": "2500" }, + "acceleration_support": { "value": "math.ceil(acceleration_print * 2000 / 4000)" }, + "acceleration_support_interface": { "value": "acceleration_topbottom" }, + "acceleration_topbottom": { "value": "math.ceil(acceleration_print * 500 / 4000)" }, + "acceleration_wall": { "value": "math.ceil(acceleration_print * 1000 / 4000)" }, + "acceleration_wall_0": { "value": "math.ceil(acceleration_wall * 500 / 1000)" }, + "brim_width": { "value": "3" }, + "cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" }, + "cool_fan_speed": { "value": "100" }, + "cool_fan_speed_max": { "value": "100" }, + "cool_min_speed": { "value": "5" }, + "fill_outline_gaps": { "value": "True" }, + "infill_overlap": { "value": "0" }, + "min_infill_area": { "value": "1" }, + "min_skin_width_for_expansion": { "value": "2" }, + "jerk_enabled": { "value": "True" }, + "jerk_layer_0": { "value": "jerk_topbottom" }, + "jerk_prime_tower": { "value": "math.ceil(jerk_print * 15 / 25)" }, + "jerk_print": { "value": "25" }, + "jerk_support": { "value": "math.ceil(jerk_print * 15 / 25)" }, + "jerk_support_interface": { "value": "jerk_topbottom" }, + "jerk_topbottom": { "value": "math.ceil(jerk_print * 5 / 25)" }, + "jerk_wall": { "value": "math.ceil(jerk_print * 10 / 25)" }, + "jerk_wall_0": { "value": "math.ceil(jerk_wall * 5 / 10)" }, + "wall_thickness": { "value": "1.2"}, + "line_width": { "value": "extruderValue(-1,'machine_nozzle_size')" }, + "wall_0_inset": { "value": "0.05" }, + "wall_line_width_x": { "value": "line_width" }, + "wall_line_width_0": { "value": "line_width-0.05" }, + "support_line_width": { "value": "max(min(line_width,0.4),line_width/2)" }, + "support_interface_line_width": { "value": "support_line_width" }, + "machine_min_cool_heat_time_window": { "value": "15" }, + "default_material_print_temperature": { "value": "200" }, + "material_print_temperature_layer_0": { "value": "material_print_temperature - 5" }, + "material_bed_temperature": { "maximum_value": "115" }, + "material_bed_temperature_layer_0": { "maximum_value": "115" }, + "raft_airgap": { "value": "0" }, + "raft_base_thickness": { "value": "0.3" }, + "raft_interface_line_spacing": { "value": "0.5" }, + "raft_interface_line_width": { "value": "0.5" }, + "raft_interface_thickness": { "value": "0.2" }, + "raft_jerk": { "value": "jerk_layer_0" }, + "raft_margin": { "value": "10" }, + "raft_surface_layers": { "value": "1" }, + "retraction_amount": { "value": "4" }, + "retraction_count_max": { "value": "10" }, + "retraction_extrusion_window": { "value": "1" }, + "retraction_hop": { "value": "0.5" }, + "retraction_hop_enabled": { "value": "True" }, + "retraction_hop_only_when_collides": { "value": "True" }, + "retraction_min_travel": { "value": "5" }, + "retraction_prime_speed": { "value": "25" }, + "skin_overlap": { "value": "10" }, + "speed_layer_0": { "value": "20" }, + "speed_print": { "value": "50" }, + "speed_support": { "value": "speed_print*line_width/support_line_width" }, + "speed_support_interface": { "value": "speed_print*line_width/support_interface_line_width" }, + "speed_topbottom": { "value": "speed_print*line_width/skin_line_width" }, + "speed_travel": { "value": "90" }, + "speed_infill": { "value": "speed_print*line_width/infill_line_width" }, + "speed_wall": { "value": "speed_print*wall_line_width_0/line_width" }, + "speed_wall_0": { "value": "math.ceil(speed_wall * 0.6)" }, + "speed_wall_x": { "value": "speed_wall" }, + "speed_equalize_flow_enabled": { "value": "False" }, + "support_angle": { "value": "50" }, + "support_xy_distance": { "value": "1" }, + "support_z_distance": { "value": "max((0.2 if(0.2%layer_height==0) else layer_height*int((0.2+layer_height)/layer_height)),layer_height)" }, + "support_bottom_distance": { "value": "max(support_z_distance,layer_height*int(0.45/layer_height))" }, + "top_bottom_thickness": { "value": "max(1.2,layer_height*6)" }, + "travel_avoid_distance": { "value": "3" }, + "gradual_support_infill_step_height": { "value": "0.2" }, + "gradual_support_infill_steps": { "value": "1" }, + "infill_sparse_density": { "value": "20" }, + "gradual_infill_step_height": { "value": "1" }, + "initial_layer_line_width_factor": { "value": "120" }, + "jerk_travel": { "value": "25" }, + "support_bottom_enable": { "value": "True" }, + "support_bottom_height": { "value": "max((0.15 if(0.15%layer_height==0) else layer_height*int((0.15+layer_height)/layer_height)),layer_height)" }, + "support_bottom_pattern": { "value": "'zigzag'" }, + "support_connect_zigzags": { "value": "False" }, + "support_infill_rate": { "value": "8" }, + "support_interface_density": { "value": "80" }, + "support_interface_enable": { "value": "True" }, + "support_interface_height": { "value": "0.5" }, + "support_roof_pattern": { "value": "'concentric'" }, + "z_seam_type": { "value": "'shortest'" }, + "speed_equalize_flow_max": { "value": "65" } + } +} From dd9d4d1e2b5ef0336e0b3adcbe49da1c4d9cc185 Mon Sep 17 00:00:00 2001 From: milkok Date: Tue, 24 Jul 2018 17:19:36 +0800 Subject: [PATCH 04/54] Create winbo_dragonl4.def.json --- resources/definitions/winbo_dragonl4.def.json | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 resources/definitions/winbo_dragonl4.def.json diff --git a/resources/definitions/winbo_dragonl4.def.json b/resources/definitions/winbo_dragonl4.def.json new file mode 100644 index 0000000000..cb5566c46b --- /dev/null +++ b/resources/definitions/winbo_dragonl4.def.json @@ -0,0 +1,139 @@ +{ + "version": 2, + "name": "Winbo FDM-Dragon(L)4", + "inherits": "fdmprinter", + "metadata": { + "author": "Winbo", + "manufacturer": "Winbo Smart Tech Co., Ltd.", + "category": "Other", + "visible": true, + "file_formats": "text/x-gcode", + "supports_usb_connection": false, + "machine_extruder_trains": + { + "0": "winbo_dragonl4_extruder" + } + }, + + "overrides": { + "machine_name": { "default_value": "Winbo FDM-Dragon(L)4" }, + "machine_width": { "default_value": 615 }, + "machine_depth": { "default_value": 463 }, + "machine_height": { "default_value": 615 }, + "machine_heated_bed": { "default_value": true }, + "material_bed_temp_wait": { "default_value": false }, + "machine_filament_park_distance": { "value": "machine_heat_zone_length" }, + "machine_nozzle_heat_up_speed": { "default_value": 1.4 }, + "machine_nozzle_cool_down_speed": { "default_value": 0.8 }, + "machine_nozzle_size": { "default_value": 0.8 }, + "machine_head_with_fans_polygon": + { + "default_value": + [ + [ -50, 90 ],[ -50, -60 ],[ 50, -60 ],[ 50, 90 ] + ] + }, + "machine_gcode_flavor": { "default_value": "Marlin" }, + "machine_max_feedrate_x": { "default_value": 300 }, + "machine_max_feedrate_y": { "default_value": 300 }, + "machine_max_feedrate_z": { "default_value": 40 }, + "machine_acceleration": { "default_value": 2000 }, + "gantry_height": { "default_value": 80 }, + "machine_extruder_count": { "default_value": 1 }, + "machine_start_gcode": { "default_value": "G21\nG90\nM82\nM107\nM9998\nG28 X0 Y0\nG28 Z0\nG1 F6000 Z0.3\nG92 E0\nG1 F800 X585 E12\nG92 E0" }, + "machine_end_gcode": { "default_value": "M104 S0\nM140 S0\nG92 E2\nG1 E0 F200\nG28 X0 Y0\nM84 X Y E" }, + "material_diameter": { "default_value": 3.0 }, + "prime_blob_enable": { "enabled": true }, + "acceleration_enabled": { "value": "True" }, + "acceleration_layer_0": { "value": "acceleration_topbottom" }, + "acceleration_prime_tower": { "value": "math.ceil(acceleration_print * 2000 / 4000)" }, + "acceleration_print": { "value": "1800" }, + "acceleration_travel": { "value": "2000" }, + "acceleration_support": { "value": "math.ceil(acceleration_print * 2000 / 4000)" }, + "acceleration_support_interface": { "value": "acceleration_topbottom" }, + "acceleration_topbottom": { "value": "math.ceil(acceleration_print * 500 / 4000)" }, + "acceleration_wall": { "value": "math.ceil(acceleration_print * 1000 / 4000)" }, + "acceleration_wall_0": { "value": "math.ceil(acceleration_wall * 500 / 1000)" }, + "brim_width": { "value": "4" }, + "cool_fan_full_at_height": { "value": "layer_height_0 + 2 * layer_height" }, + "cool_fan_speed": { "value": "100" }, + "cool_fan_speed_max": { "value": "100" }, + "cool_min_speed": { "value": "5" }, + "fill_outline_gaps": { "value": "True" }, + "infill_overlap": { "value": "0" }, + "min_infill_area": { "value": "1" }, + "min_skin_width_for_expansion": { "value": "2" }, + "jerk_enabled": { "value": "True" }, + "jerk_layer_0": { "value": "jerk_topbottom" }, + "jerk_prime_tower": { "value": "math.ceil(jerk_print * 15 / 25)" }, + "jerk_print": { "value": "25" }, + "jerk_support": { "value": "math.ceil(jerk_print * 15 / 25)" }, + "jerk_support_interface": { "value": "jerk_topbottom" }, + "jerk_topbottom": { "value": "math.ceil(jerk_print * 5 / 25)" }, + "jerk_wall": { "value": "math.ceil(jerk_print * 10 / 25)" }, + "jerk_wall_0": { "value": "math.ceil(jerk_wall * 5 / 10)" }, + "wall_thickness": { "value": "2.4"}, + "line_width": { "value": "extruderValue(-1,'machine_nozzle_size')" }, + "wall_0_inset": { "value": "0.05" }, + "wall_line_width_x": { "value": "line_width" }, + "wall_line_width_0": { "value": "line_width-0.05" }, + "support_line_width": { "value": "max(min(line_width,0.4),line_width/2)" }, + "support_interface_line_width": { "value": "support_line_width" }, + "machine_min_cool_heat_time_window": { "value": "15" }, + "default_material_print_temperature": { "value": "200" }, + "material_print_temperature_layer_0": { "value": "material_print_temperature - 5" }, + "material_bed_temperature": { "maximum_value": "100" }, + "material_bed_temperature_layer_0": { "maximum_value": "100" }, + "raft_airgap": { "value": "0" }, + "raft_base_thickness": { "value": "0.3" }, + "raft_interface_line_spacing": { "value": "0.5" }, + "raft_interface_line_width": { "value": "0.5" }, + "raft_interface_thickness": { "value": "0.2" }, + "raft_jerk": { "value": "jerk_layer_0" }, + "raft_margin": { "value": "5" }, + "raft_surface_layers": { "value": "2" }, + "retraction_amount": { "value": "4" }, + "retraction_count_max": { "value": "10" }, + "retraction_extrusion_window": { "value": "1" }, + "retraction_hop": { "value": "0.5" }, + "retraction_hop_enabled": { "value": "True" }, + "retraction_hop_only_when_collides": { "value": "True" }, + "retraction_min_travel": { "value": "5" }, + "retraction_prime_speed": { "value": "25" }, + "skin_overlap": { "value": "10" }, + "speed_layer_0": { "value": "25" }, + "speed_print": { "value": "70" }, + "speed_support": { "value": "speed_print*line_width/support_line_width" }, + "speed_support_interface": { "value": "speed_print*line_width/support_interface_line_width" }, + "speed_topbottom": { "value": "speed_print*line_width/skin_line_width" }, + "speed_travel": { "value": "100" }, + "speed_infill": { "value": "speed_print*line_width/infill_line_width" }, + "speed_wall": { "value": "speed_print*wall_line_width_0/line_width" }, + "speed_wall_0": { "value": "math.ceil(speed_wall * 0.6)" }, + "speed_wall_x": { "value": "speed_wall" }, + "speed_equalize_flow_enabled": { "value": "False" }, + "support_angle": { "value": "50" }, + "support_xy_distance": { "value": "1" }, + "support_z_distance": { "value": "max((0.2 if(0.2%layer_height==0) else layer_height*int((0.2+layer_height)/layer_height)),layer_height)" }, + "support_bottom_distance": { "value": "max(support_z_distance,layer_height*int(0.45/layer_height))" }, + "top_bottom_thickness": { "value": "max(1.2,layer_height*6)" }, + "travel_avoid_distance": { "value": "3" }, + "gradual_support_infill_step_height": { "value": "0.2" }, + "gradual_support_infill_steps": { "value": "1" }, + "infill_sparse_density": { "value": "20" }, + "gradual_infill_step_height": { "value": "1" }, + "initial_layer_line_width_factor": { "value": "120" }, + "jerk_travel": { "value": "25" }, + "support_bottom_enable": { "value": "True" }, + "support_bottom_height": { "value": "max((0.15 if(0.15%layer_height==0) else layer_height*int((0.15+layer_height)/layer_height)),layer_height)" }, + "support_bottom_pattern": { "value": "'zigzag'" }, + "support_connect_zigzags": { "value": "False" }, + "support_infill_rate": { "value": "8" }, + "support_interface_density": { "value": "80" }, + "support_interface_enable": { "value": "True" }, + "support_interface_height": { "value": "0.5" }, + "support_roof_pattern": { "value": "'concentric'" }, + "z_seam_type": { "value": "'shortest'" }, + "speed_equalize_flow_max": { "value": "65" } + } +} From 147b66845e89dd95550e33f49b0e6bd1d7566821 Mon Sep 17 00:00:00 2001 From: milkok Date: Tue, 24 Jul 2018 17:22:58 +0800 Subject: [PATCH 05/54] Create winbo_mini2_extruder.def.json --- .../extruders/winbo_mini2_extruder.def.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 resources/extruders/winbo_mini2_extruder.def.json diff --git a/resources/extruders/winbo_mini2_extruder.def.json b/resources/extruders/winbo_mini2_extruder.def.json new file mode 100644 index 0000000000..5348e6ad43 --- /dev/null +++ b/resources/extruders/winbo_mini2_extruder.def.json @@ -0,0 +1,19 @@ +{ + "id": "winbo_mini2_extruder", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "winbo_mini2", + "position": "0" + }, + + "overrides": { + "extruder_nr": { + "default_value": 0, + "maximum_value": "2" + }, + "machine_nozzle_offset_x": { "default_value": 0.0 }, + "machine_nozzle_offset_y": { "default_value": 0.0 } + } +} From 17493d92a0fd79dea2719ecb906be8a7d5bbc09f Mon Sep 17 00:00:00 2001 From: milkok Date: Tue, 24 Jul 2018 17:24:01 +0800 Subject: [PATCH 06/54] Create winbo_dragonl4_extruder.def.json --- .../winbo_dragonl4_extruder.def.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 resources/extruders/winbo_dragonl4_extruder.def.json diff --git a/resources/extruders/winbo_dragonl4_extruder.def.json b/resources/extruders/winbo_dragonl4_extruder.def.json new file mode 100644 index 0000000000..a4f65bbf0e --- /dev/null +++ b/resources/extruders/winbo_dragonl4_extruder.def.json @@ -0,0 +1,19 @@ +{ + "id": "winbo_dragonl4_extruder", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "winbo_dragonl4", + "position": "0" + }, + + "overrides": { + "extruder_nr": { + "default_value": 0, + "maximum_value": "2" + }, + "machine_nozzle_offset_x": { "default_value": 0.0 }, + "machine_nozzle_offset_y": { "default_value": 0.0 } + } +} From 43e5110db666c487a5eccdb0656192b580aba941 Mon Sep 17 00:00:00 2001 From: milkok Date: Sat, 28 Jul 2018 17:12:32 +0800 Subject: [PATCH 07/54] Update winbo_mini2_extruder.def.json --- resources/extruders/winbo_mini2_extruder.def.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/extruders/winbo_mini2_extruder.def.json b/resources/extruders/winbo_mini2_extruder.def.json index 5348e6ad43..a57ec28109 100644 --- a/resources/extruders/winbo_mini2_extruder.def.json +++ b/resources/extruders/winbo_mini2_extruder.def.json @@ -1,5 +1,4 @@ { - "id": "winbo_mini2_extruder", "version": 2, "name": "Extruder 1", "inherits": "fdmextruder", @@ -13,6 +12,8 @@ "default_value": 0, "maximum_value": "2" }, + "material_diameter": { "default_value": 1.75 }, + "machine_nozzle_size": { "default_value": 0.4 }, "machine_nozzle_offset_x": { "default_value": 0.0 }, "machine_nozzle_offset_y": { "default_value": 0.0 } } From c05d85f1e426bba65f646830f5e51cda80084640 Mon Sep 17 00:00:00 2001 From: milkok Date: Sat, 28 Jul 2018 17:14:04 +0800 Subject: [PATCH 08/54] Update winbo_dragonl4_extruder.def.json --- resources/extruders/winbo_dragonl4_extruder.def.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/extruders/winbo_dragonl4_extruder.def.json b/resources/extruders/winbo_dragonl4_extruder.def.json index a4f65bbf0e..37a08c8d08 100644 --- a/resources/extruders/winbo_dragonl4_extruder.def.json +++ b/resources/extruders/winbo_dragonl4_extruder.def.json @@ -1,5 +1,4 @@ { - "id": "winbo_dragonl4_extruder", "version": 2, "name": "Extruder 1", "inherits": "fdmextruder", @@ -13,6 +12,8 @@ "default_value": 0, "maximum_value": "2" }, + "material_diameter": { "default_value": 3.0 }, + "machine_nozzle_size": { "default_value": 0.8 }, "machine_nozzle_offset_x": { "default_value": 0.0 }, "machine_nozzle_offset_y": { "default_value": 0.0 } } From 29f2bb8a3a6344f66ee1bd64118dbd2759e3ca52 Mon Sep 17 00:00:00 2001 From: milkok Date: Sat, 28 Jul 2018 17:15:36 +0800 Subject: [PATCH 09/54] Update winbo_dragonl4.def.json --- resources/definitions/winbo_dragonl4.def.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/definitions/winbo_dragonl4.def.json b/resources/definitions/winbo_dragonl4.def.json index cb5566c46b..5aa004414a 100644 --- a/resources/definitions/winbo_dragonl4.def.json +++ b/resources/definitions/winbo_dragonl4.def.json @@ -25,7 +25,6 @@ "machine_filament_park_distance": { "value": "machine_heat_zone_length" }, "machine_nozzle_heat_up_speed": { "default_value": 1.4 }, "machine_nozzle_cool_down_speed": { "default_value": 0.8 }, - "machine_nozzle_size": { "default_value": 0.8 }, "machine_head_with_fans_polygon": { "default_value": @@ -42,7 +41,6 @@ "machine_extruder_count": { "default_value": 1 }, "machine_start_gcode": { "default_value": "G21\nG90\nM82\nM107\nM9998\nG28 X0 Y0\nG28 Z0\nG1 F6000 Z0.3\nG92 E0\nG1 F800 X585 E12\nG92 E0" }, "machine_end_gcode": { "default_value": "M104 S0\nM140 S0\nG92 E2\nG1 E0 F200\nG28 X0 Y0\nM84 X Y E" }, - "material_diameter": { "default_value": 3.0 }, "prime_blob_enable": { "enabled": true }, "acceleration_enabled": { "value": "True" }, "acceleration_layer_0": { "value": "acceleration_topbottom" }, From 380ab22c4237a39f36ba50e351f23e45cb9b3832 Mon Sep 17 00:00:00 2001 From: milkok Date: Sat, 28 Jul 2018 17:19:13 +0800 Subject: [PATCH 10/54] Update winbo_mini2.def.json --- resources/definitions/winbo_mini2.def.json | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/definitions/winbo_mini2.def.json b/resources/definitions/winbo_mini2.def.json index d166b7847e..85c5a445bd 100644 --- a/resources/definitions/winbo_mini2.def.json +++ b/resources/definitions/winbo_mini2.def.json @@ -41,7 +41,6 @@ "machine_extruder_count": { "default_value": 1 }, "machine_start_gcode": { "default_value": "G21\nG90\nM82\nM107\nG28 X0 Y0\nG28 Z0\nG1 F1000 Z3\nG1 F4000 X0\nG1 F4000 Y0\nG1 F1000 Z0.2\nG92 E0\nG1 F1000 X30 E8\nG92 E0\nM117 Printing." }, "machine_end_gcode": { "default_value": "M104 S0\nM140 S0\nG92 E2\nG1 E0 F200\nG28 X0 Y0\nM84 X Y E" }, - "material_diameter": { "default_value": 1.75 }, "prime_blob_enable": { "enabled": true }, "acceleration_enabled": { "value": "True" }, "acceleration_layer_0": { "value": "acceleration_topbottom" }, From 846c55a99d0486bea3623e47412c36d0ab71f0fe Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 31 Jul 2018 09:47:18 +0200 Subject: [PATCH 11/54] Simplify detection of url-encoded filepatch --- plugins/USBPrinting/USBPrinterOutputDevice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 6a97a3c163..7c4d85a5cd 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -127,8 +127,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice): @pyqtSlot(str) def updateFirmware(self, file): - # the file path could be qurl encoded. - if file[:7] == "file://": + # the file path could be url-encoded. + if file.startswith("file://"): self._firmware_location = QUrl(file).toLocalFile() else: self._firmware_location = file From c14539d63ae56836ec3840c3bed7f620d53b6573 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 2 Aug 2018 13:12:01 +0200 Subject: [PATCH 12/54] Fix the sliders that went out of range when using the arrow keys. --- plugins/SimulationView/LayerSlider.qml | 24 ++++++++++++++++++------ plugins/SimulationView/PathSlider.qml | 11 +++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/plugins/SimulationView/LayerSlider.qml b/plugins/SimulationView/LayerSlider.qml index 22f9d91340..d7e22d601e 100644 --- a/plugins/SimulationView/LayerSlider.qml +++ b/plugins/SimulationView/LayerSlider.qml @@ -40,33 +40,41 @@ Item { property bool layersVisible: true - function getUpperValueFromSliderHandle () { + function getUpperValueFromSliderHandle() { return upperHandle.getValue() } - function setUpperValue (value) { + function setUpperValue(value) { upperHandle.setValue(value) updateRangeHandle() } - function getLowerValueFromSliderHandle () { + function getLowerValueFromSliderHandle() { return lowerHandle.getValue() } - function setLowerValue (value) { + function setLowerValue(value) { lowerHandle.setValue(value) updateRangeHandle() } - function updateRangeHandle () { + function updateRangeHandle() { rangeHandle.height = lowerHandle.y - (upperHandle.y + upperHandle.height) } // set the active handle to show only one label at a time - function setActiveHandle (handle) { + function setActiveHandle(handle) { activeHandle = handle } + function normalizeValue(value) { + if (value > sliderRoot.maximumValue) + return sliderRoot.maximumValue + else if (value < sliderRoot.minimumValue) + return sliderRoot.minimumValue + return value + } + // slider track Rectangle { id: track @@ -188,6 +196,8 @@ Item { // set the slider position based on the upper value function setValue (value) { + // Normalize values between range, since using arrow keys will create out-of-the-range values + value = sliderRoot.normalizeValue(value) UM.SimulationView.setCurrentLayer(value) @@ -274,6 +284,8 @@ Item { // set the slider position based on the lower value function setValue (value) { + // Normalize values between range, since using arrow keys will create out-of-the-range values + value = sliderRoot.normalizeValue(value) UM.SimulationView.setMinimumLayer(value) diff --git a/plugins/SimulationView/PathSlider.qml b/plugins/SimulationView/PathSlider.qml index 0a4af904aa..a73cacee69 100644 --- a/plugins/SimulationView/PathSlider.qml +++ b/plugins/SimulationView/PathSlider.qml @@ -29,6 +29,7 @@ Item { // value properties property real maximumValue: 100 + property real minimumValue: 0 property bool roundValues: true property real handleValue: maximumValue @@ -47,6 +48,14 @@ Item { rangeHandle.width = handle.x - sliderRoot.handleSize } + function normalizeValue(value) { + if (value > sliderRoot.maximumValue) + return sliderRoot.maximumValue + else if (value < sliderRoot.minimumValue) + return sliderRoot.minimumValue + return value + } + // slider track Rectangle { id: track @@ -110,6 +119,8 @@ Item { // set the slider position based on the value function setValue (value) { + // Normalize values between range, since using arrow keys will create out-of-the-range values + value = sliderRoot.normalizeValue(value) UM.SimulationView.setCurrentPath(value) From 7157118889d2520b1fa630092d10e03eadb351a2 Mon Sep 17 00:00:00 2001 From: milkok Date: Fri, 3 Aug 2018 09:53:25 +0800 Subject: [PATCH 13/54] Update winbo_mini2.def.json --- resources/definitions/winbo_mini2.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/winbo_mini2.def.json b/resources/definitions/winbo_mini2.def.json index 85c5a445bd..7393fdf910 100644 --- a/resources/definitions/winbo_mini2.def.json +++ b/resources/definitions/winbo_mini2.def.json @@ -1,6 +1,6 @@ { "version": 2, - "name": "Winbo FDM-Mini2", + "name": "Winbo Mini2", "inherits": "fdmprinter", "metadata": { "author": "Winbo", @@ -16,7 +16,7 @@ }, "overrides": { - "machine_name": { "default_value": "Winbo FDM-Mini2" }, + "machine_name": { "default_value": "Winbo Mini2" }, "machine_width": { "default_value": 205 }, "machine_depth": { "default_value": 155 }, "machine_height": { "default_value": 205 }, From 85575f67820570c3188965a0b464aeec8b86723b Mon Sep 17 00:00:00 2001 From: milkok Date: Fri, 3 Aug 2018 09:58:23 +0800 Subject: [PATCH 14/54] Update winbo_dragonl4.def.json --- resources/definitions/winbo_dragonl4.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/winbo_dragonl4.def.json b/resources/definitions/winbo_dragonl4.def.json index 5aa004414a..0ca68cdcee 100644 --- a/resources/definitions/winbo_dragonl4.def.json +++ b/resources/definitions/winbo_dragonl4.def.json @@ -1,6 +1,6 @@ { "version": 2, - "name": "Winbo FDM-Dragon(L)4", + "name": "Winbo Dragon(L)4", "inherits": "fdmprinter", "metadata": { "author": "Winbo", @@ -16,7 +16,7 @@ }, "overrides": { - "machine_name": { "default_value": "Winbo FDM-Dragon(L)4" }, + "machine_name": { "default_value": "Winbo Dragon(L)4" }, "machine_width": { "default_value": 615 }, "machine_depth": { "default_value": 463 }, "machine_height": { "default_value": 615 }, From 1a189ba4f5be0d0a377ddcdd8671e0a59b5d7533 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 3 Aug 2018 09:13:19 +0200 Subject: [PATCH 15/54] Simplify normalization formula. --- plugins/SimulationView/LayerSlider.qml | 6 +----- plugins/SimulationView/PathSlider.qml | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/plugins/SimulationView/LayerSlider.qml b/plugins/SimulationView/LayerSlider.qml index d7e22d601e..6dcaa3f475 100644 --- a/plugins/SimulationView/LayerSlider.qml +++ b/plugins/SimulationView/LayerSlider.qml @@ -68,11 +68,7 @@ Item { } function normalizeValue(value) { - if (value > sliderRoot.maximumValue) - return sliderRoot.maximumValue - else if (value < sliderRoot.minimumValue) - return sliderRoot.minimumValue - return value + return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue) } // slider track diff --git a/plugins/SimulationView/PathSlider.qml b/plugins/SimulationView/PathSlider.qml index a73cacee69..999912e3ba 100644 --- a/plugins/SimulationView/PathSlider.qml +++ b/plugins/SimulationView/PathSlider.qml @@ -49,11 +49,7 @@ Item { } function normalizeValue(value) { - if (value > sliderRoot.maximumValue) - return sliderRoot.maximumValue - else if (value < sliderRoot.minimumValue) - return sliderRoot.minimumValue - return value + return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue) } // slider track From 1a050968a51f6f1e91e9ff72a8b856b2510ecc28 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 3 Aug 2018 10:51:01 +0200 Subject: [PATCH 16/54] Clear out old gcode This way if you move the model, and don't hit prepare again, you can end up exporting the old gcode again thinking its new gcode. --- plugins/CuraEngineBackend/CuraEngineBackend.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index beec374d8d..796495fef8 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -541,6 +541,9 @@ class CuraEngineBackend(QObject, Backend): ## Remove old layer data (if any) def _clearLayerData(self, build_plate_numbers: Set = None) -> None: + # Clear out any old gcode + self._scene.gcode_dict = {} # type: dict + for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if node.callDecoration("getLayerData"): if not build_plate_numbers or node.callDecoration("getBuildPlateNumber") in build_plate_numbers: From 1d0e88dc161844f4342176b1da4b810348bf0ba0 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 3 Aug 2018 11:00:56 +0200 Subject: [PATCH 17/54] Fix typing issue --- plugins/CuraEngineBackend/CuraEngineBackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 796495fef8..56763a6632 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -542,7 +542,7 @@ class CuraEngineBackend(QObject, Backend): ## Remove old layer data (if any) def _clearLayerData(self, build_plate_numbers: Set = None) -> None: # Clear out any old gcode - self._scene.gcode_dict = {} # type: dict + self._scene.gcode_dict = {} # type: ignore for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if node.callDecoration("getLayerData"): From aadbebd4572c8be0886c9a78c99daa89ba45a52d Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 3 Aug 2018 11:13:35 +0200 Subject: [PATCH 18/54] After enabling the video strem from a printer the first image is Null CURA-5612 --- cura/CameraImageProvider.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cura/CameraImageProvider.py b/cura/CameraImageProvider.py index ddf978f625..428b174ed4 100644 --- a/cura/CameraImageProvider.py +++ b/cura/CameraImageProvider.py @@ -12,7 +12,13 @@ class CameraImageProvider(QQuickImageProvider): def requestImage(self, id, size): for output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices(): try: - return output_device.activePrinter.camera.getImage(), QSize(15, 15) + + image = output_device.activePrinter.camera.getImage() + + if image.isNull(): + return QImage(), QSize(15, 15) + + return image, QSize(15, 15) except AttributeError: pass return QImage(), QSize(15, 15) \ No newline at end of file From bcedef3bbeffbe3317fbb49b43bd97efd9860d2a Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 3 Aug 2018 11:38:42 +0200 Subject: [PATCH 19/54] Continue scrolling over compatibility charts Contributes to CURA-5598 --- .../Toolbox/resources/qml/ToolboxCompatibilityChart.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index 4978af6168..f4ee82e7cf 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -29,6 +29,14 @@ Item anchors.topMargin: UM.Theme.getSize("default_margin").height width: parent.width frameVisible: false + + // Workaround for scroll issues (QTBUG-49652) + flickableItem.interactive: false + Component.onCompleted: { + for (var i = 0; i < flickableItem.children.length; ++i) { + flickableItem.children[i].enabled = false + } + } selectionMode: 0 model: packageData.supported_configs headerDelegate: Rectangle From c47de049830f236ebbfd2479a59c8a4b585c5a66 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 3 Aug 2018 11:41:08 +0200 Subject: [PATCH 20/54] Fix bracket style Contributes to CURA-5598 --- plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index f4ee82e7cf..1efcde2110 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -32,8 +32,10 @@ Item // Workaround for scroll issues (QTBUG-49652) flickableItem.interactive: false - Component.onCompleted: { - for (var i = 0; i < flickableItem.children.length; ++i) { + Component.onCompleted: + { + for (var i = 0; i < flickableItem.children.length; ++i) + { flickableItem.children[i].enabled = false } } From 9c8ac6e9ed25a52e9370f520fae703404be7f6c5 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 3 Aug 2018 11:42:55 +0200 Subject: [PATCH 21/54] After switching the Monitor Tab set Null to active output printer CURA-5612 --- plugins/UM3NetworkPrinting/ClusterMonitorItem.qml | 11 +++++++++++ plugins/UM3NetworkPrinting/PrinterVideoStream.qml | 10 ++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml b/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml index 86bdaae0a5..c21ff3f472 100644 --- a/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml +++ b/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml @@ -9,6 +9,7 @@ Component { Rectangle { + id: rectangle width: maximumWidth height: maximumHeight color: UM.Theme.getColor("viewport_background") @@ -103,5 +104,15 @@ Component visible: OutputDevice.activePrinter != null anchors.fill:parent } + + onVisibleChanged: + { + if(rectangle != null && !rectangle.visible) + { + // After switching the Tab ensure that active printer is Null, the video stream image + // might be active + OutputDevice.setActivePrinter(null) + } + } } } diff --git a/plugins/UM3NetworkPrinting/PrinterVideoStream.qml b/plugins/UM3NetworkPrinting/PrinterVideoStream.qml index 7f7b2ad546..e34924ba88 100644 --- a/plugins/UM3NetworkPrinting/PrinterVideoStream.qml +++ b/plugins/UM3NetworkPrinting/PrinterVideoStream.qml @@ -89,9 +89,11 @@ Item MouseArea { - anchors.fill: cameraImage - onClicked: { /* no-op */ } - z: 1 + anchors.fill: cameraImage + onClicked: + { + OutputDevice.setActivePrinter(null) + } + z: 1 } - } From 38a0c9b66de36f46390b42834f595557a56d1df0 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 3 Aug 2018 12:02:11 +0200 Subject: [PATCH 22/54] Add some extra information when the Cura can't write the files. --- plugins/3MFWriter/ThreeMFWriter.py | 4 ++++ plugins/GCodeGzWriter/GCodeGzWriter.py | 8 +++++++- plugins/GCodeWriter/GCodeWriter.py | 5 +++++ plugins/UFPWriter/UFPWriter.py | 10 +++++++++- 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index 8d54f475d6..640aabd30c 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -25,6 +25,9 @@ except ImportError: import zipfile import UM.Application +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + class ThreeMFWriter(MeshWriter): def __init__(self): @@ -173,6 +176,7 @@ class ThreeMFWriter(MeshWriter): archive.writestr(relations_file, b' \n' + ET.tostring(relations_element)) except Exception as e: Logger.logException("e", "Error writing zip file") + self.setInformation(catalog.i18nc("@error:zip", "Error writing 3mf file.")) return False finally: if not self._store_archive: diff --git a/plugins/GCodeGzWriter/GCodeGzWriter.py b/plugins/GCodeGzWriter/GCodeGzWriter.py index 6ddecdb0bd..e191a9c427 100644 --- a/plugins/GCodeGzWriter/GCodeGzWriter.py +++ b/plugins/GCodeGzWriter/GCodeGzWriter.py @@ -10,6 +10,9 @@ from UM.Mesh.MeshWriter import MeshWriter #The class we're extending/implementin from UM.PluginRegistry import PluginRegistry from UM.Scene.SceneNode import SceneNode #For typing. +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + ## A file writer that writes gzipped g-code. # # If you're zipping g-code, you might as well use gzip! @@ -28,12 +31,15 @@ class GCodeGzWriter(MeshWriter): def write(self, stream: BufferedIOBase, nodes: List[SceneNode], mode = MeshWriter.OutputMode.BinaryMode) -> bool: if mode != MeshWriter.OutputMode.BinaryMode: Logger.log("e", "GCodeGzWriter does not support text mode.") + self.setInformation(catalog.i18nc("@error:not supported", "GCodeGzWriter does not support text mode.")) return False #Get the g-code from the g-code writer. gcode_textio = StringIO() #We have to convert the g-code into bytes. - success = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter")).write(gcode_textio, None) + gcode_writer = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter")) + success = gcode_writer.write(gcode_textio, None) if not success: #Writing the g-code failed. Then I can also not write the gzipped g-code. + self.setInformation(gcode_writer.getInformation()) return False result = gzip.compress(gcode_textio.getvalue().encode("utf-8")) diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index aea0698d19..59e9a29691 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -12,6 +12,8 @@ from UM.Settings.InstanceContainer import InstanceContainer from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") ## Writes g-code to a file. # @@ -62,11 +64,13 @@ class GCodeWriter(MeshWriter): def write(self, stream, nodes, mode = MeshWriter.OutputMode.TextMode): if mode != MeshWriter.OutputMode.TextMode: Logger.log("e", "GCodeWriter does not support non-text mode.") + self.setInformation(catalog.i18nc("@error:not supported", "GCodeWriter does not support non-text mode.")) return False active_build_plate = Application.getInstance().getMultiBuildPlateModel().activeBuildPlate scene = Application.getInstance().getController().getScene() if not hasattr(scene, "gcode_dict"): + self.setInformation(catalog.i18nc("@warning:status", "Please generate G-code before saving.")) return False gcode_dict = getattr(scene, "gcode_dict") gcode_list = gcode_dict.get(active_build_plate, None) @@ -82,6 +86,7 @@ class GCodeWriter(MeshWriter): stream.write(settings) return True + self.setInformation(catalog.i18nc("@warning:status", "Please generate G-code before saving.")) return False ## Create a new container with container 2 as base and container 1 written over it. diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index aca293e25a..9344bf54da 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -1,5 +1,6 @@ #Copyright (c) 2018 Ultimaker B.V. #Cura is released under the terms of the LGPLv3 or higher. +from typing import cast from Charon.VirtualFile import VirtualFile #To open UFP files. from Charon.OpenMode import OpenMode #To indicate that we want to write to UFP files. @@ -13,6 +14,9 @@ from PyQt5.QtCore import QBuffer from cura.Snapshot import Snapshot +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + class UFPWriter(MeshWriter): def __init__(self): @@ -32,7 +36,11 @@ class UFPWriter(MeshWriter): #Store the g-code from the scene. archive.addContentType(extension = "gcode", mime_type = "text/x-gcode") gcode_textio = StringIO() #We have to convert the g-code into bytes. - PluginRegistry.getInstance().getPluginObject("GCodeWriter").write(gcode_textio, None) + gcode_writer = cast(MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter")) + success = gcode_writer.write(gcode_textio, None) + if not success: #Writing the g-code failed. Then I can also not write the gzipped g-code. + self.setInformation(gcode_writer.getInformation()) + return False gcode = archive.getStream("/3D/model.gcode") gcode.write(gcode_textio.getvalue().encode("UTF-8")) archive.addRelation(virtual_path = "/3D/model.gcode", relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode") From 977c0958a337505b3ef982bc6d77c11e5b8614b6 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 3 Aug 2018 13:40:54 +0200 Subject: [PATCH 23/54] The manually added priters by IP have "(manual) suffix --- plugins/UM3NetworkPrinting/DiscoverUM3Action.qml | 7 +------ plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml index 8ecbe016f1..e4d75e8f6d 100644 --- a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml @@ -349,11 +349,6 @@ Cura.MachineAction addressField.focus = true; } - onAccepted: - { - manager.setManualDevice(printerKey, addressText) - } - Column { anchors.fill: parent spacing: UM.Theme.getSize("default_margin").height @@ -393,7 +388,7 @@ Cura.MachineAction text: catalog.i18nc("@action:button", "OK") onClicked: { - manualPrinterDialog.accept() + manager.setManualDevice(manualPrinterDialog.printerKey, manualPrinterDialog.addressText) manualPrinterDialog.hide() } enabled: manualPrinterDialog.addressText.trim() != "" diff --git a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py index 3644bbf056..f4749a6747 100644 --- a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py @@ -198,7 +198,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): has_cluster_capable_firmware = Version(system_info["firmware"]) > self._min_cluster_version instance_name = "manual:%s" % address properties = { - b"name": system_info["name"].encode("utf-8"), + b"name": (system_info["name"] + " (manual)").encode("utf-8"), b"address": address.encode("utf-8"), b"firmware_version": system_info["firmware"].encode("utf-8"), b"manual": b"true", From 38ce73365a8315e0b5ccaa52dab3b5dc49ac8469 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 3 Aug 2018 13:45:08 +0200 Subject: [PATCH 24/54] Manually added printers have suffix "( manual)" --- plugins/UM3NetworkPrinting/DiscoverUM3Action.qml | 7 +------ plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml index 8ecbe016f1..e4d75e8f6d 100644 --- a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml @@ -349,11 +349,6 @@ Cura.MachineAction addressField.focus = true; } - onAccepted: - { - manager.setManualDevice(printerKey, addressText) - } - Column { anchors.fill: parent spacing: UM.Theme.getSize("default_margin").height @@ -393,7 +388,7 @@ Cura.MachineAction text: catalog.i18nc("@action:button", "OK") onClicked: { - manualPrinterDialog.accept() + manager.setManualDevice(manualPrinterDialog.printerKey, manualPrinterDialog.addressText) manualPrinterDialog.hide() } enabled: manualPrinterDialog.addressText.trim() != "" diff --git a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py index 3644bbf056..f4749a6747 100644 --- a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py @@ -198,7 +198,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): has_cluster_capable_firmware = Version(system_info["firmware"]) > self._min_cluster_version instance_name = "manual:%s" % address properties = { - b"name": system_info["name"].encode("utf-8"), + b"name": (system_info["name"] + " (manual)").encode("utf-8"), b"address": address.encode("utf-8"), b"firmware_version": system_info["firmware"].encode("utf-8"), b"manual": b"true", From 99cb9e93b30fcc8ca061ef379d0df81b7b9ba1d4 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 3 Aug 2018 13:46:22 +0200 Subject: [PATCH 25/54] Revert "The manually added priters by IP have "(manual) suffix" This reverts commit 977c0958a337505b3ef982bc6d77c11e5b8614b6. --- plugins/UM3NetworkPrinting/DiscoverUM3Action.qml | 7 ++++++- plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml index e4d75e8f6d..8ecbe016f1 100644 --- a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml @@ -349,6 +349,11 @@ Cura.MachineAction addressField.focus = true; } + onAccepted: + { + manager.setManualDevice(printerKey, addressText) + } + Column { anchors.fill: parent spacing: UM.Theme.getSize("default_margin").height @@ -388,7 +393,7 @@ Cura.MachineAction text: catalog.i18nc("@action:button", "OK") onClicked: { - manager.setManualDevice(manualPrinterDialog.printerKey, manualPrinterDialog.addressText) + manualPrinterDialog.accept() manualPrinterDialog.hide() } enabled: manualPrinterDialog.addressText.trim() != "" diff --git a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py index f4749a6747..3644bbf056 100644 --- a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py @@ -198,7 +198,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): has_cluster_capable_firmware = Version(system_info["firmware"]) > self._min_cluster_version instance_name = "manual:%s" % address properties = { - b"name": (system_info["name"] + " (manual)").encode("utf-8"), + b"name": system_info["name"].encode("utf-8"), b"address": address.encode("utf-8"), b"firmware_version": system_info["firmware"].encode("utf-8"), b"manual": b"true", From ea8d10783ce1e3ba58309c2e5fe1cdbb6654c0a3 Mon Sep 17 00:00:00 2001 From: DavidGergely Date: Fri, 3 Aug 2018 14:12:36 +0200 Subject: [PATCH 26/54] Cura-5457 prime tower settings New settings for the prime towers for all profiles expect PP0.8 printcore --- resources/definitions/fdmprinter.def.json | 2 +- .../quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg | 2 -- .../quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg | 2 -- resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg | 1 - .../quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg | 1 - .../quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg | 1 - .../ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg | 2 -- .../ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg | 2 -- .../quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg | 1 - .../ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg | 1 - .../ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg | 1 - resources/variants/ultimaker3_bb0.8.inst.cfg | 1 - resources/variants/ultimaker3_bb04.inst.cfg | 1 - resources/variants/ultimaker3_extended_bb0.8.inst.cfg | 1 - resources/variants/ultimaker3_extended_bb04.inst.cfg | 1 - resources/variants/ultimaker_s5_bb0.8.inst.cfg | 1 - resources/variants/ultimaker_s5_bb04.inst.cfg | 1 - 17 files changed, 1 insertion(+), 21 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index b767aac7b9..daf09d2701 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -5057,7 +5057,7 @@ "description": "The minimum volume for each layer of the prime tower in order to purge enough material.", "unit": "mm³", "type": "float", - "default_value": 5, + "default_value": 6, "minimum_value": "0", "maximum_value_warning": "((resolveOrValue('prime_tower_size') * 0.5) ** 2 * 3.14159 * resolveOrValue('layer_height') if prime_tower_circular else resolveOrValue('prime_tower_size') ** 2 * resolveOrValue('layer_height')) - sum(extruderValues('prime_tower_min_volume')) + prime_tower_min_volume", "enabled": "resolveOrValue('prime_tower_enable')", diff --git a/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg index 428f5c1101..5139a1fea8 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg @@ -15,8 +15,6 @@ variant = AA 0.25 cool_fan_speed = 40 infill_overlap = 15 material_final_print_temperature = =material_print_temperature - 5 -prime_tower_size = 12 -prime_tower_min_volume = 2 retraction_prime_speed = 25 speed_topbottom = =math.ceil(speed_print * 30 / 55) wall_thickness = 0.92 diff --git a/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg index 127032e118..4e81b4f39e 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg @@ -12,8 +12,6 @@ material = generic_cpe variant = AA 0.25 [values] -prime_tower_size = 12 -prime_tower_min_volume = 2 retraction_extrusion_window = 0.5 speed_infill = =math.ceil(speed_print * 40 / 55) speed_topbottom = =math.ceil(speed_print * 30 / 55) diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg index 1d5cea601f..b6e6fdecb6 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg @@ -34,7 +34,6 @@ material_standby_temperature = 100 multiple_mesh_overlap = 0.2 prime_tower_enable = True prime_tower_flow = 100 -prime_tower_min_volume = 10 retract_at_layer_change = False retraction_count_max = 12 retraction_extra_prime_amount = 0.5 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg index 0c94b64159..b64d37310e 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg @@ -35,7 +35,6 @@ material_standby_temperature = 100 multiple_mesh_overlap = 0.2 prime_tower_enable = True prime_tower_flow = 100 -prime_tower_min_volume = 15 retract_at_layer_change = False retraction_count_max = 12 retraction_extra_prime_amount = 0.5 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg index 50282f8b49..d9e8f9ec2e 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg @@ -34,7 +34,6 @@ material_standby_temperature = 100 multiple_mesh_overlap = 0.2 prime_tower_enable = True prime_tower_flow = 100 -prime_tower_min_volume = 20 retract_at_layer_change = False retraction_count_max = 12 retraction_extra_prime_amount = 0.5 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg index e58a1bd87d..f2e05b08e8 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg @@ -15,8 +15,6 @@ variant = AA 0.25 cool_fan_speed = 40 infill_overlap = 15 material_final_print_temperature = =material_print_temperature - 5 -prime_tower_size = 12 -prime_tower_min_volume = 2 retraction_prime_speed = 25 speed_topbottom = =math.ceil(speed_print * 30 / 55) wall_thickness = 0.92 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg index 1c1833a385..2068ed51c0 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg @@ -12,8 +12,6 @@ material = generic_cpe variant = AA 0.25 [values] -prime_tower_size = 12 -prime_tower_min_volume = 2 retraction_extrusion_window = 0.5 speed_infill = =math.ceil(speed_print * 40 / 55) speed_topbottom = =math.ceil(speed_print * 30 / 55) diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg index f88da43ac1..e8c58ce32c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg @@ -32,7 +32,6 @@ material_standby_temperature = 100 multiple_mesh_overlap = 0.2 prime_tower_enable = True prime_tower_flow = 100 -prime_tower_min_volume = 10 retract_at_layer_change = False retraction_count_max = 12 retraction_extra_prime_amount = 0.5 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg index 2967f3539b..ff723c4ed4 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg @@ -33,7 +33,6 @@ material_standby_temperature = 100 multiple_mesh_overlap = 0.2 prime_tower_enable = True prime_tower_flow = 100 -prime_tower_min_volume = 20 retract_at_layer_change = False retraction_count_max = 12 retraction_extra_prime_amount = 0.5 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg index caa87f9437..7e36e9d354 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg @@ -32,7 +32,6 @@ material_standby_temperature = 100 multiple_mesh_overlap = 0.2 prime_tower_enable = True prime_tower_flow = 100 -prime_tower_min_volume = 15 retract_at_layer_change = False retraction_count_max = 12 retraction_extra_prime_amount = 0.5 diff --git a/resources/variants/ultimaker3_bb0.8.inst.cfg b/resources/variants/ultimaker3_bb0.8.inst.cfg index 9ad4284b40..ace0bf3a94 100644 --- a/resources/variants/ultimaker3_bb0.8.inst.cfg +++ b/resources/variants/ultimaker3_bb0.8.inst.cfg @@ -40,7 +40,6 @@ material_print_temperature = =default_material_print_temperature + 10 material_standby_temperature = 100 multiple_mesh_overlap = 0 prime_tower_enable = False -prime_tower_min_volume = 20 prime_tower_wipe_enabled = True raft_acceleration = =acceleration_layer_0 raft_airgap = 0 diff --git a/resources/variants/ultimaker3_bb04.inst.cfg b/resources/variants/ultimaker3_bb04.inst.cfg index f07a4a55f9..d571cabc9b 100644 --- a/resources/variants/ultimaker3_bb04.inst.cfg +++ b/resources/variants/ultimaker3_bb04.inst.cfg @@ -22,7 +22,6 @@ jerk_support_bottom = =math.ceil(jerk_support_interface * 1 / 10) machine_nozzle_heat_up_speed = 1.5 machine_nozzle_id = BB 0.4 machine_nozzle_tip_outer_diameter = 1.0 -prime_tower_min_volume = 15 raft_base_speed = 20 raft_interface_speed = 20 raft_speed = 25 diff --git a/resources/variants/ultimaker3_extended_bb0.8.inst.cfg b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg index d7a76d538a..fe760c93b8 100644 --- a/resources/variants/ultimaker3_extended_bb0.8.inst.cfg +++ b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg @@ -40,7 +40,6 @@ material_print_temperature = =default_material_print_temperature + 10 material_standby_temperature = 100 multiple_mesh_overlap = 0 prime_tower_enable = False -prime_tower_min_volume = 20 prime_tower_wipe_enabled = True raft_acceleration = =acceleration_layer_0 raft_airgap = 0 diff --git a/resources/variants/ultimaker3_extended_bb04.inst.cfg b/resources/variants/ultimaker3_extended_bb04.inst.cfg index 6e882cfa04..742dc9896e 100644 --- a/resources/variants/ultimaker3_extended_bb04.inst.cfg +++ b/resources/variants/ultimaker3_extended_bb04.inst.cfg @@ -22,7 +22,6 @@ jerk_support_bottom = =math.ceil(jerk_support_interface * 1 / 10) machine_nozzle_heat_up_speed = 1.5 machine_nozzle_id = BB 0.4 machine_nozzle_tip_outer_diameter = 1.0 -prime_tower_min_volume = 15 raft_base_speed = 20 raft_interface_speed = 20 raft_speed = 25 diff --git a/resources/variants/ultimaker_s5_bb0.8.inst.cfg b/resources/variants/ultimaker_s5_bb0.8.inst.cfg index 6b954041ab..c1c5c1a10b 100644 --- a/resources/variants/ultimaker_s5_bb0.8.inst.cfg +++ b/resources/variants/ultimaker_s5_bb0.8.inst.cfg @@ -40,7 +40,6 @@ material_print_temperature = =default_material_print_temperature + 10 material_standby_temperature = 100 multiple_mesh_overlap = 0 prime_tower_enable = False -prime_tower_min_volume = 20 prime_tower_wipe_enabled = True raft_acceleration = =acceleration_layer_0 raft_airgap = 0 diff --git a/resources/variants/ultimaker_s5_bb04.inst.cfg b/resources/variants/ultimaker_s5_bb04.inst.cfg index 634920ca65..b5ff8d51f6 100644 --- a/resources/variants/ultimaker_s5_bb04.inst.cfg +++ b/resources/variants/ultimaker_s5_bb04.inst.cfg @@ -22,7 +22,6 @@ jerk_support_bottom = =math.ceil(jerk_support_interface * 1 / 10) machine_nozzle_heat_up_speed = 1.5 machine_nozzle_id = BB 0.4 machine_nozzle_tip_outer_diameter = 1.0 -prime_tower_min_volume = 20 raft_base_speed = 20 raft_interface_speed = 20 raft_speed = 25 From 2a6da9fe8ea901aaa3942b20363285ec78fe55c8 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 3 Aug 2018 14:28:45 +0200 Subject: [PATCH 27/54] Only show material packages when clicking an author Contributes to CURA-5604 --- .../resources/qml/ToolboxDownloadsGridTile.qml | 4 ++++ plugins/Toolbox/src/Toolbox.py | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 392b867071..f7b964b957 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -115,6 +115,10 @@ Item { toolbox.viewPage = "author" toolbox.filterModelByProp("packages", "author_id", model.id) + toolbox.setFilters("packages", { + "author_id": model.id, + "type": "material" + }) } break default: diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index a9d0bb03d1..fbc2f98b3b 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -783,7 +783,16 @@ class Toolbox(QObject, Extension): self._models[modelType].setFilter({ filterType: parameter }) self.filterChanged.emit() - @pyqtSlot() + @pyqtSlot(str, "QVariantMap") + def setFilters(self, modelType: str, filterDict: dict): + if not self._models[modelType]: + Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", modelType) + return + print(filterDict) + self._models[modelType].setFilter(filterDict) + self.filterChanged.emit() + + @pyqtSlot(str) def removeFilters(self, modelType: str): if not self._models[modelType]: Logger.log("w", "Toolbox: Couldn't remove filters on %s model because it doesn't exist.", modelType) From 7856e4746711c648a24d34b25a9d95f821495763 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 3 Aug 2018 14:30:49 +0200 Subject: [PATCH 28/54] Remove old filtering Contributes to CURA-5604 --- plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index f7b964b957..ebd4c006f8 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -114,7 +114,6 @@ Item else { toolbox.viewPage = "author" - toolbox.filterModelByProp("packages", "author_id", model.id) toolbox.setFilters("packages", { "author_id": model.id, "type": "material" From 8268419eda658541ca03e793047a0ff064954aad Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 3 Aug 2018 14:31:30 +0200 Subject: [PATCH 29/54] Remove debug print() Contributes to CURA-5604 --- plugins/Toolbox/src/Toolbox.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index fbc2f98b3b..e948236baf 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -788,7 +788,6 @@ class Toolbox(QObject, Extension): if not self._models[modelType]: Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", modelType) return - print(filterDict) self._models[modelType].setFilter(filterDict) self.filterChanged.emit() From 20806eeb72afeae35b33808381986978a982088c Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 3 Aug 2018 14:37:09 +0200 Subject: [PATCH 30/54] Refactor some code CURA-5612 --- cura/CameraImageProvider.py | 9 ++++----- plugins/UM3NetworkPrinting/ClusterMonitorItem.qml | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cura/CameraImageProvider.py b/cura/CameraImageProvider.py index 428b174ed4..ff5c51f24b 100644 --- a/cura/CameraImageProvider.py +++ b/cura/CameraImageProvider.py @@ -4,21 +4,20 @@ from PyQt5.QtCore import QSize from UM.Application import Application + class CameraImageProvider(QQuickImageProvider): def __init__(self): - QQuickImageProvider.__init__(self, QQuickImageProvider.Image) + super().__init__(QQuickImageProvider.Image) ## Request a new image. def requestImage(self, id, size): for output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices(): try: - image = output_device.activePrinter.camera.getImage() - if image.isNull(): - return QImage(), QSize(15, 15) + image = QImage() return image, QSize(15, 15) except AttributeError: pass - return QImage(), QSize(15, 15) \ No newline at end of file + return QImage(), QSize(15, 15) diff --git a/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml b/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml index c21ff3f472..0e86d55de8 100644 --- a/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml +++ b/plugins/UM3NetworkPrinting/ClusterMonitorItem.qml @@ -9,7 +9,7 @@ Component { Rectangle { - id: rectangle + id: monitorFrame width: maximumWidth height: maximumHeight color: UM.Theme.getColor("viewport_background") @@ -107,7 +107,7 @@ Component onVisibleChanged: { - if(rectangle != null && !rectangle.visible) + if (!monitorFrame.visible) { // After switching the Tab ensure that active printer is Null, the video stream image // might be active From 1bacdabf358ae8a52342aca8b29263cc680c42f2 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 3 Aug 2018 14:42:34 +0200 Subject: [PATCH 31/54] Minor refactor --- plugins/Toolbox/src/Toolbox.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index e948236baf..c4205b8ed5 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -776,25 +776,25 @@ class Toolbox(QObject, Extension): # Filter Models: # -------------------------------------------------------------------------- @pyqtSlot(str, str, str) - def filterModelByProp(self, modelType: str, filterType: str, parameter: str): - if not self._models[modelType]: - Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", modelType) + def filterModelByProp(self, model_type: str, filter_type: str, parameter: str) -> None: + if not self._models[model_type]: + Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", model_type) return - self._models[modelType].setFilter({ filterType: parameter }) + self._models[model_type].setFilter({filter_type: parameter}) self.filterChanged.emit() @pyqtSlot(str, "QVariantMap") - def setFilters(self, modelType: str, filterDict: dict): - if not self._models[modelType]: - Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", modelType) + def setFilters(self, model_type: str, filter_dict: dict) -> None: + if not self._models[model_type]: + Logger.log("w", "Toolbox: Couldn't filter %s model because it doesn't exist.", model_type) return - self._models[modelType].setFilter(filterDict) + self._models[model_type].setFilter(filter_dict) self.filterChanged.emit() @pyqtSlot(str) - def removeFilters(self, modelType: str): - if not self._models[modelType]: - Logger.log("w", "Toolbox: Couldn't remove filters on %s model because it doesn't exist.", modelType) + def removeFilters(self, model_type: str) -> None: + if not self._models[model_type]: + Logger.log("w", "Toolbox: Couldn't remove filters on %s model because it doesn't exist.", model_type) return - self._models[modelType].setFilter({}) + self._models[model_type].setFilter({}) self.filterChanged.emit() From 944ababa1cb44e7c33249e81692ce2a5c8d5994e Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 3 Aug 2018 14:48:04 +0200 Subject: [PATCH 32/54] Copy normal tile behavior to showcase tiles Contributes to CURA-5604 --- .../qml/ToolboxDownloadsShowcaseTile.qml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index 2ca0b522fe..15d1ae302c 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -105,8 +105,21 @@ Rectangle switch(toolbox.viewCategory) { case "material": - toolbox.viewPage = "author" - toolbox.filterModelByProp("packages", "author_name", model.name) + + // If model has a type, it must be a package + if (model.type !== undefined) + { + toolbox.viewPage = "detail" + toolbox.filterModelByProp("packages", "id", model.id) + } + else + { + toolbox.viewPage = "author" + toolbox.setFilters("packages", { + "author_id": model.id, + "type": "material" + }) + } break default: toolbox.viewPage = "detail" From 4491d5164a91b6ba5d5b4fe9b6fa7ba3daedb81b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 6 Aug 2018 09:24:48 +0200 Subject: [PATCH 33/54] Fix the gcode start/end formatter. Fixes #4181. --- plugins/CuraEngineBackend/StartSliceJob.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index eb392de121..76ff1cfc85 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -41,7 +41,7 @@ class StartJobResult(IntEnum): ## Formatter class that handles token expansion in start/end gcode class GcodeStartEndFormatter(Formatter): - def get_value(self, key: str, *args: str, default_extruder_nr: str = "-1", **kwargs) -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class] + def get_value(self, key: str, args: str, kwargs: Dict[int, Dict[str, Any]], default_extruder_nr: str = "-1") -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class] # The kwargs dictionary contains a dictionary for each stack (with a string of the extruder_nr as their key), # and a default_extruder_nr to use when no extruder_nr is specified From bfaaa4a47103da633ce57a34862eb8fd61282944 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 6 Aug 2018 09:32:12 +0200 Subject: [PATCH 34/54] Fix code-style --- plugins/CuraEngineBackend/StartSliceJob.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 76ff1cfc85..0ebcafdbb2 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -41,7 +41,7 @@ class StartJobResult(IntEnum): ## Formatter class that handles token expansion in start/end gcode class GcodeStartEndFormatter(Formatter): - def get_value(self, key: str, args: str, kwargs: Dict[int, Dict[str, Any]], default_extruder_nr: str = "-1") -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class] + def get_value(self, key: str, args: str, kwargs: dict, default_extruder_nr: str = "-1") -> str: #type: ignore # [CodeStyle: get_value is an overridden function from the Formatter class] # The kwargs dictionary contains a dictionary for each stack (with a string of the extruder_nr as their key), # and a default_extruder_nr to use when no extruder_nr is specified From 95481b856061f18aac6f4a0c58b90366cb90dd40 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 6 Aug 2018 17:11:28 +0200 Subject: [PATCH 35/54] Fix print order for one-at-a-time mode --- cura/OneAtATimeIterator.py | 183 +++++++++++++++--------------- cura/Scene/ConvexHullDecorator.py | 2 +- cura/Settings/GlobalStack.py | 3 + 3 files changed, 93 insertions(+), 95 deletions(-) diff --git a/cura/OneAtATimeIterator.py b/cura/OneAtATimeIterator.py index 84d65bae8e..cb063bfde5 100644 --- a/cura/OneAtATimeIterator.py +++ b/cura/OneAtATimeIterator.py @@ -1,112 +1,107 @@ -# Copyright (c) 2015 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import sys + from UM.Scene.Iterator import Iterator from UM.Scene.SceneNode import SceneNode -from functools import cmp_to_key -from UM.Application import Application -## Iterator that returns a list of nodes in the order that they need to be printed -# If there is no solution an empty list is returned. -# Take note that the list of nodes can have children (that may or may not contain mesh data) + +# Iterator that determines the object print order when one-at a time mode is enabled. +# +# In one-at-a-time mode, only one extruder can be enabled to print. In order to maximize the number of objects we can +# print, we need to print from the corner that's closest to the extruder that's being used. Here is an illustration: +# +# +--------------------------------+ +# | | +# | | +# | | - Rectangle represents the complete print head including fans, etc. +# | X X | y - X's are the nozzles +# | (1) (2) | | +# | | | +# +--------------------------------+ +--> x +# +# In this case, the nozzles are symmetric, nozzle (1) is closer to the bottom left corner while (2) is closer to the +# bottom right. If we use nozzle (1) to print, then we better off printing from the bottom left corner so the print +# head will not collide into an object on its top-right side, which is a very large unused area. Following the same +# logic, if we are printing with nozzle (2), then it's better to print from the bottom-right side. +# +# This iterator determines the print order following the rules above. +# class OneAtATimeIterator(Iterator.Iterator): + def __init__(self, scene_node): - super().__init__(scene_node) # Call super to make multiple inheritence work. - self._hit_map = [[]] + from cura.CuraApplication import CuraApplication + self._global_stack = CuraApplication.getInstance().getGlobalContainerStack() self._original_node_list = [] - + super().__init__(scene_node) # Call super to make multiple inheritance work. + + def getMachineNearestCornerToExtruder(self, global_stack): + head_and_fans_coordinates = global_stack.getHeadAndFansCoordinates() + + used_extruder = None + for extruder in global_stack.extruders.values(): + if extruder.isEnabled: + used_extruder = extruder + break + + extruder_offsets = [used_extruder.getProperty("machine_nozzle_offset_x", "value"), + used_extruder.getProperty("machine_nozzle_offset_y", "value")] + + # find the corner that's closest to the origin + min_distance2 = sys.maxsize + min_coord = None + for coord in head_and_fans_coordinates: + x = coord[0] - extruder_offsets[0] + y = coord[1] - extruder_offsets[1] + + distance2 = x**2 + y**2 + if distance2 <= min_distance2: + min_distance2 = distance2 + min_coord = coord + + return min_coord + def _fillStack(self): + min_coord = self.getMachineNearestCornerToExtruder(self._global_stack) + transform_x = -int(round(min_coord[0] / abs(min_coord[0]))) + transform_y = -int(round(min_coord[1] / abs(min_coord[1]))) + + machine_size = [self._global_stack.getProperty("machine_width", "value"), + self._global_stack.getProperty("machine_depth", "value")] + + def flip_x(polygon): + tm2 = [-1, 0, 0, 1, 0, 0] + return affinity.affine_transform(affinity.translate(polygon, xoff = -machine_size[0]), tm2) + def flip_y(polygon): + tm2 = [1, 0, 0, -1, 0, 0] + return affinity.affine_transform(affinity.translate(polygon, yoff = -machine_size[1]), tm2) + + from shapely import affinity + from shapely.geometry import Polygon + node_list = [] for node in self._scene_node.getChildren(): if not issubclass(type(node), SceneNode): continue - if node.callDecoration("getConvexHull"): - node_list.append(node) + convex_hull = node.callDecoration("getConvexHull") + if convex_hull: + xmin = min(x for x, _ in convex_hull._points) + xmax = max(x for x, _ in convex_hull._points) + ymin = min(y for _, y in convex_hull._points) + ymax = max(y for _, y in convex_hull._points) + convex_hull_polygon = Polygon.from_bounds(xmin, ymin, xmax, ymax) + if transform_x < 0: + convex_hull_polygon = flip_x(convex_hull_polygon) + if transform_y < 0: + convex_hull_polygon = flip_y(convex_hull_polygon) - if len(node_list) < 2: - self._node_stack = node_list[:] - return + node_list.append({"node": node, + "min_coord": [convex_hull_polygon.bounds[0], convex_hull_polygon.bounds[1]], + }) - # Copy the list - self._original_node_list = node_list[:] - - ## Initialise the hit map (pre-compute all hits between all objects) - self._hit_map = [[self._checkHit(i,j) for i in node_list] for j in node_list] - - # Check if we have to files that block eachother. If this is the case, there is no solution! - for a in range(0,len(node_list)): - for b in range(0,len(node_list)): - if a != b and self._hit_map[a][b] and self._hit_map[b][a]: - return - - # Sort the original list so that items that block the most other objects are at the beginning. - # This does not decrease the worst case running time, but should improve it in most cases. - sorted(node_list, key = cmp_to_key(self._calculateScore)) - - todo_node_list = [_ObjectOrder([], node_list)] - while len(todo_node_list) > 0: - current = todo_node_list.pop() - for node in current.todo: - # Check if the object can be placed with what we have and still allows for a solution in the future - if not self._checkHitMultiple(node, current.order) and not self._checkBlockMultiple(node, current.todo): - # We found a possible result. Create new todo & order list. - new_todo_list = current.todo[:] - new_todo_list.remove(node) - new_order = current.order[:] + [node] - if len(new_todo_list) == 0: - # We have no more nodes to check, so quit looking. - todo_node_list = None - self._node_stack = new_order - - return - todo_node_list.append(_ObjectOrder(new_order, new_todo_list)) - self._node_stack = [] #No result found! - - - # Check if first object can be printed before the provided list (using the hit map) - def _checkHitMultiple(self, node, other_nodes): - node_index = self._original_node_list.index(node) - for other_node in other_nodes: - other_node_index = self._original_node_list.index(other_node) - if self._hit_map[node_index][other_node_index]: - return True - return False - - def _checkBlockMultiple(self, node, other_nodes): - node_index = self._original_node_list.index(node) - for other_node in other_nodes: - other_node_index = self._original_node_list.index(other_node) - if self._hit_map[other_node_index][node_index] and node_index != other_node_index: - return True - return False - - ## Calculate score simply sums the number of other objects it 'blocks' - def _calculateScore(self, a, b): - score_a = sum(self._hit_map[self._original_node_list.index(a)]) - score_b = sum(self._hit_map[self._original_node_list.index(b)]) - return score_a - score_b - - # Checks if A can be printed before B - def _checkHit(self, a, b): - if a == b: - return False - - overlap = a.callDecoration("getConvexHullBoundary").intersectsPolygon(b.callDecoration("getConvexHullHeadFull")) - if overlap: - return True - else: - return False - - -## Internal object used to keep track of a possible order in which to print objects. -class _ObjectOrder(): - def __init__(self, order, todo): - """ - :param order: List of indexes in which to print objects, ordered by printing order. - :param todo: List of indexes which are not yet inserted into the order list. - """ - self.order = order - self.todo = todo + node_list = sorted(node_list, key = lambda d: d["min_coord"]) + self._node_stack = [d["node"] for d in node_list] diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index 66bc8a7fc3..367144abfc 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -229,7 +229,7 @@ class ConvexHullDecorator(SceneNodeDecorator): return offset_hull def _getHeadAndFans(self): - return Polygon(numpy.array(self._global_stack.getProperty("machine_head_with_fans_polygon", "value"), numpy.float32)) + return Polygon(numpy.array(self._global_stack.getHeadAndFansCoordinates(), numpy.float32)) def _compute2DConvexHeadFull(self): return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans()) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index 66f3290b85..ea955d24b0 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -172,6 +172,9 @@ class GlobalStack(CuraContainerStack): return False return True + def getHeadAndFansCoordinates(self): + return self.getProperty("machine_head_with_fans_polygon", "value") + ## private: global_stack_mime = MimeType( From aac6fe486066d7244d53c3337f64a03103e2ebc6 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 7 Aug 2018 09:35:50 +0200 Subject: [PATCH 36/54] Fix some code style --- cura/OneAtATimeIterator.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cura/OneAtATimeIterator.py b/cura/OneAtATimeIterator.py index cb063bfde5..990ed37ab7 100644 --- a/cura/OneAtATimeIterator.py +++ b/cura/OneAtATimeIterator.py @@ -3,6 +3,9 @@ import sys +from shapely import affinity +from shapely.geometry import Polygon + from UM.Scene.Iterator import Iterator from UM.Scene.SceneNode import SceneNode @@ -73,13 +76,11 @@ class OneAtATimeIterator(Iterator.Iterator): def flip_x(polygon): tm2 = [-1, 0, 0, 1, 0, 0] return affinity.affine_transform(affinity.translate(polygon, xoff = -machine_size[0]), tm2) + def flip_y(polygon): tm2 = [1, 0, 0, -1, 0, 0] return affinity.affine_transform(affinity.translate(polygon, yoff = -machine_size[1]), tm2) - from shapely import affinity - from shapely.geometry import Polygon - node_list = [] for node in self._scene_node.getChildren(): if not issubclass(type(node), SceneNode): From 59bb7744368626df2b5333f7b139d8c56c47b0d9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 7 Aug 2018 10:51:56 +0200 Subject: [PATCH 37/54] Remove unused variables --- plugins/USBPrinting/AutoDetectBaudJob.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/USBPrinting/AutoDetectBaudJob.py b/plugins/USBPrinting/AutoDetectBaudJob.py index 30ab65bc55..f8af61c567 100644 --- a/plugins/USBPrinting/AutoDetectBaudJob.py +++ b/plugins/USBPrinting/AutoDetectBaudJob.py @@ -1,4 +1,4 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from UM.Job import Job @@ -21,7 +21,6 @@ class AutoDetectBaudJob(Job): def run(self): Logger.log("d", "Auto detect baud rate started.") - timeout = 3 wait_response_timeouts = [3, 15, 30] wait_bootloader_times = [1.5, 5, 15] write_timeout = 3 @@ -52,7 +51,7 @@ class AutoDetectBaudJob(Job): if serial is None: try: serial = Serial(str(self._serial_port), baud_rate, timeout = read_timeout, writeTimeout = write_timeout) - except SerialException as e: + except SerialException: Logger.logException("w", "Unable to create serial") continue else: From 58e5a8123625eaf14279efb3e4c9ac9032cea44d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 7 Aug 2018 13:10:03 +0200 Subject: [PATCH 38/54] Fix units for initial layer print/travel acceleration These two were wrong. Contributes to issue CURA-5495. --- resources/definitions/fdmprinter.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index daf09d2701..2ea87a890d 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3025,7 +3025,7 @@ { "label": "Initial Layer Print Acceleration", "description": "The acceleration during the printing of the initial layer.", - "unit": "mm/s", + "unit": "mm/s²", "type": "float", "default_value": 3000, "value": "acceleration_layer_0", @@ -3039,7 +3039,7 @@ { "label": "Initial Layer Travel Acceleration", "description": "The acceleration for travel moves in the initial layer.", - "unit": "mm/s", + "unit": "mm/s²", "type": "float", "default_value": 3000, "value": "acceleration_layer_0 * acceleration_travel / acceleration_print", From 910ffe0ad53dd71e83d46b74cfad932061a92cd5 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 7 Aug 2018 16:15:03 +0200 Subject: [PATCH 39/54] Fix quality reset upon material deletion CURA-5621 --- cura/Settings/MachineManager.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index ff585deb54..f945cbb30c 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1455,9 +1455,13 @@ class MachineManager(QObject): if quality_group.node_for_global is None: Logger.log("e", "Could not set quality group [%s] because it has no node_for_global", str(quality_group)) return + # This is not changing the quality for the active machine !!!!!!!! global_stack.quality = quality_group.node_for_global.getContainer() for extruder_nr, extruder_stack in global_stack.extruders.items(): - extruder_stack.quality = quality_group.nodes_for_extruders[extruder_nr].getContainer() + quality_container = self._empty_quality_container + if extruder_nr in quality_group.nodes_for_extruders: + quality_container = quality_group.nodes_for_extruders[extruder_nr].getContainer() + extruder_stack.quality = quality_container return self.blurSettings.emit() From 55cf1fa137bac81d3f83564e9e656456ac4dfa32 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 7 Aug 2018 16:51:31 +0200 Subject: [PATCH 40/54] Turn on CuraEngine debug mode when front-end is in debug mode You can test this by running Cura's front-end with parameter --debug. --- plugins/CuraEngineBackend/CuraEngineBackend.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 56763a6632..9a5c95b04d 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -1,6 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import argparse #To run the engine in debug mode if the front-end is in debug mode. from collections import defaultdict import os from PyQt5.QtCore import QObject, QTimer, pyqtSlot @@ -179,7 +180,15 @@ class CuraEngineBackend(QObject, Backend): # \return list of commands and args / parameters. def getEngineCommand(self) -> List[str]: json_path = Resources.getPath(Resources.DefinitionContainers, "fdmprinter.def.json") - return [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""] + command = [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), "-j", json_path, ""] + + parser = argparse.ArgumentParser(prog = "cura", add_help = False) + parser.add_argument("--debug", action = "store_true", default = False, help = "Turn on the debug mode by setting this option.") + known_args = vars(parser.parse_known_args()[0]) + if known_args["debug"]: + command.append("-vvv") + + return command ## Emitted when we get a message containing print duration and material amount. # This also implies the slicing has finished. From 387fb3ce1f7af5f6e8b37c966291e4710cb2d143 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 8 Aug 2018 11:31:21 +0200 Subject: [PATCH 41/54] Fix missing attribute CURA-5622 self._plugins_loaded should be there in the beginning or Cura will crash when it gets used. --- cura/CuraApplication.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9f4ea7b3d3..6164fc8756 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -226,6 +226,8 @@ class CuraApplication(QtApplication): self._need_to_show_user_agreement = True + self._plugins_loaded = False + # Backups self._auto_save = None self._save_data_enabled = True From 9239e82b1f16e112aaa9da042515f995c6532e93 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 8 Aug 2018 12:56:46 +0200 Subject: [PATCH 42/54] Make mypy happy CURA-5578 --- cura/Settings/MachineManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index f945cbb30c..a14977df03 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1460,7 +1460,8 @@ class MachineManager(QObject): for extruder_nr, extruder_stack in global_stack.extruders.items(): quality_container = self._empty_quality_container if extruder_nr in quality_group.nodes_for_extruders: - quality_container = quality_group.nodes_for_extruders[extruder_nr].getContainer() + container = quality_group.nodes_for_extruders[extruder_nr].getContainer() + quality_container = container if container is not None else quality_container extruder_stack.quality = quality_container return From d7e8cc9224dfe16028ce46cad2b5e676dbf4173b Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Wed, 8 Aug 2018 17:03:57 +0200 Subject: [PATCH 43/54] Fix rendering support eraser cubes MeshBuilder.addCube creates cubes that don't have per-vertex normals. Geometry like that does not render properly in most shaders used in Cura. This becomes obvious when a user changes the "Mesh Type" using Per Model Settings. --- plugins/SupportEraser/SupportEraser.py | 30 ++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/plugins/SupportEraser/SupportEraser.py b/plugins/SupportEraser/SupportEraser.py index 44d4831c04..9f7e3269bc 100644 --- a/plugins/SupportEraser/SupportEraser.py +++ b/plugins/SupportEraser/SupportEraser.py @@ -25,6 +25,8 @@ from cura.Scene.BuildPlateDecorator import BuildPlateDecorator from UM.Settings.SettingInstance import SettingInstance +import numpy + class SupportEraser(Tool): def __init__(self): super().__init__() @@ -96,8 +98,7 @@ class SupportEraser(Tool): node.setName("Eraser") node.setSelectable(True) - mesh = MeshBuilder() - mesh.addCube(10,10,10) + mesh = self._createCube(10) node.setMeshData(mesh.build()) active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate @@ -160,3 +161,28 @@ class SupportEraser(Tool): self._skip_press = False self._had_selection = has_selection + + def _createCube(self, size): + mesh = MeshBuilder() + + # Can't use MeshBuilder.addCube() because that does not get per-vertex normals + # Per-vertex normals require duplication of vertices + s = size / 2 + verts = [ # 6 faces with 4 corners each + [-s, -s, s], [-s, s, s], [ s, s, s], [ s, -s, s], + [-s, s, -s], [-s, -s, -s], [ s, -s, -s], [ s, s, -s], + [ s, -s, -s], [-s, -s, -s], [-s, -s, s], [ s, -s, s], + [-s, s, -s], [ s, s, -s], [ s, s, s], [-s, s, s], + [-s, -s, s], [-s, -s, -s], [-s, s, -s], [-s, s, s], + [ s, -s, -s], [ s, -s, s], [ s, s, s], [ s, s, -s] + ] + mesh.setVertices(numpy.asarray(verts, dtype=numpy.float32)) + + indices = [] + for i in range(0, 24, 4): # All 6 quads (12 triangles) + indices.append([i, i+2, i+1]) + indices.append([i, i+3, i+2]) + mesh.setIndices(numpy.asarray(indices, dtype=numpy.int32)) + + mesh.calculateNormals() + return mesh From ea770a26a37e8673b24067e814344f01b29bf488 Mon Sep 17 00:00:00 2001 From: ValentinPitre <35694201+ValentinPitre@users.noreply.github.com> Date: Wed, 8 Aug 2018 23:23:32 +0200 Subject: [PATCH 44/54] Add mesh offset --- resources/definitions/tizyx_k25.def.json | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/definitions/tizyx_k25.def.json b/resources/definitions/tizyx_k25.def.json index 55256a29d6..94a20b371e 100644 --- a/resources/definitions/tizyx_k25.def.json +++ b/resources/definitions/tizyx_k25.def.json @@ -9,6 +9,7 @@ "manufacturer": "TiZYX", "file_formats": "text/x-gcode", "platform": "tizyx_k25_platform.stl", + "platform_offset": [0, -4, 0], "exclude_materials": ["chromatik_pla", "dsm_arnitel2045_175", "dsm_novamid1070_175", "fabtotum_abs", "fabtotum_nylon", "fabtotum_pla", "fabtotum_tpu", "fiberlogy_hd_pla", "filo3d_pla", "filo3d_pla_green", "filo3d_pla_red", "generic_abs", "generic_abs_175", "generic_bam", "generic_cpe", "generic_cpe_175", "generic_cpe_plus", "generic_hips", "generic_hips_175", "generic_nylon", "generic_nylon_175", "generic_pc", "generic_pc_175", "generic_petg", "generic_petg_175", "generic_pla", "generic_pla_175", "generic_pp", "generic_pva", "generic_pva_175", "generic_tough_pla", "generic_tpu", "imade3d_petg_green", "imade3d_petg_pink", "imade3d_pla_green", "imade3d_pla_pink", "innofill_innoflex60_175", "octofiber_pla", "polyflex_pla", "polymax_pla", "polyplus_pla", "polywood_pla", "ultimaker_abs_black", "ultimaker_abs_blue", "ultimaker_abs_green", "ultimaker_abs_grey", "ultimaker_abs_orange", "ultimaker_abs_pearl-gold", "ultimaker_abs_red", "ultimaker_abs_silver-metallic", "ultimaker_abs_white", "ultimaker_abs_yellow", "ultimaker_bam", "ultimaker_cpe_black", "ultimaker_cpe_blue", "ultimaker_cpe_dark-grey", "ultimaker_cpe_green", "ultimaker_cpe_light-grey", "ultimaker_cpe_plus_black", "ultimaker_cpe_plus_transparent", "ultimaker_cpe_plus_white", "ultimaker_cpe_red", "ultimaker_cpe_transparent", "ultimaker_cpe_white", "ultimaker_cpe_yellow", "ultimaker_nylon_black", "ultimaker_nylon_transparent", "ultimaker_pc_black", "ultimaker_pc_transparent", "ultimaker_pc_white", "ultimaker_pla_black", "ultimaker_pla_blue", "ultimaker_pla_green", "ultimaker_pla_magenta", "ultimaker_pla_orange", "ultimaker_pla_pearl-white", "ultimaker_pla_red", "ultimaker_pla_silver-metallic", "ultimaker_pla_transparent", "ultimaker_pla_white", "ultimaker_pla_yellow", "ultimaker_pp_transparent", "ultimaker_pva", "ultimaker_tough_pla_black", "ultimaker_tough_pla_green", "ultimaker_tough_pla_red", "ultimaker_tough_pla_white", "ultimaker_tpu_black", "ultimaker_tpu_blue", "ultimaker_tpu_red", "ultimaker_tpu_white", "verbatim_bvoh_175", "Vertex_Delta_ABS", "Vertex_Delta_PET", "Vertex_Delta_PLA", "Vertex_Delta_TPU", "zyyx_pro_flex", "zyyx_pro_pla" ], "preferred_material": "tizyx_pla", "has_machine_quality": true, From 41a892796d29d6240ce135c7f5a838e14dd398c2 Mon Sep 17 00:00:00 2001 From: Andreea Scorojitu Date: Thu, 9 Aug 2018 09:14:25 +0200 Subject: [PATCH 45/54] CURA-5626 Remove UM3 from connection message "This printer is not set up to host a group of Ultimaker 3 printers.", Ultimker 3 was removed from the message. --- plugins/UM3NetworkPrinting/DiscoverUM3Action.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml index e4d75e8f6d..127b3c35bd 100644 --- a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml @@ -299,11 +299,11 @@ Cura.MachineAction } else if (base.selectedDevice.clusterSize === 0) { - return catalog.i18nc("@label", "This printer is not set up to host a group of Ultimaker 3 printers."); + return catalog.i18nc("@label", "This printer is not set up to host a group of printers."); } else { - return catalog.i18nc("@label", "This printer is the host for a group of %1 Ultimaker 3 printers.".arg(base.selectedDevice.clusterSize)); + return catalog.i18nc("@label", "This printer is the host for a group of %1 printers.".arg(base.selectedDevice.clusterSize)); } } From 5c860d95928204dbeccc35fed7edfcaf456abbc9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 9 Aug 2018 11:11:41 +0200 Subject: [PATCH 46/54] Fixed typo --- cura/PrinterOutput/PrinterOutputModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrinterOutput/PrinterOutputModel.py b/cura/PrinterOutput/PrinterOutputModel.py index 6fafa368bb..f10d6bd75b 100644 --- a/cura/PrinterOutput/PrinterOutputModel.py +++ b/cura/PrinterOutput/PrinterOutputModel.py @@ -120,7 +120,7 @@ class PrinterOutputModel(QObject): @pyqtProperty(QVariant, notify = headPositionChanged) def headPosition(self): - return {"x": self._head_position.x, "y": self._head_position.y, "z": self.head_position_z} + return {"x": self._head_position.x, "y": self._head_position.y, "z": self.head_position.z} def updateHeadPosition(self, x, y, z): if self._head_position.x != x or self._head_position.y != y or self._head_position.z != z: From 1180fad62f41a3af08f412dc6dc220d60d6a8f36 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 9 Aug 2018 11:33:04 +0200 Subject: [PATCH 47/54] Fix binding loop --- resources/qml/PrintMonitor.qml | 1 - resources/qml/PrinterOutput/OutputDeviceHeader.qml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/qml/PrintMonitor.qml b/resources/qml/PrintMonitor.qml index b24bcb6d6c..7727f9cb52 100644 --- a/resources/qml/PrintMonitor.qml +++ b/resources/qml/PrintMonitor.qml @@ -26,7 +26,6 @@ Column OutputDeviceHeader { - width: parent.width outputDevice: connectedDevice } diff --git a/resources/qml/PrinterOutput/OutputDeviceHeader.qml b/resources/qml/PrinterOutput/OutputDeviceHeader.qml index d5ce32786c..03e6d78699 100644 --- a/resources/qml/PrinterOutput/OutputDeviceHeader.qml +++ b/resources/qml/PrinterOutput/OutputDeviceHeader.qml @@ -16,7 +16,7 @@ Item Rectangle { - anchors.fill: parent + height: childrenRect.height color: UM.Theme.getColor("setting_category") property var activePrinter: outputDevice != null ? outputDevice.activePrinter : null From d31df8ec679281280421a97458e089047e74c8a4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 9 Aug 2018 12:47:49 +0200 Subject: [PATCH 48/54] Fixed that long printernames overlap other GUI elements --- plugins/UM3NetworkPrinting/ClusterControlItem.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/UM3NetworkPrinting/ClusterControlItem.qml b/plugins/UM3NetworkPrinting/ClusterControlItem.qml index b42515de51..5cf550955c 100644 --- a/plugins/UM3NetworkPrinting/ClusterControlItem.qml +++ b/plugins/UM3NetworkPrinting/ClusterControlItem.qml @@ -30,7 +30,12 @@ Component anchors.horizontalCenter: parent.horizontalCenter anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.right:parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width text: Cura.MachineManager.printerOutputDevices[0].name + elide: Text.ElideRight } Rectangle From e4bdd8096a3e99ca304938c8ad26a819add31cd2 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 10 Aug 2018 11:02:16 +0200 Subject: [PATCH 49/54] Remap shortcut This is a bit more logical than 'G', I'd say. E is now available since other shortcuts were remapped as well. Contributes to issue CURA-5634 and fixes #3969. --- plugins/SupportEraser/SupportEraser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SupportEraser/SupportEraser.py b/plugins/SupportEraser/SupportEraser.py index 44d4831c04..97850c3f3d 100644 --- a/plugins/SupportEraser/SupportEraser.py +++ b/plugins/SupportEraser/SupportEraser.py @@ -28,7 +28,7 @@ from UM.Settings.SettingInstance import SettingInstance class SupportEraser(Tool): def __init__(self): super().__init__() - self._shortcut_key = Qt.Key_G + self._shortcut_key = Qt.Key_E self._controller = self.getController() self._selection_pass = None From 4a22bb3b695e531a9ddf6f89f3a3d53109d13580 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 10 Aug 2018 11:06:25 +0200 Subject: [PATCH 50/54] Code style --- resources/qml/Toolbar.qml | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 613a462db9..6483804c38 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Ultimaker B.V. +// Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -21,8 +21,8 @@ Item { id: buttons; - anchors.bottom: parent.bottom; - anchors.left: parent.left; + anchors.bottom: parent.bottom + anchors.left: parent.left spacing: UM.Theme.getSize("button_lining").width Repeater @@ -41,13 +41,15 @@ Item enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled style: UM.Theme.styles.tool_button - onCheckedChanged: { - if (checked) { - base.activeY = y + onCheckedChanged: + { + if (checked) + { + base.activeY = y; } } - //Workaround since using ToolButton"s onClicked would break the binding of the checked property, instead + //Workaround since using ToolButton's onClicked would break the binding of the checked property, instead //just catch the click so we do not trigger that behaviour. MouseArea { @@ -57,7 +59,7 @@ Item forceActiveFocus() //First grab focus, so all the text fields are updated if(parent.checked) { - UM.Controller.setActiveTool(null) + UM.Controller.setActiveTool(null); } else { @@ -96,11 +98,13 @@ Item width: { - if (panel.item && panel.width > 0){ - return Math.max(panel.width + 2 * UM.Theme.getSize("default_margin").width) + if (panel.item && panel.width > 0) + { + return Math.max(panel.width + 2 * UM.Theme.getSize("default_margin").width); } - else { - return 0 + else + { + return 0; } } height: panel.item ? panel.height + 2 * UM.Theme.getSize("default_margin").height : 0; @@ -124,7 +128,7 @@ Item x: UM.Theme.getSize("default_margin").width; y: UM.Theme.getSize("default_margin").height; - source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : ""; + source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : "" enabled: UM.Controller.toolsEnabled; } } @@ -148,6 +152,6 @@ Item anchors.horizontalCenter: parent.horizontalCenter } - visible: toolHint.text != ""; + visible: toolHint.text != "" } } From f247c328c695ba27d9273c53b06cf7f8a73cdc61 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 10 Aug 2018 11:22:33 +0200 Subject: [PATCH 51/54] Show shortcut key in tool tooltips In brackets. I couldn't translate it to an icon easily. It was possible but I'd have to create a giant mapping from every key to the key icons and add a bunch of icons and I don't think that's necessary. Contributes to issue CURA-5634. --- resources/qml/Toolbar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 6483804c38..a04b3650df 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -34,7 +34,7 @@ Item height: childrenRect.height Button { - text: model.name + text: model.name + (model.shortcut ? (" (" + model.shortcut + ")") : "") iconSource: (UM.Theme.getIcon(model.icon) != "") ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon checkable: true checked: model.active From 89ed2bcff80a7a414f77c455baa0023c631eae7c Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 10 Aug 2018 11:45:25 +0200 Subject: [PATCH 52/54] Revert "Merge pull request #4203 from Ultimaker/CURA-5538-fix-one-at-a-time-order-2" This reverts commit 82e1a7c5fc43a12d1498779d392286c6e49ee4ea, reversing changes made to 1915659393b72f7e4d4dbd9b73e92b8a665efcdc. --- cura/OneAtATimeIterator.py | 184 +++++++++++++++--------------- cura/Scene/ConvexHullDecorator.py | 2 +- cura/Settings/GlobalStack.py | 3 - 3 files changed, 95 insertions(+), 94 deletions(-) diff --git a/cura/OneAtATimeIterator.py b/cura/OneAtATimeIterator.py index 990ed37ab7..84d65bae8e 100644 --- a/cura/OneAtATimeIterator.py +++ b/cura/OneAtATimeIterator.py @@ -1,108 +1,112 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import sys - -from shapely import affinity -from shapely.geometry import Polygon - from UM.Scene.Iterator import Iterator from UM.Scene.SceneNode import SceneNode +from functools import cmp_to_key +from UM.Application import Application - -# Iterator that determines the object print order when one-at a time mode is enabled. -# -# In one-at-a-time mode, only one extruder can be enabled to print. In order to maximize the number of objects we can -# print, we need to print from the corner that's closest to the extruder that's being used. Here is an illustration: -# -# +--------------------------------+ -# | | -# | | -# | | - Rectangle represents the complete print head including fans, etc. -# | X X | y - X's are the nozzles -# | (1) (2) | | -# | | | -# +--------------------------------+ +--> x -# -# In this case, the nozzles are symmetric, nozzle (1) is closer to the bottom left corner while (2) is closer to the -# bottom right. If we use nozzle (1) to print, then we better off printing from the bottom left corner so the print -# head will not collide into an object on its top-right side, which is a very large unused area. Following the same -# logic, if we are printing with nozzle (2), then it's better to print from the bottom-right side. -# -# This iterator determines the print order following the rules above. -# +## Iterator that returns a list of nodes in the order that they need to be printed +# If there is no solution an empty list is returned. +# Take note that the list of nodes can have children (that may or may not contain mesh data) class OneAtATimeIterator(Iterator.Iterator): - def __init__(self, scene_node): - from cura.CuraApplication import CuraApplication - self._global_stack = CuraApplication.getInstance().getGlobalContainerStack() + super().__init__(scene_node) # Call super to make multiple inheritence work. + self._hit_map = [[]] self._original_node_list = [] - super().__init__(scene_node) # Call super to make multiple inheritance work. - - def getMachineNearestCornerToExtruder(self, global_stack): - head_and_fans_coordinates = global_stack.getHeadAndFansCoordinates() - - used_extruder = None - for extruder in global_stack.extruders.values(): - if extruder.isEnabled: - used_extruder = extruder - break - - extruder_offsets = [used_extruder.getProperty("machine_nozzle_offset_x", "value"), - used_extruder.getProperty("machine_nozzle_offset_y", "value")] - - # find the corner that's closest to the origin - min_distance2 = sys.maxsize - min_coord = None - for coord in head_and_fans_coordinates: - x = coord[0] - extruder_offsets[0] - y = coord[1] - extruder_offsets[1] - - distance2 = x**2 + y**2 - if distance2 <= min_distance2: - min_distance2 = distance2 - min_coord = coord - - return min_coord - + def _fillStack(self): - min_coord = self.getMachineNearestCornerToExtruder(self._global_stack) - transform_x = -int(round(min_coord[0] / abs(min_coord[0]))) - transform_y = -int(round(min_coord[1] / abs(min_coord[1]))) - - machine_size = [self._global_stack.getProperty("machine_width", "value"), - self._global_stack.getProperty("machine_depth", "value")] - - def flip_x(polygon): - tm2 = [-1, 0, 0, 1, 0, 0] - return affinity.affine_transform(affinity.translate(polygon, xoff = -machine_size[0]), tm2) - - def flip_y(polygon): - tm2 = [1, 0, 0, -1, 0, 0] - return affinity.affine_transform(affinity.translate(polygon, yoff = -machine_size[1]), tm2) - node_list = [] for node in self._scene_node.getChildren(): if not issubclass(type(node), SceneNode): continue - convex_hull = node.callDecoration("getConvexHull") - if convex_hull: - xmin = min(x for x, _ in convex_hull._points) - xmax = max(x for x, _ in convex_hull._points) - ymin = min(y for _, y in convex_hull._points) - ymax = max(y for _, y in convex_hull._points) + if node.callDecoration("getConvexHull"): + node_list.append(node) - convex_hull_polygon = Polygon.from_bounds(xmin, ymin, xmax, ymax) - if transform_x < 0: - convex_hull_polygon = flip_x(convex_hull_polygon) - if transform_y < 0: - convex_hull_polygon = flip_y(convex_hull_polygon) - node_list.append({"node": node, - "min_coord": [convex_hull_polygon.bounds[0], convex_hull_polygon.bounds[1]], - }) + if len(node_list) < 2: + self._node_stack = node_list[:] + return - node_list = sorted(node_list, key = lambda d: d["min_coord"]) + # Copy the list + self._original_node_list = node_list[:] + + ## Initialise the hit map (pre-compute all hits between all objects) + self._hit_map = [[self._checkHit(i,j) for i in node_list] for j in node_list] + + # Check if we have to files that block eachother. If this is the case, there is no solution! + for a in range(0,len(node_list)): + for b in range(0,len(node_list)): + if a != b and self._hit_map[a][b] and self._hit_map[b][a]: + return + + # Sort the original list so that items that block the most other objects are at the beginning. + # This does not decrease the worst case running time, but should improve it in most cases. + sorted(node_list, key = cmp_to_key(self._calculateScore)) + + todo_node_list = [_ObjectOrder([], node_list)] + while len(todo_node_list) > 0: + current = todo_node_list.pop() + for node in current.todo: + # Check if the object can be placed with what we have and still allows for a solution in the future + if not self._checkHitMultiple(node, current.order) and not self._checkBlockMultiple(node, current.todo): + # We found a possible result. Create new todo & order list. + new_todo_list = current.todo[:] + new_todo_list.remove(node) + new_order = current.order[:] + [node] + if len(new_todo_list) == 0: + # We have no more nodes to check, so quit looking. + todo_node_list = None + self._node_stack = new_order + + return + todo_node_list.append(_ObjectOrder(new_order, new_todo_list)) + self._node_stack = [] #No result found! + + + # Check if first object can be printed before the provided list (using the hit map) + def _checkHitMultiple(self, node, other_nodes): + node_index = self._original_node_list.index(node) + for other_node in other_nodes: + other_node_index = self._original_node_list.index(other_node) + if self._hit_map[node_index][other_node_index]: + return True + return False + + def _checkBlockMultiple(self, node, other_nodes): + node_index = self._original_node_list.index(node) + for other_node in other_nodes: + other_node_index = self._original_node_list.index(other_node) + if self._hit_map[other_node_index][node_index] and node_index != other_node_index: + return True + return False + + ## Calculate score simply sums the number of other objects it 'blocks' + def _calculateScore(self, a, b): + score_a = sum(self._hit_map[self._original_node_list.index(a)]) + score_b = sum(self._hit_map[self._original_node_list.index(b)]) + return score_a - score_b + + # Checks if A can be printed before B + def _checkHit(self, a, b): + if a == b: + return False + + overlap = a.callDecoration("getConvexHullBoundary").intersectsPolygon(b.callDecoration("getConvexHullHeadFull")) + if overlap: + return True + else: + return False + + +## Internal object used to keep track of a possible order in which to print objects. +class _ObjectOrder(): + def __init__(self, order, todo): + """ + :param order: List of indexes in which to print objects, ordered by printing order. + :param todo: List of indexes which are not yet inserted into the order list. + """ + self.order = order + self.todo = todo - self._node_stack = [d["node"] for d in node_list] diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index 367144abfc..66bc8a7fc3 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -229,7 +229,7 @@ class ConvexHullDecorator(SceneNodeDecorator): return offset_hull def _getHeadAndFans(self): - return Polygon(numpy.array(self._global_stack.getHeadAndFansCoordinates(), numpy.float32)) + return Polygon(numpy.array(self._global_stack.getProperty("machine_head_with_fans_polygon", "value"), numpy.float32)) def _compute2DConvexHeadFull(self): return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans()) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index ea955d24b0..66f3290b85 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -172,9 +172,6 @@ class GlobalStack(CuraContainerStack): return False return True - def getHeadAndFansCoordinates(self): - return self.getProperty("machine_head_with_fans_polygon", "value") - ## private: global_stack_mime = MimeType( From 6924d28ee7eb92c47e5d1df63874a7da5b2b0be4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 10 Aug 2018 11:52:35 +0200 Subject: [PATCH 53/54] Disambiguate translation of 'Settings' from 'Preferences' The 'settings' entry contains the set-up of the printer so Konfiguration is a better word there. Discovered during investigation of #3971. --- resources/i18n/de_DE/cura.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/i18n/de_DE/cura.po b/resources/i18n/de_DE/cura.po index 37b8840468..31ce980615 100644 --- a/resources/i18n/de_DE/cura.po +++ b/resources/i18n/de_DE/cura.po @@ -4036,7 +4036,7 @@ msgstr "&Ansicht" #: /home/ruben/Projects/Cura/resources/qml/Cura.qml:184 msgctxt "@title:menu" msgid "&Settings" -msgstr "&Einstellungen" +msgstr "&Konfiguration" #: /home/ruben/Projects/Cura/resources/qml/Cura.qml:186 msgctxt "@title:menu menubar:toplevel" From f91c696e4bfaa1ace21dabfdb91d4651599768ea Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 10 Aug 2018 13:17:20 +0200 Subject: [PATCH 54/54] Clarify error message a bit more The QA engineer was a bit confused by this one. Let's make the language a bit easier. --- plugins/UM3NetworkPrinting/SendMaterialJob.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/SendMaterialJob.py b/plugins/UM3NetworkPrinting/SendMaterialJob.py index 0ac38843a1..8491e79c29 100644 --- a/plugins/UM3NetworkPrinting/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/SendMaterialJob.py @@ -39,12 +39,12 @@ class SendMaterialJob(Job): try: remote_materials_list = json.loads(remote_materials_list) except json.JSONDecodeError: - Logger.log("e", "Current material storage on printer was a corrupted reply.") + Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.") return try: remote_materials_by_guid = {material["guid"]: material for material in remote_materials_list} #Index by GUID. except KeyError: - Logger.log("e", "Current material storage on printer was an invalid reply (missing GUIDs).") + Logger.log("e", "Request material storage on printer: Printer's answer was missing GUIDs.") return container_registry = ContainerRegistry.getInstance()