From f2428ccb52a1f28f1680edcf7524b3c496ffe335 Mon Sep 17 00:00:00 2001 From: novamxd Date: Mon, 9 Dec 2019 01:56:01 -0600 Subject: [PATCH 01/37] Revamped ChangeAtZ. Added comments. Added support for changing Retract Length and Speed. Applied code refactoring to clean up various coding styles. Broke up functions for clarity. Fixed issue of cumulative ChangeZ mods so they can now properly be stacked on top of each other. Removed layer spread option in favor for fixing ChangeZ stacking Split up class so it can be debugged outside of Cura. --- .../PostProcessingPlugin/scripts/ChangeAtZ.py | 1412 +++++++++++------ 1 file changed, 935 insertions(+), 477 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py index cdbb4a79ef..9e93600fbf 100644 --- a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py +++ b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py @@ -4,496 +4,954 @@ # It runs with the PostProcessingPlugin which is released under the terms of the AGPLv3 or higher. # This script is licensed under the Creative Commons - Attribution - Share Alike (CC BY-SA) terms -#Authors of the ChangeAtZ plugin / script: +# Authors of the ChangeAtZ plugin / script: # Written by Steven Morlock, smorloc@gmail.com # Modified by Ricardo Gomez, ricardoga@otulook.com, to add Bed Temperature and make it work with Cura_13.06.04+ # Modified by Stefan Heule, Dim3nsioneer@gmx.ch since V3.0 (see changelog below) # Modified by Jaime van Kessel (Ultimaker), j.vankessel@ultimaker.com to make it work for 15.10 / 2.x # Modified by Ruben Dulek (Ultimaker), r.dulek@ultimaker.com, to debug. +# Modified by Wes Hanney, https://github.com/novamxd, Retract Length + Speed, Clean up -##history / changelog: -##V3.0.1: TweakAtZ-state default 1 (i.e. the plugin works without any TweakAtZ comment) -##V3.1: Recognizes UltiGCode and deactivates value reset, fan speed added, alternatively layer no. to tweak at, -## extruder three temperature disabled by "#Ex3" -##V3.1.1: Bugfix reset flow rate -##V3.1.2: Bugfix disable TweakAtZ on Cool Head Lift -##V3.2: Flow rate for specific extruder added (only for 2 extruders), bugfix parser, -## added speed reset at the end of the print -##V4.0: Progress bar, tweaking over multiple layers, M605&M606 implemented, reset after one layer option, -## extruder three code removed, tweaking print speed, save call of Publisher class, -## uses previous value from other plugins also on UltiGCode -##V4.0.1: Bugfix for doubled G1 commands -##V4.0.2: uses Cura progress bar instead of its own -##V4.0.3: Bugfix for cool head lift (contributed by luisonoff) -##V4.9.91: First version for Cura 15.06.x and PostProcessingPlugin -##V4.9.92: Modifications for Cura 15.10 -##V4.9.93: Minor bugfixes (input settings) / documentation -##V4.9.94: Bugfix Combobox-selection; remove logger -##V5.0: Bugfix for fall back after one layer and doubled G0 commands when using print speed tweak, Initial version for Cura 2.x -##V5.0.1: Bugfix for calling unknown property 'bedTemp' of previous settings storage and unkown variable 'speed' -##V5.1: API Changes included for use with Cura 2.2 +# history / changelog: +# V3.0.1: TweakAtZ-state default 1 (i.e. the plugin works without any TweakAtZ comment) +# V3.1: Recognizes UltiGCode and deactivates value reset, fan speed added, alternatively layer no. to tweak at, +# extruder three temperature disabled by "#Ex3" +# V3.1.1: Bugfix reset flow rate +# V3.1.2: Bugfix disable TweakAtZ on Cool Head Lift +# V3.2: Flow rate for specific extruder added (only for 2 extruders), bugfix parser, +# added speed reset at the end of the print +# V4.0: Progress bar, tweaking over multiple layers, M605&M606 implemented, reset after one layer option, +# extruder three code removed, tweaking print speed, save call of Publisher class, +# uses previous value from other plugins also on UltiGCode +# V4.0.1: Bugfix for doubled G1 commands +# V4.0.2: Uses Cura progress bar instead of its own +# V4.0.3: Bugfix for cool head lift (contributed by luisonoff) +# V4.9.91: First version for Cura 15.06.x and PostProcessingPlugin +# V4.9.92: Modifications for Cura 15.10 +# V4.9.93: Minor bugfixes (input settings) / documentation +# V4.9.94: Bugfix Combobox-selection; remove logger +# V5.0: Bugfix for fall back after one layer and doubled G0 commands when using print speed tweak, Initial version for Cura 2.x +# V5.0.1: Bugfix for calling unknown property 'bedTemp' of previous settings storage and unkown variable 'speed' +# V5.1: API Changes included for use with Cura 2.2 +# V5.2.0: Wes Hanney. Added support for changing Retract Length and Speed. Removed layer spread option. Fixed issue of cumulative ChangeZ +# mods so they can now properly be stacked on top of each other. Applied code refactoring to clean up various coding styles. Added comments. +# Broke up functions for clarity. Split up class so it can be debugged outside of Cura. -## Uses - -## M220 S - set speed factor override percentage -## M221 S - set flow factor override percentage -## M221 S T<0-#toolheads> - set flow factor override percentage for single extruder -## M104 S T<0-#toolheads> - set extruder to target temperature -## M140 S - set bed target temperature -## M106 S - set fan speed to target speed -## M605/606 to save and recall material settings on the UM2 +# Uses - +# M220 S - set speed factor override percentage +# M221 S - set flow factor override percentage +# M221 S T<0-#toolheads> - set flow factor override percentage for single extruder +# M104 S T<0-#toolheads> - set extruder to target temperature +# M140 S - set bed target temperature +# M106 S - set fan speed to target speed +# M605/606 to save and recall material settings on the UM2 from ..Script import Script -#from UM.Logger import Logger import re + +# this was broken up into a separate class so the main ChangeZ script could be debugged outside of Cura class ChangeAtZ(Script): - version = "5.1.1" - def __init__(self): - super().__init__() - def getSettingDataString(self): - return """{ - "name":"ChangeAtZ """ + self.version + """ (Experimental)", - "key":"ChangeAtZ", - "metadata": {}, - "version": 2, - "settings": - { - "a_trigger": - { - "label": "Trigger", - "description": "Trigger at height or at layer no.", - "type": "enum", - "options": {"height":"Height","layer_no":"Layer No."}, - "default_value": "height" - }, - "b_targetZ": - { - "label": "Change Height", - "description": "Z height to change at", - "unit": "mm", - "type": "float", - "default_value": 5.0, - "minimum_value": "0", - "minimum_value_warning": "0.1", - "maximum_value_warning": "230", - "enabled": "a_trigger == 'height'" - }, - "b_targetL": - { - "label": "Change Layer", - "description": "Layer no. to change at", - "unit": "", - "type": "int", - "default_value": 1, - "minimum_value": "-100", - "minimum_value_warning": "-1", - "enabled": "a_trigger == 'layer_no'" - }, - "c_behavior": - { - "label": "Behavior", - "description": "Select behavior: Change value and keep it for the rest, Change value for single layer only", - "type": "enum", - "options": {"keep_value":"Keep value","single_layer":"Single Layer"}, - "default_value": "keep_value" - }, - "d_twLayers": - { - "label": "Layer Spread", - "description": "The change will be gradual over this many layers. Enter 1 to make the change immediate.", - "unit": "", - "type": "int", - "default_value": 1, - "minimum_value": "1", - "maximum_value_warning": "50", - "enabled": "c_behavior == 'keep_value'" - }, - "e1_Change_speed": - { - "label": "Change Speed", - "description": "Select if total speed (print and travel) has to be changed", - "type": "bool", - "default_value": false - }, - "e2_speed": - { - "label": "Speed", - "description": "New total speed (print and travel)", - "unit": "%", - "type": "int", - "default_value": 100, - "minimum_value": "1", - "minimum_value_warning": "10", - "maximum_value_warning": "200", - "enabled": "e1_Change_speed" - }, - "f1_Change_printspeed": - { - "label": "Change Print Speed", - "description": "Select if print speed has to be changed", - "type": "bool", - "default_value": false - }, - "f2_printspeed": - { - "label": "Print Speed", - "description": "New print speed", - "unit": "%", - "type": "int", - "default_value": 100, - "minimum_value": "1", - "minimum_value_warning": "10", - "maximum_value_warning": "200", - "enabled": "f1_Change_printspeed" - }, - "g1_Change_flowrate": - { - "label": "Change Flow Rate", - "description": "Select if flow rate has to be changed", - "type": "bool", - "default_value": false - }, - "g2_flowrate": - { - "label": "Flow Rate", - "description": "New Flow rate", - "unit": "%", - "type": "int", - "default_value": 100, - "minimum_value": "1", - "minimum_value_warning": "10", - "maximum_value_warning": "200", - "enabled": "g1_Change_flowrate" - }, - "g3_Change_flowrateOne": - { - "label": "Change Flow Rate 1", - "description": "Select if first extruder flow rate has to be changed", - "type": "bool", - "default_value": false - }, - "g4_flowrateOne": - { - "label": "Flow Rate One", - "description": "New Flow rate Extruder 1", - "unit": "%", - "type": "int", - "default_value": 100, - "minimum_value": "1", - "minimum_value_warning": "10", - "maximum_value_warning": "200", - "enabled": "g3_Change_flowrateOne" - }, - "g5_Change_flowrateTwo": - { - "label": "Change Flow Rate 2", - "description": "Select if second extruder flow rate has to be changed", - "type": "bool", - "default_value": false - }, - "g6_flowrateTwo": - { - "label": "Flow Rate two", - "description": "New Flow rate Extruder 2", - "unit": "%", - "type": "int", - "default_value": 100, - "minimum_value": "1", - "minimum_value_warning": "10", - "maximum_value_warning": "200", - "enabled": "g5_Change_flowrateTwo" - }, - "h1_Change_bedTemp": - { - "label": "Change Bed Temp", - "description": "Select if Bed Temperature has to be changed", - "type": "bool", - "default_value": false - }, - "h2_bedTemp": - { - "label": "Bed Temp", - "description": "New Bed Temperature", - "unit": "C", - "type": "float", - "default_value": 60, - "minimum_value": "0", - "minimum_value_warning": "30", - "maximum_value_warning": "120", - "enabled": "h1_Change_bedTemp" - }, - "i1_Change_extruderOne": - { - "label": "Change Extruder 1 Temp", - "description": "Select if First Extruder Temperature has to be changed", - "type": "bool", - "default_value": false - }, - "i2_extruderOne": - { - "label": "Extruder 1 Temp", - "description": "New First Extruder Temperature", - "unit": "C", - "type": "float", - "default_value": 190, - "minimum_value": "0", - "minimum_value_warning": "160", - "maximum_value_warning": "250", - "enabled": "i1_Change_extruderOne" - }, - "i3_Change_extruderTwo": - { - "label": "Change Extruder 2 Temp", - "description": "Select if Second Extruder Temperature has to be changed", - "type": "bool", - "default_value": false - }, - "i4_extruderTwo": - { - "label": "Extruder 2 Temp", - "description": "New Second Extruder Temperature", - "unit": "C", - "type": "float", - "default_value": 190, - "minimum_value": "0", - "minimum_value_warning": "160", - "maximum_value_warning": "250", - "enabled": "i3_Change_extruderTwo" - }, - "j1_Change_fanSpeed": - { - "label": "Change Fan Speed", - "description": "Select if Fan Speed has to be changed", - "type": "bool", - "default_value": false - }, - "j2_fanSpeed": - { - "label": "Fan Speed", - "description": "New Fan Speed (0-255)", - "unit": "PWM", - "type": "int", - "default_value": 255, - "minimum_value": "0", - "minimum_value_warning": "15", - "maximum_value_warning": "255", - "enabled": "j1_Change_fanSpeed" - } - } - }""" + version = "5.2.0" - def getValue(self, line, key, default = None): #replace default getvalue due to comment-reading feature - if not key in line or (";" in line and line.find(key) > line.find(";") and - not ";ChangeAtZ" in key and not ";LAYER:" in key): - return default - subPart = line[line.find(key) + len(key):] #allows for string lengths larger than 1 - if ";ChangeAtZ" in key: - m = re.search("^[0-4]", subPart) - elif ";LAYER:" in key: - m = re.search("^[+-]?[0-9]*", subPart) - else: - #the minus at the beginning allows for negative values, e.g. for delta printers - m = re.search("^[-]?[0-9]*\.?[0-9]*", subPart) - if m == None: - return default - try: - return float(m.group(0)) - except: - return default + def getSettingDataString(self): + return """{ + "name": "ChangeAtZ """ + self.version + """(Experimental)", + "key": "ChangeAtZ", + "metadata": {}, + "version": 2, + "settings": { + "a_trigger": { + "label": "Trigger", + "description": "Trigger at height or at layer no.", + "type": "enum", + "options": { + "height": "Height", + "layer_no": "Layer No." + }, + "default_value": "height" + }, + "b_targetZ": { + "label": "Change Height", + "description": "Z height to change at", + "unit": "mm", + "type": "float", + "default_value": 5.0, + "minimum_value": "0", + "minimum_value_warning": "0.1", + "maximum_value_warning": "230", + "enabled": "a_trigger == 'height'" + }, + "b_targetL": { + "label": "Change Layer", + "description": "Layer no. to change at", + "unit": "", + "type": "int", + "default_value": 1, + "minimum_value": "-100", + "minimum_value_warning": "-1", + "enabled": "a_trigger == 'layer_no'" + }, + "c_behavior": { + "label": "Behavior", + "description": "Select behavior: Change value and keep it for the rest, Change value for single layer only", + "type": "enum", + "options": { + "keep_value": "Keep value", + "single_layer": "Single Layer" + }, + "default_value": "keep_value" + }, + "e1_Change_speed": { + "label": "Change Speed", + "description": "Select if total speed (print and travel) has to be changed", + "type": "bool", + "default_value": false + }, + "e2_speed": { + "label": "Speed", + "description": "New total speed (print and travel)", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "1", + "minimum_value_warning": "10", + "maximum_value_warning": "200", + "enabled": "e1_Change_speed" + }, + "f1_Change_printspeed": { + "label": "Change Print Speed", + "description": "Select if print speed has to be changed", + "type": "bool", + "default_value": false + }, + "f2_printspeed": { + "label": "Print Speed", + "description": "New print speed", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "1", + "minimum_value_warning": "10", + "maximum_value_warning": "200", + "enabled": "f1_Change_printspeed" + }, + "g1_Change_flowrate": { + "label": "Change Flow Rate", + "description": "Select if flow rate has to be changed", + "type": "bool", + "default_value": false + }, + "g2_flowrate": { + "label": "Flow Rate", + "description": "New Flow rate", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "1", + "minimum_value_warning": "10", + "maximum_value_warning": "200", + "enabled": "g1_Change_flowrate" + }, + "g3_Change_flowrateOne": { + "label": "Change Flow Rate 1", + "description": "Select if first extruder flow rate has to be changed", + "type": "bool", + "default_value": false + }, + "g4_flowrateOne": { + "label": "Flow Rate One", + "description": "New Flow rate Extruder 1", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "1", + "minimum_value_warning": "10", + "maximum_value_warning": "200", + "enabled": "g3_Change_flowrateOne" + }, + "g5_Change_flowrateTwo": { + "label": "Change Flow Rate 2", + "description": "Select if second extruder flow rate has to be changed", + "type": "bool", + "default_value": false + }, + "g6_flowrateTwo": { + "label": "Flow Rate two", + "description": "New Flow rate Extruder 2", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "1", + "minimum_value_warning": "10", + "maximum_value_warning": "200", + "enabled": "g5_Change_flowrateTwo" + }, + "h1_Change_bedTemp": { + "label": "Change Bed Temp", + "description": "Select if Bed Temperature has to be changed", + "type": "bool", + "default_value": false + }, + "h2_bedTemp": { + "label": "Bed Temp", + "description": "New Bed Temperature", + "unit": "C", + "type": "float", + "default_value": 60, + "minimum_value": "0", + "minimum_value_warning": "30", + "maximum_value_warning": "120", + "enabled": "h1_Change_bedTemp" + }, + "i1_Change_extruderOne": { + "label": "Change Extruder 1 Temp", + "description": "Select if First Extruder Temperature has to be changed", + "type": "bool", + "default_value": false + }, + "i2_extruderOne": { + "label": "Extruder 1 Temp", + "description": "New First Extruder Temperature", + "unit": "C", + "type": "float", + "default_value": 190, + "minimum_value": "0", + "minimum_value_warning": "160", + "maximum_value_warning": "250", + "enabled": "i1_Change_extruderOne" + }, + "i3_Change_extruderTwo": { + "label": "Change Extruder 2 Temp", + "description": "Select if Second Extruder Temperature has to be changed", + "type": "bool", + "default_value": false + }, + "i4_extruderTwo": { + "label": "Extruder 2 Temp", + "description": "New Second Extruder Temperature", + "unit": "C", + "type": "float", + "default_value": 190, + "minimum_value": "0", + "minimum_value_warning": "160", + "maximum_value_warning": "250", + "enabled": "i3_Change_extruderTwo" + }, + "j1_Change_fanSpeed": { + "label": "Change Fan Speed", + "description": "Select if Fan Speed has to be changed", + "type": "bool", + "default_value": false + }, + "j2_fanSpeed": { + "label": "Fan Speed", + "description": "New Fan Speed (0-100)", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "0", + "minimum_value_warning": "0", + "maximum_value_warning": "100", + "enabled": "j1_Change_fanSpeed" + }, + "caz_change_retractfeedrate": { + "label": "Change Retract Feed Rate", + "description": "Changes the retraction feed rate during print (M207)", + "type": "bool", + "default_value": false + }, + "caz_retractfeedrate": { + "label": "Retract Feed Rate", + "description": "New Retract Feed Rate (units/s)", + "unit": "units/s", + "type": "int", + "default_value": 40, + "minimum_value": "0", + "minimum_value_warning": "0", + "maximum_value_warning": "100", + "enabled": "caz_change_retractfeedrate" + }, + "caz_change_retractlength": { + "label": "Change Retract Length", + "description": "Changes the retraction length during print (M207)", + "type": "bool", + "default_value": false + }, + "caz_retractlength": { + "label": "Retract Length", + "description": "New Retract Length (units)", + "unit": "units", + "type": "int", + "default_value": 6, + "minimum_value": "0", + "minimum_value_warning": "0", + "maximum_value_warning": "20", + "enabled": "caz_change_retractlength" + } + } + }""" - def execute(self, data): - #Check which changes should apply - ChangeProp = {"speed": self.getSettingValueByKey("e1_Change_speed"), - "flowrate": self.getSettingValueByKey("g1_Change_flowrate"), - "flowrateOne": self.getSettingValueByKey("g3_Change_flowrateOne"), - "flowrateTwo": self.getSettingValueByKey("g5_Change_flowrateTwo"), - "bedTemp": self.getSettingValueByKey("h1_Change_bedTemp"), - "extruderOne": self.getSettingValueByKey("i1_Change_extruderOne"), - "extruderTwo": self.getSettingValueByKey("i3_Change_extruderTwo"), - "fanSpeed": self.getSettingValueByKey("j1_Change_fanSpeed")} - ChangePrintSpeed = self.getSettingValueByKey("f1_Change_printspeed") - ChangeStrings = {"speed": "M220 S%f\n", - "flowrate": "M221 S%f\n", - "flowrateOne": "M221 T0 S%f\n", - "flowrateTwo": "M221 T1 S%f\n", - "bedTemp": "M140 S%f\n", - "extruderOne": "M104 S%f T0\n", - "extruderTwo": "M104 S%f T1\n", - "fanSpeed": "M106 S%d\n"} - target_values = {"speed": self.getSettingValueByKey("e2_speed"), - "printspeed": self.getSettingValueByKey("f2_printspeed"), - "flowrate": self.getSettingValueByKey("g2_flowrate"), - "flowrateOne": self.getSettingValueByKey("g4_flowrateOne"), - "flowrateTwo": self.getSettingValueByKey("g6_flowrateTwo"), - "bedTemp": self.getSettingValueByKey("h2_bedTemp"), - "extruderOne": self.getSettingValueByKey("i2_extruderOne"), - "extruderTwo": self.getSettingValueByKey("i4_extruderTwo"), - "fanSpeed": self.getSettingValueByKey("j2_fanSpeed")} - old = {"speed": -1, "flowrate": 100, "flowrateOne": -1, "flowrateTwo": -1, "platformTemp": -1, "extruderOne": -1, - "extruderTwo": -1, "bedTemp": -1, "fanSpeed": -1, "state": -1} - twLayers = self.getSettingValueByKey("d_twLayers") - if self.getSettingValueByKey("c_behavior") == "single_layer": - behavior = 1 - else: - behavior = 0 - try: - twLayers = max(int(twLayers),1) #for the case someone entered something as "funny" as -1 - except: - twLayers = 1 - pres_ext = 0 - done_layers = 0 - z = 0 - x = None - y = None - layer = -100000 #layer no. may be negative (raft) but never that low - # state 0: deactivated, state 1: activated, state 2: active, but below z, - # state 3: active and partially executed (multi layer), state 4: active and passed z - state = 1 - # IsUM2: Used for reset of values (ok for Marlin/Sprinter), - # has to be set to 1 for UltiGCode (work-around for missing default values) - IsUM2 = False - oldValueUnknown = False - TWinstances = 0 + def __init__(self): + super().__init__() - if self.getSettingValueByKey("a_trigger") == "layer_no": - targetL_i = int(self.getSettingValueByKey("b_targetL")) - targetZ = 100000 - else: - targetL_i = -100000 - targetZ = self.getSettingValueByKey("b_targetZ") - index = 0 - for active_layer in data: - modified_gcode = "" - lines = active_layer.split("\n") - for line in lines: - if line.strip() == "": - continue - if ";Generated with Cura_SteamEngine" in line: - TWinstances += 1 - modified_gcode += ";ChangeAtZ instances: %d\n" % TWinstances - if not ("M84" in line or "M25" in line or ("G1" in line and ChangePrintSpeed and (state==3 or state==4)) or - ";ChangeAtZ instances:" in line): - modified_gcode += line + "\n" - IsUM2 = ("FLAVOR:UltiGCode" in line) or IsUM2 #Flavor is UltiGCode! - if ";ChangeAtZ-state" in line: #checks for state change comment - state = self.getValue(line, ";ChangeAtZ-state", state) - if ";ChangeAtZ instances:" in line: - try: - tempTWi = int(line[20:]) - except: - tempTWi = TWinstances - TWinstances = tempTWi - if ";Small layer" in line: #checks for begin of Cool Head Lift - old["state"] = state - state = 0 - if ";LAYER:" in line: #new layer no. found - if state == 0: - state = old["state"] - layer = self.getValue(line, ";LAYER:", layer) - if targetL_i > -100000: #target selected by layer no. - if (state == 2 or targetL_i == 0) and layer == targetL_i: #determine targetZ from layer no.; checks for change on layer 0 - state = 2 - targetZ = z + 0.001 - if (self.getValue(line, "T", None) is not None) and (self.getValue(line, "M", None) is None): #looking for single T-cmd - pres_ext = self.getValue(line, "T", pres_ext) - if "M190" in line or "M140" in line and state < 3: #looking for bed temp, stops after target z is passed - old["bedTemp"] = self.getValue(line, "S", old["bedTemp"]) - if "M109" in line or "M104" in line and state < 3: #looking for extruder temp, stops after target z is passed - if self.getValue(line, "T", pres_ext) == 0: - old["extruderOne"] = self.getValue(line, "S", old["extruderOne"]) - elif self.getValue(line, "T", pres_ext) == 1: - old["extruderTwo"] = self.getValue(line, "S", old["extruderTwo"]) - if "M107" in line: #fan is stopped; is always updated in order not to miss switch off for next object - old["fanSpeed"] = 0 - if "M106" in line and state < 3: #looking for fan speed - old["fanSpeed"] = self.getValue(line, "S", old["fanSpeed"]) - if "M221" in line and state < 3: #looking for flow rate - tmp_extruder = self.getValue(line, "T", None) - if tmp_extruder == None: #check if extruder is specified - old["flowrate"] = self.getValue(line, "S", old["flowrate"]) - if old["flowrate"] == -1: - old["flowrate"] = 100.0 - elif tmp_extruder == 0: #first extruder - old["flowrateOne"] = self.getValue(line, "S", old["flowrateOne"]) - elif tmp_extruder == 1: #second extruder - old["flowrateTwo"] = self.getValue(line, "S", old["flowrateTwo"]) - if ("M84" in line or "M25" in line): - if state>0 and ChangeProp["speed"]: #"finish" commands for UM Original and UM2 - modified_gcode += "M220 S100 ; speed reset to 100% at the end of print\n" - modified_gcode += "M117 \n" - modified_gcode += line + "\n" - if "G1" in line or "G0" in line: - newZ = self.getValue(line, "Z", z) - x = self.getValue(line, "X", None) - y = self.getValue(line, "Y", None) - e = self.getValue(line, "E", None) - f = self.getValue(line, "F", None) - if 'G1' in line and ChangePrintSpeed and (state==3 or state==4): - # check for pure print movement in target range: - if x != None and y != None and f != None and e != None and newZ==z: - modified_gcode += "G1 F%d X%1.3f Y%1.3f E%1.5f\n" % (int(f / 100.0 * float(target_values["printspeed"])), self.getValue(line, "X"), - self.getValue(line, "Y"), self.getValue(line, "E")) - else: #G1 command but not a print movement - modified_gcode += line + "\n" - # no changing on retraction hops which have no x and y coordinate: - if (newZ != z) and (x is not None) and (y is not None): - z = newZ - if z < targetZ and state == 1: - state = 2 - if z >= targetZ and state == 2: - state = 3 - done_layers = 0 - for key in ChangeProp: - if ChangeProp[key] and old[key]==-1: #old value is not known - oldValueUnknown = True - if oldValueUnknown: #the changing has to happen within one layer - twLayers = 1 - if IsUM2: #Parameters have to be stored in the printer (UltiGCode=UM2) - modified_gcode += "M605 S%d;stores parameters before changing\n" % (TWinstances-1) - if behavior == 1: #single layer change only and then reset - twLayers = 1 - if ChangePrintSpeed and behavior == 0: - twLayers = done_layers + 1 - if state==3: - if twLayers-done_layers>0: #still layers to go? - if targetL_i > -100000: - modified_gcode += ";ChangeAtZ V%s: executed at Layer %d\n" % (self.version,layer) - modified_gcode += "M117 Printing... ch@L%4d\n" % layer - else: - modified_gcode += (";ChangeAtZ V%s: executed at %1.2f mm\n" % (self.version,z)) - modified_gcode += "M117 Printing... ch@%5.1f\n" % z - for key in ChangeProp: - if ChangeProp[key]: - modified_gcode += ChangeStrings[key] % float(old[key]+(float(target_values[key])-float(old[key]))/float(twLayers)*float(done_layers+1)) - done_layers += 1 - else: - state = 4 - if behavior == 1: #reset values after one layer - if targetL_i > -100000: - modified_gcode += ";ChangeAtZ V%s: reset on Layer %d\n" % (self.version,layer) - else: - modified_gcode += ";ChangeAtZ V%s: reset at %1.2f mm\n" % (self.version,z) - if IsUM2 and oldValueUnknown: #executes on UM2 with Ultigcode and machine setting - modified_gcode += "M606 S%d;recalls saved settings\n" % (TWinstances-1) - else: #executes on RepRap, UM2 with Ultigcode and Cura setting - for key in ChangeProp: - if ChangeProp[key]: - modified_gcode += ChangeStrings[key] % float(old[key]) - # re-activates the plugin if executed by pre-print G-command, resets settings: - if (z < targetZ or layer == 0) and state >= 3: #resets if below change level or at level 0 - state = 2 - done_layers = 0 - if targetL_i > -100000: - modified_gcode += ";ChangeAtZ V%s: reset below Layer %d\n" % (self.version, targetL_i) - else: - modified_gcode += ";ChangeAtZ V%s: reset below %1.2f mm\n" % (self.version, targetZ) - if IsUM2 and oldValueUnknown: #executes on UM2 with Ultigcode and machine setting - modified_gcode += "M606 S%d;recalls saved settings\n" % (TWinstances-1) - else: #executes on RepRap, UM2 with Ultigcode and Cura setting - for key in ChangeProp: - if ChangeProp[key]: - modified_gcode += ChangeStrings[key] % float(old[key]) - data[index] = modified_gcode - index += 1 - return data + def execute(self, data): + + caz_instance = ChangeAtZProcessor() + + caz_instance.TargetValues = {} + + # copy over our settings to our change z class + self.setIntSettingIfEnabled(caz_instance, "e1_Change_speed", "speed", "e2_speed") + self.setIntSettingIfEnabled(caz_instance, "f1_Change_printspeed", "printspeed", "f2_printspeed") + self.setIntSettingIfEnabled(caz_instance, "g1_Change_flowrate", "flowrate", "g2_flowrate") + self.setIntSettingIfEnabled(caz_instance, "g3_Change_flowrateOne", "flowrateOne", "g4_flowrateOne") + self.setIntSettingIfEnabled(caz_instance, "g5_Change_flowrateTwo", "flowrateTwo", "g6_flowrateTwo") + self.setIntSettingIfEnabled(caz_instance, "h1_Change_bedTemp", "bedTemp", "h2_bedTemp") + self.setIntSettingIfEnabled(caz_instance, "i1_Change_extruderOne", "extruderOne", "i2_extruderOne") + self.setIntSettingIfEnabled(caz_instance, "i3_Change_extruderTwo", "extruderTwo", "i4_extruderTwo") + self.setIntSettingIfEnabled(caz_instance, "j1_Change_fanSpeed", "fanSpeed", "j2_fanSpeed") + self.setIntSettingIfEnabled(caz_instance, "caz_change_retractfeedrate", "retractfeedrate", "caz_retractfeedrate") + self.setIntSettingIfEnabled(caz_instance, "caz_change_retractlength", "retractlength", "caz_retractlength") + + # see if we're applying to a single layer or to all layers hence forth + caz_instance.IsApplyToSingleLayer = self.getSettingValueByKey("c_behavior") == "single_layer" + + # used for easy reference of layer or height targeting + caz_instance.IsTargetByLayer = self.getSettingValueByKey("a_trigger") == "layer_no" + + # change our target based on what we're targeting + caz_instance.TargetLayer = self.getIntSettingByKey("b_targetL", None) + caz_instance.TargetZ = self.getIntSettingByKey("b_targetZ", None) + + # run our script + return caz_instance.execute(data) + + # Sets the given TargetValue in the ChangeAtZ instance if the trigger is specified + def setIntSettingIfEnabled(self, caz_instance, trigger, target, setting): + + # stop here if our trigger isn't enabled + if not self.getSettingValueByKey(trigger): + return + + # get our value from the settings + value = self.getIntSettingByKey(setting, None) + + # skip if there's no value or we can't interpret it + if value is None: + return + + # set our value in the target settings + caz_instance.TargetValues[target] = value + + # Returns the given settings value as an integer or the default if it cannot parse it + def getIntSettingByKey(self, key, default): + + # change our target based on what we're targeting + try: + return int(self.getSettingValueByKey(key)) + except: + return default + + +# The primary ChangeAtZ class that does all the gcode editing. This was broken out into an +# independent class so it could be debugged using a standard IDE +class ChangeAtZProcessor: + + TargetValues = {} + IsApplyToSingleLayer = False + LastE = None + CurrentZ = None + CurrentLayer = None + IsTargetByLayer = True + TargetLayer = None + TargetZ = None + LayerHeight = None + RetractLength = 0 + + # boots up the class with defaults + def __init__(self): + self.reset() + + # Modifies the given GCODE and injects the commands at the various targets + def execute(self, data): + + # indicates if we should inject our defaults or not + inject_defaults = True + + # our layer cursor + index = 0 + + for active_layer in data: + + # will hold our updated gcode + modified_gcode = "" + + # mark all the defaults for deletion + active_layer = self.markDefaultsForDeletion(active_layer) + + # break apart the layer into commands + lines = active_layer.split("\n") + + # evaluate each command individually + for line in lines: + + # skip empty lines + if line.strip() == "": + continue + + # update our layer number if applicable + self.processLayerNumber(line) + + # update our layer height if applicable + self.processLayerHeight(line) + + # skip this line if we're not there yet + if not self.isTargetLayerOrHeight(line): + + # read any settings we might need + self.processSetting(line) + + # if we haven't hit our target yet, leave the defaults as is (unmark them for deletion) + if "[CAZD:DELETE:" in line: + line = line.replace("[CAZD:DELETE:", "[CAZD:") + + # set our line + modified_gcode += line + "\n" + + # skip to the next line + continue + + # inject our defaults before linear motion commands + if inject_defaults and ("G1" in line or "G0" in line): + + # inject the defaults + modified_gcode += self.getTargetDefaults() + "\n" + + # mark that we've injected the defaults + inject_defaults = False + + # append to our modified layer + modified_gcode += self.processLinearMove(line) + "\n" + + # inject our defaults after the layer indicator + if inject_defaults and ";LAYER:" in line: + + # inject the defaults + modified_gcode += self.getTargetDefaults() + "\n" + + # mark that we've injected the defaults + inject_defaults = False + + # remove any marked defaults + modified_gcode = self.removeMarkedTargetDefaults(modified_gcode) + + # append our modified line + data[index] = modified_gcode + + index += 1 + return data + + # Converts the command parameter to a float or returns the default + @staticmethod + def getFloatValue(line, key, default=None): + + # get the value from the command + value = ChangeAtZProcessor.getValue(line, key, default) + + # stop here if it's the default + if value == default: + return value + + try: + return float(value) + except: + return default + + # Converts the command parameter to a int or returns the default + @staticmethod + def getIntValue(line, key, default=None): + + # get the value from the command + value = ChangeAtZProcessor.getValue(line, key, default) + + # stop here if it's the default + if value == default: + return value + + try: + return int(value) + except: + return default + + # Handy function for reading a linear move command + def getLinearMoveParams(self, line): + + # get our motion parameters + feed_rate = self.getFloatValue(line, "F", None) + x_coord = self.getFloatValue(line, "X", None) + y_coord = self.getFloatValue(line, "Y", None) + z_coord = self.getFloatValue(line, "Z", None) + extrude_length = self.getFloatValue(line, "E", None) + + return extrude_length, feed_rate, x_coord, y_coord, z_coord + + # Returns the unmodified GCODE line from previous ChangeZ edits + @staticmethod + def getOriginalLine(line): + + # get the change at z original (cazo) details + original_line = re.search(r"\[CAZO:(.*?):CAZO\]", line) + + # if we didn't get a hit, this is the original line + if original_line is None: + return line + + return original_line.group(1) + + # Builds the layer defaults based on the settings and returns the relevant GCODE lines + def getTargetDefaults(self): + + # will hold all the default settings for the target layer + defaults = [] + + # used to trim other defaults + defaults.append(";[CAZD:") + + # looking for wait for bed temp + if "bedTemp" in self.TargetValues: + defaults.append("M190 S" + str(self.TargetValues["bedTemp"])) + + # set our extruder temps + if "extruderOne" in self.TargetValues: + defaults.append("M109 S" + str(self.TargetValues["extruderOne"])) + elif "extruderTwo" in self.TargetValues: + defaults.append("M109 S" + str(self.TargetValues["extruderTwo"])) + + # set our fan speed + if "fanSpeed" in self.TargetValues: + + # convert our fan speed percentage to PWM + fan_speed = int((float(self.TargetValues["fanSpeed"]) / 100.0) * 255) + + # add our fan speed to the defaults + defaults.append("M106 S" + str(fan_speed)) + + # set global flow rate + if "flowrate" in self.TargetValues: + defaults.append("M221 S" + str(self.TargetValues["flowrate"])) + + # set extruder 0 flow rate + if "flowrateOne" in self.TargetValues: + defaults.append("M221 S" + str(self.TargetValues["flowrateOne"]) + " T0") + + # set second extruder flow rate + if "flowrateTwo" in self.TargetValues: + defaults.append("M221 S" + str(self.TargetValues["flowrateTwo"]) + " T1") + + # set feedrate percentage + if "speed" in self.TargetValues: + defaults.append("M220 S" + str(self.TargetValues["speed"]) + " T1") + + # set print rate percentage + if "printspeed" in self.TargetValues: + defaults.append(";PRINTSPEED " + str(self.TargetValues["printspeed"]) + "") + + # set retract rate + if "retractfeedrate" in self.TargetValues: + defaults.append(";RETRACTFEEDRATE " + str(self.TargetValues["retractfeedrate"]) + "") + + # set retract length + if "retractlength" in self.TargetValues: + defaults.append(";RETRACTLENGTH " + str(self.TargetValues["retractlength"]) + "") + + # used to trim other defaults + defaults.append(";:CAZD]") + + # if there are no defaults, stop here + if len(defaults) == 2: + return "" + + # return our default block for this layer + return "\n".join(defaults) + + # Allows retrieving values from the given GCODE line + @staticmethod + def getValue(line, key, default=None): + + if not key in line or (";" in line and line.find(key) > line.find(";") and not ";ChangeAtZ" in key and not ";LAYER:" in key): + return default + + sub_part = line[line.find(key) + len(key):] # allows for string lengths larger than 1 + if ";ChangeAtZ" in key: + m = re.search("^[0-4]", sub_part) + elif ";LAYER:" in key: + m = re.search("^[+-]?[0-9]*", sub_part) + else: + # the minus at the beginning allows for negative values, e.g. for delta printers + m = re.search(r"^[-]?[0-9]*\.?[0-9]*", sub_part) + if m is None: + return default + try: + return float(m.group(0)) + except: + return default + + # Determines if the current line is at or below the target required to start modifying + def isTargetLayerOrHeight(self, line): + + # target selected by layer no. + if self.IsTargetByLayer: + + # if we don't have a current layer, we're not there yet + if self.CurrentLayer is None: + return False + + # if we're applying to a single layer, stop if our layer is not identical + if self.IsApplyToSingleLayer: + return self.CurrentLayer == self.TargetLayer + else: + return self.CurrentLayer >= self.TargetLayer + + else: + + # if we don't have a current Z, we're not there yet + if self.CurrentZ is None: + return False + + # if we're applying to a single layer, stop if our Z is not identical + if self.IsApplyToSingleLayer: + return self.CurrentZ == self.TargetZ + else: + return self.CurrentZ >= self.TargetZ + + # Marks any current ChangeZ layer defaults in the layer for deletion + @staticmethod + def markDefaultsForDeletion(layer): + return re.sub(r";\[CAZD:", ";[CAZD:DELETE:", layer) + + # Grabs the current height + def processLayerHeight(self, line): + + # stop here if we haven't entered a layer yet + if self.CurrentLayer is None: + return + + # expose the main command + line_no_comments = self.stripComments(line) + + # stop here if this isn't a linear move command + if not ("G1" in line_no_comments or "G0" in line_no_comments): + return + + # stop here if we don't have a Z value defined, we can't get the height from this command + if "Z" not in line_no_comments: + return + + # get our value from the command + current_z = self.getIntValue(line_no_comments, "Z", None) + + # stop if there's no change + if current_z == self.CurrentZ: + return + + # set our current Z value + self.CurrentZ = current_z + + # if we don't have a layer height yet, set it based on the current Z value + if self.LayerHeight is None: + self.LayerHeight = self.CurrentZ + + # Grabs the current layer number + def processLayerNumber(self, line): + + # if this isn't a layer comment, stop here, nothing to update + if ";LAYER:" not in line: + return + + # get our current layer number + current_layer = self.getIntValue(line, ";LAYER:", None) + + # this should never happen, but if our layer number hasn't changed, stop here + if current_layer == self.CurrentLayer: + return + + # update our current layer + self.CurrentLayer = current_layer + + # Handles any linear moves in the current line + def processLinearMove(self, line): + + # if it's not a linear motion command we're not interested + if not ("G1" in line or "G0" in line): + return line + + # always get our original line, otherwise the effect will be cumulative + line = self.getOriginalLine(line) + + # get the details from our linear move command + extrude_length, feed_rate, x_coord, y_coord, z_coord = self.getLinearMoveParams(line) + + # set our new line to our old line + new_line = line + + # handle retract length + new_line = self.processRetractLength(extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord) + + # handle retract feed rate + new_line = self.processRetractFeedRate(extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord) + + # handle print speed adjustments + new_line = self.processPrintSpeed(feed_rate, new_line) + + # set our current extrude position + self.LastE = extrude_length if extrude_length is not None else self.LastE + + # if no changes have been made, stop here + if new_line == line: + return line + + # return our updated command + return self.setOriginalLine(new_line, line) + + # Handles any changes to print speed for the given linear motion command + def processPrintSpeed(self, feed_rate, new_line): + + # if we're not setting print speed or we don't have a feed rate, stop here + if "printspeed" not in self.TargetValues or feed_rate is None: + return new_line + + # get our requested print speed + print_speed = int(self.TargetValues["printspeed"]) + + # if they requested no change to print speed (ie: 100%), stop here + if print_speed == 100: + return new_line + + # get our feed rate from the command + feed_rate = float(self.getValue(new_line, "F")) * (float(print_speed) / 100.0) + + # change our feed rate + return self.replaceParameter(new_line, "F", feed_rate) + + # Handles any changes to retraction length for the given linear motion command + def processRetractLength(self, extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord): + + # if we don't have a retract length in the file we can't add one + if self.RetractLength == 0: + return new_line + + # if we're not changing retraction length, stop here + if "retractlength" not in self.TargetValues: + return new_line + + # retractions are only F (feed rate) and E (extrude), at least in cura + if x_coord is not None or y_coord is not None or z_coord is not None: + return new_line + + # since retractions require both F and E, and we don't have either, we can't process + if feed_rate is None or extrude_length is None: + return new_line + + # stop here if we don't know our last extrude value + if self.LastE is None: + return new_line + + # if there's no change in extrude we have nothing to change + if self.LastE == extrude_length: + return new_line + + # if our last extrude was lower than our current, we're restoring, so skip + if self.LastE < extrude_length: + return new_line + + # get our desired retract length + retract_length = int(self.TargetValues["retractlength"]) + + # subtract the difference between the default and the desired + extrude_length -= (retract_length - self.RetractLength) + + # replace our extrude amount + return self.replaceParameter(new_line, "E", extrude_length) + + # Used for picking out the retract length set by Cura + def processRetractLengthSetting(self, line): + + # if it's not a linear move, we don't care + if "G0" not in line and "G1" not in line: + return + + # get the details from our linear move command + extrude_length, feed_rate, x_coord, y_coord, z_coord = self.getLinearMoveParams(line) + + # the command we're looking for only has extrude and feed rate + if x_coord is not None or y_coord is not None or z_coord is not None: + return + + # if either extrude or feed is missing we're likely looking at the wrong command + if extrude_length is None or feed_rate is None: + return + + # cura stores the retract length as a negative E just before it starts printing + extrude_length = extrude_length * -1 + + # if it's a negative extrude after being inverted, it's not our retract length + if extrude_length < 0: + return + + # what ever the last negative retract length is it wins + self.RetractLength = extrude_length + + # Handles any changes to retraction feed rate for the given linear motion command + def processRetractFeedRate(self, extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord): + + # if we're not changing retraction length, stop here + if "retractfeedrate" not in self.TargetValues: + return new_line + + # retractions are only F (feed rate) and E (extrude), at least in cura + if x_coord is not None or y_coord is not None or z_coord is not None: + return new_line + + # since retractions require both F and E, and we don't have either, we can't process + if feed_rate is None or extrude_length is None: + return new_line + + # get our desired retract feed rate + retract_feed_rate = int(self.TargetValues["retractfeedrate"]) + + # convert to units/min + retract_feed_rate *= 60 + + # replace our feed rate + return self.replaceParameter(new_line, "F", retract_feed_rate) + + # Used for finding settings in the print file before we process anything else + def processSetting(self, line): + + # if we're in layers already we're out of settings + if self.CurrentLayer is not None: + return + + # check our retract length + self.processRetractLengthSetting(line) + + # Removes all the ChangeZ layer defaults from the given layer + @staticmethod + def removeMarkedTargetDefaults(layer): + return re.sub(r";\[CAZD:DELETE:[\s\S]+?:CAZD\](\n|$)", "", layer) + + # Easy function for replacing any GCODE parameter variable in a given GCODE command + @staticmethod + def replaceParameter(line, key, value): + return re.sub(r"(^|\s)" + key + r"[\d\.]+(\s|$)", r"\1" + key + str(value) + r"\2", line) + + # Resets the class contents to defaults + def reset(self): + + self.TargetValues = {} + self.IsApplyToSingleLayer = False + self.LastE = None + self.CurrentZ = None + self.CurrentLayer = None + self.IsTargetByLayer = True + self.TargetLayer = None + self.TargetZ = None + self.LayerHeight = None + self.RetractLength = 0 + + # Sets the original GCODE line in a given GCODE command + @staticmethod + def setOriginalLine(line, original): + return line + ";[CAZO:" + original + ":CAZO]" + + # Removes the gcode comments from a given gcode command + @staticmethod + def stripComments(line): + return re.sub(r";.*?$", "", line).strip() + + +def debug(): + # get our input file + file = r"PATH_TO_SOME_GCODE.gcode" + + # read the whole thing + f = open(file, "r") + gcode = f.read() + f.close() + + # boot up change + caz_instance = ChangeAtZProcessor() + caz_instance.IsTargetByLayer = False + caz_instance.TargetZ = 5 + caz_instance.TargetValues["printspeed"] = 100 + caz_instance.TargetValues["retractfeedrate"] = 60 + + # process gcode + gcode = debug_iteration(gcode, caz_instance) + + # write our file + debug_write(gcode, file + ".1.modified") + + caz_instance.reset() + caz_instance.IsTargetByLayer = False + caz_instance.TargetZ = 10 + caz_instance.TargetValues["bedTemp"] = 75 + caz_instance.TargetValues["printspeed"] = 150 + caz_instance.TargetValues["retractfeedrate"] = 40 + caz_instance.TargetValues["retractlength"] = 10 + + # and again + gcode = debug_iteration(gcode, caz_instance) + + # write our file + debug_write(gcode, file + ".2.modified") + + caz_instance.reset() + caz_instance.IsTargetByLayer = False + caz_instance.TargetZ = 15 + caz_instance.TargetValues["bedTemp"] = 80 + caz_instance.TargetValues["printspeed"] = 100 + caz_instance.TargetValues["retractfeedrate"] = 10 + caz_instance.TargetValues["retractlength"] = 0 + + # and again + gcode = debug_iteration(gcode, caz_instance) + + # write our file + debug_write(gcode, file + ".3.modified") + + +def debug_write(gcode, file): + # write our file + f = open(file, "w") + f.write(gcode) + f.close() + + +def debug_iteration(gcode, caz_instance): + index = 0 + + # break apart the GCODE like cura + layers = re.split(r"^;LAYER:\d+\n", gcode) + + # add the layer numbers back + for layer in layers: + + # if this is the first layer, skip it, basically + if index == 0: + # leave our first layer as is + layers[index] = layer + + # move the cursor + index += 1 + + # skip to the next layer + continue + + layers[index] = ";LAYER:" + str(index - 1) + ";\n" + layer + + return "".join(caz_instance.execute(layers)) + +# debug() From 5d832a35cbc6dad3f52c16b020d52403b864c4cf Mon Sep 17 00:00:00 2001 From: novamxd Date: Thu, 12 Dec 2019 21:16:27 -0600 Subject: [PATCH 02/37] Revamped ChangeAtZ Fixed bugs where TargetZ, Bed Temp, Extruder Temps, Retract Feed Rate and Retract Length weren't properly considered as floats. Now capable of properly targeting decimal Z values, and altering others as decimals Updated debug cases to test for it. --- .../PostProcessingPlugin/scripts/ChangeAtZ.py | 59 +++++++++++++------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py index 9e93600fbf..42dcae2ece 100644 --- a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py +++ b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py @@ -266,7 +266,7 @@ class ChangeAtZ(Script): "label": "Retract Feed Rate", "description": "New Retract Feed Rate (units/s)", "unit": "units/s", - "type": "int", + "type": "float", "default_value": 40, "minimum_value": "0", "minimum_value_warning": "0", @@ -283,7 +283,7 @@ class ChangeAtZ(Script): "label": "Retract Length", "description": "New Retract Length (units)", "unit": "units", - "type": "int", + "type": "float", "default_value": 6, "minimum_value": "0", "minimum_value_warning": "0", @@ -308,12 +308,12 @@ class ChangeAtZ(Script): self.setIntSettingIfEnabled(caz_instance, "g1_Change_flowrate", "flowrate", "g2_flowrate") self.setIntSettingIfEnabled(caz_instance, "g3_Change_flowrateOne", "flowrateOne", "g4_flowrateOne") self.setIntSettingIfEnabled(caz_instance, "g5_Change_flowrateTwo", "flowrateTwo", "g6_flowrateTwo") - self.setIntSettingIfEnabled(caz_instance, "h1_Change_bedTemp", "bedTemp", "h2_bedTemp") - self.setIntSettingIfEnabled(caz_instance, "i1_Change_extruderOne", "extruderOne", "i2_extruderOne") - self.setIntSettingIfEnabled(caz_instance, "i3_Change_extruderTwo", "extruderTwo", "i4_extruderTwo") + self.setFloatSettingIfEnabled(caz_instance, "h1_Change_bedTemp", "bedTemp", "h2_bedTemp") + self.setFloatSettingIfEnabled(caz_instance, "i1_Change_extruderOne", "extruderOne", "i2_extruderOne") + self.setFloatSettingIfEnabled(caz_instance, "i3_Change_extruderTwo", "extruderTwo", "i4_extruderTwo") self.setIntSettingIfEnabled(caz_instance, "j1_Change_fanSpeed", "fanSpeed", "j2_fanSpeed") - self.setIntSettingIfEnabled(caz_instance, "caz_change_retractfeedrate", "retractfeedrate", "caz_retractfeedrate") - self.setIntSettingIfEnabled(caz_instance, "caz_change_retractlength", "retractlength", "caz_retractlength") + self.setFloatSettingIfEnabled(caz_instance, "caz_change_retractfeedrate", "retractfeedrate", "caz_retractfeedrate") + self.setFloatSettingIfEnabled(caz_instance, "caz_change_retractlength", "retractlength", "caz_retractlength") # see if we're applying to a single layer or to all layers hence forth caz_instance.IsApplyToSingleLayer = self.getSettingValueByKey("c_behavior") == "single_layer" @@ -323,7 +323,7 @@ class ChangeAtZ(Script): # change our target based on what we're targeting caz_instance.TargetLayer = self.getIntSettingByKey("b_targetL", None) - caz_instance.TargetZ = self.getIntSettingByKey("b_targetZ", None) + caz_instance.TargetZ = self.getFloatSettingByKey("b_targetZ", None) # run our script return caz_instance.execute(data) @@ -345,6 +345,23 @@ class ChangeAtZ(Script): # set our value in the target settings caz_instance.TargetValues[target] = value + # Sets the given TargetValue in the ChangeAtZ instance if the trigger is specified + def setFloatSettingIfEnabled(self, caz_instance, trigger, target, setting): + + # stop here if our trigger isn't enabled + if not self.getSettingValueByKey(trigger): + return + + # get our value from the settings + value = self.getFloatSettingByKey(setting, None) + + # skip if there's no value or we can't interpret it + if value is None: + return + + # set our value in the target settings + caz_instance.TargetValues[target] = value + # Returns the given settings value as an integer or the default if it cannot parse it def getIntSettingByKey(self, key, default): @@ -354,6 +371,14 @@ class ChangeAtZ(Script): except: return default + # Returns the given settings value as an integer or the default if it cannot parse it + def getFloatSettingByKey(self, key, default): + + # change our target based on what we're targeting + try: + return float(self.getSettingValueByKey(key)) + except: + return default # The primary ChangeAtZ class that does all the gcode editing. This was broken out into an # independent class so it could be debugged using a standard IDE @@ -408,7 +433,7 @@ class ChangeAtZProcessor: self.processLayerHeight(line) # skip this line if we're not there yet - if not self.isTargetLayerOrHeight(line): + if not self.isTargetLayerOrHeight(): # read any settings we might need self.processSetting(line) @@ -599,7 +624,7 @@ class ChangeAtZProcessor: return default # Determines if the current line is at or below the target required to start modifying - def isTargetLayerOrHeight(self, line): + def isTargetLayerOrHeight(self): # target selected by layer no. if self.IsTargetByLayer: @@ -650,7 +675,7 @@ class ChangeAtZProcessor: return # get our value from the command - current_z = self.getIntValue(line_no_comments, "Z", None) + current_z = self.getFloatValue(line_no_comments, "Z", None) # stop if there's no change if current_z == self.CurrentZ: @@ -767,7 +792,7 @@ class ChangeAtZProcessor: return new_line # get our desired retract length - retract_length = int(self.TargetValues["retractlength"]) + retract_length = float(self.TargetValues["retractlength"]) # subtract the difference between the default and the desired extrude_length -= (retract_length - self.RetractLength) @@ -819,7 +844,7 @@ class ChangeAtZProcessor: return new_line # get our desired retract feed rate - retract_feed_rate = int(self.TargetValues["retractfeedrate"]) + retract_feed_rate = float(self.TargetValues["retractfeedrate"]) # convert to units/min retract_feed_rate *= 60 @@ -896,11 +921,11 @@ def debug(): caz_instance.reset() caz_instance.IsTargetByLayer = False - caz_instance.TargetZ = 10 - caz_instance.TargetValues["bedTemp"] = 75 + caz_instance.TargetZ = 10.5 + caz_instance.TargetValues["bedTemp"] = 75.111 caz_instance.TargetValues["printspeed"] = 150 - caz_instance.TargetValues["retractfeedrate"] = 40 - caz_instance.TargetValues["retractlength"] = 10 + caz_instance.TargetValues["retractfeedrate"] = 40.555 + caz_instance.TargetValues["retractlength"] = 10.3333 # and again gcode = debug_iteration(gcode, caz_instance) From 6c22a5ed06d0296a7f2a503fb31b5226786efce2 Mon Sep 17 00:00:00 2001 From: novamxd Date: Thu, 26 Dec 2019 00:56:57 -0600 Subject: [PATCH 03/37] * Revamped ChangeAtZ Fixed a bug where extruder temps for extruder one and two were being applied universally. Now sets individually as expected. --- plugins/PostProcessingPlugin/scripts/ChangeAtZ.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py index 42dcae2ece..52e8096a7f 100644 --- a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py +++ b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py @@ -548,11 +548,13 @@ class ChangeAtZProcessor: if "bedTemp" in self.TargetValues: defaults.append("M190 S" + str(self.TargetValues["bedTemp"])) - # set our extruder temps + # set our extruder one temp (if specified) if "extruderOne" in self.TargetValues: - defaults.append("M109 S" + str(self.TargetValues["extruderOne"])) - elif "extruderTwo" in self.TargetValues: - defaults.append("M109 S" + str(self.TargetValues["extruderTwo"])) + defaults.append("M109 S" + str(self.TargetValues["extruderOne"]) + " T0") + + # set our extruder two temp (if specified) + if "extruderTwo" in self.TargetValues: + defaults.append("M109 S" + str(self.TargetValues["extruderTwo"]) + " T1") # set our fan speed if "fanSpeed" in self.TargetValues: @@ -940,6 +942,8 @@ def debug(): caz_instance.TargetValues["printspeed"] = 100 caz_instance.TargetValues["retractfeedrate"] = 10 caz_instance.TargetValues["retractlength"] = 0 + caz_instance.TargetValues["extruderOne"] = 100 + caz_instance.TargetValues["extruderTwo"] = 200 # and again gcode = debug_iteration(gcode, caz_instance) From bcc12030a2f2d6cbca6c34e166d4b25cb089d611 Mon Sep 17 00:00:00 2001 From: novamxd Date: Fri, 3 Jan 2020 19:01:33 -0600 Subject: [PATCH 04/37] Revamped ChangeAtZ Adjusted coding style to use spaces instead of tabs --- .../PostProcessingPlugin/scripts/ChangeAtZ.py | 1473 ++++++++--------- 1 file changed, 736 insertions(+), 737 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py index 52e8096a7f..f37676a28c 100644 --- a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py +++ b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py @@ -52,935 +52,934 @@ import re # this was broken up into a separate class so the main ChangeZ script could be debugged outside of Cura class ChangeAtZ(Script): + version = "5.2.0" - version = "5.2.0" + def getSettingDataString(self): + return """{ + "name": "ChangeAtZ """ + self.version + """(Experimental)", + "key": "ChangeAtZ", + "metadata": {}, + "version": 2, + "settings": { + "a_trigger": { + "label": "Trigger", + "description": "Trigger at height or at layer no.", + "type": "enum", + "options": { + "height": "Height", + "layer_no": "Layer No." + }, + "default_value": "height" + }, + "b_targetZ": { + "label": "Change Height", + "description": "Z height to change at", + "unit": "mm", + "type": "float", + "default_value": 5.0, + "minimum_value": "0", + "minimum_value_warning": "0.1", + "maximum_value_warning": "230", + "enabled": "a_trigger == 'height'" + }, + "b_targetL": { + "label": "Change Layer", + "description": "Layer no. to change at", + "unit": "", + "type": "int", + "default_value": 1, + "minimum_value": "-100", + "minimum_value_warning": "-1", + "enabled": "a_trigger == 'layer_no'" + }, + "c_behavior": { + "label": "Behavior", + "description": "Select behavior: Change value and keep it for the rest, Change value for single layer only", + "type": "enum", + "options": { + "keep_value": "Keep value", + "single_layer": "Single Layer" + }, + "default_value": "keep_value" + }, + "e1_Change_speed": { + "label": "Change Speed", + "description": "Select if total speed (print and travel) has to be changed", + "type": "bool", + "default_value": false + }, + "e2_speed": { + "label": "Speed", + "description": "New total speed (print and travel)", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "1", + "minimum_value_warning": "10", + "maximum_value_warning": "200", + "enabled": "e1_Change_speed" + }, + "f1_Change_printspeed": { + "label": "Change Print Speed", + "description": "Select if print speed has to be changed", + "type": "bool", + "default_value": false + }, + "f2_printspeed": { + "label": "Print Speed", + "description": "New print speed", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "1", + "minimum_value_warning": "10", + "maximum_value_warning": "200", + "enabled": "f1_Change_printspeed" + }, + "g1_Change_flowrate": { + "label": "Change Flow Rate", + "description": "Select if flow rate has to be changed", + "type": "bool", + "default_value": false + }, + "g2_flowrate": { + "label": "Flow Rate", + "description": "New Flow rate", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "1", + "minimum_value_warning": "10", + "maximum_value_warning": "200", + "enabled": "g1_Change_flowrate" + }, + "g3_Change_flowrateOne": { + "label": "Change Flow Rate 1", + "description": "Select if first extruder flow rate has to be changed", + "type": "bool", + "default_value": false + }, + "g4_flowrateOne": { + "label": "Flow Rate One", + "description": "New Flow rate Extruder 1", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "1", + "minimum_value_warning": "10", + "maximum_value_warning": "200", + "enabled": "g3_Change_flowrateOne" + }, + "g5_Change_flowrateTwo": { + "label": "Change Flow Rate 2", + "description": "Select if second extruder flow rate has to be changed", + "type": "bool", + "default_value": false + }, + "g6_flowrateTwo": { + "label": "Flow Rate two", + "description": "New Flow rate Extruder 2", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "1", + "minimum_value_warning": "10", + "maximum_value_warning": "200", + "enabled": "g5_Change_flowrateTwo" + }, + "h1_Change_bedTemp": { + "label": "Change Bed Temp", + "description": "Select if Bed Temperature has to be changed", + "type": "bool", + "default_value": false + }, + "h2_bedTemp": { + "label": "Bed Temp", + "description": "New Bed Temperature", + "unit": "C", + "type": "float", + "default_value": 60, + "minimum_value": "0", + "minimum_value_warning": "30", + "maximum_value_warning": "120", + "enabled": "h1_Change_bedTemp" + }, + "i1_Change_extruderOne": { + "label": "Change Extruder 1 Temp", + "description": "Select if First Extruder Temperature has to be changed", + "type": "bool", + "default_value": false + }, + "i2_extruderOne": { + "label": "Extruder 1 Temp", + "description": "New First Extruder Temperature", + "unit": "C", + "type": "float", + "default_value": 190, + "minimum_value": "0", + "minimum_value_warning": "160", + "maximum_value_warning": "250", + "enabled": "i1_Change_extruderOne" + }, + "i3_Change_extruderTwo": { + "label": "Change Extruder 2 Temp", + "description": "Select if Second Extruder Temperature has to be changed", + "type": "bool", + "default_value": false + }, + "i4_extruderTwo": { + "label": "Extruder 2 Temp", + "description": "New Second Extruder Temperature", + "unit": "C", + "type": "float", + "default_value": 190, + "minimum_value": "0", + "minimum_value_warning": "160", + "maximum_value_warning": "250", + "enabled": "i3_Change_extruderTwo" + }, + "j1_Change_fanSpeed": { + "label": "Change Fan Speed", + "description": "Select if Fan Speed has to be changed", + "type": "bool", + "default_value": false + }, + "j2_fanSpeed": { + "label": "Fan Speed", + "description": "New Fan Speed (0-100)", + "unit": "%", + "type": "int", + "default_value": 100, + "minimum_value": "0", + "minimum_value_warning": "0", + "maximum_value_warning": "100", + "enabled": "j1_Change_fanSpeed" + }, + "caz_change_retractfeedrate": { + "label": "Change Retract Feed Rate", + "description": "Changes the retraction feed rate during print (M207)", + "type": "bool", + "default_value": false + }, + "caz_retractfeedrate": { + "label": "Retract Feed Rate", + "description": "New Retract Feed Rate (units/s)", + "unit": "units/s", + "type": "float", + "default_value": 40, + "minimum_value": "0", + "minimum_value_warning": "0", + "maximum_value_warning": "100", + "enabled": "caz_change_retractfeedrate" + }, + "caz_change_retractlength": { + "label": "Change Retract Length", + "description": "Changes the retraction length during print (M207)", + "type": "bool", + "default_value": false + }, + "caz_retractlength": { + "label": "Retract Length", + "description": "New Retract Length (units)", + "unit": "units", + "type": "float", + "default_value": 6, + "minimum_value": "0", + "minimum_value_warning": "0", + "maximum_value_warning": "20", + "enabled": "caz_change_retractlength" + } + } + }""" - def getSettingDataString(self): - return """{ - "name": "ChangeAtZ """ + self.version + """(Experimental)", - "key": "ChangeAtZ", - "metadata": {}, - "version": 2, - "settings": { - "a_trigger": { - "label": "Trigger", - "description": "Trigger at height or at layer no.", - "type": "enum", - "options": { - "height": "Height", - "layer_no": "Layer No." - }, - "default_value": "height" - }, - "b_targetZ": { - "label": "Change Height", - "description": "Z height to change at", - "unit": "mm", - "type": "float", - "default_value": 5.0, - "minimum_value": "0", - "minimum_value_warning": "0.1", - "maximum_value_warning": "230", - "enabled": "a_trigger == 'height'" - }, - "b_targetL": { - "label": "Change Layer", - "description": "Layer no. to change at", - "unit": "", - "type": "int", - "default_value": 1, - "minimum_value": "-100", - "minimum_value_warning": "-1", - "enabled": "a_trigger == 'layer_no'" - }, - "c_behavior": { - "label": "Behavior", - "description": "Select behavior: Change value and keep it for the rest, Change value for single layer only", - "type": "enum", - "options": { - "keep_value": "Keep value", - "single_layer": "Single Layer" - }, - "default_value": "keep_value" - }, - "e1_Change_speed": { - "label": "Change Speed", - "description": "Select if total speed (print and travel) has to be changed", - "type": "bool", - "default_value": false - }, - "e2_speed": { - "label": "Speed", - "description": "New total speed (print and travel)", - "unit": "%", - "type": "int", - "default_value": 100, - "minimum_value": "1", - "minimum_value_warning": "10", - "maximum_value_warning": "200", - "enabled": "e1_Change_speed" - }, - "f1_Change_printspeed": { - "label": "Change Print Speed", - "description": "Select if print speed has to be changed", - "type": "bool", - "default_value": false - }, - "f2_printspeed": { - "label": "Print Speed", - "description": "New print speed", - "unit": "%", - "type": "int", - "default_value": 100, - "minimum_value": "1", - "minimum_value_warning": "10", - "maximum_value_warning": "200", - "enabled": "f1_Change_printspeed" - }, - "g1_Change_flowrate": { - "label": "Change Flow Rate", - "description": "Select if flow rate has to be changed", - "type": "bool", - "default_value": false - }, - "g2_flowrate": { - "label": "Flow Rate", - "description": "New Flow rate", - "unit": "%", - "type": "int", - "default_value": 100, - "minimum_value": "1", - "minimum_value_warning": "10", - "maximum_value_warning": "200", - "enabled": "g1_Change_flowrate" - }, - "g3_Change_flowrateOne": { - "label": "Change Flow Rate 1", - "description": "Select if first extruder flow rate has to be changed", - "type": "bool", - "default_value": false - }, - "g4_flowrateOne": { - "label": "Flow Rate One", - "description": "New Flow rate Extruder 1", - "unit": "%", - "type": "int", - "default_value": 100, - "minimum_value": "1", - "minimum_value_warning": "10", - "maximum_value_warning": "200", - "enabled": "g3_Change_flowrateOne" - }, - "g5_Change_flowrateTwo": { - "label": "Change Flow Rate 2", - "description": "Select if second extruder flow rate has to be changed", - "type": "bool", - "default_value": false - }, - "g6_flowrateTwo": { - "label": "Flow Rate two", - "description": "New Flow rate Extruder 2", - "unit": "%", - "type": "int", - "default_value": 100, - "minimum_value": "1", - "minimum_value_warning": "10", - "maximum_value_warning": "200", - "enabled": "g5_Change_flowrateTwo" - }, - "h1_Change_bedTemp": { - "label": "Change Bed Temp", - "description": "Select if Bed Temperature has to be changed", - "type": "bool", - "default_value": false - }, - "h2_bedTemp": { - "label": "Bed Temp", - "description": "New Bed Temperature", - "unit": "C", - "type": "float", - "default_value": 60, - "minimum_value": "0", - "minimum_value_warning": "30", - "maximum_value_warning": "120", - "enabled": "h1_Change_bedTemp" - }, - "i1_Change_extruderOne": { - "label": "Change Extruder 1 Temp", - "description": "Select if First Extruder Temperature has to be changed", - "type": "bool", - "default_value": false - }, - "i2_extruderOne": { - "label": "Extruder 1 Temp", - "description": "New First Extruder Temperature", - "unit": "C", - "type": "float", - "default_value": 190, - "minimum_value": "0", - "minimum_value_warning": "160", - "maximum_value_warning": "250", - "enabled": "i1_Change_extruderOne" - }, - "i3_Change_extruderTwo": { - "label": "Change Extruder 2 Temp", - "description": "Select if Second Extruder Temperature has to be changed", - "type": "bool", - "default_value": false - }, - "i4_extruderTwo": { - "label": "Extruder 2 Temp", - "description": "New Second Extruder Temperature", - "unit": "C", - "type": "float", - "default_value": 190, - "minimum_value": "0", - "minimum_value_warning": "160", - "maximum_value_warning": "250", - "enabled": "i3_Change_extruderTwo" - }, - "j1_Change_fanSpeed": { - "label": "Change Fan Speed", - "description": "Select if Fan Speed has to be changed", - "type": "bool", - "default_value": false - }, - "j2_fanSpeed": { - "label": "Fan Speed", - "description": "New Fan Speed (0-100)", - "unit": "%", - "type": "int", - "default_value": 100, - "minimum_value": "0", - "minimum_value_warning": "0", - "maximum_value_warning": "100", - "enabled": "j1_Change_fanSpeed" - }, - "caz_change_retractfeedrate": { - "label": "Change Retract Feed Rate", - "description": "Changes the retraction feed rate during print (M207)", - "type": "bool", - "default_value": false - }, - "caz_retractfeedrate": { - "label": "Retract Feed Rate", - "description": "New Retract Feed Rate (units/s)", - "unit": "units/s", - "type": "float", - "default_value": 40, - "minimum_value": "0", - "minimum_value_warning": "0", - "maximum_value_warning": "100", - "enabled": "caz_change_retractfeedrate" - }, - "caz_change_retractlength": { - "label": "Change Retract Length", - "description": "Changes the retraction length during print (M207)", - "type": "bool", - "default_value": false - }, - "caz_retractlength": { - "label": "Retract Length", - "description": "New Retract Length (units)", - "unit": "units", - "type": "float", - "default_value": 6, - "minimum_value": "0", - "minimum_value_warning": "0", - "maximum_value_warning": "20", - "enabled": "caz_change_retractlength" - } - } - }""" + def __init__(self): + super().__init__() - def __init__(self): - super().__init__() + def execute(self, data): - def execute(self, data): + caz_instance = ChangeAtZProcessor() - caz_instance = ChangeAtZProcessor() + caz_instance.TargetValues = {} - caz_instance.TargetValues = {} + # copy over our settings to our change z class + self.setIntSettingIfEnabled(caz_instance, "e1_Change_speed", "speed", "e2_speed") + self.setIntSettingIfEnabled(caz_instance, "f1_Change_printspeed", "printspeed", "f2_printspeed") + self.setIntSettingIfEnabled(caz_instance, "g1_Change_flowrate", "flowrate", "g2_flowrate") + self.setIntSettingIfEnabled(caz_instance, "g3_Change_flowrateOne", "flowrateOne", "g4_flowrateOne") + self.setIntSettingIfEnabled(caz_instance, "g5_Change_flowrateTwo", "flowrateTwo", "g6_flowrateTwo") + self.setFloatSettingIfEnabled(caz_instance, "h1_Change_bedTemp", "bedTemp", "h2_bedTemp") + self.setFloatSettingIfEnabled(caz_instance, "i1_Change_extruderOne", "extruderOne", "i2_extruderOne") + self.setFloatSettingIfEnabled(caz_instance, "i3_Change_extruderTwo", "extruderTwo", "i4_extruderTwo") + self.setIntSettingIfEnabled(caz_instance, "j1_Change_fanSpeed", "fanSpeed", "j2_fanSpeed") + self.setFloatSettingIfEnabled(caz_instance, "caz_change_retractfeedrate", "retractfeedrate", "caz_retractfeedrate") + self.setFloatSettingIfEnabled(caz_instance, "caz_change_retractlength", "retractlength", "caz_retractlength") - # copy over our settings to our change z class - self.setIntSettingIfEnabled(caz_instance, "e1_Change_speed", "speed", "e2_speed") - self.setIntSettingIfEnabled(caz_instance, "f1_Change_printspeed", "printspeed", "f2_printspeed") - self.setIntSettingIfEnabled(caz_instance, "g1_Change_flowrate", "flowrate", "g2_flowrate") - self.setIntSettingIfEnabled(caz_instance, "g3_Change_flowrateOne", "flowrateOne", "g4_flowrateOne") - self.setIntSettingIfEnabled(caz_instance, "g5_Change_flowrateTwo", "flowrateTwo", "g6_flowrateTwo") - self.setFloatSettingIfEnabled(caz_instance, "h1_Change_bedTemp", "bedTemp", "h2_bedTemp") - self.setFloatSettingIfEnabled(caz_instance, "i1_Change_extruderOne", "extruderOne", "i2_extruderOne") - self.setFloatSettingIfEnabled(caz_instance, "i3_Change_extruderTwo", "extruderTwo", "i4_extruderTwo") - self.setIntSettingIfEnabled(caz_instance, "j1_Change_fanSpeed", "fanSpeed", "j2_fanSpeed") - self.setFloatSettingIfEnabled(caz_instance, "caz_change_retractfeedrate", "retractfeedrate", "caz_retractfeedrate") - self.setFloatSettingIfEnabled(caz_instance, "caz_change_retractlength", "retractlength", "caz_retractlength") + # see if we're applying to a single layer or to all layers hence forth + caz_instance.IsApplyToSingleLayer = self.getSettingValueByKey("c_behavior") == "single_layer" - # see if we're applying to a single layer or to all layers hence forth - caz_instance.IsApplyToSingleLayer = self.getSettingValueByKey("c_behavior") == "single_layer" + # used for easy reference of layer or height targeting + caz_instance.IsTargetByLayer = self.getSettingValueByKey("a_trigger") == "layer_no" - # used for easy reference of layer or height targeting - caz_instance.IsTargetByLayer = self.getSettingValueByKey("a_trigger") == "layer_no" + # change our target based on what we're targeting + caz_instance.TargetLayer = self.getIntSettingByKey("b_targetL", None) + caz_instance.TargetZ = self.getFloatSettingByKey("b_targetZ", None) - # change our target based on what we're targeting - caz_instance.TargetLayer = self.getIntSettingByKey("b_targetL", None) - caz_instance.TargetZ = self.getFloatSettingByKey("b_targetZ", None) + # run our script + return caz_instance.execute(data) - # run our script - return caz_instance.execute(data) + # Sets the given TargetValue in the ChangeAtZ instance if the trigger is specified + def setIntSettingIfEnabled(self, caz_instance, trigger, target, setting): - # Sets the given TargetValue in the ChangeAtZ instance if the trigger is specified - def setIntSettingIfEnabled(self, caz_instance, trigger, target, setting): + # stop here if our trigger isn't enabled + if not self.getSettingValueByKey(trigger): + return - # stop here if our trigger isn't enabled - if not self.getSettingValueByKey(trigger): - return + # get our value from the settings + value = self.getIntSettingByKey(setting, None) - # get our value from the settings - value = self.getIntSettingByKey(setting, None) + # skip if there's no value or we can't interpret it + if value is None: + return - # skip if there's no value or we can't interpret it - if value is None: - return + # set our value in the target settings + caz_instance.TargetValues[target] = value - # set our value in the target settings - caz_instance.TargetValues[target] = value + # Sets the given TargetValue in the ChangeAtZ instance if the trigger is specified + def setFloatSettingIfEnabled(self, caz_instance, trigger, target, setting): - # Sets the given TargetValue in the ChangeAtZ instance if the trigger is specified - def setFloatSettingIfEnabled(self, caz_instance, trigger, target, setting): + # stop here if our trigger isn't enabled + if not self.getSettingValueByKey(trigger): + return - # stop here if our trigger isn't enabled - if not self.getSettingValueByKey(trigger): - return + # get our value from the settings + value = self.getFloatSettingByKey(setting, None) - # get our value from the settings - value = self.getFloatSettingByKey(setting, None) + # skip if there's no value or we can't interpret it + if value is None: + return - # skip if there's no value or we can't interpret it - if value is None: - return + # set our value in the target settings + caz_instance.TargetValues[target] = value - # set our value in the target settings - caz_instance.TargetValues[target] = value + # Returns the given settings value as an integer or the default if it cannot parse it + def getIntSettingByKey(self, key, default): - # Returns the given settings value as an integer or the default if it cannot parse it - def getIntSettingByKey(self, key, default): + # change our target based on what we're targeting + try: + return int(self.getSettingValueByKey(key)) + except: + return default - # change our target based on what we're targeting - try: - return int(self.getSettingValueByKey(key)) - except: - return default + # Returns the given settings value as an integer or the default if it cannot parse it + def getFloatSettingByKey(self, key, default): - # Returns the given settings value as an integer or the default if it cannot parse it - def getFloatSettingByKey(self, key, default): + # change our target based on what we're targeting + try: + return float(self.getSettingValueByKey(key)) + except: + return default - # change our target based on what we're targeting - try: - return float(self.getSettingValueByKey(key)) - except: - return default # The primary ChangeAtZ class that does all the gcode editing. This was broken out into an # independent class so it could be debugged using a standard IDE class ChangeAtZProcessor: + TargetValues = {} + IsApplyToSingleLayer = False + LastE = None + CurrentZ = None + CurrentLayer = None + IsTargetByLayer = True + TargetLayer = None + TargetZ = None + LayerHeight = None + RetractLength = 0 - TargetValues = {} - IsApplyToSingleLayer = False - LastE = None - CurrentZ = None - CurrentLayer = None - IsTargetByLayer = True - TargetLayer = None - TargetZ = None - LayerHeight = None - RetractLength = 0 + # boots up the class with defaults + def __init__(self): + self.reset() - # boots up the class with defaults - def __init__(self): - self.reset() + # Modifies the given GCODE and injects the commands at the various targets + def execute(self, data): - # Modifies the given GCODE and injects the commands at the various targets - def execute(self, data): + # indicates if we should inject our defaults or not + inject_defaults = True - # indicates if we should inject our defaults or not - inject_defaults = True + # our layer cursor + index = 0 - # our layer cursor - index = 0 + for active_layer in data: - for active_layer in data: + # will hold our updated gcode + modified_gcode = "" - # will hold our updated gcode - modified_gcode = "" + # mark all the defaults for deletion + active_layer = self.markDefaultsForDeletion(active_layer) - # mark all the defaults for deletion - active_layer = self.markDefaultsForDeletion(active_layer) + # break apart the layer into commands + lines = active_layer.split("\n") - # break apart the layer into commands - lines = active_layer.split("\n") + # evaluate each command individually + for line in lines: - # evaluate each command individually - for line in lines: + # skip empty lines + if line.strip() == "": + continue - # skip empty lines - if line.strip() == "": - continue + # update our layer number if applicable + self.processLayerNumber(line) - # update our layer number if applicable - self.processLayerNumber(line) + # update our layer height if applicable + self.processLayerHeight(line) - # update our layer height if applicable - self.processLayerHeight(line) + # skip this line if we're not there yet + if not self.isTargetLayerOrHeight(): - # skip this line if we're not there yet - if not self.isTargetLayerOrHeight(): + # read any settings we might need + self.processSetting(line) - # read any settings we might need - self.processSetting(line) + # if we haven't hit our target yet, leave the defaults as is (unmark them for deletion) + if "[CAZD:DELETE:" in line: + line = line.replace("[CAZD:DELETE:", "[CAZD:") - # if we haven't hit our target yet, leave the defaults as is (unmark them for deletion) - if "[CAZD:DELETE:" in line: - line = line.replace("[CAZD:DELETE:", "[CAZD:") + # set our line + modified_gcode += line + "\n" - # set our line - modified_gcode += line + "\n" + # skip to the next line + continue - # skip to the next line - continue + # inject our defaults before linear motion commands + if inject_defaults and ("G1" in line or "G0" in line): - # inject our defaults before linear motion commands - if inject_defaults and ("G1" in line or "G0" in line): + # inject the defaults + modified_gcode += self.getTargetDefaults() + "\n" - # inject the defaults - modified_gcode += self.getTargetDefaults() + "\n" + # mark that we've injected the defaults + inject_defaults = False - # mark that we've injected the defaults - inject_defaults = False + # append to our modified layer + modified_gcode += self.processLinearMove(line) + "\n" - # append to our modified layer - modified_gcode += self.processLinearMove(line) + "\n" + # inject our defaults after the layer indicator + if inject_defaults and ";LAYER:" in line: - # inject our defaults after the layer indicator - if inject_defaults and ";LAYER:" in line: + # inject the defaults + modified_gcode += self.getTargetDefaults() + "\n" - # inject the defaults - modified_gcode += self.getTargetDefaults() + "\n" + # mark that we've injected the defaults + inject_defaults = False - # mark that we've injected the defaults - inject_defaults = False + # remove any marked defaults + modified_gcode = self.removeMarkedTargetDefaults(modified_gcode) - # remove any marked defaults - modified_gcode = self.removeMarkedTargetDefaults(modified_gcode) + # append our modified line + data[index] = modified_gcode - # append our modified line - data[index] = modified_gcode + index += 1 + return data - index += 1 - return data + # Converts the command parameter to a float or returns the default + @staticmethod + def getFloatValue(line, key, default=None): - # Converts the command parameter to a float or returns the default - @staticmethod - def getFloatValue(line, key, default=None): + # get the value from the command + value = ChangeAtZProcessor.getValue(line, key, default) - # get the value from the command - value = ChangeAtZProcessor.getValue(line, key, default) + # stop here if it's the default + if value == default: + return value - # stop here if it's the default - if value == default: - return value + try: + return float(value) + except: + return default - try: - return float(value) - except: - return default + # Converts the command parameter to a int or returns the default + @staticmethod + def getIntValue(line, key, default=None): - # Converts the command parameter to a int or returns the default - @staticmethod - def getIntValue(line, key, default=None): + # get the value from the command + value = ChangeAtZProcessor.getValue(line, key, default) - # get the value from the command - value = ChangeAtZProcessor.getValue(line, key, default) + # stop here if it's the default + if value == default: + return value - # stop here if it's the default - if value == default: - return value + try: + return int(value) + except: + return default - try: - return int(value) - except: - return default + # Handy function for reading a linear move command + def getLinearMoveParams(self, line): - # Handy function for reading a linear move command - def getLinearMoveParams(self, line): + # get our motion parameters + feed_rate = self.getFloatValue(line, "F", None) + x_coord = self.getFloatValue(line, "X", None) + y_coord = self.getFloatValue(line, "Y", None) + z_coord = self.getFloatValue(line, "Z", None) + extrude_length = self.getFloatValue(line, "E", None) - # get our motion parameters - feed_rate = self.getFloatValue(line, "F", None) - x_coord = self.getFloatValue(line, "X", None) - y_coord = self.getFloatValue(line, "Y", None) - z_coord = self.getFloatValue(line, "Z", None) - extrude_length = self.getFloatValue(line, "E", None) + return extrude_length, feed_rate, x_coord, y_coord, z_coord - return extrude_length, feed_rate, x_coord, y_coord, z_coord + # Returns the unmodified GCODE line from previous ChangeZ edits + @staticmethod + def getOriginalLine(line): - # Returns the unmodified GCODE line from previous ChangeZ edits - @staticmethod - def getOriginalLine(line): + # get the change at z original (cazo) details + original_line = re.search(r"\[CAZO:(.*?):CAZO\]", line) - # get the change at z original (cazo) details - original_line = re.search(r"\[CAZO:(.*?):CAZO\]", line) + # if we didn't get a hit, this is the original line + if original_line is None: + return line - # if we didn't get a hit, this is the original line - if original_line is None: - return line + return original_line.group(1) - return original_line.group(1) + # Builds the layer defaults based on the settings and returns the relevant GCODE lines + def getTargetDefaults(self): - # Builds the layer defaults based on the settings and returns the relevant GCODE lines - def getTargetDefaults(self): + # will hold all the default settings for the target layer + defaults = [] - # will hold all the default settings for the target layer - defaults = [] + # used to trim other defaults + defaults.append(";[CAZD:") - # used to trim other defaults - defaults.append(";[CAZD:") + # looking for wait for bed temp + if "bedTemp" in self.TargetValues: + defaults.append("M190 S" + str(self.TargetValues["bedTemp"])) - # looking for wait for bed temp - if "bedTemp" in self.TargetValues: - defaults.append("M190 S" + str(self.TargetValues["bedTemp"])) + # set our extruder one temp (if specified) + if "extruderOne" in self.TargetValues: + defaults.append("M109 S" + str(self.TargetValues["extruderOne"]) + " T0") - # set our extruder one temp (if specified) - if "extruderOne" in self.TargetValues: - defaults.append("M109 S" + str(self.TargetValues["extruderOne"]) + " T0") + # set our extruder two temp (if specified) + if "extruderTwo" in self.TargetValues: + defaults.append("M109 S" + str(self.TargetValues["extruderTwo"]) + " T1") - # set our extruder two temp (if specified) - if "extruderTwo" in self.TargetValues: - defaults.append("M109 S" + str(self.TargetValues["extruderTwo"]) + " T1") + # set our fan speed + if "fanSpeed" in self.TargetValues: - # set our fan speed - if "fanSpeed" in self.TargetValues: + # convert our fan speed percentage to PWM + fan_speed = int((float(self.TargetValues["fanSpeed"]) / 100.0) * 255) - # convert our fan speed percentage to PWM - fan_speed = int((float(self.TargetValues["fanSpeed"]) / 100.0) * 255) + # add our fan speed to the defaults + defaults.append("M106 S" + str(fan_speed)) - # add our fan speed to the defaults - defaults.append("M106 S" + str(fan_speed)) + # set global flow rate + if "flowrate" in self.TargetValues: + defaults.append("M221 S" + str(self.TargetValues["flowrate"])) - # set global flow rate - if "flowrate" in self.TargetValues: - defaults.append("M221 S" + str(self.TargetValues["flowrate"])) + # set extruder 0 flow rate + if "flowrateOne" in self.TargetValues: + defaults.append("M221 S" + str(self.TargetValues["flowrateOne"]) + " T0") - # set extruder 0 flow rate - if "flowrateOne" in self.TargetValues: - defaults.append("M221 S" + str(self.TargetValues["flowrateOne"]) + " T0") + # set second extruder flow rate + if "flowrateTwo" in self.TargetValues: + defaults.append("M221 S" + str(self.TargetValues["flowrateTwo"]) + " T1") - # set second extruder flow rate - if "flowrateTwo" in self.TargetValues: - defaults.append("M221 S" + str(self.TargetValues["flowrateTwo"]) + " T1") + # set feedrate percentage + if "speed" in self.TargetValues: + defaults.append("M220 S" + str(self.TargetValues["speed"]) + " T1") - # set feedrate percentage - if "speed" in self.TargetValues: - defaults.append("M220 S" + str(self.TargetValues["speed"]) + " T1") + # set print rate percentage + if "printspeed" in self.TargetValues: + defaults.append(";PRINTSPEED " + str(self.TargetValues["printspeed"]) + "") - # set print rate percentage - if "printspeed" in self.TargetValues: - defaults.append(";PRINTSPEED " + str(self.TargetValues["printspeed"]) + "") + # set retract rate + if "retractfeedrate" in self.TargetValues: + defaults.append(";RETRACTFEEDRATE " + str(self.TargetValues["retractfeedrate"]) + "") - # set retract rate - if "retractfeedrate" in self.TargetValues: - defaults.append(";RETRACTFEEDRATE " + str(self.TargetValues["retractfeedrate"]) + "") + # set retract length + if "retractlength" in self.TargetValues: + defaults.append(";RETRACTLENGTH " + str(self.TargetValues["retractlength"]) + "") - # set retract length - if "retractlength" in self.TargetValues: - defaults.append(";RETRACTLENGTH " + str(self.TargetValues["retractlength"]) + "") + # used to trim other defaults + defaults.append(";:CAZD]") - # used to trim other defaults - defaults.append(";:CAZD]") + # if there are no defaults, stop here + if len(defaults) == 2: + return "" - # if there are no defaults, stop here - if len(defaults) == 2: - return "" + # return our default block for this layer + return "\n".join(defaults) - # return our default block for this layer - return "\n".join(defaults) + # Allows retrieving values from the given GCODE line + @staticmethod + def getValue(line, key, default=None): - # Allows retrieving values from the given GCODE line - @staticmethod - def getValue(line, key, default=None): + if key not in line or (";" in line and line.find(key) > line.find(";") and ";ChangeAtZ" not in key and ";LAYER:" not in key): + return default - if not key in line or (";" in line and line.find(key) > line.find(";") and not ";ChangeAtZ" in key and not ";LAYER:" in key): - return default + sub_part = line[line.find(key) + len(key):] # allows for string lengths larger than 1 + if ";ChangeAtZ" in key: + m = re.search("^[0-4]", sub_part) + elif ";LAYER:" in key: + m = re.search("^[+-]?[0-9]*", sub_part) + else: + # the minus at the beginning allows for negative values, e.g. for delta printers + m = re.search(r"^[-]?[0-9]*\.?[0-9]*", sub_part) + if m is None: + return default + try: + return float(m.group(0)) + except: + return default - sub_part = line[line.find(key) + len(key):] # allows for string lengths larger than 1 - if ";ChangeAtZ" in key: - m = re.search("^[0-4]", sub_part) - elif ";LAYER:" in key: - m = re.search("^[+-]?[0-9]*", sub_part) - else: - # the minus at the beginning allows for negative values, e.g. for delta printers - m = re.search(r"^[-]?[0-9]*\.?[0-9]*", sub_part) - if m is None: - return default - try: - return float(m.group(0)) - except: - return default + # Determines if the current line is at or below the target required to start modifying + def isTargetLayerOrHeight(self): - # Determines if the current line is at or below the target required to start modifying - def isTargetLayerOrHeight(self): + # target selected by layer no. + if self.IsTargetByLayer: - # target selected by layer no. - if self.IsTargetByLayer: + # if we don't have a current layer, we're not there yet + if self.CurrentLayer is None: + return False - # if we don't have a current layer, we're not there yet - if self.CurrentLayer is None: - return False + # if we're applying to a single layer, stop if our layer is not identical + if self.IsApplyToSingleLayer: + return self.CurrentLayer == self.TargetLayer + else: + return self.CurrentLayer >= self.TargetLayer - # if we're applying to a single layer, stop if our layer is not identical - if self.IsApplyToSingleLayer: - return self.CurrentLayer == self.TargetLayer - else: - return self.CurrentLayer >= self.TargetLayer + else: - else: + # if we don't have a current Z, we're not there yet + if self.CurrentZ is None: + return False - # if we don't have a current Z, we're not there yet - if self.CurrentZ is None: - return False + # if we're applying to a single layer, stop if our Z is not identical + if self.IsApplyToSingleLayer: + return self.CurrentZ == self.TargetZ + else: + return self.CurrentZ >= self.TargetZ - # if we're applying to a single layer, stop if our Z is not identical - if self.IsApplyToSingleLayer: - return self.CurrentZ == self.TargetZ - else: - return self.CurrentZ >= self.TargetZ + # Marks any current ChangeZ layer defaults in the layer for deletion + @staticmethod + def markDefaultsForDeletion(layer): + return re.sub(r";\[CAZD:", ";[CAZD:DELETE:", layer) - # Marks any current ChangeZ layer defaults in the layer for deletion - @staticmethod - def markDefaultsForDeletion(layer): - return re.sub(r";\[CAZD:", ";[CAZD:DELETE:", layer) + # Grabs the current height + def processLayerHeight(self, line): - # Grabs the current height - def processLayerHeight(self, line): + # stop here if we haven't entered a layer yet + if self.CurrentLayer is None: + return - # stop here if we haven't entered a layer yet - if self.CurrentLayer is None: - return + # expose the main command + line_no_comments = self.stripComments(line) - # expose the main command - line_no_comments = self.stripComments(line) + # stop here if this isn't a linear move command + if not ("G1" in line_no_comments or "G0" in line_no_comments): + return - # stop here if this isn't a linear move command - if not ("G1" in line_no_comments or "G0" in line_no_comments): - return + # stop here if we don't have a Z value defined, we can't get the height from this command + if "Z" not in line_no_comments: + return - # stop here if we don't have a Z value defined, we can't get the height from this command - if "Z" not in line_no_comments: - return + # get our value from the command + current_z = self.getFloatValue(line_no_comments, "Z", None) - # get our value from the command - current_z = self.getFloatValue(line_no_comments, "Z", None) + # stop if there's no change + if current_z == self.CurrentZ: + return - # stop if there's no change - if current_z == self.CurrentZ: - return + # set our current Z value + self.CurrentZ = current_z - # set our current Z value - self.CurrentZ = current_z + # if we don't have a layer height yet, set it based on the current Z value + if self.LayerHeight is None: + self.LayerHeight = self.CurrentZ - # if we don't have a layer height yet, set it based on the current Z value - if self.LayerHeight is None: - self.LayerHeight = self.CurrentZ + # Grabs the current layer number + def processLayerNumber(self, line): - # Grabs the current layer number - def processLayerNumber(self, line): + # if this isn't a layer comment, stop here, nothing to update + if ";LAYER:" not in line: + return - # if this isn't a layer comment, stop here, nothing to update - if ";LAYER:" not in line: - return + # get our current layer number + current_layer = self.getIntValue(line, ";LAYER:", None) - # get our current layer number - current_layer = self.getIntValue(line, ";LAYER:", None) + # this should never happen, but if our layer number hasn't changed, stop here + if current_layer == self.CurrentLayer: + return - # this should never happen, but if our layer number hasn't changed, stop here - if current_layer == self.CurrentLayer: - return + # update our current layer + self.CurrentLayer = current_layer - # update our current layer - self.CurrentLayer = current_layer + # Handles any linear moves in the current line + def processLinearMove(self, line): - # Handles any linear moves in the current line - def processLinearMove(self, line): + # if it's not a linear motion command we're not interested + if not ("G1" in line or "G0" in line): + return line - # if it's not a linear motion command we're not interested - if not ("G1" in line or "G0" in line): - return line + # always get our original line, otherwise the effect will be cumulative + line = self.getOriginalLine(line) - # always get our original line, otherwise the effect will be cumulative - line = self.getOriginalLine(line) + # get the details from our linear move command + extrude_length, feed_rate, x_coord, y_coord, z_coord = self.getLinearMoveParams(line) - # get the details from our linear move command - extrude_length, feed_rate, x_coord, y_coord, z_coord = self.getLinearMoveParams(line) + # set our new line to our old line + new_line = line - # set our new line to our old line - new_line = line + # handle retract length + new_line = self.processRetractLength(extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord) - # handle retract length - new_line = self.processRetractLength(extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord) + # handle retract feed rate + new_line = self.processRetractFeedRate(extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord) - # handle retract feed rate - new_line = self.processRetractFeedRate(extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord) + # handle print speed adjustments + new_line = self.processPrintSpeed(feed_rate, new_line) - # handle print speed adjustments - new_line = self.processPrintSpeed(feed_rate, new_line) + # set our current extrude position + self.LastE = extrude_length if extrude_length is not None else self.LastE - # set our current extrude position - self.LastE = extrude_length if extrude_length is not None else self.LastE + # if no changes have been made, stop here + if new_line == line: + return line - # if no changes have been made, stop here - if new_line == line: - return line + # return our updated command + return self.setOriginalLine(new_line, line) - # return our updated command - return self.setOriginalLine(new_line, line) + # Handles any changes to print speed for the given linear motion command + def processPrintSpeed(self, feed_rate, new_line): - # Handles any changes to print speed for the given linear motion command - def processPrintSpeed(self, feed_rate, new_line): + # if we're not setting print speed or we don't have a feed rate, stop here + if "printspeed" not in self.TargetValues or feed_rate is None: + return new_line - # if we're not setting print speed or we don't have a feed rate, stop here - if "printspeed" not in self.TargetValues or feed_rate is None: - return new_line + # get our requested print speed + print_speed = int(self.TargetValues["printspeed"]) - # get our requested print speed - print_speed = int(self.TargetValues["printspeed"]) + # if they requested no change to print speed (ie: 100%), stop here + if print_speed == 100: + return new_line - # if they requested no change to print speed (ie: 100%), stop here - if print_speed == 100: - return new_line + # get our feed rate from the command + feed_rate = float(self.getValue(new_line, "F")) * (float(print_speed) / 100.0) - # get our feed rate from the command - feed_rate = float(self.getValue(new_line, "F")) * (float(print_speed) / 100.0) + # change our feed rate + return self.replaceParameter(new_line, "F", feed_rate) - # change our feed rate - return self.replaceParameter(new_line, "F", feed_rate) + # Handles any changes to retraction length for the given linear motion command + def processRetractLength(self, extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord): - # Handles any changes to retraction length for the given linear motion command - def processRetractLength(self, extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord): + # if we don't have a retract length in the file we can't add one + if self.RetractLength == 0: + return new_line - # if we don't have a retract length in the file we can't add one - if self.RetractLength == 0: - return new_line + # if we're not changing retraction length, stop here + if "retractlength" not in self.TargetValues: + return new_line - # if we're not changing retraction length, stop here - if "retractlength" not in self.TargetValues: - return new_line + # retractions are only F (feed rate) and E (extrude), at least in cura + if x_coord is not None or y_coord is not None or z_coord is not None: + return new_line - # retractions are only F (feed rate) and E (extrude), at least in cura - if x_coord is not None or y_coord is not None or z_coord is not None: - return new_line + # since retractions require both F and E, and we don't have either, we can't process + if feed_rate is None or extrude_length is None: + return new_line - # since retractions require both F and E, and we don't have either, we can't process - if feed_rate is None or extrude_length is None: - return new_line + # stop here if we don't know our last extrude value + if self.LastE is None: + return new_line - # stop here if we don't know our last extrude value - if self.LastE is None: - return new_line + # if there's no change in extrude we have nothing to change + if self.LastE == extrude_length: + return new_line - # if there's no change in extrude we have nothing to change - if self.LastE == extrude_length: - return new_line + # if our last extrude was lower than our current, we're restoring, so skip + if self.LastE < extrude_length: + return new_line - # if our last extrude was lower than our current, we're restoring, so skip - if self.LastE < extrude_length: - return new_line + # get our desired retract length + retract_length = float(self.TargetValues["retractlength"]) - # get our desired retract length - retract_length = float(self.TargetValues["retractlength"]) + # subtract the difference between the default and the desired + extrude_length -= (retract_length - self.RetractLength) - # subtract the difference between the default and the desired - extrude_length -= (retract_length - self.RetractLength) + # replace our extrude amount + return self.replaceParameter(new_line, "E", extrude_length) - # replace our extrude amount - return self.replaceParameter(new_line, "E", extrude_length) + # Used for picking out the retract length set by Cura + def processRetractLengthSetting(self, line): - # Used for picking out the retract length set by Cura - def processRetractLengthSetting(self, line): + # if it's not a linear move, we don't care + if "G0" not in line and "G1" not in line: + return - # if it's not a linear move, we don't care - if "G0" not in line and "G1" not in line: - return + # get the details from our linear move command + extrude_length, feed_rate, x_coord, y_coord, z_coord = self.getLinearMoveParams(line) - # get the details from our linear move command - extrude_length, feed_rate, x_coord, y_coord, z_coord = self.getLinearMoveParams(line) + # the command we're looking for only has extrude and feed rate + if x_coord is not None or y_coord is not None or z_coord is not None: + return - # the command we're looking for only has extrude and feed rate - if x_coord is not None or y_coord is not None or z_coord is not None: - return + # if either extrude or feed is missing we're likely looking at the wrong command + if extrude_length is None or feed_rate is None: + return - # if either extrude or feed is missing we're likely looking at the wrong command - if extrude_length is None or feed_rate is None: - return + # cura stores the retract length as a negative E just before it starts printing + extrude_length = extrude_length * -1 - # cura stores the retract length as a negative E just before it starts printing - extrude_length = extrude_length * -1 + # if it's a negative extrude after being inverted, it's not our retract length + if extrude_length < 0: + return - # if it's a negative extrude after being inverted, it's not our retract length - if extrude_length < 0: - return + # what ever the last negative retract length is it wins + self.RetractLength = extrude_length - # what ever the last negative retract length is it wins - self.RetractLength = extrude_length + # Handles any changes to retraction feed rate for the given linear motion command + def processRetractFeedRate(self, extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord): - # Handles any changes to retraction feed rate for the given linear motion command - def processRetractFeedRate(self, extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord): + # if we're not changing retraction length, stop here + if "retractfeedrate" not in self.TargetValues: + return new_line - # if we're not changing retraction length, stop here - if "retractfeedrate" not in self.TargetValues: - return new_line + # retractions are only F (feed rate) and E (extrude), at least in cura + if x_coord is not None or y_coord is not None or z_coord is not None: + return new_line - # retractions are only F (feed rate) and E (extrude), at least in cura - if x_coord is not None or y_coord is not None or z_coord is not None: - return new_line + # since retractions require both F and E, and we don't have either, we can't process + if feed_rate is None or extrude_length is None: + return new_line - # since retractions require both F and E, and we don't have either, we can't process - if feed_rate is None or extrude_length is None: - return new_line + # get our desired retract feed rate + retract_feed_rate = float(self.TargetValues["retractfeedrate"]) - # get our desired retract feed rate - retract_feed_rate = float(self.TargetValues["retractfeedrate"]) + # convert to units/min + retract_feed_rate *= 60 - # convert to units/min - retract_feed_rate *= 60 + # replace our feed rate + return self.replaceParameter(new_line, "F", retract_feed_rate) - # replace our feed rate - return self.replaceParameter(new_line, "F", retract_feed_rate) + # Used for finding settings in the print file before we process anything else + def processSetting(self, line): - # Used for finding settings in the print file before we process anything else - def processSetting(self, line): + # if we're in layers already we're out of settings + if self.CurrentLayer is not None: + return - # if we're in layers already we're out of settings - if self.CurrentLayer is not None: - return + # check our retract length + self.processRetractLengthSetting(line) - # check our retract length - self.processRetractLengthSetting(line) + # Removes all the ChangeZ layer defaults from the given layer + @staticmethod + def removeMarkedTargetDefaults(layer): + return re.sub(r";\[CAZD:DELETE:[\s\S]+?:CAZD\](\n|$)", "", layer) - # Removes all the ChangeZ layer defaults from the given layer - @staticmethod - def removeMarkedTargetDefaults(layer): - return re.sub(r";\[CAZD:DELETE:[\s\S]+?:CAZD\](\n|$)", "", layer) + # Easy function for replacing any GCODE parameter variable in a given GCODE command + @staticmethod + def replaceParameter(line, key, value): + return re.sub(r"(^|\s)" + key + r"[\d\.]+(\s|$)", r"\1" + key + str(value) + r"\2", line) - # Easy function for replacing any GCODE parameter variable in a given GCODE command - @staticmethod - def replaceParameter(line, key, value): - return re.sub(r"(^|\s)" + key + r"[\d\.]+(\s|$)", r"\1" + key + str(value) + r"\2", line) + # Resets the class contents to defaults + def reset(self): - # Resets the class contents to defaults - def reset(self): + self.TargetValues = {} + self.IsApplyToSingleLayer = False + self.LastE = None + self.CurrentZ = None + self.CurrentLayer = None + self.IsTargetByLayer = True + self.TargetLayer = None + self.TargetZ = None + self.LayerHeight = None + self.RetractLength = 0 - self.TargetValues = {} - self.IsApplyToSingleLayer = False - self.LastE = None - self.CurrentZ = None - self.CurrentLayer = None - self.IsTargetByLayer = True - self.TargetLayer = None - self.TargetZ = None - self.LayerHeight = None - self.RetractLength = 0 + # Sets the original GCODE line in a given GCODE command + @staticmethod + def setOriginalLine(line, original): + return line + ";[CAZO:" + original + ":CAZO]" - # Sets the original GCODE line in a given GCODE command - @staticmethod - def setOriginalLine(line, original): - return line + ";[CAZO:" + original + ":CAZO]" - - # Removes the gcode comments from a given gcode command - @staticmethod - def stripComments(line): - return re.sub(r";.*?$", "", line).strip() + # Removes the gcode comments from a given gcode command + @staticmethod + def stripComments(line): + return re.sub(r";.*?$", "", line).strip() def debug(): - # get our input file - file = r"PATH_TO_SOME_GCODE.gcode" + # get our input file + file = r"PATH_TO_SOME_GCODE.gcode" - # read the whole thing - f = open(file, "r") - gcode = f.read() - f.close() + # read the whole thing + f = open(file, "r") + gcode = f.read() + f.close() - # boot up change - caz_instance = ChangeAtZProcessor() - caz_instance.IsTargetByLayer = False - caz_instance.TargetZ = 5 - caz_instance.TargetValues["printspeed"] = 100 - caz_instance.TargetValues["retractfeedrate"] = 60 + # boot up change + caz_instance = ChangeAtZProcessor() + caz_instance.IsTargetByLayer = False + caz_instance.TargetZ = 5 + caz_instance.TargetValues["printspeed"] = 100 + caz_instance.TargetValues["retractfeedrate"] = 60 - # process gcode - gcode = debug_iteration(gcode, caz_instance) + # process gcode + gcode = debug_iteration(gcode, caz_instance) - # write our file - debug_write(gcode, file + ".1.modified") + # write our file + debug_write(gcode, file + ".1.modified") - caz_instance.reset() - caz_instance.IsTargetByLayer = False - caz_instance.TargetZ = 10.5 - caz_instance.TargetValues["bedTemp"] = 75.111 - caz_instance.TargetValues["printspeed"] = 150 - caz_instance.TargetValues["retractfeedrate"] = 40.555 - caz_instance.TargetValues["retractlength"] = 10.3333 + caz_instance.reset() + caz_instance.IsTargetByLayer = False + caz_instance.TargetZ = 10.5 + caz_instance.TargetValues["bedTemp"] = 75.111 + caz_instance.TargetValues["printspeed"] = 150 + caz_instance.TargetValues["retractfeedrate"] = 40.555 + caz_instance.TargetValues["retractlength"] = 10.3333 - # and again - gcode = debug_iteration(gcode, caz_instance) + # and again + gcode = debug_iteration(gcode, caz_instance) - # write our file - debug_write(gcode, file + ".2.modified") + # write our file + debug_write(gcode, file + ".2.modified") - caz_instance.reset() - caz_instance.IsTargetByLayer = False - caz_instance.TargetZ = 15 - caz_instance.TargetValues["bedTemp"] = 80 - caz_instance.TargetValues["printspeed"] = 100 - caz_instance.TargetValues["retractfeedrate"] = 10 - caz_instance.TargetValues["retractlength"] = 0 - caz_instance.TargetValues["extruderOne"] = 100 - caz_instance.TargetValues["extruderTwo"] = 200 + caz_instance.reset() + caz_instance.IsTargetByLayer = False + caz_instance.TargetZ = 15 + caz_instance.TargetValues["bedTemp"] = 80 + caz_instance.TargetValues["printspeed"] = 100 + caz_instance.TargetValues["retractfeedrate"] = 10 + caz_instance.TargetValues["retractlength"] = 0 + caz_instance.TargetValues["extruderOne"] = 100 + caz_instance.TargetValues["extruderTwo"] = 200 - # and again - gcode = debug_iteration(gcode, caz_instance) + # and again + gcode = debug_iteration(gcode, caz_instance) - # write our file - debug_write(gcode, file + ".3.modified") + # write our file + debug_write(gcode, file + ".3.modified") def debug_write(gcode, file): - # write our file - f = open(file, "w") - f.write(gcode) - f.close() + # write our file + f = open(file, "w") + f.write(gcode) + f.close() def debug_iteration(gcode, caz_instance): - index = 0 + index = 0 - # break apart the GCODE like cura - layers = re.split(r"^;LAYER:\d+\n", gcode) + # break apart the GCODE like cura + layers = re.split(r"^;LAYER:\d+\n", gcode) - # add the layer numbers back - for layer in layers: + # add the layer numbers back + for layer in layers: - # if this is the first layer, skip it, basically - if index == 0: - # leave our first layer as is - layers[index] = layer + # if this is the first layer, skip it, basically + if index == 0: + # leave our first layer as is + layers[index] = layer - # move the cursor - index += 1 + # move the cursor + index += 1 - # skip to the next layer - continue + # skip to the next layer + continue - layers[index] = ";LAYER:" + str(index - 1) + ";\n" + layer + layers[index] = ";LAYER:" + str(index - 1) + ";\n" + layer - return "".join(caz_instance.execute(layers)) + return "".join(caz_instance.execute(layers)) # debug() From 943d04a734d5bb49ab0e20941a492575d4fefc9e Mon Sep 17 00:00:00 2001 From: novamxd Date: Mon, 17 Feb 2020 22:58:39 -0600 Subject: [PATCH 05/37] * Revamped ChangeAtZ Added additional option for disabling all retract options Added more comments. Added support for firmware based retractions. Added support for outputting changes to LCD (untested). Added type hints to most functions and variables. Created GCodeCommand class for better detection of G1 vs G10 or G11 commands, and accessing arguments. Fixed issue of properly restoring previous values in single layer option. Improved wording of Single Layer vs Keep Layer to better reflect what was happening. Moved most GCode methods to GCodeCommand class. --- .../PostProcessingPlugin/scripts/ChangeAtZ.py | 1025 ++++++++++++----- 1 file changed, 764 insertions(+), 261 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py index f37676a28c..513ae13550 100644 --- a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py +++ b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py @@ -36,6 +36,10 @@ # V5.2.0: Wes Hanney. Added support for changing Retract Length and Speed. Removed layer spread option. Fixed issue of cumulative ChangeZ # mods so they can now properly be stacked on top of each other. Applied code refactoring to clean up various coding styles. Added comments. # Broke up functions for clarity. Split up class so it can be debugged outside of Cura. +# V5.2.1: Wes Hanney. Added support for firmware based retractions. Fixed issue of properly restoring previous values in single layer option. +# Added support for outputting changes to LCD (untested). Added type hints to most functions and variables. Added more comments. Created GCodeCommand +# class for better detection of G1 vs G10 or G11 commands, and accessing arguments. Moved most GCode methods to GCodeCommand class. Improved wording +# of Single Layer vs Keep Layer to better reflect what was happening. # Uses - # M220 S - set speed factor override percentage @@ -44,15 +48,17 @@ # M104 S T<0-#toolheads> - set extruder to target temperature # M140 S - set bed target temperature # M106 S - set fan speed to target speed -# M605/606 to save and recall material settings on the UM2 +# M207 S F - set the retract length or feed rate +# M117 - output the current changes +from typing import List, Optional, Dict from ..Script import Script import re # this was broken up into a separate class so the main ChangeZ script could be debugged outside of Cura class ChangeAtZ(Script): - version = "5.2.0" + version = "5.2.1" def getSettingDataString(self): return """{ @@ -93,15 +99,21 @@ class ChangeAtZ(Script): "enabled": "a_trigger == 'layer_no'" }, "c_behavior": { - "label": "Behavior", - "description": "Select behavior: Change value and keep it for the rest, Change value for single layer only", + "label": "Apply To", + "description": "Target Layer + Subsequent Layers is good for testing changes between ranges of layers, ex: Layer 0 to 10 or 0mm to 5mm. Single layer is good for testing changes at a single layer, ex: at Layer 10 or 5mm only.", "type": "enum", "options": { - "keep_value": "Keep value", - "single_layer": "Single Layer" + "keep_value": "Target Layer + Subsequent Layers", + "single_layer": "Target Layer Only" }, "default_value": "keep_value" - }, + }, + "caz_output_to_display": { + "label": "Output to Display", + "description": "Displays the current changes to the LCD", + "type": "bool", + "default_value": false + }, "e1_Change_speed": { "label": "Change Speed", "description": "Select if total speed (print and travel) has to be changed", @@ -255,16 +267,34 @@ class ChangeAtZ(Script): "maximum_value_warning": "100", "enabled": "j1_Change_fanSpeed" }, - "caz_change_retractfeedrate": { - "label": "Change Retract Feed Rate", - "description": "Changes the retraction feed rate during print (M207)", + "caz_change_retract": { + "label": "Change Retraction", + "description": "Indicates you would like to modify retraction properties.", "type": "bool", "default_value": false - }, + }, + "caz_retractstyle": { + "label": "Retract Style", + "description": "Specify if you're using firmware retraction or linear move based retractions. Check your printer settings to see which you're using.", + "type": "enum", + "options": { + "linear": "Linear Move", + "firmware": "Firmware" + }, + "default_value": "linear", + "enabled": "caz_change_retract" + }, + "caz_change_retractfeedrate": { + "label": "Change Retract Feed Rate", + "description": "Changes the retraction feed rate during print", + "type": "bool", + "default_value": false, + "enabled": "caz_change_retract" + }, "caz_retractfeedrate": { "label": "Retract Feed Rate", - "description": "New Retract Feed Rate (units/s)", - "unit": "units/s", + "description": "New Retract Feed Rate (mm/s)", + "unit": "mm/s", "type": "float", "default_value": 40, "minimum_value": "0", @@ -274,14 +304,15 @@ class ChangeAtZ(Script): }, "caz_change_retractlength": { "label": "Change Retract Length", - "description": "Changes the retraction length during print (M207)", + "description": "Changes the retraction length during print", "type": "bool", - "default_value": false + "default_value": false, + "enabled": "caz_change_retract" }, "caz_retractlength": { "label": "Retract Length", - "description": "New Retract Length (units)", - "unit": "units", + "description": "New Retract Length (mm)", + "unit": "mm", "type": "float", "default_value": 6, "minimum_value": "0", @@ -314,6 +345,12 @@ class ChangeAtZ(Script): self.setFloatSettingIfEnabled(caz_instance, "caz_change_retractfeedrate", "retractfeedrate", "caz_retractfeedrate") self.setFloatSettingIfEnabled(caz_instance, "caz_change_retractlength", "retractlength", "caz_retractlength") + # are we emitting data to the LCD? + caz_instance.IsDisplayingChangesToLcd = self.getSettingValueByKey("caz_output_to_display") + + # are we doing linear move retractions? + caz_instance.IsLinearRetraction = self.getSettingValueByKey("caz_retractstyle") == "linear" + # see if we're applying to a single layer or to all layers hence forth caz_instance.IsApplyToSingleLayer = self.getSettingValueByKey("c_behavior") == "single_layer" @@ -380,109 +417,153 @@ class ChangeAtZ(Script): return default -# The primary ChangeAtZ class that does all the gcode editing. This was broken out into an -# independent class so it could be debugged using a standard IDE -class ChangeAtZProcessor: - TargetValues = {} - IsApplyToSingleLayer = False - LastE = None - CurrentZ = None - CurrentLayer = None - IsTargetByLayer = True - TargetLayer = None - TargetZ = None - LayerHeight = None - RetractLength = 0 +# This is a utility class for getting details of gcodes from a given line +class GCodeCommand: - # boots up the class with defaults + # The GCode command itself (ex: G10) + Command: str = None, + + # Contains any arguments passed to the command. The key is the argument name, the value is the value of the argument. + Arguments: Dict[str, any] = {} + + # Constructor. Sets up defaults def __init__(self): self.reset() - # Modifies the given GCODE and injects the commands at the various targets - def execute(self, data): - - # indicates if we should inject our defaults or not - inject_defaults = True - - # our layer cursor - index = 0 - - for active_layer in data: - - # will hold our updated gcode - modified_gcode = "" - - # mark all the defaults for deletion - active_layer = self.markDefaultsForDeletion(active_layer) - - # break apart the layer into commands - lines = active_layer.split("\n") - - # evaluate each command individually - for line in lines: - - # skip empty lines - if line.strip() == "": - continue - - # update our layer number if applicable - self.processLayerNumber(line) - - # update our layer height if applicable - self.processLayerHeight(line) - - # skip this line if we're not there yet - if not self.isTargetLayerOrHeight(): - - # read any settings we might need - self.processSetting(line) - - # if we haven't hit our target yet, leave the defaults as is (unmark them for deletion) - if "[CAZD:DELETE:" in line: - line = line.replace("[CAZD:DELETE:", "[CAZD:") - - # set our line - modified_gcode += line + "\n" - - # skip to the next line - continue - - # inject our defaults before linear motion commands - if inject_defaults and ("G1" in line or "G0" in line): - - # inject the defaults - modified_gcode += self.getTargetDefaults() + "\n" - - # mark that we've injected the defaults - inject_defaults = False - - # append to our modified layer - modified_gcode += self.processLinearMove(line) + "\n" - - # inject our defaults after the layer indicator - if inject_defaults and ";LAYER:" in line: - - # inject the defaults - modified_gcode += self.getTargetDefaults() + "\n" - - # mark that we've injected the defaults - inject_defaults = False - - # remove any marked defaults - modified_gcode = self.removeMarkedTargetDefaults(modified_gcode) - - # append our modified line - data[index] = modified_gcode - - index += 1 - return data - - # Converts the command parameter to a float or returns the default + # Gets a GCode Command from the given single line of GCode @staticmethod - def getFloatValue(line, key, default=None): + def getFromLine(line: str): + + # obviously if we don't have a command, we can't return anything + if line is None or len(line) == 0: + return None + + # we only support G or M commands + if line[0] != "G" and line[0] != "M": + return None + + # remove any comments + line = re.sub(r";.*$", "", line) + + # break into the individual components + command_pieces: List[str] = line.strip().split(" ") + + # our return command details + command = GCodeCommand() + + # stop here if we don't even have something to interpret + if len(command_pieces) == 0: + return None + + # set the actual command + command.Command = command_pieces[0] + + # stop here if we don't have any parameters + if len(command_pieces) == 1: + return None + + # remove the command from the pieces + del command_pieces[0] + + # iterate and index all of our parameters + for param in command_pieces: + + # get the first character of the parameter, which is the name + param_name:str = param[0] + + # get the value of the parameter (the rest of the string + param_value:str = None + + # get our value if we have one + if len(param) > 1: + param_value = param[1:] + + # index the argument + command.Arguments[param_name] = param_value + + # return our indexed command + return command + + # Handy function for reading a linear move command + @staticmethod + def getLinearMoveCommand(line: str): + + # get our command from the line + linear_command = GCodeCommand.getFromLine(line) + + # if it's not a linear move, we don't care + if linear_command is None or (linear_command.Command != "G0" and linear_command.Command != "G1"): + return None + + # convert our values to floats (or defaults) + linear_command.Arguments["F"] = linear_command.getArgumentAsFloat("F", None) + linear_command.Arguments["X"] = linear_command.getArgumentAsFloat("X", None) + linear_command.Arguments["Y"] = linear_command.getArgumentAsFloat("Y", None) + linear_command.Arguments["Z"] = linear_command.getArgumentAsFloat("Z", None) + linear_command.Arguments["E"] = linear_command.getArgumentAsFloat("E", None) + + # return our new command + return linear_command + + # Gets the value of a parameter or returns the default if there is none + def getArgument(self, name: str, default: str = None) -> str: + + # if we don't have the parameter, return the default + if name not in self.Arguments: + return default + + # otherwise return the value + return self.Arguments[name] + + # Gets the value of a parameter as a float or returns the default + def getArgumentAsFloat(self, name: str, default: float = None) -> float: + + # try to parse as a float, otherwise return the default + try: + return float(self.getArgument(name, default)) + except: + return default + + # Gets the value of a parameter as an integer or returns the default + def getArgumentAsInt(self, name: str, default: int = None) -> int: + + # try to parse as a integer, otherwise return the default + try: + return int(self.getArgument(name, default)) + except: + return default + + # Allows retrieving values from the given GCODE line + @staticmethod + def getDirectArgument(line: str, key: str, default: str = None) -> str: + + if key not in line or (";" in line and line.find(key) > line.find(";") and ";ChangeAtZ" not in key and ";LAYER:" not in key): + return default + + # allows for string lengths larger than 1 + sub_part = line[line.find(key) + len(key):] + + if ";ChangeAtZ" in key: + m = re.search("^[0-4]", sub_part) + elif ";LAYER:" in key: + m = re.search("^[+-]?[0-9]*", sub_part) + else: + # the minus at the beginning allows for negative values, e.g. for delta printers + m = re.search(r"^[-]?[0-9]*\.?[0-9]*", sub_part) + if m is None: + return default + + try: + return m.group(0) + except: + return default + + # Converts the command parameter to a int or returns the default + @staticmethod + def getDirectArgumentAsFloat(line: str, key: str, default: float = None) -> float: # get the value from the command - value = ChangeAtZProcessor.getValue(line, key, default) + value = GCodeCommand.getDirectArgument(line, key, default) # stop here if it's the default if value == default: @@ -495,10 +576,10 @@ class ChangeAtZProcessor: # Converts the command parameter to a int or returns the default @staticmethod - def getIntValue(line, key, default=None): + def getDirectArgumentAsInt(line: str, key: str, default: int = None) -> int: # get the value from the command - value = ChangeAtZProcessor.getValue(line, key, default) + value = GCodeCommand.getDirectArgument(line, key, default) # stop here if it's the default if value == default: @@ -509,21 +590,330 @@ class ChangeAtZProcessor: except: return default - # Handy function for reading a linear move command - def getLinearMoveParams(self, line): + # Easy function for replacing any GCODE parameter variable in a given GCODE command + @staticmethod + def replaceDirectArgument(line: str, key: str, value: str) -> str: + return re.sub(r"(^|\s)" + key + r"[\d\.]+(\s|$)", r"\1" + key + str(value) + r"\2", line) - # get our motion parameters - feed_rate = self.getFloatValue(line, "F", None) - x_coord = self.getFloatValue(line, "X", None) - y_coord = self.getFloatValue(line, "Y", None) - z_coord = self.getFloatValue(line, "Z", None) - extrude_length = self.getFloatValue(line, "E", None) + # Resets the model back to defaults + def reset(self): + self.Command = None + self.Arguments = {} - return extrude_length, feed_rate, x_coord, y_coord, z_coord + +# The primary ChangeAtZ class that does all the gcode editing. This was broken out into an +# independent class so it could be debugged using a standard IDE +class ChangeAtZProcessor: + + # Holds our current height + CurrentZ: float = None + + # Holds our current layer number + CurrentLayer: int = None + + # Indicates if we're only supposed to apply our settings to a single layer or multiple layers + IsApplyToSingleLayer: bool = False + + # Indicates if this should emit the changes as they happen to the LCD + IsDisplayingChangesToLcd: bool = False + + # Indicates if we're processing inside the target layer or not + IsInsideTargetLayer: bool = False + + # Indicates if we have restored the previous values from before we started our pass + IsLastValuesRestored: bool = False + + # Indicates if the user has opted for linear move retractions or firmware retractions + IsLinearRetraction: bool = True + + # Indicates if we're targetting by layer or height value + IsTargetByLayer: bool = True + + # Indicates if we have injected our changed values for the given layer yet + IsTargetValuesInjected: bool = False + + # Holds the last extrusion value, used with detecting when a retraction is made + LastE: float = None + + # An index of our gcodes which we're monitoring + LastValues: Dict[str, any] = {} + + # The detected layer height from the gcode + LayerHeight: float = None + + # The target layer + TargetLayer: int = None + + # Holds the values the user has requested to change + TargetValues: Dict[str, any] = {} + + # The target height in mm + TargetZ: float = None + + # Used to track if we've been inside our target layer yet + WasInsideTargetLayer: bool = False + + # boots up the class with defaults + def __init__(self): + self.reset() + + # Modifies the given GCODE and injects the commands at the various targets + def execute(self, data): + + # our layer cursor + index: int = 0 + + for active_layer in data: + + # will hold our updated gcode + modified_gcode: str = "" + + # mark all the defaults for deletion + active_layer = self.markChangesForDeletion(active_layer) + + # break apart the layer into commands + lines: List[str] = active_layer.split("\n") + + # evaluate each command individually + for line in lines: + + # trim or command + line = line.strip() + + # skip empty lines + if len(line) == 0: + continue + + if "Z10.8" in line: + derp = True + + # update our layer number if applicable + self.processLayerNumber(line) + + # update our layer height if applicable + self.processLayerHeight(line) + + # check if we're at the target layer or not + self.processTargetLayer() + + # process any changes to the gcode + modified_gcode += self.processLine(line) + + # remove any marked defaults + modified_gcode = self.removeMarkedChanges(modified_gcode) + + # append our modified line + data[index] = modified_gcode + + index += 1 + + # return our modified gcode + return data + + # Builds the restored layer settings based on the previous settings and returns the relevant GCODE lines + def getChangedLastValues(self) -> Dict[str, any]: + + # capture the values that we've changed + changed: Dict[str, any] = {} + + # for each of our target values, get the value to restore + # no point in restoring values we haven't changed + for key in self.TargetValues: + + # skip target values we can't restore + if key not in self.LastValues: + continue + + # save into our changed + changed[key] = self.LastValues[key] + + # return our collection of changed values + return changed + + # Builds the relevant display feedback for each of the values + def getDisplayChangesFromValues(self, values: Dict[str, any]) -> str: + + # stop here if we're not outputting data + if not self.IsDisplayingChangesToLcd: + return "" + + # will hold all the default settings for the target layer + codes: List[str] = [] + + # looking for wait for bed temp + if "bedTemp" in values: + codes.append("BedTemp: " + str(values["bedTemp"])) + + # set our extruder one temp (if specified) + if "extruderOne" in values: + codes.append("Extruder 1 Temp: " + str(values["extruderOne"])) + + # set our extruder two temp (if specified) + if "extruderTwo" in values: + codes.append("Extruder 2 Temp: " + str(values["extruderTwo"])) + + # set global flow rate + if "flowrate" in values: + codes.append("Extruder A Flow Rate: " + str(values["flowrate"])) + + # set extruder 0 flow rate + if "flowrateOne" in values: + codes.append("Extruder 1 Flow Rate: " + str(values["flowrateOne"])) + + # set second extruder flow rate + if "flowrateTwo" in values: + codes.append("Extruder 2 Flow Rate: " + str(values["flowrateTwo"])) + + # set our fan speed + if "fanSpeed" in values: + codes.append("Fan Speed: " + str(values["fanSpeed"])) + + # set feedrate percentage + if "speed" in values: + codes.append("Print Speed: " + str(values["speed"])) + + # set print rate percentage + if "printspeed" in values: + codes.append("Linear Print Speed: " + str(values["printspeed"])) + + # set retract rate + if "retractfeedrate" in values: + codes.append("Retract Feed Rate: " + str(values["retractfeedrate"])) + + # set retract length + if "retractlength" in values: + codes.append("Retract Length: " + str(values["retractlength"])) + + # stop here if there's nothing to output + if len(codes) == 0: + return "" + + # output our command to display the data + return "M117 " + ", ".join(codes) + "\n" + + # Converts the last values to something that can be output on the LCD + def getLastDisplayValues(self) -> str: + + # convert our last values to something we can output + return self.getDisplayChangesFromValues(self.getChangedLastValues()) + + # Converts the target values to something that can be output on the LCD + def getTargetDisplayValues(self) -> str: + + # convert our target values to something we can output + return self.getDisplayChangesFromValues(self.TargetValues) + + # Builds the the relevant GCODE lines from the given collection of values + def getCodeFromValues(self, values: Dict[str, any]) -> str: + + # will hold all the desired settings for the target layer + codes: List[str] = self.getCodeLinesFromValues(values) + + # stop here if there are no values that require changing + if len(codes) == 0: + return "" + + # return our default block for this layer + return ";[CAZD:\n" + "\n".join(codes) + "\n;:CAZD]" + + # Builds the relevant GCODE lines from the given collection of values + def getCodeLinesFromValues(self, values: Dict[str, any]) -> List[str]: + + # will hold all the default settings for the target layer + codes: List[str] = [] + + # looking for wait for bed temp + if "bedTemp" in values: + codes.append("M140 S" + str(values["bedTemp"])) + + # set our extruder one temp (if specified) + if "extruderOne" in values: + codes.append("M104 S" + str(values["extruderOne"]) + " T0") + + # set our extruder two temp (if specified) + if "extruderTwo" in values: + codes.append("M104 S" + str(values["extruderTwo"]) + " T1") + + # set our fan speed + if "fanSpeed" in values: + + # convert our fan speed percentage to PWM + fan_speed = int((float(values["fanSpeed"]) / 100.0) * 255) + + # add our fan speed to the defaults + codes.append("M106 S" + str(fan_speed)) + + # set global flow rate + if "flowrate" in values: + codes.append("M221 S" + str(values["flowrate"])) + + # set extruder 0 flow rate + if "flowrateOne" in values: + codes.append("M221 S" + str(values["flowrateOne"]) + " T0") + + # set second extruder flow rate + if "flowrateTwo" in values: + codes.append("M221 S" + str(values["flowrateTwo"]) + " T1") + + # set feedrate percentage + if "speed" in values: + codes.append("M220 S" + str(values["speed"]) + " T1") + + # set print rate percentage + if "printspeed" in values: + codes.append(";PRINTSPEED " + str(values["printspeed"]) + "") + + # set retract rate + if "retractfeedrate" in values: + + if self.IsLinearRetraction: + codes.append(";RETRACTFEEDRATE " + str(values["retractfeedrate"] * 60) + "") + else: + codes.append("M207 F" + str(values["retractfeedrate"] * 60) + "") + + # set retract length + if "retractlength" in values: + + if self.IsLinearRetraction: + codes.append(";RETRACTLENGTH " + str(values["retractlength"]) + "") + else: + codes.append("M207 S" + str(values["retractlength"]) + "") + + return codes + + # Builds the restored layer settings based on the previous settings and returns the relevant GCODE lines + def getLastValues(self) -> str: + + # build the gcode to restore our last values + return self.getCodeFromValues(self.getChangedLastValues()) + + # Builds the gcode to inject either the changed values we want or restore the previous values + def getInjectCode(self) -> str: + + # if we're now outside of our target layer and haven't restored our last values, do so now + if not self.IsInsideTargetLayer and self.WasInsideTargetLayer and not self.IsLastValuesRestored: + + # mark that we've injected the last values + self.IsLastValuesRestored = True + + # inject the defaults + return self.getLastValues() + "\n" + self.getLastDisplayValues() + + # if we're inside our target layer but haven't added our values yet, do so now + if self.IsInsideTargetLayer and not self.IsTargetValuesInjected: + + # mark that we've injected the target values + self.IsTargetValuesInjected = True + + # inject the defaults + return self.getTargetValues() + "\n" + self.getTargetDisplayValues() + + # nothing to do + return "" # Returns the unmodified GCODE line from previous ChangeZ edits @staticmethod - def getOriginalLine(line): + def getOriginalLine(line: str) -> str: # get the change at z original (cazo) details original_line = re.search(r"\[CAZO:(.*?):CAZO\]", line) @@ -534,98 +924,14 @@ class ChangeAtZProcessor: return original_line.group(1) - # Builds the layer defaults based on the settings and returns the relevant GCODE lines - def getTargetDefaults(self): + # Builds the target layer settings based on the specified values and returns the relevant GCODE lines + def getTargetValues(self) -> str: - # will hold all the default settings for the target layer - defaults = [] - - # used to trim other defaults - defaults.append(";[CAZD:") - - # looking for wait for bed temp - if "bedTemp" in self.TargetValues: - defaults.append("M190 S" + str(self.TargetValues["bedTemp"])) - - # set our extruder one temp (if specified) - if "extruderOne" in self.TargetValues: - defaults.append("M109 S" + str(self.TargetValues["extruderOne"]) + " T0") - - # set our extruder two temp (if specified) - if "extruderTwo" in self.TargetValues: - defaults.append("M109 S" + str(self.TargetValues["extruderTwo"]) + " T1") - - # set our fan speed - if "fanSpeed" in self.TargetValues: - - # convert our fan speed percentage to PWM - fan_speed = int((float(self.TargetValues["fanSpeed"]) / 100.0) * 255) - - # add our fan speed to the defaults - defaults.append("M106 S" + str(fan_speed)) - - # set global flow rate - if "flowrate" in self.TargetValues: - defaults.append("M221 S" + str(self.TargetValues["flowrate"])) - - # set extruder 0 flow rate - if "flowrateOne" in self.TargetValues: - defaults.append("M221 S" + str(self.TargetValues["flowrateOne"]) + " T0") - - # set second extruder flow rate - if "flowrateTwo" in self.TargetValues: - defaults.append("M221 S" + str(self.TargetValues["flowrateTwo"]) + " T1") - - # set feedrate percentage - if "speed" in self.TargetValues: - defaults.append("M220 S" + str(self.TargetValues["speed"]) + " T1") - - # set print rate percentage - if "printspeed" in self.TargetValues: - defaults.append(";PRINTSPEED " + str(self.TargetValues["printspeed"]) + "") - - # set retract rate - if "retractfeedrate" in self.TargetValues: - defaults.append(";RETRACTFEEDRATE " + str(self.TargetValues["retractfeedrate"]) + "") - - # set retract length - if "retractlength" in self.TargetValues: - defaults.append(";RETRACTLENGTH " + str(self.TargetValues["retractlength"]) + "") - - # used to trim other defaults - defaults.append(";:CAZD]") - - # if there are no defaults, stop here - if len(defaults) == 2: - return "" - - # return our default block for this layer - return "\n".join(defaults) - - # Allows retrieving values from the given GCODE line - @staticmethod - def getValue(line, key, default=None): - - if key not in line or (";" in line and line.find(key) > line.find(";") and ";ChangeAtZ" not in key and ";LAYER:" not in key): - return default - - sub_part = line[line.find(key) + len(key):] # allows for string lengths larger than 1 - if ";ChangeAtZ" in key: - m = re.search("^[0-4]", sub_part) - elif ";LAYER:" in key: - m = re.search("^[+-]?[0-9]*", sub_part) - else: - # the minus at the beginning allows for negative values, e.g. for delta printers - m = re.search(r"^[-]?[0-9]*\.?[0-9]*", sub_part) - if m is None: - return default - try: - return float(m.group(0)) - except: - return default + # build the gcode to change our current values + return self.getCodeFromValues(self.TargetValues) # Determines if the current line is at or below the target required to start modifying - def isTargetLayerOrHeight(self): + def isTargetLayerOrHeight(self) -> bool: # target selected by layer no. if self.IsTargetByLayer: @@ -654,29 +960,33 @@ class ChangeAtZProcessor: # Marks any current ChangeZ layer defaults in the layer for deletion @staticmethod - def markDefaultsForDeletion(layer): + def markChangesForDeletion(layer: str): return re.sub(r";\[CAZD:", ";[CAZD:DELETE:", layer) # Grabs the current height - def processLayerHeight(self, line): + def processLayerHeight(self, line: str): # stop here if we haven't entered a layer yet if self.CurrentLayer is None: return - # expose the main command - line_no_comments = self.stripComments(line) + # get our gcode command + command = GCodeCommand.getFromLine(line) - # stop here if this isn't a linear move command - if not ("G1" in line_no_comments or "G0" in line_no_comments): + # skip if it's not a command we're interested in + if command is None: return - # stop here if we don't have a Z value defined, we can't get the height from this command - if "Z" not in line_no_comments: + # stop here if this isn't a linear move command + if command.Command != "G0" and command.Command != "G1": return # get our value from the command - current_z = self.getFloatValue(line_no_comments, "Z", None) + current_z = command.getArgumentAsFloat("Z", None) + + # stop here if we don't have a Z value defined, we can't get the height from this command + if current_z is None: + return # stop if there's no change if current_z == self.CurrentZ: @@ -690,14 +1000,14 @@ class ChangeAtZProcessor: self.LayerHeight = self.CurrentZ # Grabs the current layer number - def processLayerNumber(self, line): + def processLayerNumber(self, line: str): # if this isn't a layer comment, stop here, nothing to update if ";LAYER:" not in line: return # get our current layer number - current_layer = self.getIntValue(line, ";LAYER:", None) + current_layer = GCodeCommand.getDirectArgumentAsInt(line, ";LAYER:", None) # this should never happen, but if our layer number hasn't changed, stop here if current_layer == self.CurrentLayer: @@ -706,21 +1016,70 @@ class ChangeAtZProcessor: # update our current layer self.CurrentLayer = current_layer + # Makes any linear move changes and also injects either target or restored values depending on the plugin state + def processLine(self, line: str) -> str: + + # used to change the given line of code + modified_gcode: str = "" + + # track any values that we may be interested in + self.trackChangeableValues(line) + + # if we're not inside the target layer, simply read the any + # settings we can and revert any ChangeAtZ deletions + if not self.IsInsideTargetLayer: + + # read any settings if we haven't hit our target layer yet + if not self.WasInsideTargetLayer: + self.processSetting(line) + + # if we haven't hit our target yet, leave the defaults as is (unmark them for deletion) + if "[CAZD:DELETE:" in line: + line = line.replace("[CAZD:DELETE:", "[CAZD:") + + # if we're targeting by Z, we want to add our values before the first linear move + if "G1 " in line or "G0 " in line: + modified_gcode += self.getInjectCode() + + # modify our command if we're still inside our target layer, otherwise pass unmodified + if self.IsInsideTargetLayer: + modified_gcode += self.processLinearMove(line) + "\n" + else: + modified_gcode += line + "\n" + + # if we're targetting by layer we want to add our values just after the layer label + if ";LAYER:" in line: + modified_gcode += self.getInjectCode() + + # return our changed code + return modified_gcode + # Handles any linear moves in the current line - def processLinearMove(self, line): + def processLinearMove(self, line: str) -> str: # if it's not a linear motion command we're not interested - if not ("G1" in line or "G0" in line): + if not ("G1 " in line or "G0 " in line): return line # always get our original line, otherwise the effect will be cumulative line = self.getOriginalLine(line) - # get the details from our linear move command - extrude_length, feed_rate, x_coord, y_coord, z_coord = self.getLinearMoveParams(line) + # get our command from the line + linear_command: Optional[GCodeCommand] = GCodeCommand.getLinearMoveCommand(line) + + # if it's not a linear move, we don't care + if linear_command is None: + return + + # get our linear move parameters + feed_rate: float = linear_command.Arguments["F"] + x_coord: float = linear_command.Arguments["X"] + y_coord: float = linear_command.Arguments["Y"] + z_coord: float = linear_command.Arguments["Z"] + extrude_length: float = linear_command.Arguments["E"] # set our new line to our old line - new_line = line + new_line: str = line # handle retract length new_line = self.processRetractLength(extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord) @@ -742,30 +1101,30 @@ class ChangeAtZProcessor: return self.setOriginalLine(new_line, line) # Handles any changes to print speed for the given linear motion command - def processPrintSpeed(self, feed_rate, new_line): + def processPrintSpeed(self, feed_rate: float, new_line: str) -> str: # if we're not setting print speed or we don't have a feed rate, stop here if "printspeed" not in self.TargetValues or feed_rate is None: return new_line # get our requested print speed - print_speed = int(self.TargetValues["printspeed"]) + print_speed: int = int(self.TargetValues["printspeed"]) # if they requested no change to print speed (ie: 100%), stop here if print_speed == 100: return new_line # get our feed rate from the command - feed_rate = float(self.getValue(new_line, "F")) * (float(print_speed) / 100.0) + feed_rate: float = GCodeCommand.getDirectArgumentAsFloat(new_line, "F") * (float(print_speed) / 100.0) # change our feed rate - return self.replaceParameter(new_line, "F", feed_rate) + return GCodeCommand.replaceDirectArgument(new_line, "F", feed_rate) # Handles any changes to retraction length for the given linear motion command - def processRetractLength(self, extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord): + def processRetractLength(self, extrude_length: float, feed_rate: float, new_line: str, x_coord: float, y_coord: float, z_coord: float) -> str: # if we don't have a retract length in the file we can't add one - if self.RetractLength == 0: + if "retractlength" not in self.LastValues or self.LastValues["retractlength"] == 0: return new_line # if we're not changing retraction length, stop here @@ -796,20 +1155,31 @@ class ChangeAtZProcessor: retract_length = float(self.TargetValues["retractlength"]) # subtract the difference between the default and the desired - extrude_length -= (retract_length - self.RetractLength) + extrude_length -= (retract_length - self.LastValues["retractlength"]) # replace our extrude amount - return self.replaceParameter(new_line, "E", extrude_length) + return GCodeCommand.replaceDirectArgument(new_line, "E", extrude_length) # Used for picking out the retract length set by Cura - def processRetractLengthSetting(self, line): + def processRetractLengthSetting(self, line: str): - # if it's not a linear move, we don't care - if "G0" not in line and "G1" not in line: + # skip if we're not doing linear retractions + if not self.IsLinearRetraction: return - # get the details from our linear move command - extrude_length, feed_rate, x_coord, y_coord, z_coord = self.getLinearMoveParams(line) + # get our command from the line + linear_command = GCodeCommand.getLinearMoveCommand(line) + + # if it's not a linear move, we don't care + if linear_command is None: + return + + # get our linear move parameters + feed_rate = linear_command.Arguments["F"] + x_coord = linear_command.Arguments["X"] + y_coord = linear_command.Arguments["Y"] + z_coord = linear_command.Arguments["Z"] + extrude_length = linear_command.Arguments["E"] # the command we're looking for only has extrude and feed rate if x_coord is not None or y_coord is not None or z_coord is not None: @@ -827,10 +1197,14 @@ class ChangeAtZProcessor: return # what ever the last negative retract length is it wins - self.RetractLength = extrude_length + self.LastValues["retractlength"] = extrude_length # Handles any changes to retraction feed rate for the given linear motion command - def processRetractFeedRate(self, extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord): + def processRetractFeedRate(self, extrude_length: float, feed_rate: float, new_line: str, x_coord: float, y_coord: float, z_coord: float) -> str: + + # skip if we're not doing linear retractions + if not self.IsLinearRetraction: + return new_line # if we're not changing retraction length, stop here if "retractfeedrate" not in self.TargetValues: @@ -851,10 +1225,10 @@ class ChangeAtZProcessor: retract_feed_rate *= 60 # replace our feed rate - return self.replaceParameter(new_line, "F", retract_feed_rate) + return GCodeCommand.replaceDirectArgument(new_line, "F", retract_feed_rate) # Used for finding settings in the print file before we process anything else - def processSetting(self, line): + def processSetting(self, line: str): # if we're in layers already we're out of settings if self.CurrentLayer is not None: @@ -863,16 +1237,29 @@ class ChangeAtZProcessor: # check our retract length self.processRetractLengthSetting(line) + # Sets the flags if we're at the target layer or not + def processTargetLayer(self): + + # skip this line if we're not there yet + if not self.isTargetLayerOrHeight(): + + # flag that we're outside our target layer + self.IsInsideTargetLayer = False + + # skip to the next line + return + + # flip if we hit our target layer + self.WasInsideTargetLayer = True + + # flag that we're inside our target layer + self.IsInsideTargetLayer = True + # Removes all the ChangeZ layer defaults from the given layer @staticmethod - def removeMarkedTargetDefaults(layer): + def removeMarkedChanges(layer: str) -> str: return re.sub(r";\[CAZD:DELETE:[\s\S]+?:CAZD\](\n|$)", "", layer) - # Easy function for replacing any GCODE parameter variable in a given GCODE command - @staticmethod - def replaceParameter(line, key, value): - return re.sub(r"(^|\s)" + key + r"[\d\.]+(\s|$)", r"\1" + key + str(value) + r"\2", line) - # Resets the class contents to defaults def reset(self): @@ -885,22 +1272,135 @@ class ChangeAtZProcessor: self.TargetLayer = None self.TargetZ = None self.LayerHeight = None - self.RetractLength = 0 + self.LastValues = {} + self.IsLinearRetraction = True + self.IsInsideTargetLayer = False + self.IsTargetValuesInjected = False + self.IsLastValuesRestored = False + self.WasInsideTargetLayer = False # Sets the original GCODE line in a given GCODE command @staticmethod - def setOriginalLine(line, original): + def setOriginalLine(line, original) -> str: return line + ";[CAZO:" + original + ":CAZO]" - # Removes the gcode comments from a given gcode command - @staticmethod - def stripComments(line): - return re.sub(r";.*?$", "", line).strip() + # Tracks the change in gcode values we're interested in + def trackChangeableValues(self, line: str): + + # simulate a print speed command + if ";PRINTSPEED" in line: + line = line.replace(";PRINTSPEED ", "M220 S") + + # simulate a retract feedrate command + if ";RETRACTFEEDRATE" in line: + line = line.replace(";RETRACTFEEDRATE ", "M207 F") + + # simulate a retract length command + if ";RETRACTLENGTH" in line: + line = line.replace(";RETRACTLENGTH ", "M207 S") + + # get our gcode command + command: Optional[GCodeCommand] = GCodeCommand.getFromLine(line) + + # stop here if it isn't a G or M command + if command is None: + return + + # handle retract length changes + if command.Command == "M207": + + # get our retract length if provided + if "S" in command.Arguments: + self.LastValues["retractlength"] = command.getArgumentAsFloat("S") + + # get our retract feedrate if provided, convert from mm/m to mm/s + if "F" in command.Arguments: + self.LastValues["retractfeedrate"] = command.getArgumentAsFloat("F") / 60.0 + + # move to the next command + return + + # handle bed temp changes + if command.Command == "M140" or command.Command == "M190": + + # get our bed temp if provided + if "S" in command.Arguments: + self.LastValues["bedTemp"] = command.getArgumentAsFloat("S") + + # move to the next command + return + + # handle extruder temp changes + if command.Command == "M104" or command.Command == "M109": + + # get our tempurature + tempurature: float = command.getArgumentAsFloat("S") + + # don't bother if we don't have a tempurature + if tempurature is None: + return + + # get our extruder, default to extruder one + extruder = command.getArgumentAsInt("T", None) + + # set our extruder temp based on the extruder + if extruder is None or extruder == 0: + self.LastValues["extruderOne"] = tempurature + + if extruder is None or extruder == 1: + self.LastValues["extruderTwo"] = tempurature + + # move to the next command + return + + # handle fan speed changes + if command.Command == "M106": + + # get our bed temp if provided + if "S" in command.Arguments: + self.LastValues["fanSpeed"] = (command.getArgumentAsInt("S") / 255.0) * 100 + + # move to the next command + return + + # handle flow rate changes + if command.Command == "M221": + + # get our flow rate + tempurature: float = command.getArgumentAsFloat("S") + + # don't bother if we don't have a flow rate (for some reason) + if tempurature is None: + return + + # get our extruder, default to global + extruder = command.getArgumentAsInt("T", None) + + # set our extruder temp based on the extruder + if extruder is None: + self.LastValues["flowrate"] = tempurature + elif extruder == 1: + self.LastValues["flowrateOne"] = tempurature + elif extruder == 1: + self.LastValues["flowrateTwo"] = tempurature + + # move to the next command + return + + # handle print speed changes + if command.Command == "M220": + + # get our speed if provided + if "S" in command.Arguments: + self.LastValues["speed"] = command.getArgumentAsInt("S") + + # move to the next command + return def debug(): # get our input file - file = r"PATH_TO_SOME_GCODE.gcode" + file = r"C:\Users\Wes\Desktop\Archive\gcode test\emit + single layer\AC_Retraction.gcode" # read the whole thing f = open(file, "r") @@ -922,7 +1422,9 @@ def debug(): caz_instance.reset() caz_instance.IsTargetByLayer = False - caz_instance.TargetZ = 10.5 + caz_instance.IsDisplayingChangesToLcd = True + caz_instance.IsApplyToSingleLayer = False + caz_instance.TargetZ = 10.6 caz_instance.TargetValues["bedTemp"] = 75.111 caz_instance.TargetValues["printspeed"] = 150 caz_instance.TargetValues["retractfeedrate"] = 40.555 @@ -937,6 +1439,7 @@ def debug(): caz_instance.reset() caz_instance.IsTargetByLayer = False caz_instance.TargetZ = 15 + caz_instance.IsApplyToSingleLayer = True caz_instance.TargetValues["bedTemp"] = 80 caz_instance.TargetValues["printspeed"] = 100 caz_instance.TargetValues["retractfeedrate"] = 10 From 4adf4dceb8528098c0c832a9be10eb1b8333ac1d Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Fri, 3 Apr 2020 12:02:27 +0200 Subject: [PATCH 06/37] Automatically make discovered printers available to use. instead of the list of available printers to be connected CURA-7055 --- .../src/Cloud/CloudOutputDeviceManager.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index ccc64f8073..308d6ca0c5 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import Dict, List, Optional + from PyQt5.QtCore import QTimer from UM import i18nCatalog @@ -11,7 +12,6 @@ from cura.API import Account from cura.CuraApplication import CuraApplication from cura.Settings.CuraStackBuilder import CuraStackBuilder from cura.Settings.GlobalStack import GlobalStack - from .CloudApiClient import CloudApiClient from .CloudOutputDevice import CloudOutputDevice from ..Models.Http.CloudClusterResponse import CloudClusterResponse @@ -100,15 +100,13 @@ class CloudOutputDeviceManager: def _onDeviceDiscovered(self, cluster_data: CloudClusterResponse) -> None: device = CloudOutputDevice(self._api, cluster_data) - CuraApplication.getInstance().getDiscoveredPrintersModel().addDiscoveredPrinter( - ip_address=device.key, - key=device.getId(), - name=device.getName(), - create_callback=self._createMachineFromDiscoveredDevice, - machine_type=device.printerType, - device=device - ) self._remote_clusters[device.getId()] = device + + # Create a machine if we don't already have it. Do not make it the active machine. + meta_data = {self.META_CLUSTER_ID: device.key} + if CuraApplication.getInstance().getMachineManager().getMachine(device.printerType, meta_data) is None: + self._createMachineFromDiscoveredDevice(device.getId(), activate = False) + self.discoveredDevicesChanged.emit() self._connectToActiveMachine() @@ -134,19 +132,22 @@ class CloudOutputDeviceManager: output_device_manager.removeOutputDevice(device.key) self.discoveredDevicesChanged.emit() - def _createMachineFromDiscoveredDevice(self, key: str) -> None: + def _createMachineFromDiscoveredDevice(self, key: str, activate: bool = True) -> None: device = self._remote_clusters[key] if not device: return - # Create a new machine and activate it. + # Create a new machine. # We do not use use MachineManager.addMachine here because we need to set the cluster ID before activating it. new_machine = CuraStackBuilder.createMachine(device.name, device.printerType) if not new_machine: Logger.log("e", "Failed creating a new machine") return new_machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) - CuraApplication.getInstance().getMachineManager().setActiveMachine(new_machine.getId()) + + if activate: + CuraApplication.getInstance().getMachineManager().setActiveMachine(new_machine.getId()) + self._connectToOutputDevice(device, new_machine) ## Callback for when the active machine was changed by the user or a new remote cluster was found. From a80a5f36ff2b5ebce243daa7d279323249efbbf8 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Mon, 6 Apr 2020 13:44:48 +0200 Subject: [PATCH 07/37] Add a Message informing users of progress on cloud syncing printers. CURA-7055 --- .../src/Cloud/CloudOutputDeviceManager.py | 51 +++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 308d6ca0c5..64661aefba 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -7,6 +7,7 @@ from PyQt5.QtCore import QTimer from UM import i18nCatalog from UM.Logger import Logger # To log errors talking to the API. +from UM.Message import Message from UM.Signal import Signal from cura.API import Account from cura.CuraApplication import CuraApplication @@ -87,28 +88,59 @@ class CloudOutputDeviceManager: ## Callback for when the request for getting the clusters is finished. def _onGetRemoteClustersFinished(self, clusters: List[CloudClusterResponse]) -> None: + new_clusters = [] online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudClusterResponse] for device_id, cluster_data in online_clusters.items(): if device_id not in self._remote_clusters: - self._onDeviceDiscovered(cluster_data) + new_clusters.append(cluster_data) else: self._onDiscoveredDeviceUpdated(cluster_data) + self._onDevicesDiscovered(new_clusters) + removed_device_keys = set(self._remote_clusters.keys()) - set(online_clusters.keys()) for device_id in removed_device_keys: self._onDiscoveredDeviceRemoved(device_id) - def _onDeviceDiscovered(self, cluster_data: CloudClusterResponse) -> None: - device = CloudOutputDevice(self._api, cluster_data) - self._remote_clusters[device.getId()] = device + if new_clusters or removed_device_keys: + self.discoveredDevicesChanged.emit() + if removed_device_keys: + # If the removed device was active we should connect to the new active device + self._connectToActiveMachine() - # Create a machine if we don't already have it. Do not make it the active machine. - meta_data = {self.META_CLUSTER_ID: device.key} - if CuraApplication.getInstance().getMachineManager().getMachine(device.printerType, meta_data) is None: + def _onDevicesDiscovered(self, clusters: [CloudClusterResponse]) -> None: + new_devices = [] + for cluster_data in clusters: + device = CloudOutputDevice(self._api, cluster_data) + # Create a machine if we don't already have it. Do not make it the active machine. + meta_data = {self.META_CLUSTER_ID: device.key} + if CuraApplication.getInstance().getMachineManager().getMachine(device.printerType, meta_data) is None: + new_devices.append(device) + + if not new_devices: + return + + message = Message( + title = self.I18N_CATALOG.i18ncp("info:status", "New cloud printer detected", "New cloud printers detected", len(new_devices)), + progress = 0, + lifetime = 0 + ) + message.show() + + for idx, device in enumerate(new_devices): + message.setText(self.I18N_CATALOG.i18nc("info:status", "Adding printer '{}' from your account", device.name)) + message.setProgress((idx / len(new_devices)) * 100) + CuraApplication.getInstance().processEvents() + self._remote_clusters[device.getId()] = device self._createMachineFromDiscoveredDevice(device.getId(), activate = False) - self.discoveredDevicesChanged.emit() - self._connectToActiveMachine() + message.setProgress(100) + message.setText(self.I18N_CATALOG.i18ncp( + "info:status", + "{} cloud printer added from your account", + "{} cloud printers added from your account", + len(new_devices) + )) def _onDiscoveredDeviceUpdated(self, cluster_data: CloudClusterResponse) -> None: device = self._remote_clusters.get(cluster_data.cluster_id) @@ -130,7 +162,6 @@ class CloudOutputDeviceManager: output_device_manager = CuraApplication.getInstance().getOutputDeviceManager() if device.key in output_device_manager.getOutputDeviceIds(): output_device_manager.removeOutputDevice(device.key) - self.discoveredDevicesChanged.emit() def _createMachineFromDiscoveredDevice(self, key: str, activate: bool = True) -> None: device = self._remote_clusters[key] From 52ef794c4db5197c99c8bf4c848f3889c12978bc Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Mon, 6 Apr 2020 14:49:45 +0200 Subject: [PATCH 08/37] Add a Message informing users of progress on cloud syncing printers. CURA-7055 --- .../src/Cloud/CloudOutputDeviceManager.py | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 64661aefba..2393b535aa 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -109,6 +109,13 @@ class CloudOutputDeviceManager: self._connectToActiveMachine() def _onDevicesDiscovered(self, clusters: [CloudClusterResponse]) -> None: + """**Synchronously** create machines for discovered devices + + Any new machines are made available to the user. + May take a long time to complete. As this code needs access to the Application + and blocks the GIL, creating a Job for this would not make sense. + Shows a Message informing the user of progress. + """ new_devices = [] for cluster_data in clusters: device = CloudOutputDevice(self._api, cluster_data) @@ -121,26 +128,37 @@ class CloudOutputDeviceManager: return message = Message( - title = self.I18N_CATALOG.i18ncp("info:status", "New cloud printer detected", "New cloud printers detected", len(new_devices)), + title = self.I18N_CATALOG.i18ncp( + "info:status", + "New cloud printer detected from your Ultimaker account", + "New cloud printers detected from your Ultimaker account", + len(new_devices) + ), progress = 0, lifetime = 0 ) message.show() for idx, device in enumerate(new_devices): - message.setText(self.I18N_CATALOG.i18nc("info:status", "Adding printer '{}' from your account", device.name)) - message.setProgress((idx / len(new_devices)) * 100) + message_text = self.I18N_CATALOG.i18nc( + "info:status", "Adding printer {} ({}) from your account", + device.name, + device.printerTypeName + ) + message.setText(message_text) + if len(new_devices) > 1: + message.setProgress((idx / len(new_devices)) * 100) CuraApplication.getInstance().processEvents() self._remote_clusters[device.getId()] = device self._createMachineFromDiscoveredDevice(device.getId(), activate = False) - message.setProgress(100) - message.setText(self.I18N_CATALOG.i18ncp( + message.setProgress(None) + message_text = self.I18N_CATALOG.i18nc( "info:status", - "{} cloud printer added from your account", - "{} cloud printers added from your account", - len(new_devices) - )) + "Cloud printers added from your account:\n{}", + "\n".join(["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices]) + ) + message.setText(message_text) def _onDiscoveredDeviceUpdated(self, cluster_data: CloudClusterResponse) -> None: device = self._remote_clusters.get(cluster_data.cluster_id) From b141bb139eb58d57e79d6162edc28e28d83de17f Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Mon, 6 Apr 2020 15:04:39 +0200 Subject: [PATCH 09/37] Update documentation of _connectToActiveMachine CURA-7055 --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 2393b535aa..95a6a73610 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -199,8 +199,9 @@ class CloudOutputDeviceManager: self._connectToOutputDevice(device, new_machine) - ## Callback for when the active machine was changed by the user or a new remote cluster was found. def _connectToActiveMachine(self) -> None: + """Callback for when the active machine was changed by the user""" + active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return From 0ec3e0972b84cb3bbde88b17614176361aea5fd7 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Mon, 6 Apr 2020 18:01:57 +0200 Subject: [PATCH 10/37] Add an image to the cloud printer sync message CURA-7055 --- .../src/Cloud/CloudOutputDeviceManager.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 95a6a73610..91c1ba645c 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,6 +1,6 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - +import os from typing import Dict, List, Optional from PyQt5.QtCore import QTimer @@ -127,6 +127,11 @@ class CloudOutputDeviceManager: if not new_devices: return + image_path = os.path.join( + CuraApplication.getInstance().getPluginRegistry().getPluginPath("UM3NetworkPrinting") or "", + "resources", "svg", "cloud-flow-completed.svg" + ) + message = Message( title = self.I18N_CATALOG.i18ncp( "info:status", @@ -135,7 +140,8 @@ class CloudOutputDeviceManager: len(new_devices) ), progress = 0, - lifetime = 0 + lifetime = 0, + image_source = image_path ) message.show() From 22a06fca3b2491c0bde8ea3875f461656d6802ee Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Tue, 7 Apr 2020 13:50:24 +0200 Subject: [PATCH 11/37] Shorten new cloud printers message So the plural form also fits on one line CURA-7055 --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 91c1ba645c..674ad0960b 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -135,8 +135,8 @@ class CloudOutputDeviceManager: message = Message( title = self.I18N_CATALOG.i18ncp( "info:status", - "New cloud printer detected from your Ultimaker account", - "New cloud printers detected from your Ultimaker account", + "New printer detected from your Ultimaker account", + "New printers detected from your Ultimaker account", len(new_devices) ), progress = 0, From 99b4b86052328212d683ee2ab1194d539679dec7 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Tue, 7 Apr 2020 14:33:59 +0200 Subject: [PATCH 12/37] Display max 3 printer names in the printer discover message CURA-7055 --- .../src/Cloud/CloudOutputDeviceManager.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 674ad0960b..3a1fa7af51 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -127,6 +127,8 @@ class CloudOutputDeviceManager: if not new_devices: return + new_devices.sort(key = lambda x: x.name.lower()) + image_path = os.path.join( CuraApplication.getInstance().getPluginRegistry().getPluginPath("UM3NetworkPrinting") or "", "resources", "svg", "cloud-flow-completed.svg" @@ -159,10 +161,20 @@ class CloudOutputDeviceManager: self._createMachineFromDiscoveredDevice(device.getId(), activate = False) message.setProgress(None) + + max_disp_devices = 3 + if len(new_devices) > max_disp_devices: + num_hidden = len(new_devices) - max_disp_devices + 1 + device_name_list = ["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices[0:num_hidden]] + device_name_list.append(self.I18N_CATALOG.i18nc("info:hidden list items", "- and {} others", num_hidden)) + device_names = "\n".join(device_name_list) + else: + device_names = "\n".join(["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices]) + message_text = self.I18N_CATALOG.i18nc( "info:status", "Cloud printers added from your account:\n{}", - "\n".join(["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices]) + device_names ) message.setText(message_text) From ff017e6a5202ec515463f486a7b039a687758643 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Tue, 7 Apr 2020 14:44:24 +0200 Subject: [PATCH 13/37] Remove unnecessary call to remove printer from qt model Since cloud printers are not added to that model anymore CURA-7055 --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 3a1fa7af51..c1d8fdb6d2 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -194,7 +194,6 @@ class CloudOutputDeviceManager: if not device: return device.close() - CuraApplication.getInstance().getDiscoveredPrintersModel().removeDiscoveredPrinter(device.key) output_device_manager = CuraApplication.getInstance().getOutputDeviceManager() if device.key in output_device_manager.getOutputDeviceIds(): output_device_manager.removeOutputDevice(device.key) From 33b3bb77c3f1f777a8e29015a219105231ba96d7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 7 Apr 2020 16:58:08 +0200 Subject: [PATCH 14/37] Fix typing List wasn't added, so mypy borked. CURA-7055 --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index c1d8fdb6d2..20d4c0d9c3 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -108,7 +108,7 @@ class CloudOutputDeviceManager: # If the removed device was active we should connect to the new active device self._connectToActiveMachine() - def _onDevicesDiscovered(self, clusters: [CloudClusterResponse]) -> None: + def _onDevicesDiscovered(self, clusters: List[CloudClusterResponse]) -> None: """**Synchronously** create machines for discovered devices Any new machines are made available to the user. From d1337c9e22a9fbe7f02a5785f2962b28a913bde2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 9 Apr 2020 16:39:47 +0200 Subject: [PATCH 15/37] Prevent printer from being added twice if it's in cloud and local CURA-7055 --- .../src/Cloud/CloudOutputDeviceManager.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 20d4c0d9c3..e2249a621c 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -120,8 +120,11 @@ class CloudOutputDeviceManager: for cluster_data in clusters: device = CloudOutputDevice(self._api, cluster_data) # Create a machine if we don't already have it. Do not make it the active machine. - meta_data = {self.META_CLUSTER_ID: device.key} - if CuraApplication.getInstance().getMachineManager().getMachine(device.printerType, meta_data) is None: + machine_manager = CuraApplication.getInstance().getMachineManager() + + # We only need to add it if it wasn't already added by "local" network or by cloud. + if machine_manager.getMachine(device.printerType, {self.META_CLUSTER_ID: device.key}) is None \ + and machine_manager.getMachine(device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None: # The host name is part of the network key. new_devices.append(device) if not new_devices: From 8b206751f84dcc21be1b42887647432bfbf62875 Mon Sep 17 00:00:00 2001 From: novamxd Date: Mon, 13 Apr 2020 22:22:21 -0500 Subject: [PATCH 16/37] Revamped ChangeAtZ Added an enabled flag, allowing users to enable/disable ChangeAtZ layers at will without removing them Improved performance of GCodeCommand, deferred parsing of arguments to when it is first requested as opposed to all the time Removed type hints because the supported python version in Cura is too low --- .../PostProcessingPlugin/scripts/ChangeAtZ.py | 152 +++++++++++------- 1 file changed, 94 insertions(+), 58 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py index 513ae13550..02a5f9cb69 100644 --- a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py +++ b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py @@ -67,6 +67,12 @@ class ChangeAtZ(Script): "metadata": {}, "version": 2, "settings": { + "caz_enabled": { + "label": "Enabled", + "description": "Allows adding multiple ChangeZ mods and disabling them as needed.", + "type": "bool", + "default_value": true + }, "a_trigger": { "label": "Trigger", "description": "Trigger at height or at layer no.", @@ -345,6 +351,9 @@ class ChangeAtZ(Script): self.setFloatSettingIfEnabled(caz_instance, "caz_change_retractfeedrate", "retractfeedrate", "caz_retractfeedrate") self.setFloatSettingIfEnabled(caz_instance, "caz_change_retractlength", "retractlength", "caz_retractlength") + # is this mod enabled? + caz_instance.IsEnabled = self.getSettingValueByKey("caz_enabled") + # are we emitting data to the LCD? caz_instance.IsDisplayingChangesToLcd = self.getSettingValueByKey("caz_output_to_display") @@ -421,10 +430,13 @@ class ChangeAtZ(Script): class GCodeCommand: # The GCode command itself (ex: G10) - Command: str = None, + Command = None, # Contains any arguments passed to the command. The key is the argument name, the value is the value of the argument. - Arguments: Dict[str, any] = {} + Arguments = {} + + # Contains the components of the command broken into pieces + Components = [] # Constructor. Sets up defaults def __init__(self): @@ -446,7 +458,7 @@ class GCodeCommand: line = re.sub(r";.*$", "", line) # break into the individual components - command_pieces: List[str] = line.strip().split(" ") + command_pieces = line.strip().split(" ") # our return command details command = GCodeCommand() @@ -455,6 +467,9 @@ class GCodeCommand: if len(command_pieces) == 0: return None + # stores all the components of the command within the class for later + command.Components = command_pieces + # set the actual command command.Command = command_pieces[0] @@ -462,25 +477,6 @@ class GCodeCommand: if len(command_pieces) == 1: return None - # remove the command from the pieces - del command_pieces[0] - - # iterate and index all of our parameters - for param in command_pieces: - - # get the first character of the parameter, which is the name - param_name:str = param[0] - - # get the value of the parameter (the rest of the string - param_value:str = None - - # get our value if we have one - if len(param) > 1: - param_value = param[1:] - - # index the argument - command.Arguments[param_name] = param_value - # return our indexed command return command @@ -508,6 +504,9 @@ class GCodeCommand: # Gets the value of a parameter or returns the default if there is none def getArgument(self, name: str, default: str = None) -> str: + # parse our arguments (only happens once) + self.parseArguments() + # if we don't have the parameter, return the default if name not in self.Arguments: return default @@ -590,6 +589,35 @@ class GCodeCommand: except: return default + # Parses the arguments of the command on demand, only once + def parseArguments(self): + + # stop here if we don't have any remaining components + if len(self.Components) <= 1: + return None + + # iterate and index all of our parameters, skip the first component as it's the command + for i in range(1, len(self.Components)): + + # get our component + component = self.Components[i] + + # get the first character of the parameter, which is the name + component_name = component[0] + + # get the value of the parameter (the rest of the string + component_value = None + + # get our value if we have one + if len(component) > 1: + component_value = component[1:] + + # index the argument + self.Arguments[component_name] = component_value + + # clear the components to we don't process again + self.Components = [] + # Easy function for replacing any GCODE parameter variable in a given GCODE command @staticmethod def replaceDirectArgument(line: str, key: str, value: str) -> str: @@ -606,52 +634,55 @@ class GCodeCommand: class ChangeAtZProcessor: # Holds our current height - CurrentZ: float = None + CurrentZ = None # Holds our current layer number - CurrentLayer: int = None + CurrentLayer = None # Indicates if we're only supposed to apply our settings to a single layer or multiple layers - IsApplyToSingleLayer: bool = False + IsApplyToSingleLayer = False # Indicates if this should emit the changes as they happen to the LCD - IsDisplayingChangesToLcd: bool = False + IsDisplayingChangesToLcd = False + + # Indicates that this mod is still enabled (or not) + IsEnabled = True # Indicates if we're processing inside the target layer or not - IsInsideTargetLayer: bool = False + IsInsideTargetLayer = False # Indicates if we have restored the previous values from before we started our pass - IsLastValuesRestored: bool = False + IsLastValuesRestored = False # Indicates if the user has opted for linear move retractions or firmware retractions - IsLinearRetraction: bool = True + IsLinearRetraction = True # Indicates if we're targetting by layer or height value - IsTargetByLayer: bool = True + IsTargetByLayer = True # Indicates if we have injected our changed values for the given layer yet - IsTargetValuesInjected: bool = False + IsTargetValuesInjected = False # Holds the last extrusion value, used with detecting when a retraction is made - LastE: float = None + LastE = None # An index of our gcodes which we're monitoring - LastValues: Dict[str, any] = {} + LastValues = {} # The detected layer height from the gcode - LayerHeight: float = None + LayerHeight = None # The target layer - TargetLayer: int = None + TargetLayer = None # Holds the values the user has requested to change - TargetValues: Dict[str, any] = {} + TargetValues = {} # The target height in mm - TargetZ: float = None + TargetZ = None # Used to track if we've been inside our target layer yet - WasInsideTargetLayer: bool = False + WasInsideTargetLayer = False # boots up the class with defaults def __init__(self): @@ -660,19 +691,23 @@ class ChangeAtZProcessor: # Modifies the given GCODE and injects the commands at the various targets def execute(self, data): + # short cut the whole thing if we're not enabled + if not self.IsEnabled: + return data + # our layer cursor - index: int = 0 + index = 0 for active_layer in data: # will hold our updated gcode - modified_gcode: str = "" + modified_gcode = "" # mark all the defaults for deletion active_layer = self.markChangesForDeletion(active_layer) # break apart the layer into commands - lines: List[str] = active_layer.split("\n") + lines = active_layer.split("\n") # evaluate each command individually for line in lines: @@ -714,7 +749,7 @@ class ChangeAtZProcessor: def getChangedLastValues(self) -> Dict[str, any]: # capture the values that we've changed - changed: Dict[str, any] = {} + changed = {} # for each of our target values, get the value to restore # no point in restoring values we haven't changed @@ -738,7 +773,7 @@ class ChangeAtZProcessor: return "" # will hold all the default settings for the target layer - codes: List[str] = [] + codes = [] # looking for wait for bed temp if "bedTemp" in values: @@ -807,7 +842,7 @@ class ChangeAtZProcessor: def getCodeFromValues(self, values: Dict[str, any]) -> str: # will hold all the desired settings for the target layer - codes: List[str] = self.getCodeLinesFromValues(values) + codes = self.getCodeLinesFromValues(values) # stop here if there are no values that require changing if len(codes) == 0: @@ -820,7 +855,7 @@ class ChangeAtZProcessor: def getCodeLinesFromValues(self, values: Dict[str, any]) -> List[str]: # will hold all the default settings for the target layer - codes: List[str] = [] + codes = [] # looking for wait for bed temp if "bedTemp" in values: @@ -1020,7 +1055,7 @@ class ChangeAtZProcessor: def processLine(self, line: str) -> str: # used to change the given line of code - modified_gcode: str = "" + modified_gcode = "" # track any values that we may be interested in self.trackChangeableValues(line) @@ -1065,21 +1100,21 @@ class ChangeAtZProcessor: line = self.getOriginalLine(line) # get our command from the line - linear_command: Optional[GCodeCommand] = GCodeCommand.getLinearMoveCommand(line) + linear_command = GCodeCommand.getLinearMoveCommand(line) # if it's not a linear move, we don't care if linear_command is None: return # get our linear move parameters - feed_rate: float = linear_command.Arguments["F"] - x_coord: float = linear_command.Arguments["X"] - y_coord: float = linear_command.Arguments["Y"] - z_coord: float = linear_command.Arguments["Z"] - extrude_length: float = linear_command.Arguments["E"] + feed_rate = linear_command.Arguments["F"] + x_coord = linear_command.Arguments["X"] + y_coord = linear_command.Arguments["Y"] + z_coord = linear_command.Arguments["Z"] + extrude_length = linear_command.Arguments["E"] # set our new line to our old line - new_line: str = line + new_line = line # handle retract length new_line = self.processRetractLength(extrude_length, feed_rate, new_line, x_coord, y_coord, z_coord) @@ -1108,14 +1143,14 @@ class ChangeAtZProcessor: return new_line # get our requested print speed - print_speed: int = int(self.TargetValues["printspeed"]) + print_speed = int(self.TargetValues["printspeed"]) # if they requested no change to print speed (ie: 100%), stop here if print_speed == 100: return new_line # get our feed rate from the command - feed_rate: float = GCodeCommand.getDirectArgumentAsFloat(new_line, "F") * (float(print_speed) / 100.0) + feed_rate = GCodeCommand.getDirectArgumentAsFloat(new_line, "F") * (float(print_speed) / 100.0) # change our feed rate return GCodeCommand.replaceDirectArgument(new_line, "F", feed_rate) @@ -1278,6 +1313,7 @@ class ChangeAtZProcessor: self.IsTargetValuesInjected = False self.IsLastValuesRestored = False self.WasInsideTargetLayer = False + self.IsEnabled = True # Sets the original GCODE line in a given GCODE command @staticmethod @@ -1300,7 +1336,7 @@ class ChangeAtZProcessor: line = line.replace(";RETRACTLENGTH ", "M207 S") # get our gcode command - command: Optional[GCodeCommand] = GCodeCommand.getFromLine(line) + command = GCodeCommand.getFromLine(line) # stop here if it isn't a G or M command if command is None: @@ -1334,7 +1370,7 @@ class ChangeAtZProcessor: if command.Command == "M104" or command.Command == "M109": # get our tempurature - tempurature: float = command.getArgumentAsFloat("S") + tempurature = command.getArgumentAsFloat("S") # don't bother if we don't have a tempurature if tempurature is None: @@ -1367,7 +1403,7 @@ class ChangeAtZProcessor: if command.Command == "M221": # get our flow rate - tempurature: float = command.getArgumentAsFloat("S") + tempurature = command.getArgumentAsFloat("S") # don't bother if we don't have a flow rate (for some reason) if tempurature is None: From 783b8e08ff8e7289d436f1cc6c9299781c3f2454 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Tue, 14 Apr 2020 11:40:04 +0200 Subject: [PATCH 17/37] Only schedule a new cloud sync after the previous one finished Prevents concurrent syncs, which would create duplicate printers. Also doxygen comments to docstring CURA-7055 --- .../src/Cloud/CloudOutputDeviceManager.py | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index e2249a621c..0b65f55cfd 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -18,10 +18,12 @@ from .CloudOutputDevice import CloudOutputDevice from ..Models.Http.CloudClusterResponse import CloudClusterResponse -## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. -# Keeping all cloud related logic in this class instead of the UM3OutputDevicePlugin results in more readable code. -# API spec is available on https://api.ultimaker.com/docs/connect/spec/. class CloudOutputDeviceManager: + """The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. + + Keeping all cloud related logic in this class instead of the UM3OutputDevicePlugin results in more readable code. + API spec is available on https://api.ultimaker.com/docs/connect/spec/. + """ META_CLUSTER_ID = "um_cloud_cluster_id" META_NETWORK_KEY = "um_network_key" @@ -45,14 +47,16 @@ class CloudOutputDeviceManager: # Create a timer to update the remote cluster list self._update_timer = QTimer() self._update_timer.setInterval(int(self.CHECK_CLUSTER_INTERVAL * 1000)) - self._update_timer.setSingleShot(False) + # The timer is restarted explicitly after an update was processed. This prevents 2 concurrent updates + self._update_timer.setSingleShot(True) self._update_timer.timeout.connect(self._getRemoteClusters) # Ensure we don't start twice. self._running = False - ## Starts running the cloud output device manager, thus periodically requesting cloud data. def start(self): + """Starts running the cloud output device manager, thus periodically requesting cloud data.""" + if self._running: return if not self._account.isLoggedIn: @@ -62,8 +66,9 @@ class CloudOutputDeviceManager: self._update_timer.start() self._getRemoteClusters() - ## Stops running the cloud output device manager. def stop(self): + """Stops running the cloud output device manager.""" + if not self._running: return self._running = False @@ -71,23 +76,27 @@ class CloudOutputDeviceManager: self._update_timer.stop() self._onGetRemoteClustersFinished([]) # Make sure we remove all cloud output devices. - ## Force refreshing connections. def refreshConnections(self) -> None: + """Force refreshing connections.""" + self._connectToActiveMachine() - ## Called when the uses logs in or out def _onLoginStateChanged(self, is_logged_in: bool) -> None: + """Called when the uses logs in or out""" + if is_logged_in: self.start() else: self.stop() - ## Gets all remote clusters from the API. def _getRemoteClusters(self) -> None: + """Gets all remote clusters from the API.""" + self._api.getClusters(self._onGetRemoteClustersFinished) - ## Callback for when the request for getting the clusters is finished. def _onGetRemoteClustersFinished(self, clusters: List[CloudClusterResponse]) -> None: + """Callback for when the request for getting the clusters is finished.""" + new_clusters = [] online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudClusterResponse] for device_id, cluster_data in online_clusters.items(): @@ -107,6 +116,8 @@ class CloudOutputDeviceManager: if removed_device_keys: # If the removed device was active we should connect to the new active device self._connectToActiveMachine() + # Schedule a new update + self._update_timer.start() def _onDevicesDiscovered(self, clusters: List[CloudClusterResponse]) -> None: """**Synchronously** create machines for discovered devices @@ -240,8 +251,9 @@ class CloudOutputDeviceManager: # Remove device if it is not meant for the active machine. output_device_manager.removeOutputDevice(device.key) - ## Connects to an output device and makes sure it is registered in the output device manager. def _connectToOutputDevice(self, device: CloudOutputDevice, machine: GlobalStack) -> None: + """Connects to an output device and makes sure it is registered in the output device manager.""" + machine.setName(device.name) machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) machine.setMetaDataEntry("group_name", device.name) From 7345554778498846de230a49e3571d5db37164fd Mon Sep 17 00:00:00 2001 From: maukcc Date: Tue, 14 Apr 2020 12:10:48 +0200 Subject: [PATCH 18/37] HMS434 update --- resources/definitions/hms434.def.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/resources/definitions/hms434.def.json b/resources/definitions/hms434.def.json index b2dbce3f13..5d4243e333 100644 --- a/resources/definitions/hms434.def.json +++ b/resources/definitions/hms434.def.json @@ -81,18 +81,19 @@ "layer_height": {"maximum_value": "(0.8 * min(extruderValues('machine_nozzle_size')))" }, "layer_height_0": {"maximum_value": "(0.8 * min(extruderValues('machine_nozzle_size')))" }, "line_width": {"value": "(machine_nozzle_size + 0.2)" }, - "wall_line_width_0": {"value": "(machine_nozzle_size - 0.05)" }, + "wall_line_width_0": {"value": "(machine_nozzle_size)" }, "infill_line_width": {"value": "(line_width)" }, "initial_layer_line_width_factor": {"value": 110 }, "wall_thickness": {"value": "(line_width * 3) if infill_sparse_density < 95 else line_width" }, - "roofing_layer_count": {"value": "4" }, - "top_bottom_thickness": {"value": "(layer_height_0 + (layer_height * 3))" }, - "top_layers": {"value": "4" }, + "roofing_layer_count": {"value": "1" }, + "top_bottom_thickness": {"value": "(layer_height_0 + (layer_height * (top_layers - 1)))" }, + "top_layers": {"value": "4 if infill_sparse_density < 95 else 1" }, "bottom_layers": {"value": "(top_layers)" }, "wall_0_inset": {"value": "0" }, "outer_inset_first": {"value": true }, "alternate_extra_perimeter": {"value": false }, + "travel_compensate_overlapping_walls_enabled": {"value": false }, "filter_out_tiny_gaps": {"value": true }, "fill_outline_gaps": {"value": true }, "z_seam_type": {"value": "'shortest'"}, @@ -107,7 +108,7 @@ "infill_sparse_density": {"value": 30}, "infill_pattern": {"value": "'lines'"}, - "infill_before_walls": {"value": false}, + "infill_before_walls": {"value": true}, "material_print_temperature_layer_0": {"value": "material_print_temperature"}, "material_initial_print_temperature": {"value": "material_print_temperature", @@ -116,7 +117,7 @@ "material_bed_temperature_layer_0": {"value": "material_bed_temperature"}, "material_flow_layer_0": {"value": "material_flow"}, "retraction_enable": {"value": true }, - "retract_at_layer_change": {"value": true }, + "retract_at_layer_change": {"value": false }, "retraction_min_travel": {"value": "(round(line_width * 10))"}, "switch_extruder_retraction_speeds": {"value": "(retraction_speed)"}, "switch_extruder_prime_speed": {"value": "(retraction_prime_speed)"}, From 28b8ba3748a4efa170fdbedd113c8df328e7c422 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 15 Apr 2020 11:02:02 +0200 Subject: [PATCH 19/37] Only check changed settings (and whatever it affects) for errors in POS CURA-7329 --- plugins/CuraEngineBackend/StartSliceJob.py | 36 +++++++++++++++------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index a99c559bac..62b0bd16e7 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -13,6 +13,8 @@ from UM.Job import Job from UM.Logger import Logger from UM.Scene.SceneNode import SceneNode from UM.Settings.ContainerStack import ContainerStack #For typing. +from UM.Settings.InstanceContainer import InstanceContainer +from UM.Settings.SettingDefinition import SettingDefinition from UM.Settings.SettingRelation import SettingRelation #For typing. from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator @@ -103,20 +105,33 @@ class StartSliceJob(Job): ## Check if a stack has any errors. ## returns true if it has errors, false otherwise. def _checkStackForErrors(self, stack: ContainerStack) -> bool: - if stack is None: - return False - # if there are no per-object settings we don't need to check the other settings here - stack_top = stack.getTop() - if stack_top is None or not stack_top.getAllKeys(): - return False + top_of_stack = cast(InstanceContainer, stack.getTop()) # Cache for efficiency. + changed_setting_keys = top_of_stack.getAllKeys() - for key in stack.getAllKeys(): - validation_state = stack.getProperty(key, "validationState") - if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid): - Logger.log("w", "Setting %s is not valid, but %s. Aborting slicing.", key, validation_state) + # Add all relations to changed settings as well. + for key in top_of_stack.getAllKeys(): + instance = top_of_stack.getInstance(key) + if instance is None: + continue + self._addRelations(changed_setting_keys, instance.definition.relations) + Job.yieldThread() + + for changed_setting_key in changed_setting_keys: + validation_state = stack.getProperty(changed_setting_key, "validationState") + + if validation_state is None: + definition = cast(SettingDefinition, stack.getSettingDefinition(changed_setting_key)) + validator_type = SettingDefinition.getValidatorForType(definition.type) + if validator_type: + validator = validator_type(changed_setting_key) + validation_state = validator(stack) + if validation_state in ( + ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError, ValidatorState.Invalid): + Logger.log("w", "Setting %s is not valid, but %s. Aborting slicing.", changed_setting_key, validation_state) return True Job.yieldThread() + return False ## Runs the job that initiates the slicing. @@ -511,4 +526,3 @@ class StartSliceJob(Job): relations_set.add(relation.target.key) self._addRelations(relations_set, relation.target.relations) - From 006822334ca3a1f9f24db63024e349fd1df428d8 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 16 Apr 2020 12:18:49 +0200 Subject: [PATCH 20/37] Fetch preview image from cloud for monitor page. part of CURA-7147 --- .../src/Models/Http/ClusterPrintJobStatus.py | 5 ++++- .../src/Models/UM3PrintJobOutputModel.py | 17 +++++++++++++++-- .../UltimakerNetworkedPrinterOutputDevice.py | 4 +++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrintJobStatus.py b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrintJobStatus.py index 9794da1bbb..be1768918f 100644 --- a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrintJobStatus.py +++ b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrintJobStatus.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Ultimaker B.V. +# Copyright (c) 2020 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from typing import List, Optional, Union, Dict, Any @@ -44,6 +44,7 @@ class ClusterPrintJobStatus(BaseModel): # \param compatible_machine_families: Family names of machines suitable for this print job # \param impediments_to_printing: A list of reasons that prevent this job from being printed on the associated # printer + # \param preview_url: URL to the preview image (same as wou;d've been included in the ufp). def __init__(self, created_at: str, force: bool, machine_variant: str, name: str, started: bool, status: str, time_total: int, uuid: str, configuration: List[Union[Dict[str, Any], ClusterPrintCoreConfiguration]], @@ -57,6 +58,7 @@ class ClusterPrintJobStatus(BaseModel): build_plate: Union[Dict[str, Any], ClusterBuildPlate] = None, compatible_machine_families: List[str] = None, impediments_to_printing: List[Union[Dict[str, Any], ClusterPrintJobImpediment]] = None, + preview_url = None, **kwargs) -> None: self.assigned_to = assigned_to self.configuration = self.parseModels(ClusterPrintCoreConfiguration, configuration) @@ -76,6 +78,7 @@ class ClusterPrintJobStatus(BaseModel): self.uuid = uuid self.deleted_at = deleted_at self.printed_on_uuid = printed_on_uuid + self.preview_url = preview_url self.configuration_changes_required = self.parseModels(ClusterPrintJobConfigurationChange, configuration_changes_required) \ diff --git a/plugins/UM3NetworkPrinting/src/Models/UM3PrintJobOutputModel.py b/plugins/UM3NetworkPrinting/src/Models/UM3PrintJobOutputModel.py index bfde233a35..b063a2bf5b 100644 --- a/plugins/UM3NetworkPrinting/src/Models/UM3PrintJobOutputModel.py +++ b/plugins/UM3NetworkPrinting/src/Models/UM3PrintJobOutputModel.py @@ -1,10 +1,13 @@ -# Copyright (c) 2019 Ultimaker B.V. +# Copyright (c) 2020 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import List +from typing import List, Optional from PyQt5.QtCore import pyqtProperty, pyqtSignal from PyQt5.QtGui import QImage +from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest +from UM.Logger import Logger +from UM.TaskManagement.HttpRequestManager import HttpRequestManager from cura.PrinterOutput.Models.PrintJobOutputModel import PrintJobOutputModel from cura.PrinterOutput.PrinterOutputController import PrinterOutputController @@ -32,3 +35,13 @@ class UM3PrintJobOutputModel(PrintJobOutputModel): image = QImage() image.loadFromData(data) self.updatePreviewImage(image) + + def loadPreviewImageFromUrl(self, url: str) -> None: + HttpRequestManager.getInstance().get(url=url, callback=self._onImageLoaded, error_callback=self._onImageLoaded) + + def _onImageLoaded(self, reply: QNetworkReply, error: Optional["QNetworkReply.NetworkError"] = None) -> None: + if not HttpRequestManager.replyIndicatesSuccess(reply, error): + Logger.warning("Requesting preview image failed, response code {0} while trying to connect to {1}".format( + reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url())) + return + self.updatePreviewImageData(reply.readAll()) diff --git a/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterOutputDevice.py index 73b5b456f9..7eed52a9dc 100644 --- a/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/UltimakerNetworkedPrinterOutputDevice.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Ultimaker B.V. +# Copyright (c) 2020 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import os from time import time @@ -330,6 +330,8 @@ class UltimakerNetworkedPrinterOutputDevice(NetworkedPrinterOutputDevice): self._updateAssignedPrinter(model, remote_job.printer_uuid) if remote_job.assigned_to: self._updateAssignedPrinter(model, remote_job.assigned_to) + if remote_job.preview_url: + model.loadPreviewImageFromUrl(remote_job.preview_url) return model ## Updates the printer assignment for the given print job model. From ac5213d2325d953e8ddc47d47c96c76688687a85 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 16 Apr 2020 12:59:36 +0200 Subject: [PATCH 21/37] Fix typing. part of CURA-7147 --- .../UM3NetworkPrinting/src/Models/Http/ClusterPrintJobStatus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrintJobStatus.py b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrintJobStatus.py index be1768918f..22fb9bb37a 100644 --- a/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrintJobStatus.py +++ b/plugins/UM3NetworkPrinting/src/Models/Http/ClusterPrintJobStatus.py @@ -58,7 +58,7 @@ class ClusterPrintJobStatus(BaseModel): build_plate: Union[Dict[str, Any], ClusterBuildPlate] = None, compatible_machine_families: List[str] = None, impediments_to_printing: List[Union[Dict[str, Any], ClusterPrintJobImpediment]] = None, - preview_url = None, + preview_url: Optional[str] = None, **kwargs) -> None: self.assigned_to = assigned_to self.configuration = self.parseModels(ClusterPrintCoreConfiguration, configuration) From f3093d6bc8df3f679f946d22cc1815cd44c4e130 Mon Sep 17 00:00:00 2001 From: maukcc Date: Fri, 17 Apr 2020 12:08:21 +0200 Subject: [PATCH 23/37] HMS434 update roofing layers disabled --- resources/definitions/hms434.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/hms434.def.json b/resources/definitions/hms434.def.json index 5d4243e333..c4c73a5e77 100644 --- a/resources/definitions/hms434.def.json +++ b/resources/definitions/hms434.def.json @@ -86,7 +86,7 @@ "initial_layer_line_width_factor": {"value": 110 }, "wall_thickness": {"value": "(line_width * 3) if infill_sparse_density < 95 else line_width" }, - "roofing_layer_count": {"value": "1" }, + "roofing_layer_count": {"value": "0" }, "top_bottom_thickness": {"value": "(layer_height_0 + (layer_height * (top_layers - 1)))" }, "top_layers": {"value": "4 if infill_sparse_density < 95 else 1" }, "bottom_layers": {"value": "(top_layers)" }, From 096198ec9a661095da508370be0e1fe5728180fb Mon Sep 17 00:00:00 2001 From: novamxd Date: Sun, 19 Apr 2020 04:47:54 -0500 Subject: [PATCH 24/37] Revamped ChangeAtZ Removed some derpug code --- plugins/PostProcessingPlugin/scripts/ChangeAtZ.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py index 02a5f9cb69..239463bca4 100644 --- a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py +++ b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py @@ -719,9 +719,6 @@ class ChangeAtZProcessor: if len(line) == 0: continue - if "Z10.8" in line: - derp = True - # update our layer number if applicable self.processLayerNumber(line) From e9cff28c5a00554c409c0d5247db70399cc83518 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Mon, 20 Apr 2020 11:27:12 +0200 Subject: [PATCH 25/37] Add Taiwanese translations for 4.6 CURA-7344 --- resources/i18n/zh_TW/cura.po | 83 +++++++++++---------- resources/i18n/zh_TW/fdmprinter.def.json.po | 32 ++++---- 2 files changed, 61 insertions(+), 54 deletions(-) diff --git a/resources/i18n/zh_TW/cura.po b/resources/i18n/zh_TW/cura.po index ec292072ff..6aa4de644f 100644 --- a/resources/i18n/zh_TW/cura.po +++ b/resources/i18n/zh_TW/cura.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: Cura 4.6\n" "Report-Msgid-Bugs-To: r.dulek@ultimaker.com\n" "POT-Creation-Date: 2020-04-06 16:33+0200\n" -"PO-Revision-Date: 2020-02-16 18:19+0800\n" +"PO-Revision-Date: 2020-04-18 17:49+0800\n" "Last-Translator: Zhang Heh Ji \n" "Language-Team: Zhang Heh Ji \n" "Language: zh_TW\n" @@ -347,7 +347,7 @@ msgstr "正在設定場景..." #: /home/trin/Gedeeld/Projects/Cura/cura/CuraApplication.py:868 msgctxt "@info:progress" msgid "Loading interface..." -msgstr "正在載入介面…" +msgstr "正在載入介面..." #: /home/trin/Gedeeld/Projects/Cura/cura/CuraApplication.py:873 msgctxt "@info:progress" @@ -730,13 +730,13 @@ msgstr "3MF 檔案" #: /home/trin/Gedeeld/Projects/Cura/plugins/3MFWriter/ThreeMFWorkspaceWriter.py:31 msgctxt "@error:zip" msgid "3MF Writer plug-in is corrupt." -msgstr "" +msgstr "3MF 寫入器外掛已損壞。" #: /home/trin/Gedeeld/Projects/Cura/plugins/3MFWriter/ThreeMFWorkspaceWriter.py:59 #: /home/trin/Gedeeld/Projects/Cura/plugins/3MFWriter/ThreeMFWorkspaceWriter.py:92 msgctxt "@error:zip" msgid "No permission to write the workspace here." -msgstr "" +msgstr "沒有寫入此處工作區的權限。" #: /home/trin/Gedeeld/Projects/Cura/plugins/3MFWriter/ThreeMFWriter.py:181 msgctxt "@error:zip" @@ -771,12 +771,12 @@ msgstr "上傳你的備份時發生錯誤。" #: /home/trin/Gedeeld/Projects/Cura/plugins/CuraDrive/src/CreateBackupJob.py:47 msgctxt "@info:backup_status" msgid "Creating your backup..." -msgstr "" +msgstr "正在建立備份..." #: /home/trin/Gedeeld/Projects/Cura/plugins/CuraDrive/src/CreateBackupJob.py:54 msgctxt "@info:backup_status" msgid "There was an error while creating your backup." -msgstr "" +msgstr "建立備份時發生了錯誤。" #: /home/trin/Gedeeld/Projects/Cura/plugins/CuraDrive/src/CreateBackupJob.py:58 msgctxt "@info:backup_status" @@ -791,7 +791,7 @@ msgstr "你的備份上傳完成。" #: /home/trin/Gedeeld/Projects/Cura/plugins/CuraDrive/src/CreateBackupJob.py:107 msgctxt "@error:file_size" msgid "The backup exceeds the maximum file size." -msgstr "" +msgstr "備份超過了最大檔案大小。" #: /home/trin/Gedeeld/Projects/Cura/plugins/CuraDrive/src/DriveApiService.py:82 #: /home/trin/Gedeeld/Projects/Cura/plugins/CuraDrive/src/RestoreBackupJob.py:23 @@ -850,6 +850,10 @@ msgid "" "- Are assigned to an enabled extruder\n" "- Are not all set as modifier meshes" msgstr "" +"請檢查設定並檢查你的模型是否:\n" +"- 適合列印範圍\n" +"- 分配了一個已啟用的擠出機\n" +"- 沒有全部設定成修改網格" #: /home/trin/Gedeeld/Projects/Cura/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py:50 #: /home/trin/Gedeeld/Projects/Cura/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py:256 @@ -1156,7 +1160,7 @@ msgstr "建立一塊不列印支撐的空間。" #: /home/trin/Gedeeld/Projects/Cura/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py:101 msgctxt "@info:generic" msgid "Do you want to sync material and software packages with your account?" -msgstr "" +msgstr "你要使用你的帳號同步耗材資料和軟體套件嗎?" #: /home/trin/Gedeeld/Projects/Cura/plugins/Toolbox/src/CloudSync/CloudPackageChecker.py:102 #: /home/trin/Gedeeld/Projects/Cura/plugins/Toolbox/src/CloudSync/DownloadPresenter.py:91 @@ -1172,7 +1176,7 @@ msgstr "同步" #: /home/trin/Gedeeld/Projects/Cura/plugins/Toolbox/src/CloudSync/DownloadPresenter.py:87 msgctxt "@info:generic" msgid "Syncing..." -msgstr "" +msgstr "同步中..." #: /home/trin/Gedeeld/Projects/Cura/plugins/Toolbox/src/CloudSync/LicenseModel.py:9 msgctxt "@button" @@ -2070,12 +2074,12 @@ msgstr "重疊處不建立支撐" #: /home/trin/Gedeeld/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:142 msgctxt "@item:inlistbox" msgid "Infill mesh only" -msgstr "" +msgstr "只填充網格" #: /home/trin/Gedeeld/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:143 msgctxt "@item:inlistbox" msgid "Cutting mesh" -msgstr "" +msgstr "切割網格" #: /home/trin/Gedeeld/Projects/Cura/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml:373 msgctxt "@action:button" @@ -2091,7 +2095,7 @@ msgstr "選擇對此模型的自訂設定" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Preferences/SettingVisibilityPage.qml:94 msgctxt "@label:textbox" msgid "Filter..." -msgstr "篩選…" +msgstr "篩選..." #: /home/trin/Gedeeld/Projects/Cura/plugins/PerObjectSettingsTool/SettingPickDialog.qml:70 msgctxt "@label:checkbox" @@ -2121,13 +2125,13 @@ msgstr "設定" #: /home/trin/Gedeeld/Projects/Cura/plugins/PostProcessingPlugin/PostProcessingPlugin.qml:499 msgctxt "@info:tooltip" msgid "Change active post-processing scripts." -msgstr "" +msgstr "更改目前啟用的後處理腳本。" #: /home/trin/Gedeeld/Projects/Cura/plugins/PostProcessingPlugin/PostProcessingPlugin.qml:503 msgctxt "@info:tooltip" msgid "The following script is active:" msgid_plural "The following scripts are active:" -msgstr[0] "" +msgstr[0] "下列為啟用中的腳本:" #: /home/trin/Gedeeld/Projects/Cura/plugins/SimulationView/SimulationViewMenuComponent.qml:20 #: /home/trin/Gedeeld/Projects/Cura/plugins/SimulationView/SimulationViewMenuComponent.qml:49 @@ -2148,7 +2152,7 @@ msgstr "線條類型" #: /home/trin/Gedeeld/Projects/Cura/plugins/SimulationView/SimulationViewMenuComponent.qml:115 msgctxt "@label:listbox" msgid "Speed" -msgstr "" +msgstr "速度" #: /home/trin/Gedeeld/Projects/Cura/plugins/SimulationView/SimulationViewMenuComponent.qml:119 msgctxt "@label:listbox" @@ -2354,7 +2358,7 @@ msgstr "需重新啟動 Cura,套件的更動才能生效。" #: /home/trin/Gedeeld/Projects/Cura/plugins/Toolbox/resources/qml/components/ToolboxFooter.qml:46 msgctxt "@info:button, %1 is the application name" msgid "Quit %1" -msgstr "" +msgstr "結束 %1" #: /home/trin/Gedeeld/Projects/Cura/plugins/Toolbox/resources/qml/components/ToolboxHeader.qml:30 #: /home/trin/Gedeeld/Projects/Cura/plugins/Toolbox/resources/qml/pages/ToolboxInstalledPage.qml:34 @@ -2960,7 +2964,7 @@ msgstr "登入" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Account/GeneralOperations.qml:40 msgctxt "@label" msgid "Your key to connected 3D printing" -msgstr "" +msgstr "連線 3D 列印的金鑰" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Account/GeneralOperations.qml:51 msgctxt "@text" @@ -2969,6 +2973,9 @@ msgid "" "- Stay flexible by syncing your setup and loading it anywhere\n" "- Increase efficiency with a remote workflow on Ultimaker printers" msgstr "" +"- 使用更多的列印參數設定和外掛訂做你的使用體驗\n" +"- 通過同步設定可在任何地方將其載入以保持靈活性\n" +"- 透過 Ultimaker 印表機上的遠端工作流程提高效率" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Account/GeneralOperations.qml:78 msgctxt "@button" @@ -3113,12 +3120,12 @@ msgstr "右視圖" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:167 msgctxt "@action:inmenu" msgid "Configure Cura..." -msgstr "設定 Cura…" +msgstr "設定 Cura..." #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:174 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." -msgstr "新增印表機(&A)…" +msgstr "新增印表機(&A)..." #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:180 msgctxt "@action:inmenu menubar:printer" @@ -3128,7 +3135,7 @@ msgstr "管理印表機(&I)..." #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:187 msgctxt "@action:inmenu" msgid "Manage Materials..." -msgstr "管理耗材…" +msgstr "管理耗材..." #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:195 msgctxt "@action:inmenu" @@ -3148,7 +3155,7 @@ msgstr "捨棄目前更改(&D)" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:222 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." -msgstr "從目前設定 / 覆寫值建立列印參數(&C)…" +msgstr "從目前設定 / 覆寫值建立列印參數(&C)..." #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:228 msgctxt "@action:inmenu menubar:profile" @@ -3173,7 +3180,7 @@ msgstr "新功能" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:258 msgctxt "@action:inmenu menubar:help" msgid "About..." -msgstr "關於…" +msgstr "關於..." #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:265 msgctxt "@action:inmenu menubar:edit" @@ -3266,12 +3273,12 @@ msgstr "重置所有模型旋轉" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:419 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." -msgstr "開啟檔案(&O)…" +msgstr "開啟檔案(&O)..." #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:427 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." -msgstr "新建專案(&N)…" +msgstr "新建專案(&N)..." #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Actions.qml:434 msgctxt "@action:inmenu menubar:help" @@ -3320,13 +3327,13 @@ msgstr "列印參數" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Cura.qml:563 msgctxt "@title:window %1 is the application name" msgid "Closing %1" -msgstr "" +msgstr "關閉 %1 中" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Cura.qml:564 #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Cura.qml:576 msgctxt "@label %1 is the application name" msgid "Are you sure you want to exit %1?" -msgstr "" +msgstr "是否確定要離開 %1 ?" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Cura.qml:614 #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Dialogs/OpenFilesIncludingProjectsDialog.qml:19 @@ -3362,7 +3369,7 @@ msgstr "新功能" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Dialogs/AboutDialog.qml:15 msgctxt "@title:window The argument is the application name." msgid "About %1" -msgstr "" +msgstr "關於 %1" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Dialogs/AboutDialog.qml:57 msgctxt "@label" @@ -3782,7 +3789,7 @@ msgstr "匯出(&E)" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Menus/FileMenu.qml:65 msgctxt "@action:inmenu menubar:file" msgid "Export Selection..." -msgstr "匯出選擇…" +msgstr "匯出選擇..." #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Menus/MaterialMenu.qml:13 msgctxt "@label:category menu label" @@ -4787,7 +4794,7 @@ msgstr "這個設定是所有擠出機共用的。修改它會同時更動到所 #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Settings/SettingItem.qml:191 msgctxt "@label" msgid "This setting is resolved from conflicting extruder-specific values:" -msgstr "" +msgstr "此設定是透過解決擠出機設定值衝突獲得:" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/Settings/SettingItem.qml:230 msgctxt "@label" @@ -4940,7 +4947,7 @@ msgstr "無法連接到裝置。" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/WelcomePages/AddPrinterByIpContent.qml:212 msgctxt "@label" msgid "Can't connect to your Ultimaker printer?" -msgstr "" +msgstr "無法連接到 Ultimaker 印表機?" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/WelcomePages/AddPrinterByIpContent.qml:211 msgctxt "@label" @@ -4965,27 +4972,27 @@ msgstr "連接" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/WelcomePages/CloudContent.qml:36 msgctxt "@label" msgid "Ultimaker Account" -msgstr "" +msgstr "Ultimaker 帳號" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/WelcomePages/CloudContent.qml:77 msgctxt "@text" msgid "Your key to connected 3D printing" -msgstr "" +msgstr "連線 3D 列印的金鑰" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/WelcomePages/CloudContent.qml:94 msgctxt "@text" msgid "- Customize your experience with more print profiles and plugins" -msgstr "" +msgstr "- 使用更多的列印參數設定和外掛訂做你的使用體驗" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/WelcomePages/CloudContent.qml:97 msgctxt "@text" msgid "- Stay flexible by syncing your setup and loading it anywhere" -msgstr "" +msgstr "- 通過同步設定可在任何地方將其載入以保持靈活性" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/WelcomePages/CloudContent.qml:100 msgctxt "@text" msgid "- Increase efficiency with a remote workflow on Ultimaker printers" -msgstr "" +msgstr "- 透過 Ultimaker 印表機上的遠端工作流程提高效率" #: /home/trin/Gedeeld/Projects/Cura/resources/qml/WelcomePages/CloudContent.qml:119 msgctxt "@button" @@ -5254,7 +5261,7 @@ msgstr "提供更改機器設定的方法(如列印範圍,噴頭大小等) #: MachineSettingsAction/plugin.json msgctxt "name" msgid "Machine Settings Action" -msgstr "" +msgstr "印表機設定操作" #: MonitorStage/plugin.json msgctxt "description" @@ -5589,12 +5596,12 @@ msgstr "升級版本 4.4 到 4.5" #: VersionUpgrade/VersionUpgrade45to46/plugin.json msgctxt "description" msgid "Upgrades configurations from Cura 4.5 to Cura 4.6." -msgstr "" +msgstr "將設定從 Cura 4.5 版本升級至 4.6 版本。" #: VersionUpgrade/VersionUpgrade45to46/plugin.json msgctxt "name" msgid "Version Upgrade 4.5 to 4.6" -msgstr "" +msgstr "升級版本 4.5 到 4.6" #: X3DReader/plugin.json msgctxt "description" diff --git a/resources/i18n/zh_TW/fdmprinter.def.json.po b/resources/i18n/zh_TW/fdmprinter.def.json.po index 9f001d6ae9..76a8becd31 100644 --- a/resources/i18n/zh_TW/fdmprinter.def.json.po +++ b/resources/i18n/zh_TW/fdmprinter.def.json.po @@ -83,7 +83,7 @@ msgstr "耗材 GUID" #: fdmprinter.def.json msgctxt "material_guid description" msgid "GUID of the material. This is set automatically." -msgstr "" +msgstr "耗材的 GUID,此項為自動設定。" #: fdmprinter.def.json msgctxt "material_diameter label" @@ -1253,12 +1253,12 @@ msgstr "套用到第一層所有多邊形的偏移量。負數值可以補償第 #: fdmprinter.def.json msgctxt "hole_xy_offset label" msgid "Hole Horizontal Expansion" -msgstr "" +msgstr "孔洞水平擴展" #: fdmprinter.def.json msgctxt "hole_xy_offset description" msgid "Amount of offset applied to all holes in each layer. Positive values increase the size of the holes, negative values reduce the size of the holes." -msgstr "" +msgstr "套用到每一層孔洞的偏移量。正值增加孔洞的大小,負值減小孔洞的大小。" #: fdmprinter.def.json msgctxt "z_seam_type label" @@ -2192,7 +2192,7 @@ msgstr "沖洗速度" #: fdmprinter.def.json msgctxt "material_flush_purge_speed description" msgid "How fast to prime the material after switching to a different material." -msgstr "" +msgstr "切換到另一耗材後,用多快的速度擠出耗材做沖洗。" #: fdmprinter.def.json msgctxt "material_flush_purge_length label" @@ -2202,27 +2202,27 @@ msgstr "沖洗長度" #: fdmprinter.def.json msgctxt "material_flush_purge_length description" msgid "How much material to use to purge the previous material out of the nozzle (in length of filament) when switching to a different material." -msgstr "" +msgstr "切換到另一耗材時,要使用多少耗材(以耗材長度計算)將噴頭內先前的耗材沖洗出來。" #: fdmprinter.def.json msgctxt "material_end_of_filament_purge_speed label" msgid "End of Filament Purge Speed" -msgstr "" +msgstr "耗材更換沖洗速度" #: fdmprinter.def.json msgctxt "material_end_of_filament_purge_speed description" msgid "How fast to prime the material after replacing an empty spool with a fresh spool of the same material." -msgstr "" +msgstr "更換新的一捲相同耗材後,用多快的速度擠出耗材做沖洗。" #: fdmprinter.def.json msgctxt "material_end_of_filament_purge_length label" msgid "End of Filament Purge Length" -msgstr "" +msgstr "耗材更換沖洗長度" #: fdmprinter.def.json msgctxt "material_end_of_filament_purge_length description" msgid "How much material to use to purge the previous material out of the nozzle (in length of filament) when replacing an empty spool with a fresh spool of the same material." -msgstr "" +msgstr "更換新的一捲相同耗材時,要使用多少耗材(以耗材長度計算)將噴頭內先前的耗材沖洗出來。" #: fdmprinter.def.json msgctxt "material_maximum_park_duration label" @@ -2232,7 +2232,7 @@ msgstr "最長停放時間" #: fdmprinter.def.json msgctxt "material_maximum_park_duration description" msgid "How long the material can be kept out of dry storage safely." -msgstr "" +msgstr "耗材可在乾燥箱外安全的存放多久。" #: fdmprinter.def.json msgctxt "material_no_load_move_factor label" @@ -2242,7 +2242,7 @@ msgstr "空載移動係數" #: fdmprinter.def.json msgctxt "material_no_load_move_factor description" msgid "A factor indicating how much the filament gets compressed between the feeder and the nozzle chamber, used to determine how far to move the material for a filament switch." -msgstr "" +msgstr "一個用來表示耗材在進料器和噴頭腔室之間能被壓縮多少的係數,用來決定耗材切換時需要移動多長。" #: fdmprinter.def.json msgctxt "material_flow label" @@ -3022,7 +3022,7 @@ msgstr "啟用回抽" #: fdmprinter.def.json msgctxt "retraction_enable description" msgid "Retract the filament when the nozzle is moving over a non-printed area." -msgstr "" +msgstr "當噴頭移動經過非列印區域時回抽耗材。" #: fdmprinter.def.json msgctxt "retract_at_layer_change label" @@ -3717,7 +3717,7 @@ msgstr "最小支撐 X/Y 間距" #: fdmprinter.def.json msgctxt "support_xy_distance_overhang description" msgid "Distance of the support structure from the overhang in the X/Y directions." -msgstr "" +msgstr "支撐結構在 X/Y 方向與突出部分的間距。 " #: fdmprinter.def.json msgctxt "support_bottom_stair_step_height label" @@ -4816,7 +4816,7 @@ msgstr "網格修復" #: fdmprinter.def.json msgctxt "meshfix description" msgid "Make the meshes more suited for 3D printing." -msgstr "" +msgstr "讓網格更適合 3D 列印。" #: fdmprinter.def.json msgctxt "meshfix_union_all label" @@ -4936,7 +4936,7 @@ msgstr "特殊模式" #: fdmprinter.def.json msgctxt "blackmagic description" msgid "Non-traditional ways to print your models." -msgstr "" +msgstr "以非傳統的方式列印你的模型。" #: fdmprinter.def.json msgctxt "print_sequence label" @@ -5111,7 +5111,7 @@ msgstr "實驗性" #: fdmprinter.def.json msgctxt "experimental description" msgid "Features that haven't completely been fleshed out yet." -msgstr "" +msgstr "尚未完全的功能。" #: fdmprinter.def.json msgctxt "support_tree_enable label" From b19087089737f79d3af7450886f7cb197c3412cd Mon Sep 17 00:00:00 2001 From: maht Date: Mon, 20 Apr 2020 12:50:00 +0200 Subject: [PATCH 26/37] Updated change log CURA-7380 --- resources/texts/change_log.txt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/resources/texts/change_log.txt b/resources/texts/change_log.txt index aebf21985b..926f9a145e 100644 --- a/resources/texts/change_log.txt +++ b/resources/texts/change_log.txt @@ -42,7 +42,22 @@ Fixed a bug where comments were removed from Start/End G-codes when opening from Values in the print monitor preheat fields were broken in previous versions, they have now been fixed by fieldOfview. * Stepper motor disarming during pause at height. -Some printers (like the Ultimaker S5) have a very heavy build platform. When the pause at height script was enabled, it would disarm the Z stepper motor at pause height. The weight of the build platform would cause the Z axis to lower by gravity and lose the Z position. ilyko96 has contributed a fix to the pause at height script so that the Z stepper motor now remains armed during a pause, locking the build plate in position. +Some printers automatically disable their steppers after a pause after a certain time. This script makes it possible to set that in the pause script (instead of relying on default behavior of the firmware). + +* Crash if logging in on two instances at the same time. +During the beta period we caught a critical bug where logging in to an Ultimaker account with two instances of Cura would crash the second instance. It crashes because while the web page is open, Cura opens a web server in the local host. The web page redirects to that web server when you've logged in, so that it knows that the log-in was successful and what the credentials are. Both instances try to create a web server on the same port, which is impossible. + +* "Changes detected from your Ultimaker account" message. +We fixed a bug on MacOS where the "Changes detected from your Ultimaker account" message kept showing over and over again. + +* Crashes when inactive. +Some people reported experiencing crashes when the computer had been inactive for a long time, or when the laptop got suspended or went to sleep. This has been fixed. + +* Support blocker is not blocking support. +Fixed an issue where the support blocker was not blocking support. + +* Sending slice message takes too long when using mesh helpers. +Fixed an issue where it would take too long to trigger a slice when using the mesh helpers and support blocker. * Flying Bear printers. oducceu has contributed a machine definition for the Flying Bear Ghost 4S Printer. From 9a2d2d32bfc8524e8229ea7561b4499c482bfcd9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 20 Apr 2020 13:52:40 +0200 Subject: [PATCH 27/37] Remove code that prevents Cura from starting on MAC The renderer isn't active when the view is activated. As such, no opengl context *ever* exists. In some cases this might cause it to get stuck in an indefinate loop. The comments around it are explaining a situation that can't occur. The activateView event of SolidView isn't triggered when switching to another view... --- plugins/SolidView/SolidView.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 8c9967be03..bd8215aba5 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -286,25 +286,6 @@ class SolidView(View): Logger.log("i", "X-Ray overlay found non-manifold pixels.") def event(self, event): - if event.type == Event.ViewActivateEvent: - # FIX: on Max OS X, somehow QOpenGLContext.currentContext() can become None during View switching. - # This can happen when you do the following steps: - # 1. Start Cura - # 2. Load a model - # 3. Switch to Custom mode - # 4. Select the model and click on the per-object tool icon - # 5. Switch view to Layer view or X-Ray - # 6. Cura will very likely crash - # It seems to be a timing issue that the currentContext can somehow be empty, but I have no clue why. - # This fix tries to reschedule the view changing event call on the Qt thread again if the current OpenGL - # context is None. - if Platform.isOSX(): - if QOpenGLContext.currentContext() is None: - Logger.log("d", "current context of OpenGL is empty on Mac OS X, will try to create shaders later") - Application.getInstance().callLater(lambda e = event: self.event(e)) - return - - if event.type == Event.ViewDeactivateEvent: if self._composite_pass and 'xray' in self._composite_pass.getLayerBindings(): self.getRenderer().removeRenderPass(self._xray_pass) From aa224dc6733b879230da8e973a788856dc466794 Mon Sep 17 00:00:00 2001 From: maht Date: Mon, 20 Apr 2020 15:00:32 +0200 Subject: [PATCH 28/37] Update change_log.txt --- resources/texts/change_log.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/texts/change_log.txt b/resources/texts/change_log.txt index 926f9a145e..209b9d2e25 100644 --- a/resources/texts/change_log.txt +++ b/resources/texts/change_log.txt @@ -48,7 +48,7 @@ Some printers automatically disable their steppers after a pause after a certain During the beta period we caught a critical bug where logging in to an Ultimaker account with two instances of Cura would crash the second instance. It crashes because while the web page is open, Cura opens a web server in the local host. The web page redirects to that web server when you've logged in, so that it knows that the log-in was successful and what the credentials are. Both instances try to create a web server on the same port, which is impossible. * "Changes detected from your Ultimaker account" message. -We fixed a bug on MacOS where the "Changes detected from your Ultimaker account" message kept showing over and over again. +We fixed a bug on MacOS where duplicate "Changes detected from your Ultimaker account" popups would appear at a single time. * Crashes when inactive. Some people reported experiencing crashes when the computer had been inactive for a long time, or when the laptop got suspended or went to sleep. This has been fixed. From 020f66b245c943b9d3f1cdf128198ea655a37c4e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 20 Apr 2020 15:11:28 +0200 Subject: [PATCH 29/37] Add option to overhang shader to make rendering model errors optional CURA-7147 --- cura/PreviewPass.py | 1 + resources/shaders/overhang.shader | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cura/PreviewPass.py b/cura/PreviewPass.py index da60db2d99..5e3982e1f3 100644 --- a/cura/PreviewPass.py +++ b/cura/PreviewPass.py @@ -61,6 +61,7 @@ class PreviewPass(RenderPass): self._shader.setUniformValue("u_ambientColor", [0.1, 0.1, 0.1, 1.0]) self._shader.setUniformValue("u_specularColor", [0.6, 0.6, 0.6, 1.0]) self._shader.setUniformValue("u_shininess", 20.0) + self._shader.setUniformValue("u_renderError", 0.0) # We don't want any error markers!. self._shader.setUniformValue("u_faceId", -1) # Don't render any selected faces in the preview. if not self._non_printing_shader: diff --git a/resources/shaders/overhang.shader b/resources/shaders/overhang.shader index b73ed3c701..8457158bc7 100644 --- a/resources/shaders/overhang.shader +++ b/resources/shaders/overhang.shader @@ -38,6 +38,8 @@ fragment = varying highp vec3 f_vertex; varying highp vec3 f_normal; + uniform lowp float u_renderError; + float round(float f) { return sign(f) * floor(abs(f) + 0.5); @@ -66,9 +68,11 @@ fragment = finalColor = (-normal.y > u_overhangAngle) ? u_overhangColor : finalColor; - vec3 grid = vec3(f_vertex.x - round(f_vertex.x), f_vertex.y - round(f_vertex.y), f_vertex.z - round(f_vertex.z)); - finalColor.a = dot(grid, grid) < 0.245 ? 0.667 : 1.0; - + if(u_renderError > 0.5) + { + vec3 grid = vec3(f_vertex.x - round(f_vertex.x), f_vertex.y - round(f_vertex.y), f_vertex.z - round(f_vertex.z)); + finalColor.a = dot(grid, grid) < 0.245 ? 0.667 : 1.0; + } gl_FragColor = finalColor; } @@ -104,6 +108,7 @@ fragment41core = uniform highp vec3 u_lightPosition; uniform mediump float u_shininess; uniform highp vec3 u_viewPosition; + uniform lowp float u_renderError; uniform lowp float u_overhangAngle; uniform lowp vec4 u_overhangColor; @@ -139,9 +144,11 @@ fragment41core = finalColor = (u_faceId != gl_PrimitiveID) ? ((-normal.y > u_overhangAngle) ? u_overhangColor : finalColor) : u_faceColor; frag_color = finalColor; - - vec3 grid = f_vertex - round(f_vertex); - frag_color.a = dot(grid, grid) < 0.245 ? 0.667 : 1.0; + if(u_renderError > 0.5) + { + vec3 grid = f_vertex - round(f_vertex); + frag_color.a = dot(grid, grid) < 0.245 ? 0.667 : 1.0; + } } [defaults] @@ -151,6 +158,7 @@ u_specularColor = [0.4, 0.4, 0.4, 1.0] u_overhangColor = [1.0, 0.0, 0.0, 1.0] u_faceColor = [0.0, 0.0, 1.0, 1.0] u_shininess = 20.0 +u_renderError = 1.0 [bindings] u_modelMatrix = model_matrix From 59f105c1b918881c6367362a16b20955d1f06692 Mon Sep 17 00:00:00 2001 From: maukcc Date: Mon, 20 Apr 2020 15:50:11 +0200 Subject: [PATCH 30/37] HMS434 update wipe codes changed --- resources/definitions/hms434.def.json | 2 +- resources/extruders/hms434_tool_1.def.json | 4 ++-- resources/extruders/hms434_tool_2.def.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/definitions/hms434.def.json b/resources/definitions/hms434.def.json index c4c73a5e77..846d1722ff 100644 --- a/resources/definitions/hms434.def.json +++ b/resources/definitions/hms434.def.json @@ -69,7 +69,7 @@ "material_bed_temp_wait": {"default_value": false }, "machine_max_feedrate_z": {"default_value": 10 }, "machine_acceleration": {"default_value": 180 }, - "machine_start_gcode": {"default_value": "\n;Neither Hybrid AM Systems nor any of Hybrid AM Systems representatives has any liabilities or gives any warranties on this .gcode file, or on any or all objects made with this .gcode file.\n\nM140 S{material_bed_temperature_layer_0}\n\nM117 Homing Y ......\nG28 Y\nM117 Homing X ......\nG28 X\nM117 Homing Z ......\nG28 Z F100\n\nG1 Z10 F900\nG1 X-30 Y100 F12000\n\nM190 S{material_bed_temperature_layer_0}\nM117 HMS434 Printing ...\n\nM42 P10 S255 ; chamberfans on" }, + "machine_start_gcode": {"default_value": "\n;Neither Hybrid AM Systems nor any of Hybrid AM Systems representatives has any liabilities or gives any warranties on this .gcode file, or on any or all objects made with this .gcode file.\n\nM140 S{material_bed_temperature_layer_0}\n\nM117 Homing Y ......\nG28 Y\nM117 Homing X ......\nG28 X\nM117 Homing Z ......\nG28 Z F100\n\nG1 Z10 F900\nG1 X-25 Y20 F12000\n\nM190 S{material_bed_temperature_layer_0}\nM117 HMS434 Printing ...\n\nM42 P10 S255 ; chamberfans on" }, "machine_end_gcode": {"default_value": "" }, "retraction_extra_prime_amount": {"minimum_value_warning": "-2.0" }, diff --git a/resources/extruders/hms434_tool_1.def.json b/resources/extruders/hms434_tool_1.def.json index 619d286716..ea094abea6 100644 --- a/resources/extruders/hms434_tool_1.def.json +++ b/resources/extruders/hms434_tool_1.def.json @@ -16,10 +16,10 @@ "machine_nozzle_offset_y": { "default_value": 0.0 }, "material_diameter": { "default_value": 1.75 }, "machine_extruder_start_code": { - "default_value": "\n;changing to tool1\nM83\nM109 T0 S{material_print_temperature}\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y120 F3000\nG1 X10 F12000\n\n" + "default_value": "\n;changing to tool1\nM83\nM109 T0 S{material_print_temperature}\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y40 F3000\nG1 X10 F12000\n\n" }, "machine_extruder_end_code": { - "default_value": "\nG1 X10 Y120 F12000\nG1 X-40 F12000\nM109 T0 R{material_standby_temperature}\nG1 Y100 F3000\n; ending tool1\n\n" + "default_value": "\nG1 X10 Y120 F12000\nG1 X-25 F12000\nM109 T0 R{material_standby_temperature}\nG1 Y20 F3000\n; ending tool1\n\n" } } } diff --git a/resources/extruders/hms434_tool_2.def.json b/resources/extruders/hms434_tool_2.def.json index 20e940df3b..64bc93c693 100644 --- a/resources/extruders/hms434_tool_2.def.json +++ b/resources/extruders/hms434_tool_2.def.json @@ -16,10 +16,10 @@ "machine_nozzle_offset_y": { "default_value": 0.0 }, "material_diameter": { "default_value": 1.75 }, "machine_extruder_start_code": { - "default_value": "\n;changing to tool2\nM83\nM109 T1 S{material_print_temperature}\nG1 E{switch_retraction_prime_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y120 F3000\nG1 X10 F12000\n\n" + "default_value": "\n;changing to tool2\nM83\nM109 T1 S{material_print_temperature}\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E{switch_extruder_retraction_amount} F300\nG1 E-{switch_extruder_retraction_amount} F2400\nG1 Y40 F3000\nG1 X10 F12000\n\n" }, "machine_extruder_end_code": { - "default_value": "\nG1 X10 Y120 F12000\nG1 X-40 F12000\nM109 T1 R{material_standby_temperature}\nG1 Y100 F3000\n; ending tool2\n\n" + "default_value": "\nG1 X10 Y120 F12000\nG1 X-25 F12000\nM109 T1 R{material_standby_temperature}\nG1 Y20 F3000\n; ending tool2\n\n" } } } From e3bc304d5d0b03391a84987378e7a6334ad93e1d Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Tue, 21 Apr 2020 10:59:37 +0200 Subject: [PATCH 31/37] Update sdk version to 7.2.0 CURA-7383 --- cura/ApplicationMetadata.py | 2 +- plugins/AMFReader/plugin.json | 2 +- plugins/TrimeshReader/plugin.json | 2 +- plugins/UFPReader/plugin.json | 2 +- resources/bundled_packages/cura.json | 204 +++++++++++++-------------- 5 files changed, 106 insertions(+), 106 deletions(-) diff --git a/cura/ApplicationMetadata.py b/cura/ApplicationMetadata.py index c16051d187..ee4eab597f 100644 --- a/cura/ApplicationMetadata.py +++ b/cura/ApplicationMetadata.py @@ -13,7 +13,7 @@ DEFAULT_CURA_DEBUG_MODE = False # Each release has a fixed SDK version coupled with it. It doesn't make sense to make it configurable because, for # example Cura 3.2 with SDK version 6.1 will not work. So the SDK version is hard-coded here and left out of the # CuraVersion.py.in template. -CuraSDKVersion = "7.1.0" +CuraSDKVersion = "7.2.0" try: from cura.CuraVersion import CuraAppName # type: ignore diff --git a/plugins/AMFReader/plugin.json b/plugins/AMFReader/plugin.json index cb108011ec..0f828c9393 100644 --- a/plugins/AMFReader/plugin.json +++ b/plugins/AMFReader/plugin.json @@ -3,5 +3,5 @@ "author": "fieldOfView", "version": "1.0.0", "description": "Provides support for reading AMF files.", - "api": "7.1.0" + "api": "7.2.0" } diff --git a/plugins/TrimeshReader/plugin.json b/plugins/TrimeshReader/plugin.json index e9b18946bb..1761628ac8 100644 --- a/plugins/TrimeshReader/plugin.json +++ b/plugins/TrimeshReader/plugin.json @@ -3,5 +3,5 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for reading model files.", - "api": "7.1.0" + "api": "7.2.0" } diff --git a/plugins/UFPReader/plugin.json b/plugins/UFPReader/plugin.json index 1468380035..cd1126c84b 100644 --- a/plugins/UFPReader/plugin.json +++ b/plugins/UFPReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for reading Ultimaker Format Packages.", - "supported_sdk_versions": ["7.1.0"], + "supported_sdk_versions": ["7.2.0"], "i18n-catalog": "cura" } \ No newline at end of file diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index 1378ed2b9c..0f43889d8d 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -6,7 +6,7 @@ "display_name": "3MF Reader", "description": "Provides support for reading 3MF files.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -23,7 +23,7 @@ "display_name": "3MF Writer", "description": "Provides support for writing 3MF files.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -40,7 +40,7 @@ "display_name": "AMF Reader", "description": "Provides support for reading AMF files.", "package_version": "1.0.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "fieldOfView", @@ -57,7 +57,7 @@ "display_name": "Cura Backups", "description": "Backup and restore your configuration.", "package_version": "1.2.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -74,7 +74,7 @@ "display_name": "CuraEngine Backend", "description": "Provides the link to the CuraEngine slicing backend.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -91,7 +91,7 @@ "display_name": "Cura Profile Reader", "description": "Provides support for importing Cura profiles.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -108,7 +108,7 @@ "display_name": "Cura Profile Writer", "description": "Provides support for exporting Cura profiles.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -125,7 +125,7 @@ "display_name": "Firmware Update Checker", "description": "Checks for firmware updates.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -142,7 +142,7 @@ "display_name": "Firmware Updater", "description": "Provides a machine actions for updating firmware.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -159,7 +159,7 @@ "display_name": "Compressed G-code Reader", "description": "Reads g-code from a compressed archive.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -176,7 +176,7 @@ "display_name": "Compressed G-code Writer", "description": "Writes g-code to a compressed archive.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -193,7 +193,7 @@ "display_name": "G-Code Profile Reader", "description": "Provides support for importing profiles from g-code files.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -210,7 +210,7 @@ "display_name": "G-Code Reader", "description": "Allows loading and displaying G-code files.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "VictorLarchenko", @@ -227,7 +227,7 @@ "display_name": "G-Code Writer", "description": "Writes g-code to a file.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -244,7 +244,7 @@ "display_name": "Image Reader", "description": "Enables ability to generate printable geometry from 2D image files.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -261,7 +261,7 @@ "display_name": "Legacy Cura Profile Reader", "description": "Provides support for importing profiles from legacy Cura versions.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -278,7 +278,7 @@ "display_name": "Machine Settings Action", "description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "fieldOfView", @@ -295,7 +295,7 @@ "display_name": "Model Checker", "description": "Checks models and print configuration for possible printing issues and give suggestions.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -312,7 +312,7 @@ "display_name": "Monitor Stage", "description": "Provides a monitor stage in Cura.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -329,7 +329,7 @@ "display_name": "Per-Object Settings Tool", "description": "Provides the per-model settings.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -346,7 +346,7 @@ "display_name": "Post Processing", "description": "Extension that allows for user created scripts for post processing.", "package_version": "2.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -363,7 +363,7 @@ "display_name": "Prepare Stage", "description": "Provides a prepare stage in Cura.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -380,7 +380,7 @@ "display_name": "Preview Stage", "description": "Provides a preview stage in Cura.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -397,7 +397,7 @@ "display_name": "Removable Drive Output Device", "description": "Provides removable drive hotplugging and writing support.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -414,7 +414,7 @@ "display_name": "Sentry Logger", "description": "Logs certain events so that they can be used by the crash reporter", "package_version": "1.0.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -431,7 +431,7 @@ "display_name": "Simulation View", "description": "Provides the Simulation view.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -448,7 +448,7 @@ "display_name": "Slice Info", "description": "Submits anonymous slice info. Can be disabled through preferences.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -465,7 +465,7 @@ "display_name": "Solid View", "description": "Provides a normal solid mesh view.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -482,7 +482,7 @@ "display_name": "Support Eraser Tool", "description": "Creates an eraser mesh to block the printing of support in certain places.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -499,7 +499,7 @@ "display_name": "Trimesh Reader", "description": "Provides support for reading model files.", "package_version": "1.0.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -516,7 +516,7 @@ "display_name": "Toolbox", "description": "Find, manage and install new Cura packages.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -533,7 +533,7 @@ "display_name": "UFP Reader", "description": "Provides support for reading Ultimaker Format Packages.", "package_version": "1.0.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -550,7 +550,7 @@ "display_name": "UFP Writer", "description": "Provides support for writing Ultimaker Format Packages.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -567,7 +567,7 @@ "display_name": "Ultimaker Machine Actions", "description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -584,7 +584,7 @@ "display_name": "UM3 Network Printing", "description": "Manages network connections to Ultimaker 3 printers.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -601,7 +601,7 @@ "display_name": "USB Printing", "description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.", "package_version": "1.0.2", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -618,7 +618,7 @@ "display_name": "Version Upgrade 2.1 to 2.2", "description": "Upgrades configurations from Cura 2.1 to Cura 2.2.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -635,7 +635,7 @@ "display_name": "Version Upgrade 2.2 to 2.4", "description": "Upgrades configurations from Cura 2.2 to Cura 2.4.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -652,7 +652,7 @@ "display_name": "Version Upgrade 2.5 to 2.6", "description": "Upgrades configurations from Cura 2.5 to Cura 2.6.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -669,7 +669,7 @@ "display_name": "Version Upgrade 2.6 to 2.7", "description": "Upgrades configurations from Cura 2.6 to Cura 2.7.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -686,7 +686,7 @@ "display_name": "Version Upgrade 2.7 to 3.0", "description": "Upgrades configurations from Cura 2.7 to Cura 3.0.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -703,7 +703,7 @@ "display_name": "Version Upgrade 3.0 to 3.1", "description": "Upgrades configurations from Cura 3.0 to Cura 3.1.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -720,7 +720,7 @@ "display_name": "Version Upgrade 3.2 to 3.3", "description": "Upgrades configurations from Cura 3.2 to Cura 3.3.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -737,7 +737,7 @@ "display_name": "Version Upgrade 3.3 to 3.4", "description": "Upgrades configurations from Cura 3.3 to Cura 3.4.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -754,7 +754,7 @@ "display_name": "Version Upgrade 3.4 to 3.5", "description": "Upgrades configurations from Cura 3.4 to Cura 3.5.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -771,7 +771,7 @@ "display_name": "Version Upgrade 3.5 to 4.0", "description": "Upgrades configurations from Cura 3.5 to Cura 4.0.", "package_version": "1.0.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -788,7 +788,7 @@ "display_name": "Version Upgrade 4.0 to 4.1", "description": "Upgrades configurations from Cura 4.0 to Cura 4.1.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -805,7 +805,7 @@ "display_name": "Version Upgrade 4.1 to 4.2", "description": "Upgrades configurations from Cura 4.1 to Cura 4.2.", "package_version": "1.0.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -822,7 +822,7 @@ "display_name": "Version Upgrade 4.2 to 4.3", "description": "Upgrades configurations from Cura 4.2 to Cura 4.3.", "package_version": "1.0.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -839,7 +839,7 @@ "display_name": "Version Upgrade 4.3 to 4.4", "description": "Upgrades configurations from Cura 4.3 to Cura 4.4.", "package_version": "1.0.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -856,7 +856,7 @@ "display_name": "Version Upgrade 4.4 to 4.5", "description": "Upgrades configurations from Cura 4.4 to Cura 4.5.", "package_version": "1.0.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -873,7 +873,7 @@ "display_name": "Version Upgrade 4.5 to 4.6", "description": "Upgrades configurations from Cura 4.5 to Cura 4.6.", "package_version": "1.0.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -890,7 +890,7 @@ "display_name": "Version Upgrade 4.6 to 4.7", "description": "Upgrades configurations from Cura 4.6 to Cura 4.7.", "package_version": "1.0.0", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -907,7 +907,7 @@ "display_name": "X3D Reader", "description": "Provides support for reading X3D files.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "SevaAlekseyev", @@ -924,7 +924,7 @@ "display_name": "XML Material Profiles", "description": "Provides capabilities to read and write XML-based material profiles.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -941,7 +941,7 @@ "display_name": "X-Ray View", "description": "Provides the X-Ray view.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -958,7 +958,7 @@ "display_name": "Generic ABS", "description": "The generic ABS profile which other profiles can be based upon.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -976,7 +976,7 @@ "display_name": "Generic BAM", "description": "The generic BAM profile which other profiles can be based upon.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -994,7 +994,7 @@ "display_name": "Generic CFF CPE", "description": "The generic CFF CPE profile which other profiles can be based upon.", "package_version": "1.1.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1012,7 +1012,7 @@ "display_name": "Generic CFF PA", "description": "The generic CFF PA profile which other profiles can be based upon.", "package_version": "1.1.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1030,7 +1030,7 @@ "display_name": "Generic CPE", "description": "The generic CPE profile which other profiles can be based upon.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1048,7 +1048,7 @@ "display_name": "Generic CPE+", "description": "The generic CPE+ profile which other profiles can be based upon.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1066,7 +1066,7 @@ "display_name": "Generic GFF CPE", "description": "The generic GFF CPE profile which other profiles can be based upon.", "package_version": "1.1.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1084,7 +1084,7 @@ "display_name": "Generic GFF PA", "description": "The generic GFF PA profile which other profiles can be based upon.", "package_version": "1.1.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1102,7 +1102,7 @@ "display_name": "Generic HIPS", "description": "The generic HIPS profile which other profiles can be based upon.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1120,7 +1120,7 @@ "display_name": "Generic Nylon", "description": "The generic Nylon profile which other profiles can be based upon.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1138,7 +1138,7 @@ "display_name": "Generic PC", "description": "The generic PC profile which other profiles can be based upon.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1156,7 +1156,7 @@ "display_name": "Generic PETG", "description": "The generic PETG profile which other profiles can be based upon.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1174,7 +1174,7 @@ "display_name": "Generic PLA", "description": "The generic PLA profile which other profiles can be based upon.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1192,7 +1192,7 @@ "display_name": "Generic PP", "description": "The generic PP profile which other profiles can be based upon.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1210,7 +1210,7 @@ "display_name": "Generic PVA", "description": "The generic PVA profile which other profiles can be based upon.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1228,7 +1228,7 @@ "display_name": "Generic Tough PLA", "description": "The generic Tough PLA profile which other profiles can be based upon.", "package_version": "1.0.2", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1246,7 +1246,7 @@ "display_name": "Generic TPU", "description": "The generic TPU profile which other profiles can be based upon.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1264,7 +1264,7 @@ "display_name": "Dagoma Chromatik PLA", "description": "Filament testé et approuvé pour les imprimantes 3D Dagoma. Chromatik est l'idéal pour débuter et suivre les tutoriels premiers pas. Il vous offre qualité et résistance pour chacune de vos impressions.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://dagoma.fr/boutique/filaments.html", "author": { "author_id": "Dagoma", @@ -1281,7 +1281,7 @@ "display_name": "FABtotum ABS", "description": "This material is easy to be extruded but it is not the simplest to use. It is one of the most used in 3D printing to get very well finished objects. It is not sustainable and its smoke can be dangerous if inhaled. The reason to prefer this filament to PLA is mainly because of its precision and mechanical specs. ABS (for plastic) stands for Acrylonitrile Butadiene Styrene and it is a thermoplastic which is widely used in everyday objects. It can be printed with any FFF 3D printer which can get to high temperatures as it must be extruded in a range between 220° and 245°, so it’s compatible with all versions of the FABtotum Personal fabricator.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=40", "author": { "author_id": "FABtotum", @@ -1298,7 +1298,7 @@ "display_name": "FABtotum Nylon", "description": "When 3D printing started this material was not listed among the extrudable filaments. It is flexible as well as resistant to tractions. It is well known for its uses in textile but also in industries which require a strong and flexible material. There are different kinds of Nylon: 3D printing mostly uses Nylon 6 and Nylon 6.6, which are the most common. It requires higher temperatures to be printed, so a 3D printer must be able to reach them (around 240°C): the FABtotum, of course, can.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=53", "author": { "author_id": "FABtotum", @@ -1315,7 +1315,7 @@ "display_name": "FABtotum PLA", "description": "It is the most common filament used for 3D printing. It is studied to be bio-degradable as it comes from corn starch’s sugar mainly. It is completely made of renewable sources and has no footprint on polluting. PLA stands for PolyLactic Acid and it is a thermoplastic that today is still considered the easiest material to be 3D printed. It can be extruded at lower temperatures: the standard range of FABtotum’s one is between 185° and 195°.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=39", "author": { "author_id": "FABtotum", @@ -1332,7 +1332,7 @@ "display_name": "FABtotum TPU Shore 98A", "description": "", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=66", "author": { "author_id": "FABtotum", @@ -1349,7 +1349,7 @@ "display_name": "Fiberlogy HD PLA", "description": "With our HD PLA you have many more options. You can use this material in two ways. Choose the one you like best. You can use it as a normal PLA and get prints characterized by a very good adhesion between the layers and high precision. You can also make your prints acquire similar properties to that of ABS – better impact resistance and high temperature resistance. All you need is an oven. Yes, an oven! By annealing our HD PLA in an oven, in accordance with the manual, you will avoid all the inconveniences of printing with ABS, such as unpleasant odour or hazardous fumes.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "http://fiberlogy.com/en/fiberlogy-filaments/filament-hd-pla/", "author": { "author_id": "Fiberlogy", @@ -1366,7 +1366,7 @@ "display_name": "Filo3D PLA", "description": "Fast, safe and reliable printing. PLA is ideal for the fast and reliable printing of parts and prototypes with a great surface quality.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://dagoma.fr", "author": { "author_id": "Dagoma", @@ -1383,7 +1383,7 @@ "display_name": "IMADE3D JellyBOX PETG", "description": "", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "http://shop.imade3d.com/filament.html", "author": { "author_id": "IMADE3D", @@ -1400,7 +1400,7 @@ "display_name": "IMADE3D JellyBOX PLA", "description": "", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "http://shop.imade3d.com/filament.html", "author": { "author_id": "IMADE3D", @@ -1417,7 +1417,7 @@ "display_name": "Octofiber PLA", "description": "PLA material from Octofiber.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://nl.octofiber.com/3d-printing-filament/pla.html", "author": { "author_id": "Octofiber", @@ -1434,7 +1434,7 @@ "display_name": "PolyFlex™ PLA", "description": "PolyFlex™ is a highly flexible yet easy to print 3D printing material. Featuring good elasticity and a large strain-to- failure, PolyFlex™ opens up a completely new realm of applications.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "http://www.polymaker.com/shop/polyflex/", "author": { "author_id": "Polymaker", @@ -1451,7 +1451,7 @@ "display_name": "PolyMax™ PLA", "description": "PolyMax™ PLA is a 3D printing material with excellent mechanical properties and printing quality. PolyMax™ PLA has an impact resistance of up to nine times that of regular PLA, and better overall mechanical properties than ABS.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "http://www.polymaker.com/shop/polymax/", "author": { "author_id": "Polymaker", @@ -1468,7 +1468,7 @@ "display_name": "PolyPlus™ PLA True Colour", "description": "PolyPlus™ PLA is a premium PLA designed for all desktop FDM/FFF 3D printers. It is produced with our patented Jam-Free™ technology that ensures consistent extrusion and prevents jams.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "http://www.polymaker.com/shop/polyplus-true-colour/", "author": { "author_id": "Polymaker", @@ -1485,7 +1485,7 @@ "display_name": "PolyWood™ PLA", "description": "PolyWood™ is a wood mimic printing material that contains no actual wood ensuring a clean Jam-Free™ printing experience.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "http://www.polymaker.com/shop/polywood/", "author": { "author_id": "Polymaker", @@ -1502,7 +1502,7 @@ "display_name": "Ultimaker ABS", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.2.2", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1521,7 +1521,7 @@ "display_name": "Ultimaker Breakaway", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com/products/materials/breakaway", "author": { "author_id": "UltimakerPackages", @@ -1540,7 +1540,7 @@ "display_name": "Ultimaker CPE", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.2.2", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1559,7 +1559,7 @@ "display_name": "Ultimaker CPE+", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.2.2", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com/products/materials/cpe", "author": { "author_id": "UltimakerPackages", @@ -1578,7 +1578,7 @@ "display_name": "Ultimaker Nylon", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.2.2", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1597,7 +1597,7 @@ "display_name": "Ultimaker PC", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.2.2", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com/products/materials/pc", "author": { "author_id": "UltimakerPackages", @@ -1616,7 +1616,7 @@ "display_name": "Ultimaker PLA", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.2.2", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1635,7 +1635,7 @@ "display_name": "Ultimaker PP", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.2.2", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com/products/materials/pp", "author": { "author_id": "UltimakerPackages", @@ -1654,7 +1654,7 @@ "display_name": "Ultimaker PVA", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.2.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1673,7 +1673,7 @@ "display_name": "Ultimaker TPU 95A", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.2.2", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com/products/materials/tpu-95a", "author": { "author_id": "UltimakerPackages", @@ -1692,7 +1692,7 @@ "display_name": "Ultimaker Tough PLA", "description": "Example package for material and quality profiles for Ultimaker materials.", "package_version": "1.0.3", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://ultimaker.com/products/materials/tough-pla", "author": { "author_id": "UltimakerPackages", @@ -1711,7 +1711,7 @@ "display_name": "Vertex Delta ABS", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1728,7 +1728,7 @@ "display_name": "Vertex Delta PET", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1745,7 +1745,7 @@ "display_name": "Vertex Delta PLA", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1762,7 +1762,7 @@ "display_name": "Vertex Delta TPU", "description": "ABS material and quality files for the Delta Vertex K8800.", "package_version": "1.0.1", - "sdk_version": "7.1.0", + "sdk_version": "7.2.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", From f38ae5bb96d901e8422da0ca80b3898f5b19b880 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 21 Apr 2020 11:39:49 +0200 Subject: [PATCH 32/37] Fix i18n catalog for per-object settings panel Turns out that all of these had not been working properly for a while. Contributes to issue CURA-7344. --- plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 358fec4a31..6eaadf3e83 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -54,7 +54,7 @@ Item UM.ActiveTool.setProperty("MeshType", type) } - UM.I18nCatalog { id: catalog; name: "uranium"} + UM.I18nCatalog { id: catalog; name: "cura"} Column { From 7302e8ba7fb3293f814631b838e486ff2eb57631 Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Tue, 21 Apr 2020 11:46:12 +0200 Subject: [PATCH 33/37] Update missed sdk versions from 7.1 to 7.2.0 for Cura 4.6 CURA-7383 --- plugins/3MFReader/plugin.json | 2 +- plugins/3MFWriter/plugin.json | 2 +- plugins/CuraDrive/plugin.json | 2 +- plugins/CuraEngineBackend/plugin.json | 2 +- plugins/CuraProfileReader/plugin.json | 2 +- plugins/CuraProfileWriter/plugin.json | 2 +- plugins/FirmwareUpdateChecker/plugin.json | 2 +- plugins/FirmwareUpdater/plugin.json | 2 +- plugins/GCodeGzReader/plugin.json | 2 +- plugins/GCodeGzWriter/plugin.json | 2 +- plugins/GCodeProfileReader/plugin.json | 2 +- plugins/GCodeReader/plugin.json | 2 +- plugins/GCodeWriter/plugin.json | 2 +- plugins/ImageReader/plugin.json | 2 +- plugins/LegacyProfileReader/plugin.json | 2 +- plugins/MachineSettingsAction/plugin.json | 2 +- plugins/ModelChecker/plugin.json | 2 +- plugins/MonitorStage/plugin.json | 2 +- plugins/PerObjectSettingsTool/plugin.json | 2 +- plugins/PostProcessingPlugin/plugin.json | 2 +- plugins/PrepareStage/plugin.json | 2 +- plugins/PreviewStage/plugin.json | 2 +- plugins/RemovableDriveOutputDevice/plugin.json | 2 +- plugins/SentryLogger/plugin.json | 2 +- plugins/SimulationView/plugin.json | 2 +- plugins/SliceInfoPlugin/plugin.json | 2 +- plugins/SolidView/plugin.json | 2 +- plugins/SupportEraser/plugin.json | 2 +- plugins/Toolbox/plugin.json | 2 +- plugins/UFPWriter/plugin.json | 2 +- plugins/UM3NetworkPrinting/plugin.json | 2 +- plugins/USBPrinting/plugin.json | 2 +- plugins/UltimakerMachineActions/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade46to47/plugin.json | 2 +- plugins/X3DReader/plugin.json | 2 +- plugins/XRayView/plugin.json | 2 +- plugins/XmlMaterialProfile/plugin.json | 2 +- 53 files changed, 53 insertions(+), 53 deletions(-) diff --git a/plugins/3MFReader/plugin.json b/plugins/3MFReader/plugin.json index 7e868d3bd0..7e434d03b2 100644 --- a/plugins/3MFReader/plugin.json +++ b/plugins/3MFReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for reading 3MF files.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/3MFWriter/plugin.json b/plugins/3MFWriter/plugin.json index 1d22691dfc..421b6b5b6f 100644 --- a/plugins/3MFWriter/plugin.json +++ b/plugins/3MFWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for writing 3MF files.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/CuraDrive/plugin.json b/plugins/CuraDrive/plugin.json index 58a8d95b83..506d4cf231 100644 --- a/plugins/CuraDrive/plugin.json +++ b/plugins/CuraDrive/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Backup and restore your configuration.", "version": "1.2.0", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/CuraEngineBackend/plugin.json b/plugins/CuraEngineBackend/plugin.json index 6817ad27be..2eee5deb61 100644 --- a/plugins/CuraEngineBackend/plugin.json +++ b/plugins/CuraEngineBackend/plugin.json @@ -2,7 +2,7 @@ "name": "CuraEngine Backend", "author": "Ultimaker B.V.", "description": "Provides the link to the CuraEngine slicing backend.", - "api": "7.1", + "api": "7.2.0", "version": "1.0.1", "i18n-catalog": "cura" } diff --git a/plugins/CuraProfileReader/plugin.json b/plugins/CuraProfileReader/plugin.json index 82fcd4d61b..080c9c5244 100644 --- a/plugins/CuraProfileReader/plugin.json +++ b/plugins/CuraProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for importing Cura profiles.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/CuraProfileWriter/plugin.json b/plugins/CuraProfileWriter/plugin.json index 224290cb31..77d18dc76c 100644 --- a/plugins/CuraProfileWriter/plugin.json +++ b/plugins/CuraProfileWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for exporting Cura profiles.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog":"cura" } diff --git a/plugins/FirmwareUpdateChecker/plugin.json b/plugins/FirmwareUpdateChecker/plugin.json index fc20bc5693..7c1f22c753 100644 --- a/plugins/FirmwareUpdateChecker/plugin.json +++ b/plugins/FirmwareUpdateChecker/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Checks for firmware updates.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/FirmwareUpdater/plugin.json b/plugins/FirmwareUpdater/plugin.json index 5c44e2629c..fad955a043 100644 --- a/plugins/FirmwareUpdater/plugin.json +++ b/plugins/FirmwareUpdater/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides a machine actions for updating firmware.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/GCodeGzReader/plugin.json b/plugins/GCodeGzReader/plugin.json index 6aa4fa8905..05265bf29b 100644 --- a/plugins/GCodeGzReader/plugin.json +++ b/plugins/GCodeGzReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Reads g-code from a compressed archive.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/GCodeGzWriter/plugin.json b/plugins/GCodeGzWriter/plugin.json index f663ba2165..48e39c9429 100644 --- a/plugins/GCodeGzWriter/plugin.json +++ b/plugins/GCodeGzWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Writes g-code to a compressed archive.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/GCodeProfileReader/plugin.json b/plugins/GCodeProfileReader/plugin.json index 79bb661ada..9e06ab1b73 100644 --- a/plugins/GCodeProfileReader/plugin.json +++ b/plugins/GCodeProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for importing profiles from g-code files.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/GCodeReader/plugin.json b/plugins/GCodeReader/plugin.json index 49cd06641c..d64bcb2513 100644 --- a/plugins/GCodeReader/plugin.json +++ b/plugins/GCodeReader/plugin.json @@ -3,6 +3,6 @@ "author": "Victor Larchenko, Ultimaker B.V.", "version": "1.0.1", "description": "Allows loading and displaying G-code files.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/GCodeWriter/plugin.json b/plugins/GCodeWriter/plugin.json index 9d34c7980a..31e72e2032 100644 --- a/plugins/GCodeWriter/plugin.json +++ b/plugins/GCodeWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Writes g-code to a file.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/ImageReader/plugin.json b/plugins/ImageReader/plugin.json index feaff38efe..890c13c202 100644 --- a/plugins/ImageReader/plugin.json +++ b/plugins/ImageReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Enables ability to generate printable geometry from 2D image files.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/LegacyProfileReader/plugin.json b/plugins/LegacyProfileReader/plugin.json index 58573f6446..a3036e25d2 100644 --- a/plugins/LegacyProfileReader/plugin.json +++ b/plugins/LegacyProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for importing profiles from legacy Cura versions.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/MachineSettingsAction/plugin.json b/plugins/MachineSettingsAction/plugin.json index 0e646e4e87..18e28903e1 100644 --- a/plugins/MachineSettingsAction/plugin.json +++ b/plugins/MachineSettingsAction/plugin.json @@ -3,6 +3,6 @@ "author": "fieldOfView, Ultimaker B.V.", "version": "1.0.1", "description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/ModelChecker/plugin.json b/plugins/ModelChecker/plugin.json index 6efccc39bd..4624f24ea9 100644 --- a/plugins/ModelChecker/plugin.json +++ b/plugins/ModelChecker/plugin.json @@ -2,7 +2,7 @@ "name": "Model Checker", "author": "Ultimaker B.V.", "version": "1.0.1", - "api": "7.1", + "api": "7.2.0", "description": "Checks models and print configuration for possible printing issues and give suggestions.", "i18n-catalog": "cura" } diff --git a/plugins/MonitorStage/plugin.json b/plugins/MonitorStage/plugin.json index a79a056cb8..541518b9e1 100644 --- a/plugins/MonitorStage/plugin.json +++ b/plugins/MonitorStage/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides a monitor stage in Cura.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/PerObjectSettingsTool/plugin.json b/plugins/PerObjectSettingsTool/plugin.json index 9567200a6a..ebc51a5e94 100644 --- a/plugins/PerObjectSettingsTool/plugin.json +++ b/plugins/PerObjectSettingsTool/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides the Per Model Settings.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/PostProcessingPlugin/plugin.json b/plugins/PostProcessingPlugin/plugin.json index 814499ca0f..21a7cedb75 100644 --- a/plugins/PostProcessingPlugin/plugin.json +++ b/plugins/PostProcessingPlugin/plugin.json @@ -2,7 +2,7 @@ "name": "Post Processing", "author": "Ultimaker", "version": "2.2.1", - "api": "7.1", + "api": "7.2.0", "description": "Extension that allows for user created scripts for post processing", "catalog": "cura" } \ No newline at end of file diff --git a/plugins/PrepareStage/plugin.json b/plugins/PrepareStage/plugin.json index 3c2f72dc53..49c6ba873d 100644 --- a/plugins/PrepareStage/plugin.json +++ b/plugins/PrepareStage/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides a prepare stage in Cura.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/PreviewStage/plugin.json b/plugins/PreviewStage/plugin.json index daa0ba8748..813a28d245 100644 --- a/plugins/PreviewStage/plugin.json +++ b/plugins/PreviewStage/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides a preview stage in Cura.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/RemovableDriveOutputDevice/plugin.json b/plugins/RemovableDriveOutputDevice/plugin.json index df47fdeeff..e70be24dda 100644 --- a/plugins/RemovableDriveOutputDevice/plugin.json +++ b/plugins/RemovableDriveOutputDevice/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Provides removable drive hotplugging and writing support.", "version": "1.0.1", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/SentryLogger/plugin.json b/plugins/SentryLogger/plugin.json index 03d7afbf5d..0419e70d25 100644 --- a/plugins/SentryLogger/plugin.json +++ b/plugins/SentryLogger/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Logs certain events so that they can be used by the crash reporter", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/SimulationView/plugin.json b/plugins/SimulationView/plugin.json index a6dc8052d7..9a0f322888 100644 --- a/plugins/SimulationView/plugin.json +++ b/plugins/SimulationView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides the Simulation view.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/SliceInfoPlugin/plugin.json b/plugins/SliceInfoPlugin/plugin.json index 15e4b6fa97..b5b8d48713 100644 --- a/plugins/SliceInfoPlugin/plugin.json +++ b/plugins/SliceInfoPlugin/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Submits anonymous slice info. Can be disabled through preferences.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/SolidView/plugin.json b/plugins/SolidView/plugin.json index e30f7f56c6..1619ced52f 100644 --- a/plugins/SolidView/plugin.json +++ b/plugins/SolidView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides a normal solid mesh view.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/SupportEraser/plugin.json b/plugins/SupportEraser/plugin.json index f6f21e94e3..5686995eac 100644 --- a/plugins/SupportEraser/plugin.json +++ b/plugins/SupportEraser/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Creates an eraser mesh to block the printing of support in certain places", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/Toolbox/plugin.json b/plugins/Toolbox/plugin.json index 742b25422b..f1243d2166 100644 --- a/plugins/Toolbox/plugin.json +++ b/plugins/Toolbox/plugin.json @@ -2,6 +2,6 @@ "name": "Toolbox", "author": "Ultimaker B.V.", "version": "1.0.1", - "api": "7.1", + "api": "7.2.0", "description": "Find, manage and install new Cura packages." } diff --git a/plugins/UFPWriter/plugin.json b/plugins/UFPWriter/plugin.json index 34586554b8..2aa46f7830 100644 --- a/plugins/UFPWriter/plugin.json +++ b/plugins/UFPWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for writing Ultimaker Format Packages.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/plugin.json b/plugins/UM3NetworkPrinting/plugin.json index 7de542404a..5d98658cd9 100644 --- a/plugins/UM3NetworkPrinting/plugin.json +++ b/plugins/UM3NetworkPrinting/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Manages network connections to Ultimaker networked printers.", "version": "2.0.0", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/USBPrinting/plugin.json b/plugins/USBPrinting/plugin.json index 841c8b16a5..7d947061cf 100644 --- a/plugins/USBPrinting/plugin.json +++ b/plugins/USBPrinting/plugin.json @@ -2,7 +2,7 @@ "name": "USB printing", "author": "Ultimaker B.V.", "version": "1.0.2", - "api": "7.1", + "api": "7.2.0", "description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.", "i18n-catalog": "cura" } diff --git a/plugins/UltimakerMachineActions/plugin.json b/plugins/UltimakerMachineActions/plugin.json index 3fd504681a..607597a8c8 100644 --- a/plugins/UltimakerMachineActions/plugin.json +++ b/plugins/UltimakerMachineActions/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json b/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json index 222d276463..96e2dace32 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 2.1 to Cura 2.2.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json b/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json index dde19da7a3..06f03f1760 100644 --- a/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 2.2 to Cura 2.4.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json b/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json index d6470fc0e5..952586764e 100644 --- a/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 2.5 to Cura 2.6.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json b/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json index ba7000ea3d..fe5f70baed 100644 --- a/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 2.6 to Cura 2.7.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json b/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json index 94293284bf..c4dba30510 100644 --- a/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 2.7 to Cura 3.0.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json b/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json index e3f77dd5f4..48cea8dd1f 100644 --- a/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 3.0 to Cura 3.1.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json index baed818b83..2208294b81 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 3.2 to Cura 3.3.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json b/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json index b536747c7d..3ecb32071d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 3.3 to Cura 3.4.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json b/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json index 0b58abee95..e47d4dce72 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 3.4 to Cura 3.5.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json b/plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json index 169d7748f5..01ff9b81c5 100644 --- a/plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 3.5 to Cura 4.0.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json b/plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json index 49d2f4131c..d321c71e02 100644 --- a/plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Upgrades configurations from Cura 4.0 to Cura 4.1.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json b/plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json index 42ffafe5ec..f59a5b3861 100644 --- a/plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.1 to Cura 4.2.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json b/plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json index e429e73d1c..86a3ea607e 100644 --- a/plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.2 to Cura 4.3.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json b/plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json index 85cbd5a3e7..f08b5a50bb 100644 --- a/plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.3 to Cura 4.4.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json b/plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json index 01d970e1ef..ce98c9f94a 100644 --- a/plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.4 to Cura 4.5.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json b/plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json index ceabdb6d9d..08c3c57ed1 100644 --- a/plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.5 to Cura 4.6.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade46to47/plugin.json b/plugins/VersionUpgrade/VersionUpgrade46to47/plugin.json index 33df516ea0..5a92355d0a 100644 --- a/plugins/VersionUpgrade/VersionUpgrade46to47/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade46to47/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 4.6 to Cura 4.7.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/X3DReader/plugin.json b/plugins/X3DReader/plugin.json index 052acf1e5f..0d523a1bf6 100644 --- a/plugins/X3DReader/plugin.json +++ b/plugins/X3DReader/plugin.json @@ -3,6 +3,6 @@ "author": "Seva Alekseyev, Ultimaker B.V.", "version": "1.0.1", "description": "Provides support for reading X3D files.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/XRayView/plugin.json b/plugins/XRayView/plugin.json index 64fe71e0c1..344c5e660f 100644 --- a/plugins/XRayView/plugin.json +++ b/plugins/XRayView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides the X-Ray view.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } diff --git a/plugins/XmlMaterialProfile/plugin.json b/plugins/XmlMaterialProfile/plugin.json index 1128afbeeb..121ada7b84 100644 --- a/plugins/XmlMaterialProfile/plugin.json +++ b/plugins/XmlMaterialProfile/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.1", "description": "Provides capabilities to read and write XML-based material profiles.", - "api": "7.1", + "api": "7.2.0", "i18n-catalog": "cura" } From ad6f837f44748f9740bc00341e0810fddb1f113f Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Tue, 21 Apr 2020 14:41:17 +0200 Subject: [PATCH 34/37] Force reloading the VariantNode if it exists When the MachineNode is updated through _loadAll, it was not updating its VariantNodes when they already existed. This caused an issue to UM2+Olsson block printers, where changing the G-Code flavor to Marlin was not updating the VariantNodes to search for materials (since the variants already existed). This commit fixes that by forcing an update to the existing Variant nodes. CURA-7354 --- cura/Machines/MachineNode.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cura/Machines/MachineNode.py b/cura/Machines/MachineNode.py index 0974c3dca7..6a415b01c4 100644 --- a/cura/Machines/MachineNode.py +++ b/cura/Machines/MachineNode.py @@ -171,6 +171,10 @@ class MachineNode(ContainerNode): if variant_name not in self.variants: self.variants[variant_name] = VariantNode(variant["id"], machine = self) self.variants[variant_name].materialsChanged.connect(self.materialsChanged) + else: + # Force reloading the materials if the variant already exists or else materals won't be loaded + # when the G-Code flavor changes --> CURA-7354 + self.variants[variant_name]._loadAll() if not self.variants: self.variants["empty"] = VariantNode("empty_variant", machine = self) From 888937d03b128a8e9fd4508ef6a33e11f89a3ea0 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 22 Apr 2020 11:39:10 +0200 Subject: [PATCH 35/37] Use initial layer bed/print temperatures These are more likely what the user wants the printer to start up to. Fixes #7505. --- resources/definitions/flsun_qq_s.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/flsun_qq_s.def.json b/resources/definitions/flsun_qq_s.def.json index 9c3bf571ae..a0c424d768 100644 --- a/resources/definitions/flsun_qq_s.def.json +++ b/resources/definitions/flsun_qq_s.def.json @@ -42,7 +42,7 @@ "default_value": 1.75 }, "machine_start_gcode": { - "default_value": "G21\nG90\nM82\nM107 T0\nM190 S{material_bed_temperature}\nM109 S{material_print_temperature} T0\nG28\nG92 E0\nG0 E3 F200\nG92 E0\n" + "default_value": "G21\nG90\nM82\nM107 T0\nM190 S{material_bed_temperature_layer_0}\nM109 S{material_print_temperature_layer_0} T0\nG28\nG92 E0\nG0 E3 F200\nG92 E0\n" }, "machine_end_gcode": { "default_value": "M107 T0\nM104 S0\nM104 S0 T1\nM140 S0\nG92 E0\nG91\nG1 E-1 F300 \nG1 Z+0.5 E-5 X-20 Y-20 F9000\nG28 X0 Y0\nM84 ;steppers off\nG90 ;absolute positioning\n" From 7ed59503d63c0a531f5f1d361d58be96c1d23410 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 22 Apr 2020 11:49:27 +0200 Subject: [PATCH 36/37] Round doesn't work for OpenGL 2.1 because it was only added in glsl 130. So mad at myself right now >:-( Part of the further fallout of CURA-7147 --- resources/shaders/overhang.shader | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/shaders/overhang.shader b/resources/shaders/overhang.shader index 8457158bc7..c3106a4e14 100644 --- a/resources/shaders/overhang.shader +++ b/resources/shaders/overhang.shader @@ -70,7 +70,7 @@ fragment = if(u_renderError > 0.5) { - vec3 grid = vec3(f_vertex.x - round(f_vertex.x), f_vertex.y - round(f_vertex.y), f_vertex.z - round(f_vertex.z)); + vec3 grid = vec3(f_vertex.x - floor(f_vertex.x - 0.5), f_vertex.y - floor(f_vertex.y - 0.5), f_vertex.z - floor(f_vertex.z - 0.5)); finalColor.a = dot(grid, grid) < 0.245 ? 0.667 : 1.0; } gl_FragColor = finalColor; From d078c465ee7c239f94f0f97c873c36309c81806f Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 22 Apr 2020 11:51:21 +0200 Subject: [PATCH 37/37] Reduce branches in shader. part of the further fallout of CURA-7147 --- resources/shaders/overhang.shader | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/resources/shaders/overhang.shader b/resources/shaders/overhang.shader index c3106a4e14..4add78b531 100644 --- a/resources/shaders/overhang.shader +++ b/resources/shaders/overhang.shader @@ -68,11 +68,9 @@ fragment = finalColor = (-normal.y > u_overhangAngle) ? u_overhangColor : finalColor; - if(u_renderError > 0.5) - { - vec3 grid = vec3(f_vertex.x - floor(f_vertex.x - 0.5), f_vertex.y - floor(f_vertex.y - 0.5), f_vertex.z - floor(f_vertex.z - 0.5)); - finalColor.a = dot(grid, grid) < 0.245 ? 0.667 : 1.0; - } + vec3 grid = vec3(f_vertex.x - floor(f_vertex.x - 0.5), f_vertex.y - floor(f_vertex.y - 0.5), f_vertex.z - floor(f_vertex.z - 0.5)); + finalColor.a = (u_renderError > 0.5) && dot(grid, grid) < 0.245 ? 0.667 : 1.0; + gl_FragColor = finalColor; } @@ -144,11 +142,8 @@ fragment41core = finalColor = (u_faceId != gl_PrimitiveID) ? ((-normal.y > u_overhangAngle) ? u_overhangColor : finalColor) : u_faceColor; frag_color = finalColor; - if(u_renderError > 0.5) - { - vec3 grid = f_vertex - round(f_vertex); - frag_color.a = dot(grid, grid) < 0.245 ? 0.667 : 1.0; - } + vec3 grid = f_vertex - round(f_vertex); + frag_color.a = (u_renderError > 0.5) && dot(grid, grid) < 0.245 ? 0.667 : 1.0; } [defaults]