diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c50c56f1d9..c2bf74e321 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -44,6 +44,7 @@ from . import CameraAnimation from . import PrintInformation from . import CuraActions from . import MultiMaterialDecorator +from . import ZOffsetDecorator from PyQt5.QtCore import pyqtSlot, QUrl, Qt, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from PyQt5.QtGui import QColor, QIcon @@ -349,14 +350,12 @@ class CuraApplication(QtApplication): continue #Grouped nodes don't need resetting as their parent (the group) is resetted) nodes.append(node) + if nodes: op = GroupedOperation() for node in nodes: - # Ensure that the object is above the build platform - move_distance = node.getBoundingBox().center.y - if move_distance <= 0: - move_distance = -node.getBoundingBox().bottom - op.addOperation(SetTransformOperation(node, Vector(0,move_distance,0))) + node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator) + op.addOperation(SetTransformOperation(node, Vector(0,0,0))) op.push() @@ -378,10 +377,8 @@ class CuraApplication(QtApplication): for node in nodes: # Ensure that the object is above the build platform - move_distance = node.getBoundingBox().center.y - if move_distance <= 0: - move_distance = -node.getBoundingBox().bottom - op.addOperation(SetTransformOperation(node, Vector(0,move_distance,0), Quaternion(), Vector(1, 1, 1))) + node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator) + op.addOperation(SetTransformOperation(node, Vector(0,0,0), Quaternion(), Vector(1, 1, 1))) op.push() @@ -478,17 +475,20 @@ class CuraApplication(QtApplication): group_decorator = GroupDecorator() group_node.addDecorator(group_decorator) group_node.setParent(self.getController().getScene().getRoot()) - + center = Selection.getSelectionCenter() + group_node.setPosition(center) + group_node.setCenterPosition(center) + for node in Selection.getAllSelectedObjects(): + world = node.getWorldPosition() node.setParent(group_node) - group_node.setCenterPosition(group_node.getBoundingBox().center) - #group_node.translate(Vector(0,group_node.getBoundingBox().center.y,0)) - group_node.translate(group_node.getBoundingBox().center) + node.setPosition(world - center) + for node in group_node.getChildren(): Selection.remove(node) - + Selection.add(group_node) - + @pyqtSlot() def ungroupSelected(self): ungrouped_nodes = [] @@ -499,12 +499,11 @@ class CuraApplication(QtApplication): for child in node.getChildren(): if type(child) is SceneNode: children_to_move.append(child) - + for child in children_to_move: + position = child.getWorldPosition() child.setParent(node.getParent()) - print(node.getPosition()) - child.translate(node.getPosition()) - child.setPosition(child.getPosition().scale(node.getScale())) + child.setPosition(position - node.getParent().getWorldPosition()) child.scale(node.getScale()) child.rotate(node.getOrientation()) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 2e4d77b77f..3933802135 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -17,6 +17,7 @@ from cura.ConvexHullDecorator import ConvexHullDecorator from . import PlatformPhysicsOperation from . import ConvexHullJob +from . import ZOffsetDecorator import time import threading @@ -69,8 +70,12 @@ class PlatformPhysics: # Move it downwards if bottom is above platform move_vector = Vector() if not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down + z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0 if bbox.bottom > 0: - move_vector.setY(-bbox.bottom) + move_vector.setY(-bbox.bottom + z_offset) + elif bbox.bottom < z_offset: + move_vector.setY((-bbox.bottom) - z_offset) + #if not Float.fuzzyCompare(bbox.bottom, 0.0): # pass#move_vector.setY(-bbox.bottom) @@ -149,5 +154,16 @@ class PlatformPhysics: self._enabled = False def _onToolOperationStopped(self, tool): + if tool.getPluginId() == "TranslateTool": + for node in Selection.getAllSelectedObjects(): + if node.getBoundingBox().bottom < 0: + if not node.getDecorator(ZOffsetDecorator.ZOffsetDecorator): + node.addDecorator(ZOffsetDecorator.ZOffsetDecorator()) + + node.callDecoration("setZOffset", node.getBoundingBox().bottom) + else: + if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator): + node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator) + self._enabled = True self._onChangeTimerFinished() diff --git a/cura/ZOffsetDecorator.py b/cura/ZOffsetDecorator.py new file mode 100644 index 0000000000..54bf64b9a5 --- /dev/null +++ b/cura/ZOffsetDecorator.py @@ -0,0 +1,13 @@ +from UM.Scene.SceneNodeDecorator import SceneNodeDecorator + +## A decorator that stores the amount an object has been moved below the platform. +class ZOffsetDecorator(SceneNodeDecorator): + def __init__(self): + self._z_offset = 0 + + def setZOffset(self, offset): + print("setZOffset", offset) + self._z_offset = offset + + def getZOffset(self): + return self._z_offset diff --git a/plugins/ChangeLogPlugin/ChangeLog.py b/plugins/ChangeLogPlugin/ChangeLog.py index d83466f0c6..4d4c3762f6 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.py +++ b/plugins/ChangeLogPlugin/ChangeLog.py @@ -31,6 +31,7 @@ class ChangeLog(Extension, QObject,): self._change_logs = None Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) Preferences.getInstance().addPreference("general/latest_version_changelog_shown", "15.05.90") #First version of CURA with uranium + self.addMenuItem(catalog.i18nc("@item:inmenu", "Show Changelog"), self.showChangelog) #self.showChangelog() def getChangeLogs(self): @@ -77,8 +78,8 @@ class ChangeLog(Extension, QObject,): def _onEngineCreated(self): if not self._version: return #We're on dev branch. - if self._version > Preferences.getInstance().getValue("general/latest_version_changelog_shown"): - self.showChangelog() + #if self._version > Preferences.getInstance().getValue("general/latest_version_changelog_shown"): + #self.showChangelog() def showChangelog(self): if not self._changelog_window: diff --git a/plugins/ChangeLogPlugin/__init__.py b/plugins/ChangeLogPlugin/__init__.py index 431a93c395..40c0621a63 100644 --- a/plugins/ChangeLogPlugin/__init__.py +++ b/plugins/ChangeLogPlugin/__init__.py @@ -9,7 +9,7 @@ catalog = i18nCatalog("cura") def getMetaData(): return { "plugin": { - "name": catalog.i18nc("@label", "Change Log"), + "name": catalog.i18nc("@label", "Changelog"), "author": "Ultimaker", "version": "1.0", "description": catalog.i18nc("@info:whatsthis", "Shows changes since latest checked version"), diff --git a/plugins/USBPrinting/PrinterConnection.py b/plugins/USBPrinting/PrinterConnection.py index 2a731753fc..e6e54ccbc2 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/PrinterConnection.py @@ -123,6 +123,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter): progressChanged = pyqtSignal() extruderTemperatureChanged = pyqtSignal() bedTemperatureChanged = pyqtSignal() + firmwareUpdateComplete = pyqtSignal() endstopStateChanged = pyqtSignal(str ,bool, arguments = ["key","state"]) diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index 1249c454ff..9d9b0b4a02 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -54,6 +54,15 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): addConnectionSignal = Signal() printerConnectionStateChanged = pyqtSignal() + progressChanged = pyqtSignal() + @pyqtProperty(float, notify = progressChanged) + def progress(self): + progress = 0 + for name, connection in self._printer_connections.items(): + progress += connection.progress + + return progress / len(self._printer_connections) + def start(self): self._check_updates = True self._update_thread.start() @@ -91,6 +100,7 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): try: self._printer_connections[printer_connection].updateFirmware(Resources.getPath(CuraApplication.ResourceTypes.Firmware, self._getDefaultFirmwareName())) except FileNotFoundError: + Logger.log("w", "No firmware found for printer %s", printer_connection) continue @pyqtSlot(str, result = bool) @@ -154,6 +164,7 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): connection = PrinterConnection.PrinterConnection(serial_port) connection.connect() connection.connectionStateChanged.connect(self._onPrinterConnectionStateChanged) + connection.progressChanged.connect(self.progressChanged) self._printer_connections[serial_port] = connection def _onPrinterConnectionStateChanged(self, serial_port): @@ -197,4 +208,4 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): base_list = base_list + glob.glob("/dev/ttyUSB*") + glob.glob("/dev/ttyACM*") + glob.glob("/dev/cu.*") + glob.glob("/dev/tty.usb*") + glob.glob("/dev/rfcomm*") + glob.glob("/dev/serial/by-id/*") return list(base_list) - _instance = None \ No newline at end of file + _instance = None diff --git a/plugins/USBPrinting/avr_isp/chipDB.py b/plugins/USBPrinting/avr_isp/chipDB.py index ba9cf434d5..2da6586503 100644 --- a/plugins/USBPrinting/avr_isp/chipDB.py +++ b/plugins/USBPrinting/avr_isp/chipDB.py @@ -4,22 +4,22 @@ To support more chips add the relevant data to the avrChipDB list. This is a python 3 conversion of the code created by David Braam for the Cura project. """ -avrChipDB = { - 'ATMega1280': { - 'signature': [0x1E, 0x97, 0x03], - 'pageSize': 128, - 'pageCount': 512, +avr_chip_db = { + "ATMega1280": { + "signature": [0x1E, 0x97, 0x03], + "pageSize": 128, + "pageCount": 512, }, - 'ATMega2560': { - 'signature': [0x1E, 0x98, 0x01], - 'pageSize': 128, - 'pageCount': 1024, + "ATMega2560": { + "signature": [0x1E, 0x98, 0x01], + "pageSize": 128, + "pageCount": 1024, }, } def getChipFromDB(sig): - for chip in avrChipDB.values(): - if chip['signature'] == sig: + for chip in avr_chip_db.values(): + if chip["signature"] == sig: return chip return False diff --git a/plugins/USBPrinting/avr_isp/intelHex.py b/plugins/USBPrinting/avr_isp/intelHex.py index 822936ef1b..4444006b3d 100644 --- a/plugins/USBPrinting/avr_isp/intelHex.py +++ b/plugins/USBPrinting/avr_isp/intelHex.py @@ -11,36 +11,36 @@ def readHex(filename): Read an verify an intel hex file. Return the data as an list of bytes. """ data = [] - extraAddr = 0 + extra_addr = 0 f = io.open(filename, "r") for line in f: line = line.strip() if len(line) < 1: continue - if line[0] != ':': + if line[0] != ":": raise Exception("Hex file has a line not starting with ':'") - recLen = int(line[1:3], 16) - addr = int(line[3:7], 16) + extraAddr - recType = int(line[7:9], 16) - if len(line) != recLen * 2 + 11: + rec_len = int(line[1:3], 16) + addr = int(line[3:7], 16) + extra_addr + rec_type = int(line[7:9], 16) + if len(line) != rec_len * 2 + 11: raise Exception("Error in hex file: " + line) - checkSum = 0 - for i in range(0, recLen + 5): - checkSum += int(line[i*2+1:i*2+3], 16) - checkSum &= 0xFF - if checkSum != 0: + check_sum = 0 + for i in range(0, rec_len + 5): + check_sum += int(line[i*2+1:i*2+3], 16) + check_sum &= 0xFF + if check_sum != 0: raise Exception("Checksum error in hex file: " + line) - if recType == 0:#Data record - while len(data) < addr + recLen: + if rec_type == 0:#Data record + while len(data) < addr + rec_len: data.append(0) - for i in range(0, recLen): + for i in range(0, rec_len): data[addr + i] = int(line[i*2+9:i*2+11], 16) - elif recType == 1: #End Of File record + elif rec_type == 1: #End Of File record pass - elif recType == 2: #Extended Segment Address Record - extraAddr = int(line[9:13], 16) * 16 + elif rec_type == 2: #Extended Segment Address Record + extra_addr = int(line[9:13], 16) * 16 else: - print(recType, recLen, addr, checkSum, line) + print(rec_type, rec_len, addr, check_sum, line) f.close() return data diff --git a/plugins/USBPrinting/avr_isp/ispBase.py b/plugins/USBPrinting/avr_isp/ispBase.py index 1ba1317f91..b5b5f8792e 100644 --- a/plugins/USBPrinting/avr_isp/ispBase.py +++ b/plugins/USBPrinting/avr_isp/ispBase.py @@ -14,18 +14,18 @@ class IspBase(): Base class for ISP based AVR programmers. Functions in this class raise an IspError when something goes wrong. """ - def programChip(self, flashData): + def programChip(self, flash_data): """ Program a chip with the given flash data. """ - self.curExtAddr = -1 + self.cur_ext_addr = -1 self.chip = chipDB.getChipFromDB(self.getSignature()) if not self.chip: raise IspError("Chip with signature: " + str(self.getSignature()) + "not found") self.chipErase() - print("Flashing %i bytes" % len(flashData)) - self.writeFlash(flashData) - print("Verifying %i bytes" % len(flashData)) - self.verifyFlash(flashData) + print("Flashing %i bytes" % len(flash_data)) + self.writeFlash(flash_data) + print("Verifying %i bytes" % len(flash_data)) + self.verifyFlash(flash_data) print("Completed") def getSignature(self): @@ -45,13 +45,13 @@ class IspBase(): """ self.sendISP([0xAC, 0x80, 0x00, 0x00]) - def writeFlash(self, flashData): + def writeFlash(self, flash_data): """ Write the flash data, needs to be implemented in a subclass. """ raise IspError("Called undefined writeFlash") - def verifyFlash(self, flashData): + def verifyFlash(self, flash_data): """ Verify the flash data, needs to be implemented in a subclass. """ diff --git a/plugins/USBPrinting/avr_isp/stk500v2.py b/plugins/USBPrinting/avr_isp/stk500v2.py index c0e3b0ec89..02d398ef58 100644 --- a/plugins/USBPrinting/avr_isp/stk500v2.py +++ b/plugins/USBPrinting/avr_isp/stk500v2.py @@ -19,10 +19,10 @@ class Stk500v2(ispBase.IspBase): def __init__(self): self.serial = None self.seq = 1 - self.lastAddr = -1 - self.progressCallback = None + self.last_addr = -1 + self.progress_callback = None - def connect(self, port = 'COM22', speed = 115200): + def connect(self, port = "COM22", speed = 115200): if self.serial is not None: self.close() try: @@ -82,49 +82,49 @@ class Stk500v2(ispBase.IspBase): def writeFlash(self, flash_data): #Set load addr to 0, in case we have more then 64k flash we need to enable the address extension - page_size = self.chip['pageSize'] * 2 - flashSize = page_size * self.chip['pageCount'] + page_size = self.chip["pageSize"] * 2 + flash_size = page_size * self.chip["pageCount"] print("Writing flash") - if flashSize > 0xFFFF: + if flash_size > 0xFFFF: self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00]) else: self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00]) load_count = (len(flash_data) + page_size - 1) / page_size for i in range(0, int(load_count)): recv = self.sendMessage([0x13, page_size >> 8, page_size & 0xFF, 0xc1, 0x0a, 0x40, 0x4c, 0x20, 0x00, 0x00] + flash_data[(i * page_size):(i * page_size + page_size)]) - if self.progressCallback is not None: + if self.progress_callback is not None: if self._has_checksum: - self.progressCallback(i + 1, load_count) + self.progress_callback(i + 1, load_count) else: - self.progressCallback(i + 1, load_count*2) + self.progress_callback(i + 1, load_count*2) - def verifyFlash(self, flashData): + def verifyFlash(self, flash_data): if self._has_checksum: - self.sendMessage([0x06, 0x00, (len(flashData) >> 17) & 0xFF, (len(flashData) >> 9) & 0xFF, (len(flashData) >> 1) & 0xFF]) + self.sendMessage([0x06, 0x00, (len(flash_data) >> 17) & 0xFF, (len(flash_data) >> 9) & 0xFF, (len(flash_data) >> 1) & 0xFF]) res = self.sendMessage([0xEE]) checksum_recv = res[2] | (res[3] << 8) checksum = 0 - for d in flashData: + for d in flash_data: checksum += d checksum &= 0xFFFF if hex(checksum) != hex(checksum_recv): - raise ispBase.IspError('Verify checksum mismatch: 0x%x != 0x%x' % (checksum & 0xFFFF, checksum_recv)) + raise ispBase.IspError("Verify checksum mismatch: 0x%x != 0x%x" % (checksum & 0xFFFF, checksum_recv)) else: #Set load addr to 0, in case we have more then 64k flash we need to enable the address extension - flashSize = self.chip['pageSize'] * 2 * self.chip['pageCount'] - if flashSize > 0xFFFF: + flash_size = self.chip["pageSize"] * 2 * self.chip["pageCount"] + if flash_size > 0xFFFF: self.sendMessage([0x06, 0x80, 0x00, 0x00, 0x00]) else: self.sendMessage([0x06, 0x00, 0x00, 0x00, 0x00]) - loadCount = (len(flashData) + 0xFF) / 0x100 - for i in range(0, int(loadCount)): + load_count = (len(flash_data) + 0xFF) / 0x100 + for i in range(0, int(load_count)): recv = self.sendMessage([0x14, 0x01, 0x00, 0x20])[2:0x102] - if self.progressCallback is not None: - self.progressCallback(loadCount + i + 1, loadCount*2) + if self.progress_callback is not None: + self.progress_callback(load_count + i + 1, load_count*2) for j in range(0, 0x100): - if i * 0x100 + j < len(flashData) and flashData[i * 0x100 + j] != recv[j]: - raise ispBase.IspError('Verify error at: 0x%x' % (i * 0x100 + j)) + if i * 0x100 + j < len(flash_data) and flash_data[i * 0x100 + j] != recv[j]: + raise ispBase.IspError("Verify error at: 0x%x" % (i * 0x100 + j)) def sendMessage(self, data): message = struct.pack(">BBHB", 0x1B, self.seq, len(data), 0x0E) @@ -138,12 +138,12 @@ class Stk500v2(ispBase.IspBase): self.serial.write(message) self.serial.flush() except SerialTimeoutException: - raise ispBase.IspError('Serial send timeout') + raise ispBase.IspError("Serial send timeout") self.seq = (self.seq + 1) & 0xFF return self.recvMessage() def recvMessage(self): - state = 'Start' + state = "Start" checksum = 0 while True: s = self.serial.read() @@ -152,31 +152,31 @@ class Stk500v2(ispBase.IspBase): b = struct.unpack(">B", s)[0] checksum ^= b #print(hex(b)) - if state == 'Start': + if state == "Start": if b == 0x1B: - state = 'GetSeq' + state = "GetSeq" checksum = 0x1B - elif state == 'GetSeq': - state = 'MsgSize1' - elif state == 'MsgSize1': - msgSize = b << 8 - state = 'MsgSize2' - elif state == 'MsgSize2': - msgSize |= b - state = 'Token' - elif state == 'Token': + elif state == "GetSeq": + state = "MsgSize1" + elif state == "MsgSize1": + msg_size = b << 8 + state = "MsgSize2" + elif state == "MsgSize2": + msg_size |= b + state = "Token" + elif state == "Token": if b != 0x0E: - state = 'Start' + state = "Start" else: - state = 'Data' + state = "Data" data = [] - elif state == 'Data': + elif state == "Data": data.append(b) - if len(data) == msgSize: - state = 'Checksum' - elif state == 'Checksum': + if len(data) == msg_size: + state = "Checksum" + elif state == "Checksum": if checksum != 0: - state = 'Start' + state = "Start" else: return data @@ -190,7 +190,7 @@ def portList(): values = _winreg.EnumValue(key, i) except: return ret - if 'USBSER' in values[0]: + if "USBSER" in values[0]: ret.append(values[1]) i+=1 return ret @@ -205,7 +205,7 @@ def runProgrammer(port, filename): def main(): """ Entry point to call the stk500v2 programmer from the commandline. """ import threading - if sys.argv[1] == 'AUTO': + if sys.argv[1] == "AUTO": print(portList()) for port in portList(): threading.Thread(target=runProgrammer, args=(port,sys.argv[2])).start() @@ -216,5 +216,5 @@ def main(): programmer.programChip(intelHex.readHex(sys.argv[2])) sys.exit(1) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/resources/images/cura-icon.png b/resources/images/cura-icon.png index ecfe0d1e4e..8fd6f41691 100644 Binary files a/resources/images/cura-icon.png and b/resources/images/cura-icon.png differ diff --git a/resources/machines/RigidBot.json b/resources/machines/RigidBot.json index 0f57e4e1bc..ccdd6475f8 100644 --- a/resources/machines/RigidBot.json +++ b/resources/machines/RigidBot.json @@ -18,6 +18,8 @@ "machine_nozzle_size": { "default": 0.4, "visible": true }, + "machine_nozzle_heat_up_speed": { "default": 2.0 }, + "machine_nozzle_cool_down_speed": { "default": 2.0 }, "machine_head_shape_min_x": { "default": 0 }, "machine_head_shape_min_y": { "default": 0 }, "machine_head_shape_max_x": { "default": 0 }, diff --git a/resources/machines/RigidBotBig.json b/resources/machines/RigidBotBig.json index f97d5845e4..b3f3f29728 100644 --- a/resources/machines/RigidBotBig.json +++ b/resources/machines/RigidBotBig.json @@ -16,6 +16,8 @@ "machine_heated_bed": { "default": true }, "machine_nozzle_size": { "default": 0.4}, + "machine_nozzle_heat_up_speed": { "default": 2.0 }, + "machine_nozzle_cool_down_speed": { "default": 2.0 }, "machine_head_shape_min_x": { "default": 0 }, "machine_head_shape_min_y": { "default": 0 }, "machine_head_shape_max_x": { "default": 0 }, diff --git a/resources/machines/fdmprinter.json b/resources/machines/fdmprinter.json index 3c4dcf99f3..f836fdcc13 100644 --- a/resources/machines/fdmprinter.json +++ b/resources/machines/fdmprinter.json @@ -38,6 +38,8 @@ "machine_nozzle_head_distance": { "default": 3, "SEE_machine_extruder_trains": true }, "machine_nozzle_expansion_angle": { "default": 45, "SEE_machine_extruder_trains": true }, "machine_heat_zone_length": { "default": 16, "SEE_machine_extruder_trains": true }, + "machine_nozzle_heat_up_speed": { "default": 2.0, "SEE_machine_extruder_trains": true }, + "machine_nozzle_cool_down_speed": { "default": 2.0, "SEE_machine_extruder_trains": true }, "machine_gcode_flavor": { "default": "RepRap" }, @@ -497,16 +499,7 @@ "type": "float", "default": 0.1, "visible": false, - "children": { - "infill_sparse_combine": { - "label": "Infill Layers", - "description": "Amount of layers that are combined together to form sparse infill.", - "type": "int", - "default": 1, - "visible": false, - "inherit_function": "math.floor(( parent_value + 0.001) / layer_height)" - } - } + "inherit_function": "layer_height" }, "infill_before_walls": { "label": "Infill Before Walls", @@ -522,6 +515,13 @@ "visible": true, "icon": "category_material", "settings": { + "material_flow_dependent_temperature": { + "label": "Auto Temperature", + "description": "Change the temperature each layer automatically with the average flow speed of that layer.", + "type": "boolean", + "default": true, + "visible": true + }, "material_print_temperature": { "label": "Printing Temperature", "description": "The temperature used for printing. Set at 0 to pre-heat yourself. For PLA a value of 210C is usually used.\nFor ABS a value of 230C or higher is required.", @@ -529,8 +529,35 @@ "type": "float", "default": 210, "min_value": "0", + "max_value_warning": "260", + "enabled": "not (material_flow_dependent_temperature)" + }, + "material_flow_temp_graph": { + "label": "Flow Temperature Graph", + "description": "Data linking material flow (in mm³/s) to temperature (°C).", + "unit": "", + "type": "string", + "default": "[[0.1,180],[20,230]]", + "enabled": "material_flow_dependent_temperature" + }, + "material_standby_temperature": { + "label": "Standby Temperature", + "description": "The temperature of the nozzle when another nozzle is currently used for printing.", + "unit": "°C", + "type": "float", + "default": 150, + "min_value": "0", "max_value_warning": "260" }, + "material_extrusion_cool_down_speed": { + "label": "Extrusion Cool Down Speed Modifier", + "description": "The extra speed by which the nozzle cools when while extruding. The same value is used to signify the heat up speed lost when heating up while extruding.", + "unit": "°C/s", + "type": "float", + "default": 0.5, + "min_value": "0", + "max_value_warning": "10.0" + }, "material_bed_temperature": { "label": "Bed Temperature", "description": "The temperature used for the heated printer bed. Set at 0 to pre-heat it yourself.", diff --git a/resources/machines/grr_neo.json b/resources/machines/grr_neo.json index 472062a097..488909c79c 100644 --- a/resources/machines/grr_neo.json +++ b/resources/machines/grr_neo.json @@ -16,6 +16,8 @@ "machine_depth": { "default": 150 }, "machine_center_is_zero": { "default": false }, "machine_nozzle_size": { "default": 0.5 }, + "machine_nozzle_heat_up_speed": { "default": 2.0 }, + "machine_nozzle_cool_down_speed": { "default": 2.0 }, "machine_head_shape_min_x": { "default": 75 }, "machine_head_shape_min_y": { "default": 18 }, "machine_head_shape_max_x": { "default": 18 }, diff --git a/resources/machines/maker_starter.json b/resources/machines/maker_starter.json index 8ff7bbd7c0..7e4e08f175 100644 --- a/resources/machines/maker_starter.json +++ b/resources/machines/maker_starter.json @@ -17,6 +17,8 @@ "machine_center_is_zero": { "default": false }, "machine_nozzle_size": { "default": 0.4 }, + "machine_nozzle_heat_up_speed": { "default": 2.0 }, + "machine_nozzle_cool_down_speed": { "default": 2.0 }, "machine_head_shape_min_x": { "default": 0 }, "machine_head_shape_min_y": { "default": 0 }, "machine_head_shape_max_x": { "default": 0 }, diff --git a/resources/machines/prusa_i3.json b/resources/machines/prusa_i3.json index 4311bc1fc2..997a246601 100644 --- a/resources/machines/prusa_i3.json +++ b/resources/machines/prusa_i3.json @@ -16,6 +16,8 @@ "machine_depth": { "default": 200 }, "machine_center_is_zero": { "default": false }, "machine_nozzle_size": { "default": 0.4 }, + "machine_nozzle_heat_up_speed": { "default": 2.0 }, + "machine_nozzle_cool_down_speed": { "default": 2.0 }, "machine_head_shape_min_x": { "default": 75 }, "machine_head_shape_min_y": { "default": 18 }, "machine_head_shape_max_x": { "default": 18 }, diff --git a/resources/machines/ultimaker2.json b/resources/machines/ultimaker2.json index 99f2247b0e..319e47cd57 100644 --- a/resources/machines/ultimaker2.json +++ b/resources/machines/ultimaker2.json @@ -16,6 +16,12 @@ "machine_nozzle_size": { "default": 0.4 }, + "machine_nozzle_heat_up_speed": { + "default": 2.0 + }, + "machine_nozzle_cool_down_speed": { + "default": 2.0 + }, "machine_nozzle_tip_outer_diameter": { "default": 1 }, @@ -61,6 +67,8 @@ }, "machine_center_is_zero": { "default": false }, "machine_nozzle_size": { "default": 0.4 }, + "machine_nozzle_heat_up_speed": { "default": 2.0 }, + "machine_nozzle_cool_down_speed": { "default": 2.0 }, "gantry_height": { "default": 55 }, "machine_use_extruder_offset_to_offset_coords": { "default": true }, "machine_gcode_flavor": { "default": "UltiGCode" }, diff --git a/resources/machines/ultimaker_original.json b/resources/machines/ultimaker_original.json index 6ea8d65cee..4c94d5e4c4 100644 --- a/resources/machines/ultimaker_original.json +++ b/resources/machines/ultimaker_original.json @@ -21,6 +21,12 @@ "machine_nozzle_size": { "default": 0.4 }, + "machine_nozzle_heat_up_speed": { + "default": 2.0 + }, + "machine_nozzle_cool_down_speed": { + "default": 2.0 + }, "machine_nozzle_tip_outer_diameter": { "default": 1 }, @@ -41,6 +47,8 @@ "machine_depth": { "default": 205 }, "machine_center_is_zero": { "default": false }, "machine_nozzle_size": { "default": 0.4 }, + "machine_nozzle_heat_up_speed": { "default": 2.0 }, + "machine_nozzle_cool_down_speed": { "default": 2.0 }, "machine_head_with_fans_polygon": { "default": [ diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 9a2a0a5378..ec0cfe16d1 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -47,7 +47,6 @@ Item Action { id:toggleFullScreenAction - shortcut: StandardKey.FullScreen; text: catalog.i18nc("@action:inmenu","Toggle Fu&ll Screen"); iconName: "view-fullscreen"; } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 6af5c316cc..696b37bdad 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -152,10 +152,6 @@ UM.MainWindow onObjectRemoved: top_view_menu.removeItem(object) } ExclusiveGroup { id: view_menu_top_group; } - - MenuSeparator { } - - MenuItem { action: actions.toggleFullScreen; } } Menu { diff --git a/resources/qml/ProfileSetup.qml b/resources/qml/ProfileSetup.qml index aa4a2b344a..d9869d62b7 100644 --- a/resources/qml/ProfileSetup.qml +++ b/resources/qml/ProfileSetup.qml @@ -81,6 +81,7 @@ Item{ text: catalog.i18nc("@label","Global Profile:"); width: parent.width/100*45 font: UM.Theme.fonts.default; + color: UM.Theme.colors.text_default; } diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 322b9b7a5a..2ce2e7975a 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -100,6 +100,7 @@ Item anchors.leftMargin: UM.Theme.sizes.default_margin.width anchors.verticalCenter: parent.verticalCenter font: UM.Theme.fonts.default; + color: UM.Theme.colors.text_default; } ToolButton { diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 10b935bbcd..a16b0af225 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -33,6 +33,7 @@ Item //: Infill selection label text: catalog.i18nc("@label","Infill:"); font: UM.Theme.fonts.default; + color: UM.Theme.colors.text_default; anchors.top: parent.top anchors.topMargin: UM.Theme.sizes.default_margin.height anchors.left: parent.left @@ -166,6 +167,7 @@ Item //: Helpers selection label text: catalog.i18nc("@label:listbox","Helpers:"); font: UM.Theme.fonts.default; + color: UM.Theme.colors.text_default; } } Rectangle { diff --git a/resources/qml/SidebarTooltip.qml b/resources/qml/SidebarTooltip.qml index 6e0ba74404..8b7cc1c0dc 100644 --- a/resources/qml/SidebarTooltip.qml +++ b/resources/qml/SidebarTooltip.qml @@ -47,5 +47,6 @@ Rectangle { } wrapMode: Text.Wrap; font: UM.Theme.fonts.default; + color: UM.Theme.colors.text_default; } } diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 63e04c6e6e..8130ec9bd0 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -50,6 +50,7 @@ }, "colors": { + "text_default": [0, 0, 0, 255], "sidebar": [255, 255, 255, 255], "lining": [208, 210, 211, 255],