diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index b28e17f792..dfb78cd3b2 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -319,7 +319,7 @@ class CuraApplication(QtApplication): preferences.addPreference("cura/asked_dialog_on_project_save", False) preferences.addPreference("cura/choice_on_profile_override", "always_ask") preferences.addPreference("cura/choice_on_open_project", "always_ask") - preferences.addPreference("cura/arrange_objects_on_load", True) + preferences.addPreference("cura/not_arrange_objects_on_load", False) preferences.addPreference("cura/use_multi_build_plate", False) preferences.addPreference("cura/currency", "€") @@ -1428,7 +1428,7 @@ class CuraApplication(QtApplication): self.fileLoaded.emit(filename) arrange_objects_on_load = ( not Preferences.getInstance().getValue("cura/use_multi_build_plate") or - Preferences.getInstance().getValue("cura/arrange_objects_on_load")) + not Preferences.getInstance().getValue("cura/not_arrange_objects_on_load")) target_build_plate = self.getBuildPlateModel().activeBuildPlate if arrange_objects_on_load else -1 for original_node in nodes: diff --git a/cura/Scene/CuraSceneController.py b/cura/Scene/CuraSceneController.py index 65723db52c..c3e27ca3dd 100644 --- a/cura/Scene/CuraSceneController.py +++ b/cura/Scene/CuraSceneController.py @@ -41,6 +41,14 @@ class CuraSceneController(QObject): self._build_plate_model.setMaxBuildPlate(self._max_build_plate) build_plates = [{"name": "Build Plate %d" % (i + 1), "buildPlateNumber": i} for i in range(self._max_build_plate + 1)] self._build_plate_model.setItems(build_plates) + if self._active_build_plate > self._max_build_plate: + build_plate_number = 0 + if self._last_selected_index >= 0: # go to the buildplate of the item you last selected + item = self._objects_model.getItem(self._last_selected_index) + if "node" in item: + node = item["node"] + build_plate_number = node.callDecoration("getBuildPlateNumber") + self.setActiveBuildPlate(build_plate_number) # self.buildPlateItemsChanged.emit() # TODO: necessary after setItems? def _calcMaxBuildPlate(self): @@ -75,11 +83,11 @@ class CuraSceneController(QObject): # Single select item = self._objects_model.getItem(index) node = item["node"] - Selection.clear() - Selection.add(node) build_plate_number = node.callDecoration("getBuildPlateNumber") if build_plate_number is not None and build_plate_number != -1: - self._build_plate_model.setActiveBuildPlate(build_plate_number) + self.setActiveBuildPlate(build_plate_number) + Selection.clear() + Selection.add(node) self._last_selected_index = index diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 0ca500ecec..c612e889a4 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -205,8 +205,8 @@ class CuraEngineBackend(QObject, Backend): Logger.log("d", " ## Process layers job still busy, trying later") return - if not hasattr(self._scene, "gcode_list"): - self._scene.gcode_list = {} + if not hasattr(self._scene, "gcode_dict"): + self._scene.gcode_dict = {} # see if we really have to slice active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate @@ -214,8 +214,10 @@ class CuraEngineBackend(QObject, Backend): Logger.log("d", "Going to slice build plate [%s]!" % build_plate_to_be_sliced) num_objects = self._numObjects() if build_plate_to_be_sliced not in num_objects or num_objects[build_plate_to_be_sliced] == 0: - self._scene.gcode_list[build_plate_to_be_sliced] = [] - Logger.log("d", "Build plate %s has 0 objects to be sliced, skipping", build_plate_to_be_sliced) + self._scene.gcode_dict[build_plate_to_be_sliced] = [] + Logger.log("d", "Build plate %s has no objects to be sliced, skipping", build_plate_to_be_sliced) + if self._build_plates_to_be_sliced: + self.slice() return self._stored_layer_data = [] @@ -232,7 +234,7 @@ class CuraEngineBackend(QObject, Backend): self.processingProgress.emit(0.0) self.backendStateChange.emit(BackendState.NotStarted) - self._scene.gcode_list[build_plate_to_be_sliced] = [] #[] indexed by build plate number + self._scene.gcode_dict[build_plate_to_be_sliced] = [] #[] indexed by build plate number self._slicing = True self.slicingStarted.emit() @@ -391,7 +393,7 @@ class CuraEngineBackend(QObject, Backend): self.backendStateChange.emit(BackendState.Disabled) gcode_list = node.callDecoration("getGCodeList") if gcode_list is not None: - self._scene.gcode_list[node.callDecoration("getBuildPlateNumber")] = gcode_list + self._scene.gcode_dict[node.callDecoration("getBuildPlateNumber")] = gcode_list if self._use_timer == enable_timer: return self._use_timer @@ -558,7 +560,7 @@ class CuraEngineBackend(QObject, Backend): self.backendStateChange.emit(BackendState.Done) self.processingProgress.emit(1.0) - gcode_list = self._scene.gcode_list[self._start_slice_job_build_plate] + gcode_list = self._scene.gcode_dict[self._start_slice_job_build_plate] for index, line in enumerate(gcode_list): replaced = line.replace("{print_time}", str(Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.ISO8601))) replaced = replaced.replace("{filament_amount}", str(Application.getInstance().getPrintInformation().materialLengths)) @@ -588,14 +590,14 @@ class CuraEngineBackend(QObject, Backend): # # \param message The protobuf message containing g-code, encoded as UTF-8. def _onGCodeLayerMessage(self, message): - self._scene.gcode_list[self._start_slice_job_build_plate].append(message.data.decode("utf-8", "replace")) + self._scene.gcode_dict[self._start_slice_job_build_plate].append(message.data.decode("utf-8", "replace")) ## Called when a g-code prefix message is received from the engine. # # \param message The protobuf message containing the g-code prefix, # encoded as UTF-8. def _onGCodePrefixMessage(self, message): - self._scene.gcode_list[self._start_slice_job_build_plate].insert(0, message.data.decode("utf-8", "replace")) + self._scene.gcode_dict[self._start_slice_job_build_plate].insert(0, message.data.decode("utf-8", "replace")) ## Creates a new socket connection. def _createSocket(self): diff --git a/plugins/CuraEngineBackend/ProcessGCodeJob.py b/plugins/CuraEngineBackend/ProcessGCodeJob.py index 4974907c30..ed430f8fa9 100644 --- a/plugins/CuraEngineBackend/ProcessGCodeJob.py +++ b/plugins/CuraEngineBackend/ProcessGCodeJob.py @@ -12,4 +12,6 @@ class ProcessGCodeLayerJob(Job): self._message = message def run(self): - self._scene.gcode_list.append(self._message.data.decode("utf-8", "replace")) + active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate + gcode_list = self._scene.gcode_dict[active_build_plate_id] + gcode_list.append(self._message.data.decode("utf-8", "replace")) diff --git a/plugins/GCodeReader/FlavorParser.py b/plugins/GCodeReader/FlavorParser.py index 0cd49e2fca..c604ab1b6f 100644 --- a/plugins/GCodeReader/FlavorParser.py +++ b/plugins/GCodeReader/FlavorParser.py @@ -430,7 +430,10 @@ class FlavorParser: gcode_list_decorator.setGCodeList(gcode_list) scene_node.addDecorator(gcode_list_decorator) - Application.getInstance().getController().getScene().gcode_list = gcode_list + # gcode_dict stores gcode_lists for a number of build plates. + active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate + gcode_dict = {active_build_plate_id: gcode_list} + Application.getInstance().getController().getScene().gcode_dict = gcode_dict Logger.log("d", "Finished parsing %s" % file_name) self._message.hide() diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index ad23f2c8ee..f0e5c88f37 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -61,7 +61,10 @@ class GCodeWriter(MeshWriter): active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate scene = Application.getInstance().getController().getScene() - gcode_list = getattr(scene, "gcode_list")[active_build_plate] + gcode_dict = getattr(scene, "gcode_dict") + if not gcode_dict: + return False + gcode_list = gcode_dict.get(active_build_plate) if gcode_list: for gcode in gcode_list: stream.write(gcode) diff --git a/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py index 05069d1c0d..6665380f45 100644 --- a/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py @@ -244,8 +244,8 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte self._request_job = [nodes, file_name, filter_by_machine, file_handler, kwargs] # the build plates to be sent - gcodes = getattr(Application.getInstance().getController().getScene(), "gcode_list") - self._job_list = list(gcodes.keys()) + gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict") + self._job_list = list(gcode_dict.keys()) Logger.log("d", "build plates to be sent to printer: %s", (self._job_list)) if self._stage != OutputStage.ready: @@ -281,7 +281,13 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte def sendPrintJob(self): nodes, file_name, filter_by_machine, file_handler, kwargs = self._request_job output_build_plate_number = self._job_list.pop(0) - gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list")[output_build_plate_number] + gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict")[output_build_plate_number] + if not gcode_dict: # Empty build plate + Logger.log("d", "Skipping empty job (build plate number %d).", output_build_plate_number) + return self.sendPrintJob() + + active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate + gcode_list = gcode_dict[active_build_plate_id] self._send_gcode_start = time.time() Logger.log("d", "Sending print job [%s] to host, build plate [%s]..." % (file_name, output_build_plate_number)) @@ -299,7 +305,7 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte require_printer_name = self._selected_printer["unique_name"] - new_request = self._buildSendPrintJobHttpRequest(require_printer_name, gcode) + new_request = self._buildSendPrintJobHttpRequest(require_printer_name, gcode_list) if new_request is None or self._stage != OutputStage.uploading: return self._request = new_request @@ -307,7 +313,7 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte self._reply.uploadProgress.connect(self._onUploadProgress) # See _finishedPrintJobPostRequest() - def _buildSendPrintJobHttpRequest(self, require_printer_name, gcode): + def _buildSendPrintJobHttpRequest(self, require_printer_name, gcode_list): api_url = QUrl(self._api_base_uri + "print_jobs/") request = QNetworkRequest(api_url) # Create multipart request and add the g-code. @@ -318,7 +324,7 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte part.setHeader(QNetworkRequest.ContentDispositionHeader, 'form-data; name="file"; filename="%s"' % (self._file_name)) - compressed_gcode = self._compressGcode(gcode) + compressed_gcode = self._compressGcode(gcode_list) if compressed_gcode is None: return None # User aborted print, so stop trying. @@ -336,7 +342,7 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte self._addUserAgentHeader(request) return request - def _compressGcode(self, gcode): + def _compressGcode(self, gcode_list): self._compressing_print = True batched_line = "" max_chars_per_line = int(1024 * 1024 / 4) # 1 / 4 MB @@ -351,11 +357,11 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte self._last_response_time = time.time() return compressed_data - if gcode is None: + if gcode_list is None: Logger.log("e", "Unable to find sliced gcode, returning empty.") return byte_array_file_data - for line in gcode: + for line in gcode_list: if not self._compressing_print: self._progress_message.hide() return None # Stop trying to zip, abort was called. diff --git a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py index de0a06527e..6b8946b755 100755 --- a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py @@ -676,7 +676,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): self._print_finished = True self.writeStarted.emit(self) active_build_plate = Application.getInstance().getBuildPlateModel().activeBuildPlate - self._gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list")[active_build_plate] + self._gcode = getattr(Application.getInstance().getController().getScene(), "gcode_dict")[active_build_plate] print_information = Application.getInstance().getPrintInformation() warnings = [] # There might be multiple things wrong. Keep a list of all the stuff we need to warn about. diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 1930f5402b..f4f5478216 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -163,7 +163,10 @@ class USBPrinterOutputDevice(PrinterOutputDevice): def startPrint(self): self.writeStarted.emit(self) - gcode_list = getattr( Application.getInstance().getController().getScene(), "gcode_list") + active_build_plate_id = Application.getInstance().getBuildPlateModel().activeBuildPlate + gcode_dict = getattr(Application.getInstance().getController().getScene(), "gcode_dict") + gcode_list = gcode_dict[active_build_plate_id] + self._updateJobState("printing") self.printGCode(gcode_list) diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index 1a4b421572..b5f51f4d63 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -47,6 +47,7 @@ Menu { model: Cura.BuildPlateModel MenuItem { + enabled: UM.Selection.hasSelection text: Cura.BuildPlateModel.getItem(index).name; onTriggered: CuraActions.setBuildPlateForSelection(Cura.BuildPlateModel.getItem(index).buildPlateNumber); checkable: true @@ -58,6 +59,7 @@ Menu } MenuItem { + enabled: UM.Selection.hasSelection text: "New build plate"; onTriggered: { CuraActions.setBuildPlateForSelection(Cura.BuildPlateModel.maxBuildPlate + 1); diff --git a/resources/qml/ObjectsList.qml b/resources/qml/ObjectsList.qml index a02ea2288d..489e38e8d7 100644 --- a/resources/qml/ObjectsList.qml +++ b/resources/qml/ObjectsList.qml @@ -105,7 +105,6 @@ Rectangle topMargin: UM.Theme.getSize("default_margin").height; left: parent.left; leftMargin: UM.Theme.getSize("default_margin").height; - //bottom: objectsList.top; bottomMargin: UM.Theme.getSize("default_margin").height; } @@ -139,7 +138,7 @@ Rectangle anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width width: parent.width - 2 * UM.Theme.getSize("default_margin").width - 30 - text: Cura.ObjectsModel.getItem(index) ? Cura.ObjectsModel.getItem(index).name : ""; + text: (index >= 0) && Cura.ObjectsModel.getItem(index) ? Cura.ObjectsModel.getItem(index).name : ""; color: Cura.ObjectsModel.getItem(index).isSelected ? palette.highlightedText : (Cura.ObjectsModel.getItem(index).isOutsideBuildArea ? palette.mid : palette.text) elide: Text.ElideRight } diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index e5ed9e46c5..ac5cacdbf6 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -453,34 +453,6 @@ UM.PreferencesPage text: catalog.i18nc("@label","Opening and saving files") } - UM.TooltipArea { - width: childrenRect.width - height: childrenRect.height - text: catalog.i18nc("@info:tooltip","Use multi build plate functionality (EXPERIMENTAL)") - - CheckBox - { - id: useMultiBuildPlateCheckbox - text: catalog.i18nc("@option:check","Use multi build plate functionality (EXPERIMENTAL, restart)") - checked: boolCheck(UM.Preferences.getValue("cura/use_multi_build_plate")) - onCheckedChanged: UM.Preferences.setValue("cura/use_multi_build_plate", checked) - } - } - - UM.TooltipArea { - width: childrenRect.width - height: childrenRect.height - text: catalog.i18nc("@info:tooltip","Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)") - - CheckBox - { - id: arrangeOnLoadCheckbox - text: catalog.i18nc("@option:check","Arrange objects on load (EXPERIMENTAL)") - checked: boolCheck(UM.Preferences.getValue("cura/arrange_objects_on_load")) - onCheckedChanged: UM.Preferences.setValue("cura/arrange_objects_on_load", checked) - } - } - UM.TooltipArea { width: childrenRect.width height: childrenRect.height @@ -688,6 +660,49 @@ UM.PreferencesPage onCheckedChanged: UM.Preferences.setValue("info/send_slice_info", checked) } } + + Item + { + //: Spacer + height: UM.Theme.getSize("default_margin").height + width: UM.Theme.getSize("default_margin").height + } + + Label + { + font.bold: true + text: catalog.i18nc("@label","Experimental") + } + + UM.TooltipArea { + width: childrenRect.width + height: childrenRect.height + text: catalog.i18nc("@info:tooltip","Use multi build plate functionality") + + CheckBox + { + id: useMultiBuildPlateCheckbox + text: catalog.i18nc("@option:check","Use multi build plate functionality (restart required)") + checked: boolCheck(UM.Preferences.getValue("cura/use_multi_build_plate")) + onCheckedChanged: UM.Preferences.setValue("cura/use_multi_build_plate", checked) + } + } + + UM.TooltipArea { + width: childrenRect.width + height: childrenRect.height + text: catalog.i18nc("@info:tooltip","Should newly loaded models be arranged on the build plate? Used in conjunction with multi build plate (EXPERIMENTAL)") + + CheckBox + { + id: arrangeOnLoadCheckbox + text: catalog.i18nc("@option:check","Do not arrange objects on load") + checked: boolCheck(UM.Preferences.getValue("cura/not_arrange_objects_on_load")) + onCheckedChanged: UM.Preferences.setValue("cura/not_arrange_objects_on_load", checked) + } + } + + } } }