def __init__(self, parent = None): super().__init__(parent) self.addRoleName(self.RootMaterialIdRole, "root_material_id") self.addRoleName(self.DisplayNameRole, "name") self.addRoleName(self.BrandRole, "brand") self.addRoleName(self.MaterialTypeRole, "material") self.addRoleName(self.ColorNameRole, "color_name") self.addRoleName(self.ColorCodeRole, "color_code") self.addRoleName(self.ContainerNodeRole, "container_node") self.addRoleName(self.ContainerIdRole, "container_id") self.addRoleName(self.DescriptionRole, "description") self.addRoleName(self.AdhesionInfoRole, "adhesion_info") self.addRoleName(self.ApproximateDiameterRole, "approximate_diameter") self.addRoleName(self.GuidRole, "guid") self.addRoleName(self.DensityRole, "density") self.addRoleName(self.DiameterRole, "diameter") self.addRoleName(self.IsReadOnlyRole, "is_read_only") from cura.CuraApplication import CuraApplication self._container_registry = CuraApplication.getInstance().getContainerRegistry() self._machine_manager = CuraApplication.getInstance().getMachineManager() self._extruder_manager = CuraApplication.getInstance().getExtruderManager() self._material_manager = CuraApplication.getInstance().getMaterialManager() self._machine_manager.globalContainerChanged.connect(self._update) self._extruder_manager.activeExtruderChanged.connect(self._update) self._material_manager.materialsUpdated.connect(self._update) self._update()
def __init__(self) -> None: QObject.__init__(self, None) Extension.__init__(self) # Local data caching for the UI. self._drive_window = None # type: Optional[QObject] self._backups = [] # type: List[Dict[str, Any]] self._is_restoring_backup = False self._is_creating_backup = False # Initialize services. preferences = CuraApplication.getInstance().getPreferences() self._drive_api_service = DriveApiService() # Attach signals. CuraApplication.getInstance().getCuraAPI().account.loginStateChanged.connect(self._onLoginStateChanged) self._drive_api_service.restoringStateChanged.connect(self._onRestoringStateChanged) self._drive_api_service.creatingStateChanged.connect(self._onCreatingStateChanged) # Register preferences. preferences.addPreference(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, False) preferences.addPreference(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, datetime.now().strftime(self.DATE_FORMAT)) # Register the menu item self.addMenuItem(catalog.i18nc("@item:inmenu", "Manage backups"), self.showDriveWindow) # Make auto-backup on boot if required. CuraApplication.getInstance().engineCreatedSignal.connect(self._autoBackup)
def _progressMessageActionTriggered(self, message_id=None, action_id=None): if action_id == "Abort": Logger.log("d", "User aborted sending print to remote.") self._progress_message.hide() self._compressing_gcode = False self._sending_gcode = False CuraApplication.getInstance().getController().setActiveStage("PrepareStage")
def restore(self) -> bool: if not self.zip_file or not self.meta_data or not self.meta_data.get("cura_release", None): # We can restore without the minimum required information. Logger.log("w", "Tried to restore a Cura backup without having proper data or meta data.") self._showMessage( self.catalog.i18nc("@info:backup_failed", "Tried to restore a Cura backup without having proper data or meta data.")) return False current_version = CuraApplication.getInstance().getVersion() version_to_restore = self.meta_data.get("cura_release", "master") if current_version != version_to_restore: # Cannot restore version older or newer than current because settings might have changed. # Restoring this will cause a lot of issues so we don't allow this for now. self._showMessage( self.catalog.i18nc("@info:backup_failed", "Tried to restore a Cura backup that does not match your current version.")) return False version_data_dir = Resources.getDataStoragePath() archive = ZipFile(io.BytesIO(self.zip_file), "r") extracted = self._extractArchive(archive, version_data_dir) # Under Linux, preferences are stored elsewhere, so we copy the file to there. if Platform.isLinux(): preferences_file_name = CuraApplication.getInstance().getApplicationName() preferences_file = Resources.getPath(Resources.Preferences, "{}.cfg".format(preferences_file_name)) backup_preferences_file = os.path.join(version_data_dir, "{}.cfg".format(preferences_file_name)) Logger.log("d", "Moving preferences file from %s to %s", backup_preferences_file, preferences_file) shutil.move(backup_preferences_file, preferences_file) return extracted
def _createEraserMesh(self, parent: CuraSceneNode, position: Vector): node = CuraSceneNode() node.setName("Eraser") node.setSelectable(True) node.setCalculateBoundingBox(True) mesh = self._createCube(10) node.setMeshData(mesh.build()) node.calculateBoundingBoxMesh() active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) stack = node.callDecoration("getStack") # created by SettingOverrideDecorator that is automatically added to CuraSceneNode settings = stack.getTop() definition = stack.getSettingDefinition("anti_overhang_mesh") new_instance = SettingInstance(definition, settings) new_instance.setProperty("value", True) new_instance.resetState() # Ensure that the state is not seen as a user state. settings.addInstance(new_instance) op = GroupedOperation() # First add node to the scene at the correct position/scale, before parenting, so the eraser mesh does not get scaled with the parent op.addOperation(AddSceneNodeOperation(node, self._controller.getScene().getRoot())) op.addOperation(SetParentOperation(node, parent)) op.push() node.setPosition(position, CuraSceneNode.TransformSpace.World) CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)
def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], connection_type: ConnectionType = ConnectionType.NetworkConnection, parent: QObject = None) -> None: super().__init__(device_id = device_id, connection_type = connection_type, parent = parent) self._manager = None # type: Optional[QNetworkAccessManager] self._last_manager_create_time = None # type: Optional[float] self._recreate_network_manager_time = 30 self._timeout_time = 10 # After how many seconds of no response should a timeout occur? self._last_response_time = None # type: Optional[float] self._last_request_time = None # type: Optional[float] self._api_prefix = "" self._address = address self._properties = properties self._user_agent = "%s/%s " % (CuraApplication.getInstance().getApplicationName(), CuraApplication.getInstance().getVersion()) self._onFinishedCallbacks = {} # type: Dict[str, Callable[[QNetworkReply], None]] self._authentication_state = AuthState.NotAuthenticated # QHttpMultiPart objects need to be kept alive and not garbage collected during the # HTTP which uses them. We hold references to these QHttpMultiPart objects here. self._kept_alive_multiparts = {} # type: Dict[QNetworkReply, QHttpMultiPart] self._sending_gcode = False self._compressing_gcode = False self._gcode = [] # type: List[str] self._connection_state_before_timeout = None # type: Optional[ConnectionState]
def showDriveWindow(self) -> None: if not self._drive_window: plugin_dir_path = cast(str, CuraApplication.getInstance().getPluginRegistry().getPluginPath(self.getPluginId())) # We know this plug-in exists because that's us, so this always returns str. path = os.path.join(plugin_dir_path, "src", "qml", "main.qml") self._drive_window = CuraApplication.getInstance().createQmlComponent(path, {"CuraDrive": self}) self.refreshBackups() if self._drive_window: self._drive_window.show()
def _updateEnabled(self): plugin_enabled = False global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() if global_container_stack: plugin_enabled = global_container_stack.getProperty("anti_overhang_mesh", "enabled") CuraApplication.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, plugin_enabled)
def _call_on_qt_thread_wrapper(*args, **kwargs): def _handle_call(ico, *args, **kwargs): ico.result = func(*args, **kwargs) ico.finish_event.set() inter_call_object = InterCallObject() new_args = tuple([inter_call_object] + list(args)[:]) CuraApplication.getInstance().callLater(_handle_call, *new_args, **kwargs) inter_call_object.finish_event.wait() return inter_call_object.result
def __init__(self) -> None: super().__init__("UpgradeFirmware", catalog.i18nc("@action", "Update Firmware")) self._qml_url = "FirmwareUpdaterMachineAction.qml" ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded) self._active_output_device = None # type: Optional[PrinterOutputDevice] self._active_firmware_updater = None # type: Optional[FirmwareUpdater] CuraApplication.getInstance().engineCreatedSignal.connect(self._onEngineCreated)
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: self.writeStarted.emit(self) self.sendMaterialProfiles() # Formats supported by this application (file types that we can actually write). if file_handler: file_formats = file_handler.getSupportedFileTypesWrite() else: file_formats = CuraApplication.getInstance().getMeshFileHandler().getSupportedFileTypesWrite() global_stack = CuraApplication.getInstance().getGlobalContainerStack() # Create a list from the supported file formats string. if not global_stack: Logger.log("e", "Missing global stack!") return machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";") machine_file_formats = [file_type.strip() for file_type in machine_file_formats] # Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format. if "application/x-ufp" not in machine_file_formats and Version(self.firmwareVersion) >= Version("4.4"): machine_file_formats = ["application/x-ufp"] + machine_file_formats # Take the intersection between file_formats and machine_file_formats. format_by_mimetype = {format["mime_type"]: format for format in file_formats} file_formats = [format_by_mimetype[mimetype] for mimetype in machine_file_formats] #Keep them ordered according to the preference in machine_file_formats. if len(file_formats) == 0: Logger.log("e", "There are no file formats available to write with!") raise OutputDeviceError.WriteRequestFailedError(i18n_catalog.i18nc("@info:status", "There are no file formats available to write with!")) preferred_format = file_formats[0] # Just take the first file format available. if file_handler is not None: writer = file_handler.getWriterByMimeType(cast(str, preferred_format["mime_type"])) else: writer = CuraApplication.getInstance().getMeshFileHandler().getWriterByMimeType(cast(str, preferred_format["mime_type"])) if not writer: Logger.log("e", "Unexpected error when trying to get the FileWriter") return # This function pauses with the yield, waiting on instructions on which printer it needs to print with. if not writer: Logger.log("e", "Missing file or mesh writer!") return self._sending_job = self._sendPrintJob(writer, preferred_format, nodes) if self._sending_job is not None: self._sending_job.send(None) # Start the generator. if len(self._printers) > 1: # We need to ask the user. self._spawnPrinterSelectionDialog() is_job_sent = True else: # Just immediately continue. self._sending_job.send("") # No specifically selected printer. is_job_sent = self._sending_job.send(None)
def didAgree(self, user_choice): if user_choice: Logger.log("i", "User agreed to the user agreement") Preferences.getInstance().setValue("general/accepted_user_agreement", True) self._user_agreement_window.hide() else: Logger.log("i", "User did NOT agree to the user agreement") Preferences.getInstance().setValue("general/accepted_user_agreement", False) CuraApplication.getInstance().quit() CuraApplication.getInstance().setNeedToShowUserAgreement(False)
def materialHotendChangedMessage(self, callback): CuraApplication.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Sync with your printer"), i18n_catalog.i18nc("@label", "Would you like to use your current printer configuration in Cura?"), i18n_catalog.i18nc("@label", "The PrintCores and/or materials on your printer differ from those within your current project. For the best result, always slice for the PrintCores and materials that are inserted in your printer."), buttons=QMessageBox.Yes + QMessageBox.No, icon=QMessageBox.Question, callback=callback )
def _checkCorrectGroupName(self, device_id: str, group_name: str) -> None: global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() active_machine_network_name = CuraApplication.getInstance().getMachineManager().activeMachineNetworkKey() if global_container_stack and device_id == active_machine_network_name: # Check if the group_name is correct. If not, update all the containers connected to the same printer if CuraApplication.getInstance().getMachineManager().activeMachineNetworkGroupName != group_name: metadata_filter = {"um_network_key": active_machine_network_name} containers = CuraContainerRegistry.getInstance().findContainerStacks(type="machine", **metadata_filter) for container in containers: container.setMetaDataEntry("group_name", group_name)
def __init__(self, serial_port: str, baud_rate: Optional[int] = None) -> None: super().__init__(serial_port, connection_type = ConnectionType.UsbConnection) self.setName(catalog.i18nc("@item:inmenu", "USB printing")) self.setShortDescription(catalog.i18nc("@action:button Preceded by 'Ready to'.", "Print via USB")) self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB")) self.setIconName("print") self._serial = None # type: Optional[Serial] self._serial_port = serial_port self._address = serial_port self._timeout = 3 # List of gcode lines to be printed self._gcode = [] # type: List[str] self._gcode_position = 0 self._use_auto_detect = True self._baud_rate = baud_rate self._all_baud_rates = [115200, 250000, 500000, 230400, 57600, 38400, 19200, 9600] # Instead of using a timer, we really need the update to be as a thread, as reading from serial can block. self._update_thread = Thread(target = self._update, daemon = True) self._last_temperature_request = None # type: Optional[int] self._firmware_idle_count = 0 self._is_printing = False # A print is being sent. ## Set when print is started in order to check running time. self._print_start_time = None # type: Optional[float] self._print_estimated_time = None # type: Optional[int] self._accepts_commands = True self._paused = False self._printer_busy = False # When printer is preheating and waiting (M190/M109), or when waiting for action on the printer self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB")) # Queue for commands that need to be sent. self._command_queue = Queue() # type: Queue # Event to indicate that an "ok" was received from the printer after sending a command. self._command_received = Event() self._command_received.set() self._firmware_name_requested = False self._firmware_updater = AvrFirmwareUpdater(self) self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "MonitorItem.qml") CuraApplication.getInstance().getOnExitCallbackManager().addCallback(self._checkActivePrintingUponAppExit)
def __init__(self, parent = None): super().__init__(parent) from cura.CuraApplication import CuraApplication self._machine_manager = CuraApplication.getInstance().getMachineManager() self._extruder_manager = CuraApplication.getInstance().getExtruderManager() self._material_manager = CuraApplication.getInstance().getMaterialManager() self._machine_manager.activeStackChanged.connect(self._update) #Update when switching machines. self._material_manager.materialsUpdated.connect(self._update) #Update when the list of materials changes. self._update()
def _removeEraserMesh(self, node: CuraSceneNode): parent = node.getParent() if parent == self._controller.getScene().getRoot(): parent = None op = RemoveSceneNodeOperation(node) op.push() if parent and not Selection.isSelected(parent): Selection.add(parent) CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)
def _createNetworkManager(self) -> None: Logger.log("d", "Creating network manager") if self._manager: self._manager.finished.disconnect(self._handleOnFinished) self._manager.authenticationRequired.disconnect(self._onAuthenticationRequired) self._manager = QNetworkAccessManager() self._manager.finished.connect(self._handleOnFinished) self._last_manager_create_time = time() self._manager.authenticationRequired.connect(self._onAuthenticationRequired) if self._properties.get(b"temporary", b"false") != b"true": CuraApplication.getInstance().getMachineManager().checkCorrectGroupName(self.getId(), self.name)
def __init__(self) -> None: super().__init__("DiscoverUM3Action", catalog.i18nc("@action","Connect via Network")) self._qml_url = "resources/qml/DiscoverUM3Action.qml" self._network_plugin = None #type: Optional[UM3OutputDevicePlugin] self.__additional_components_view = None #type: Optional[QObject] CuraApplication.getInstance().engineCreatedSignal.connect(self._createAdditionalComponentsView) self._last_zero_conf_event_time = time.time() #type: float # Time to wait after a zero-conf service change before allowing a zeroconf reset self._zero_conf_change_grace_period = 0.25 #type: float
def _progressMessageActionTriggered(self, message_id: Optional[str] = None, action_id: Optional[str] = None) -> None: if action_id == "Abort": Logger.log("d", "User aborted sending print to remote.") if self._progress_message is not None: self._progress_message.hide() self._compressing_gcode = False self._sending_gcode = False CuraApplication.getInstance().getController().setActiveStage("PrepareStage") # After compressing the sliced model Cura sends data to printer, to stop receiving updates from the request # the "reply" should be disconnected if self._latest_reply_handler: self._latest_reply_handler.disconnect() self._latest_reply_handler = None
def _createAdditionalComponentsView(self) -> None: Logger.log("d", "Creating additional ui components for UM3.") # Create networking dialog plugin_path = PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting") if not plugin_path: return path = os.path.join(plugin_path, "resources/qml/UM3InfoComponents.qml") self.__additional_components_view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self}) if not self.__additional_components_view: Logger.log("w", "Could not create ui components for UM3.") return # Create extra components CuraApplication.getInstance().addAdditionalComponent("monitorButtons", self.__additional_components_view.findChild(QObject, "networkPrinterConnectButton"))
def _createView(self) -> None: Logger.log("d", "Creating post processing plugin view.") self.loadAllScripts() # Create the plugin dialog component path = os.path.join(cast(str, PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin")), "PostProcessingPlugin.qml") self._view = CuraApplication.getInstance().createQmlComponent(path, {"manager": self}) if self._view is None: Logger.log("e", "Not creating PostProcessing button near save button because the QML component failed to be created.") return Logger.log("d", "Post processing view created.") # Create the save button component CuraApplication.getInstance().addAdditionalComponent("saveButton", self._view.findChild(QObject, "postProcessingSaveAreaButton"))
def requestWrite(self, nodes, file_name = None, filter_by_machine = False, file_handler = None, **kwargs): if self._is_printing: return # Aleady printing # cancel any ongoing preheat timer before starting a print self._printers[0].getController().stopPreheatTimers() CuraApplication.getInstance().getController().setActiveStage("MonitorStage") # find the G-code for the active build plate to print active_build_plate_id = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate gcode_dict = getattr(CuraApplication.getInstance().getController().getScene(), "gcode_dict") gcode_list = gcode_dict[active_build_plate_id] self._printGCode(gcode_list)
def _buildGlobalSettingsMessage(self, stack: ContainerStack) -> None: if not self._all_extruders_settings: self._cacheAllExtruderSettings() if self._all_extruders_settings is None: return settings = self._all_extruders_settings["-1"].copy() # Pre-compute material material_bed_temp_prepend and material_print_temp_prepend start_gcode = settings["machine_start_gcode"] bed_temperature_settings = ["material_bed_temperature", "material_bed_temperature_layer_0"] pattern = r"\{(%s)(,\s?\w+)?\}" % "|".join(bed_temperature_settings) # match {setting} as well as {setting, extruder_nr} settings["material_bed_temp_prepend"] = re.search(pattern, start_gcode) == None print_temperature_settings = ["material_print_temperature", "material_print_temperature_layer_0", "default_material_print_temperature", "material_initial_print_temperature", "material_final_print_temperature", "material_standby_temperature"] pattern = r"\{(%s)(,\s?\w+)?\}" % "|".join(print_temperature_settings) # match {setting} as well as {setting, extruder_nr} settings["material_print_temp_prepend"] = re.search(pattern, start_gcode) == None # Replace the setting tokens in start and end g-code. # Use values from the first used extruder by default so we get the expected temperatures initial_extruder_stack = CuraApplication.getInstance().getExtruderManager().getUsedExtruderStacks()[0] initial_extruder_nr = initial_extruder_stack.getProperty("extruder_nr", "value") settings["machine_start_gcode"] = self._expandGcodeTokens(settings["machine_start_gcode"], initial_extruder_nr) settings["machine_end_gcode"] = self._expandGcodeTokens(settings["machine_end_gcode"], initial_extruder_nr) # Add all sub-messages for each individual setting. for key, value in settings.items(): setting_message = self._slice_message.getMessage("global_settings").addRepeatedMessage("settings") setting_message.name = key setting_message.value = str(value).encode("utf-8") Job.yieldThread()
def _sendPrintJobWaitOnWriteJobFinished(self, job: WriteFileJob) -> None: if self._write_job_progress_message: self._write_job_progress_message.hide() self._progress_message = Message(i18n_catalog.i18nc("@info:status", "Sending data to printer"), lifetime = 0, dismissable = False, progress = -1, title = i18n_catalog.i18nc("@info:title", "Sending Data")) self._progress_message.addAction("Abort", i18n_catalog.i18nc("@action:button", "Cancel"), icon = None, description = "") self._progress_message.actionTriggered.connect(self._progressMessageActionTriggered) self._progress_message.show() parts = [] target_printer, preferred_format, stream = self._dummy_lambdas # If a specific printer was selected, it should be printed with that machine. if target_printer: target_printer = self._printer_uuid_to_unique_name_mapping[target_printer] parts.append(self._createFormPart("name=require_printer_name", bytes(target_printer, "utf-8"), "text/plain")) # Add user name to the print_job parts.append(self._createFormPart("name=owner", bytes(self._getUserName(), "utf-8"), "text/plain")) file_name = CuraApplication.getInstance().getPrintInformation().jobName + "." + preferred_format["extension"] output = stream.getvalue() # Either str or bytes depending on the output mode. if isinstance(stream, io.StringIO): output = cast(str, output).encode("utf-8") output = cast(bytes, output) parts.append(self._createFormPart("name=\"file\"; filename=\"%s\"" % file_name, output)) self._latest_reply_handler = self.postFormWithParts("print_jobs/", parts, on_finished = self._onPostPrintJobFinished, on_progress = self._onUploadPrintJobProgress)
def createExtruderStack(cls, new_stack_id: str, extruder_definition: DefinitionContainerInterface, machine_definition_id: str, position: int, variant_container, material_container, quality_container, global_stack) -> ExtruderStack: from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() stack = ExtruderStack(new_stack_id, parent = global_stack) stack.setName(extruder_definition.getName()) stack.setDefinition(extruder_definition) stack.addMetaDataEntry("position", position) user_container = cls.createUserChangesContainer(new_stack_id + "_user", machine_definition_id, new_stack_id, is_global_stack = False) stack.definitionChanges = cls.createDefinitionChangesContainer(stack, new_stack_id + "_settings") stack.variant = variant_container stack.material = material_container stack.quality = quality_container stack.qualityChanges = application.empty_quality_changes_container stack.userChanges = user_container # Only add the created containers to the registry after we have set all the other # properties. This makes the create operation more transactional, since any problems # setting properties will not result in incomplete containers being added. ContainerRegistry.getInstance().addContainer(user_container) return stack
def _startPrint(self): Logger.log("i", "Sending print job to printer.") if self._sending_gcode: self._error_message = Message( i18n_catalog.i18nc("@info:status", "Sending new jobs (temporarily) blocked, still sending the previous print job.")) self._error_message.show() return self._sending_gcode = True self._send_gcode_start = time() self._progress_message = Message(i18n_catalog.i18nc("@info:status", "Sending data to printer"), 0, False, -1, i18n_catalog.i18nc("@info:title", "Sending Data")) self._progress_message.addAction("Abort", i18n_catalog.i18nc("@action:button", "Cancel"), None, "") self._progress_message.actionTriggered.connect(self._progressMessageActionTriggered) self._progress_message.show() compressed_gcode = self._compressGCode() if compressed_gcode is None: # Abort was called. return file_name = "%s.gcode.gz" % CuraApplication.getInstance().getPrintInformation().jobName self.postForm("print_job", "form-data; name=\"file\";filename=\"%s\"" % file_name, compressed_gcode, on_finished=self._onPostPrintJobFinished) return
def _checkForWarnings(self): warnings = [] print_information = CuraApplication.getInstance().getPrintInformation() if not print_information.materialLengths: Logger.log("w", "There is no material length information. Unable to check for warnings.") return warnings extruder_manager = ExtruderManager.getInstance() for index, extruder in enumerate(self.activePrinter.extruders): if index < len(print_information.materialLengths) and print_information.materialLengths[index] != 0: # The extruder is by this print. # TODO: material length check # Check if the right Printcore is active. variant = extruder_manager.getExtruderStack(index).findContainer({"type": "variant"}) if variant: if variant.getName() != extruder.hotendID: warnings.append(i18n_catalog.i18nc("@label", "Different PrintCore (Cura: {cura_printcore_name}, Printer: {remote_printcore_name}) selected for extruder {extruder_id}".format(cura_printcore_name = variant.getName(), remote_printcore_name = extruder.hotendID, extruder_id = index + 1))) else: Logger.log("w", "Unable to find variant.") # Check if the right material is loaded. local_material = extruder_manager.getExtruderStack(index).findContainer({"type": "material"}) if local_material: if extruder.activeMaterial.guid != local_material.getMetaDataEntry("GUID"): Logger.log("w", "Extruder %s has a different material (%s) as Cura (%s)", index + 1, extruder.activeMaterial.guid, local_material.getMetaDataEntry("GUID")) warnings.append(i18n_catalog.i18nc("@label", "Different material (Cura: {0}, Printer: {1}) selected for extruder {2}").format(local_material.getName(), extruder.activeMaterial.name, index + 1)) else: Logger.log("w", "Unable to find material.") return warnings
def connect(self): super().connect() self._setupMessages() global_container = CuraApplication.getInstance().getGlobalContainerStack() if global_container: self._authentication_id = global_container.getMetaDataEntry("network_authentication_id", None) self._authentication_key = global_container.getMetaDataEntry("network_authentication_key", None)
def createOutputModel(self) -> MaterialOutputModel: material_manager = CuraApplication.getInstance().getMaterialManager() material_group_list = material_manager.getMaterialGroupListByGUID(self.guid) or [] # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list)) material_group = None if read_only_material_group_list: read_only_material_group_list = sorted(read_only_material_group_list, key = lambda x: x.name) material_group = read_only_material_group_list[0] elif non_read_only_material_group_list: non_read_only_material_group_list = sorted(non_read_only_material_group_list, key = lambda x: x.name) material_group = non_read_only_material_group_list[0] if material_group: container = material_group.root_material_node.getContainer() color = container.getMetaDataEntry("color_code") brand = container.getMetaDataEntry("brand") material_type = container.getMetaDataEntry("material") name = container.getName() else: Logger.log("w", "Unable to find material with guid {guid}. Using data as provided by cluster" .format(guid = self.guid)) color = self.color brand = self.brand material_type = self.material name = "Empty" if self.material == "empty" else "Unknown" return MaterialOutputModel(guid = self.guid, type = material_type, brand = brand, color = color, name = name)
def __init__(self, parent: QObject = None) -> None: super().__init__("DiscoverOctoPrintAction", catalog.i18nc("@action", "Connect OctoPrint")) self._qml_url = os.path.join("qml", "DiscoverOctoPrintAction.qml") self._application = CuraApplication.getInstance() self._network_plugin = None # type: Optional[OctoPrintOutputDevicePlugin] # QNetwork manager needs to be created in advance. If we don't it can happen that it doesn't correctly # hook itself into the event loop, which results in events never being fired / done. self._network_manager = QNetworkAccessManager() self._network_manager.finished.connect(self._onRequestFinished) self._settings_reply = None # type: Optional[QNetworkReply] self._instance_supports_appkeys = False self._appkey_reply = None # type: Optional[QNetworkReply] self._appkey_request = None # type: Optional[QNetworkRequest] self._appkey_instance_id = "" self._appkey_poll_timer = QTimer() self._appkey_poll_timer.setInterval(500) self._appkey_poll_timer.setSingleShot(True) self._appkey_poll_timer.timeout.connect(self._pollApiKey) # Try to get version information from plugin.json plugin_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "plugin.json") try: with open(plugin_file_path) as plugin_file: plugin_info = json.load(plugin_file) self._plugin_version = plugin_info["version"] except: # The actual version info is not critical to have so we can continue self._plugin_version = "0.0" Logger.logException("w", "Could not get version information for the plugin") self._user_agent = ("%s/%s %s/%s" % ( self._application.getApplicationName(), self._application.getVersion(), "OctoPrintPlugin", self._plugin_version )).encode() self._instance_responded = False self._instance_in_error = False self._instance_api_key_accepted = False self._instance_supports_sd = False self._instance_supports_camera = False self._instance_installed_plugins = [] # type: List[str] # Load keys cache from preferences self._preferences = self._application.getPreferences() self._preferences.addPreference("octoprint/keys_cache", "") try: self._keys_cache = json.loads(self._deobfuscateString(self._preferences.getValue("octoprint/keys_cache"))) except ValueError: self._keys_cache = {} # type: Dict[str, Any] if not isinstance(self._keys_cache, dict): self._keys_cache = {} # type: Dict[str, Any] self._additional_components = None # type:Optional[QObject] ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded) self._application.engineCreatedSignal.connect(self._createAdditionalComponentsView)
def _getStoredManualAddresses(self) -> List[str]: preferences = CuraApplication.getInstance().getPreferences() preferences.addPreference(self.MANUAL_DEVICES_PREFERENCE_KEY, "") manual_instances = preferences.getValue( self.MANUAL_DEVICES_PREFERENCE_KEY).split(",") return manual_instances
def createMachine( cls, name: str, definition_id: str, machine_extruder_count: Optional[int] = None ) -> Optional[GlobalStack]: """Create a new instance of a machine. :param name: The name of the new machine. :param definition_id: The ID of the machine definition to use. :param machine_extruder_count: The number of extruders in the machine. :return: The new global stack or None if an error occurred. """ from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() registry = application.getContainerRegistry() container_tree = ContainerTree.getInstance() definitions = registry.findDefinitionContainers(id=definition_id) if not definitions: ConfigurationErrorMessage.getInstance().addFaultyContainers( definition_id) Logger.log("w", "Definition {definition} was not found!", definition=definition_id) return None machine_definition = definitions[0] machine_node = container_tree.machines[machine_definition.getId()] generated_name = registry.createUniqueName( "machine", "", name, machine_definition.getName()) # Make sure the new name does not collide with any definition or (quality) profile # createUniqueName() only looks at other stacks, but not at definitions or quality profiles # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true if registry.findContainersMetadata(id=generated_name): generated_name = registry.uniqueName(generated_name) new_global_stack = cls.createGlobalStack( new_stack_id=generated_name, definition=machine_definition, variant_container=application.empty_variant_container, material_container=application.empty_material_container, quality_container=machine_node.preferredGlobalQuality().container, ) new_global_stack.setName(generated_name) # Create ExtruderStacks extruder_dict = machine_definition.getMetaDataEntry( "machine_extruder_trains") for position in extruder_dict: try: cls.createExtruderStackWithDefaultSetup( new_global_stack, position) except IndexError as e: Logger.logException( "e", "Failed to create an extruder stack for position {pos}: {err}" .format(pos=position, err=str(e))) return None # If given, set the machine_extruder_count when creating the machine, or else the extruderList used below will # not return the correct extruder list (since by default, the machine_extruder_count is 1) in machines with # settable number of extruders. if machine_extruder_count and 0 <= machine_extruder_count <= len( extruder_dict): new_global_stack.setProperty("machine_extruder_count", "value", machine_extruder_count) # Only register the extruders if we're sure that all of them are correct. for new_extruder in new_global_stack.extruderList: registry.addContainer(new_extruder) # Register the global stack after the extruder stacks are created. This prevents the registry from adding another # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1). registry.addContainer(new_global_stack) return new_global_stack
def run(self) -> None: if self._build_plate_number is None: self.setResult(StartJobResult.Error) return stack = CuraApplication.getInstance().getGlobalContainerStack() if not stack: self.setResult(StartJobResult.Error) return # Don't slice if there is a setting with an error value. if CuraApplication.getInstance().getMachineManager().stacksHaveErrors: self.setResult(StartJobResult.SettingError) return if CuraApplication.getInstance().getBuildVolume().hasErrors(): self.setResult(StartJobResult.BuildPlateError) return # Don't slice if the buildplate or the nozzle type is incompatible with the materials if not CuraApplication.getInstance().getMachineManager().variantBuildplateCompatible and \ not CuraApplication.getInstance().getMachineManager().variantBuildplateUsable: self.setResult(StartJobResult.MaterialIncompatible) return for position, extruder_stack in stack.extruders.items(): material = extruder_stack.findContainer({"type": "material"}) if not extruder_stack.isEnabled: continue if material: if material.getMetaDataEntry("compatible") == False: self.setResult(StartJobResult.MaterialIncompatible) return # Don't slice if there is a per object setting with an error value. for node in DepthFirstIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if not isinstance(node, CuraSceneNode) or not node.isSelectable(): continue if self._checkStackForErrors(node.callDecoration("getStack")): self.setResult(StartJobResult.ObjectSettingError) return with self._scene.getSceneLock(): # Remove old layer data. for node in DepthFirstIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if node.callDecoration("getLayerData") and node.callDecoration( "getBuildPlateNumber") == self._build_plate_number: node.getParent().removeChild(node) break global_enable_support = stack.getProperty("support_enable", "value") # Get the objects in their groups to print. object_groups = [] if stack.getProperty("print_sequence", "value") == "one_at_a_time": # note that one_at_a_time printing is disabled on belt printers due to collission risk for node in OneAtATimeIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. temp_list = [] # Node can't be printed, so don't bother sending it. if getattr(node, "_outside_buildarea", False): continue # Filter on current build plate build_plate_number = node.callDecoration( "getBuildPlateNumber") if build_plate_number is not None and build_plate_number != self._build_plate_number: continue children = node.getAllChildren() children.append(node) for child_node in children: if child_node.getMeshData() and child_node.getMeshData( ).getVertices() is not None: temp_list.append(child_node) if temp_list: object_groups.append(temp_list) Job.yieldThread() if len(object_groups) == 0: Logger.log( "w", "No objects suitable for one at a time found, or no correct order found" ) else: temp_list = [] has_printing_mesh = False # print convex hull nodes as "faux-raft" print_convex_hulls = stack.getProperty("blackbelt_raft", "value") for node in DepthFirstIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. slice_node = (print_convex_hulls and type(node) is ConvexHullNode ) or node.callDecoration("isSliceable") if slice_node and node.getMeshData() and node.getMeshData( ).getVertices() is not None: per_object_stack = node.callDecoration("getStack") is_non_printing_mesh = False if per_object_stack: is_non_printing_mesh = any( per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS) # Find a reason not to add the node if node.callDecoration( "getBuildPlateNumber" ) != self._build_plate_number and type( node) is not ConvexHullNode: # NB: ConvexHullNodes get none of the usual decorators, so skip checking for them continue if getattr(node, "_outside_buildarea", False) and not is_non_printing_mesh: continue temp_list.append(node) if not is_non_printing_mesh: has_printing_mesh = True Job.yieldThread() #If the list doesn't have any model with suitable settings then clean the list # otherwise CuraEngine will crash if not has_printing_mesh: temp_list.clear() if temp_list: object_groups.append(temp_list) global_stack = CuraApplication.getInstance( ).getGlobalContainerStack() if not global_stack: return extruders_enabled = { position: stack.isEnabled for position, stack in global_stack.extruders.items() } filtered_object_groups = [] has_model_with_disabled_extruders = False associated_disabled_extruders = set() for group in object_groups: stack = global_stack skip_group = False for node in group: # Only check if the printing extruder is enabled for printing meshes is_non_printing_mesh = node.callDecoration( "evaluateIsNonPrintingMesh") extruder_position = node.callDecoration( "getActiveExtruderPosition") if extruder_position is None: # raft meshes may not have an extruder position (yet) extruder_position = "0" if not is_non_printing_mesh and not extruders_enabled[ extruder_position]: skip_group = True has_model_with_disabled_extruders = True associated_disabled_extruders.add(extruder_position) if not skip_group: filtered_object_groups.append(group) if has_model_with_disabled_extruders: self.setResult(StartJobResult.ObjectsWithDisabledExtruder) associated_disabled_extruders = { str(c) for c in sorted( [int(p) + 1 for p in associated_disabled_extruders]) } self.setMessage(", ".join(associated_disabled_extruders)) return # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being # able to find a possible sequence or because there are no objects on the build plate (or they are outside # the build volume) if not filtered_object_groups: self.setResult(StartJobResult.NothingToSlice) return container_registry = ContainerRegistry.getInstance() stack_id = stack.getId() # Adapt layer_height and material_flow for a slanted gantry gantry_angle = self._scene.getRoot().callDecoration( "getGantryAngle") if gantry_angle: # not 0 or None # Act on a copy of the stack, so these changes don't cause a reslice _stack = CuraContainerStack(stack_id + "_temp") for index, container in enumerate(stack.getContainers()): if container_registry.isReadOnly(container.getId()): _stack.replaceContainer(index, container) else: _stack.replaceContainer(index, copy.deepcopy(container)) stack = _stack # Make sure CuraEngine does not create any supports # support_enable is set in the frontend so support options are settable, # but CuraEngine support structures don't work for slanted gantry stack.setProperty("support_enable", "value", False) # Make sure CuraEngine does not create a raft (we create one manually) # Adhesion type is used in the frontend to show the raft in the viewport stack.setProperty("adhesion_type", "value", "none") for key in ["layer_height", "layer_height_0"]: current_value = stack.getProperty(key, "value") stack.setProperty(key, "value", current_value / math.sin(gantry_angle)) self._buildGlobalSettingsMessage(stack) self._buildGlobalInheritsStackMessage(stack) # Build messages for extruder stacks # Send the extruder settings in the order of extruder positions. Somehow, if you send e.g. extruder 3 first, # then CuraEngine can slice with the wrong settings. This I think should be fixed in CuraEngine as well. extruder_stack_list = sorted(list(global_stack.extruders.items()), key=lambda item: int(item[0])) for _, extruder_stack in extruder_stack_list: if gantry_angle: # not 0 or None # Act on a copy of the stack, so these changes don't cause a reslice _extruder_stack = CuraContainerStack( extruder_stack.getId() + "_temp") for index, container in enumerate( extruder_stack.getContainers()): if container_registry.isReadOnly(container.getId()): _extruder_stack.replaceContainer(index, container) else: _extruder_stack.replaceContainer( index, copy.deepcopy(container)) extruder_stack = _extruder_stack extruder_stack.setNextStack(stack) for key in [ "material_flow", "prime_tower_flow", "spaghetti_flow" ]: if extruder_stack.hasProperty(key, "value"): current_value = extruder_stack.getProperty( key, "value") extruder_stack.setProperty( key, "value", current_value * math.sin(gantry_angle)) self._buildExtruderMessage(extruder_stack) bottom_cutting_meshes = [] raft_meshes = [] support_meshes = [] if gantry_angle: # not 0 or None for group in filtered_object_groups: added_meshes = [] for object in group: is_non_printing_mesh = False per_object_stack = object.callDecoration("getStack") # ConvexHullNodes get none of the usual decorators. If it made it here, it is meant to be printed if type(object) is ConvexHullNode: raft_thickness = stack.getProperty( "blackbelt_raft_thickness", "value") raft_margin = stack.getProperty( "blackbelt_raft_margin", "value") mb = MeshBuilder() hull_polygon = object.getHull() if raft_margin > 0: hull_polygon = hull_polygon.getMinkowskiHull( Polygon.approximatedCircle(raft_margin)) mb.addConvexPolygonExtrusion( hull_polygon.getPoints()[::-1], 0, raft_thickness) new_node = self._addMeshFromBuilder(mb, "raftMesh") added_meshes.append(new_node) raft_meshes.append(new_node.getName()) elif not is_non_printing_mesh: # add support mesh if needed blackbelt_support_gantry_angle_bias = None blackbelt_support_minimum_island_area = None if per_object_stack: is_non_printing_mesh = any( per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS) node_enable_support = per_object_stack.getProperty( "support_enable", "value") if per_object_stack.getProperty( "support_mesh", "value"): node_enable_support = node_enable_support or per_object_stack.getProperty( "support_mesh_drop_down", "value") add_support_mesh = node_enable_support if node_enable_support is not None else global_enable_support blackbelt_support_gantry_angle_bias = per_object_stack.getProperty( "blackbelt_support_gantry_angle_bias", "value") blackbelt_support_minimum_island_area = per_object_stack.getProperty( "blackbelt_support_minimum_island_area", "value") else: add_support_mesh = global_enable_support if add_support_mesh: if blackbelt_support_gantry_angle_bias is None: blackbelt_support_gantry_angle_bias = global_stack.getProperty( "blackbelt_support_gantry_angle_bias", "value") biased_down_angle = math.radians( blackbelt_support_gantry_angle_bias) if blackbelt_support_minimum_island_area is None: blackbelt_support_minimum_island_area = global_stack.getProperty( "blackbelt_support_minimum_island_area", "value") support_mesh_data = SupportMeshCreator( down_vector=numpy.array([ 0, -math.cos( math.radians(biased_down_angle)), -math.sin(biased_down_angle) ]), bottom_cut_off=stack.getProperty( "wall_line_width_0", "value") / 2, minimum_island_area= blackbelt_support_minimum_island_area ).createSupportMeshForNode(object) if support_mesh_data: new_node = self._addMeshFromData( support_mesh_data, "generatedSupportMesh") added_meshes.append(new_node) support_meshes.append(new_node.getName()) # check if the bottom needs to be cut off aabb = object.getBoundingBox() if aabb.bottom < 0: # mesh extends below the belt; add a cutting mesh to cut off the part below the bottom height = -aabb.bottom center = Vector(aabb.center.x, -height / 2, aabb.center.z) mb = MeshBuilder() mb.addCube(width=aabb.width, height=height, depth=aabb.depth, center=center) new_node = self._addMeshFromBuilder( mb, "bottomCuttingMesh") added_meshes.append(new_node) bottom_cutting_meshes.append( new_node.getName()) if added_meshes: group += added_meshes transform_matrix = self._scene.getRoot().callDecoration( "getTransformMatrix") front_offset = None raft_offset = 0 raft_speed = None raft_flow = 1.0 if stack.getProperty("blackbelt_raft", "value"): raft_offset = stack.getProperty( "blackbelt_raft_thickness", "value") + stack.getProperty( "blackbelt_raft_gap", "value") raft_speed = stack.getProperty("blackbelt_raft_speed", "value") raft_flow = stack.getProperty("blackbelt_raft_flow", "value") * math.sin(gantry_angle) adhesion_extruder_nr = stack.getProperty("adhesion_extruder_nr", "value") support_extruder_nr = stack.getProperty("support_extruder_nr", "value") for group in filtered_object_groups: group_message = self._slice_message.addRepeatedMessage( "object_lists") if group[0].getParent() is not None and group[0].getParent( ).callDecoration("isGroup"): self._handlePerObjectSettings(group[0].getParent(), group_message) if transform_matrix: scene_front = None for object in group: if type(object) is ConvexHullNode: continue is_non_printing_mesh = object.getName( ) in bottom_cutting_meshes or object.getName( ) in raft_meshes if not is_non_printing_mesh: per_object_stack = object.callDecoration( "getStack") if per_object_stack: is_non_printing_mesh = any( per_object_stack.getProperty(key, "value") for key in NON_PRINTING_MESH_SETTINGS) if not is_non_printing_mesh: _front = object.getBoundingBox().back if scene_front is None or _front < scene_front: scene_front = _front if scene_front is not None: front_offset = transformVertices( numpy.array([[0, 0, scene_front]]), transform_matrix)[0][1] for object in group: if type(object) is ConvexHullNode: continue mesh_data = object.getMeshData() rot_scale = object.getWorldTransformation().getTransposed( ).getData()[0:3, 0:3] translate = object.getWorldTransformation().getData()[:3, 3] # offset all non-raft objects if rafts are enabled # air gap is applied here to vertically offset objects from the raft if object.getName() not in raft_meshes: translate[1] += raft_offset if front_offset: translate[2] -= front_offset # This effectively performs a limited form of MeshData.getTransformed that ignores normals. verts = mesh_data.getVertices() verts = verts.dot(rot_scale) verts += translate if transform_matrix: verts = transformVertices(verts, transform_matrix) # Convert from Y up axes to Z up axes. Equals a 90 degree rotation. verts[:, [1, 2]] = verts[:, [2, 1]] verts[:, 1] *= -1 obj = group_message.addRepeatedMessage("objects") obj.id = id(object) obj.name = object.getName() indices = mesh_data.getIndices() if indices is not None: flat_verts = numpy.take(verts, indices.flatten(), axis=0) else: flat_verts = numpy.array(verts) obj.vertices = flat_verts if object.getName() in raft_meshes: self._addSettingsMessage( obj, { "wall_line_count": 99999999, "speed_wall_0": raft_speed, "speed_wall_x": raft_speed, "material_flow": raft_flow, "extruder_nr": adhesion_extruder_nr }) elif object.getName() in support_meshes: self._addSettingsMessage( obj, { "support_mesh": "True", "support_mesh_drop_down": "False", "extruder_nr": support_extruder_nr }) elif object.getName() in bottom_cutting_meshes: self._addSettingsMessage( obj, { "cutting_mesh": True, "wall_line_count": 0, "top_layers": 0, "bottom_layers": 0, "infill_line_distance": 0, "extruder_nr": 0 }) else: self._handlePerObjectSettings(object, obj) Job.yieldThread() # Store the front-most coordinate of the scene so the scene can be moved back into place post slicing # TODO: this should be handled per mesh-group instead of per scene # One-at-a-time printing should be disabled for slanted gantry printers for now self._scene.getRoot().callDecoration("setSceneFrontOffset", front_offset) self.setResult(StartJobResult.Finished)
def _onDontAskMeAgain(self, checked: bool) -> None: CuraApplication.getInstance().getPreferences().setValue( self._no_layers_warning_preference, not checked)
def changestage(self): CuraApplication.getInstance().getController().setActiveStage( "MonitorStage")
def _onConfirmExitDialogResult(self, result: bool) -> None: if result: application = CuraApplication.getInstance() application.triggerNextExitCheck()
def __init__(self) -> None: self._cura_api = CuraApplication.getInstance().getCuraAPI()
def write(self, stream, nodes, mode=MeshWriter.OutputMode.BinaryMode): archive = VirtualFile() archive.openStream(stream, "application/x-ufp", OpenMode.WriteOnly) #Store the g-code from the scene. archive.addContentType(extension="gcode", mime_type="text/x-gcode") gcode_textio = StringIO() #We have to convert the g-code into bytes. gcode_writer = cast( MeshWriter, PluginRegistry.getInstance().getPluginObject("GCodeWriter")) success = gcode_writer.write(gcode_textio, None) if not success: #Writing the g-code failed. Then I can also not write the gzipped g-code. self.setInformation(gcode_writer.getInformation()) return False gcode = archive.getStream("/3D/model.gcode") gcode.write(gcode_textio.getvalue().encode("UTF-8")) archive.addRelation( virtual_path="/3D/model.gcode", relation_type= "http://schemas.ultimaker.org/package/2018/relationships/gcode") self._createSnapshot() #Store the thumbnail. if self._snapshot: archive.addContentType(extension="png", mime_type="image/png") thumbnail = archive.getStream("/Metadata/thumbnail.png") thumbnail_buffer = QBuffer() thumbnail_buffer.open(QBuffer.ReadWrite) thumbnail_image = self._snapshot thumbnail_image.save(thumbnail_buffer, "PNG") thumbnail.write(thumbnail_buffer.data()) archive.addRelation( virtual_path="/Metadata/thumbnail.png", relation_type= "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail", origin="/3D/model.gcode") else: Logger.log("d", "Thumbnail not created, cannot save it") # Store the material. application = CuraApplication.getInstance() machine_manager = application.getMachineManager() container_registry = application.getContainerRegistry() global_stack = machine_manager.activeMachine material_extension = "xml.fdm_material" material_mime_type = "application/x-ultimaker-material-profile" try: archive.addContentType(extension=material_extension, mime_type=material_mime_type) except: Logger.log("w", "The material extension: %s was already added", material_extension) added_materials = [] for extruder_stack in global_stack.extruders.values(): material = extruder_stack.material try: material_file_name = material.getMetaData( )["base_file"] + ".xml.fdm_material" except KeyError: Logger.log("w", "Unable to get base_file for the material %s", material.getId()) continue material_file_name = "/Materials/" + material_file_name # The same material should not be added again. if material_file_name in added_materials: continue material_root_id = material.getMetaDataEntry("base_file") material_root_query = container_registry.findContainers( id=material_root_id) if not material_root_query: Logger.log( "e", "Cannot find material container with root id {root_id}". format(root_id=material_root_id)) return False material_container = material_root_query[0] try: serialized_material = material_container.serialize() except NotImplementedError: Logger.log( "e", "Unable serialize material container with root id: %s", material_root_id) return False material_file = archive.getStream(material_file_name) material_file.write(serialized_material.encode("UTF-8")) archive.addRelation( virtual_path=material_file_name, relation_type= "http://schemas.ultimaker.org/package/2018/relationships/material", origin="/3D/model.gcode") added_materials.append(material_file_name) try: archive.close() except OSError as e: error_msg = catalog.i18nc( "@info:error", "Can't write to UFP file:") + " " + str(e) self.setInformation(error_msg) Logger.error(error_msg) return False return True
def __init__(self, serial_port: str, baud_rate: Optional[int] = None) -> None: super().__init__(serial_port, connection_type=ConnectionType.UsbConnection) self.setName(catalog.i18nc("@item:inmenu", "USB printing")) self.setShortDescription( catalog.i18nc("@action:button Preceded by 'Ready to'.", "Print via USB")) self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB")) self.setIconName("print") self._serial = None # type: Optional[Serial] self._serial_port = serial_port self._address = serial_port self._timeout = 3 # List of gcode lines to be printed self._gcode = [] # type: List[str] self._gcode_position = 0 self._use_auto_detect = True self._baud_rate = baud_rate self._all_baud_rates = [ 115200, 250000, 500000, 230400, 76800, 57600, 38400, 19200, 9600 ] # Instead of using a timer, we really need the update to be as a thread, as reading from serial can block. self._update_thread = Thread(target=self._update, daemon=True, name="USBPrinterUpdate") self._last_temperature_request = None # type: Optional[int] self._firmware_idle_count = 0 self._is_printing = False # A print is being sent. ## Set when print is started in order to check running time. self._print_start_time = None # type: Optional[float] self._print_estimated_time = None # type: Optional[int] self._accepts_commands = True self._paused = False self._printer_busy = False # When printer is preheating and waiting (M190/M109), or when waiting for action on the printer self.setConnectionText( catalog.i18nc("@info:status", "Connected via USB")) # Queue for commands that need to be sent. self._command_queue = Queue() # type: Queue # Event to indicate that an "ok" was received from the printer after sending a command. self._command_received = Event() self._command_received.set() self._firmware_name_requested = False self._firmware_updater = AvrFirmwareUpdater(self) plugin_path = PluginRegistry.getInstance().getPluginPath("USBPrinting") if plugin_path: self._monitor_view_qml_path = os.path.join(plugin_path, "MonitorItem.qml") else: Logger.log( "e", "Cannot create Monitor QML view: cannot find plugin path for plugin [USBPrinting]" ) self._monitor_view_qml_path = "" CuraApplication.getInstance().getOnExitCallbackManager().addCallback( self._checkActivePrintingUponAppExit)
def event(self, event) -> bool: modifiers = QApplication.keyboardModifiers() ctrl_is_active = modifiers & Qt.ControlModifier shift_is_active = modifiers & Qt.ShiftModifier if event.type == Event.KeyPressEvent and ctrl_is_active: amount = 10 if shift_is_active else 1 if event.key == KeyEvent.UpKey: self.setLayer(self._current_layer_num + amount) return True if event.key == KeyEvent.DownKey: self.setLayer(self._current_layer_num - amount) return True 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" ) CuraApplication.getInstance().callLater( lambda e=event: self.event(e)) return False # Make sure the SimulationPass is created layer_pass = self.getSimulationPass() self.getRenderer().addRenderPass(layer_pass) # Make sure the NozzleNode is add to the root nozzle = self.getNozzleNode() nozzle.setParent(self.getController().getScene().getRoot()) nozzle.setVisible(False) Application.getInstance().globalContainerStackChanged.connect( self._onGlobalStackChanged) self._onGlobalStackChanged() if not self._simulationview_composite_shader: plugin_path = cast( str, PluginRegistry.getInstance().getPluginPath( "SimulationView")) self._simulationview_composite_shader = OpenGL.getInstance( ).createShaderProgram( os.path.join(plugin_path, "simulationview_composite.shader")) theme = CuraApplication.getInstance().getTheme() if theme is not None: self._simulationview_composite_shader.setUniformValue( "u_background_color", Color(*theme.getColor("viewport_background").getRgb())) self._simulationview_composite_shader.setUniformValue( "u_outline_color", Color(*theme.getColor( "model_selection_outline").getRgb())) if not self._composite_pass: self._composite_pass = cast( CompositePass, self.getRenderer().getRenderPass("composite")) self._old_layer_bindings = self._composite_pass.getLayerBindings( )[:] # make a copy so we can restore to it later self._composite_pass.getLayerBindings().append("simulationview") self._old_composite_shader = self._composite_pass.getCompositeShader( ) self._composite_pass.setCompositeShader( self._simulationview_composite_shader) elif event.type == Event.ViewDeactivateEvent: self._wireprint_warning_message.hide() Application.getInstance().globalContainerStackChanged.disconnect( self._onGlobalStackChanged) if self._global_container_stack: self._global_container_stack.propertyChanged.disconnect( self._onPropertyChanged) if self._nozzle_node: self._nozzle_node.setParent(None) self.getRenderer().removeRenderPass(self._layer_pass) if self._composite_pass: self._composite_pass.setLayerBindings( cast(List[str], self._old_layer_bindings)) self._composite_pass.setCompositeShader( cast(ShaderProgram, self._old_composite_shader)) return False
def _hasHumanReadableMachineTypeName(self, machine_type_name: str) -> bool: from cura.CuraApplication import CuraApplication results = CuraApplication.getInstance().getContainerRegistry( ).findDefinitionContainersMetadata(name=machine_type_name) return len(results) > 0
def _read(self, file_name: str) -> Union[SceneNode, List[SceneNode]]: result = [] self._object_count = 0 # Used to name objects as there is no node name yet. # The base object of 3mf is a zipped archive. try: archive = zipfile.ZipFile(file_name, "r") self._base_name = os.path.basename(file_name) parser = Savitar.ThreeMFParser() scene_3mf = parser.parse(archive.open("3D/3dmodel.model").read()) self._unit = scene_3mf.getUnit() for node in scene_3mf.getSceneNodes(): um_node = self._convertSavitarNodeToUMNode(node, file_name) if um_node is None: continue # compensate for original center position, if object(s) is/are not around its zero position transform_matrix = Matrix() mesh_data = um_node.getMeshData() if mesh_data is not None: extents = mesh_data.getExtents() if extents is not None: center_vector = Vector(extents.center.x, extents.center.y, extents.center.z) transform_matrix.setByTranslation(center_vector) transform_matrix.multiply(um_node.getLocalTransformation()) um_node.setTransformation(transform_matrix) global_container_stack = CuraApplication.getInstance( ).getGlobalContainerStack() # Create a transformation Matrix to convert from 3mf worldspace into ours. # First step: flip the y and z axis. transformation_matrix = Matrix() transformation_matrix._data[1, 1] = 0 transformation_matrix._data[1, 2] = 1 transformation_matrix._data[2, 1] = -1 transformation_matrix._data[2, 2] = 0 # Second step: 3MF defines the left corner of the machine as center, whereas cura uses the center of the # build volume. if global_container_stack: translation_vector = Vector( x=-global_container_stack.getProperty( "machine_width", "value") / 2, y=-global_container_stack.getProperty( "machine_depth", "value") / 2, z=0) translation_matrix = Matrix() translation_matrix.setByTranslation(translation_vector) transformation_matrix.multiply(translation_matrix) # Third step: 3MF also defines a unit, whereas Cura always assumes mm. scale_matrix = Matrix() scale_matrix.setByScaleVector( self._getScaleFromUnit(self._unit)) transformation_matrix.multiply(scale_matrix) # Pre multiply the transformation with the loaded transformation, so the data is handled correctly. um_node.setTransformation( um_node.getLocalTransformation().preMultiply( transformation_matrix)) # Check if the model is positioned below the build plate and honor that when loading project files. node_meshdata = um_node.getMeshData() if node_meshdata is not None: aabb = node_meshdata.getExtents( um_node.getWorldTransformation()) if aabb is not None: minimum_z_value = aabb.minimum.y # y is z in transformation coordinates if minimum_z_value < 0: um_node.addDecorator(ZOffsetDecorator()) um_node.callDecoration("setZOffset", minimum_z_value) result.append(um_node) except Exception: Logger.logException("e", "An exception occurred in 3mf reader.") return [] return result
def _onDevicesDiscovered(self, clusters: List[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) # Create a machine if we don't already have it. Do not make it the active machine. 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: 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") message = Message(title=self.I18N_CATALOG.i18ncp( "info:status", "New printer detected from your Ultimaker account", "New printers detected from your Ultimaker account", len(new_devices)), progress=0, lifetime=0, image_source=image_path) message.show() for idx, device in enumerate(new_devices): 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(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{}", device_names) message.setText(message_text)
def disconnect(self) -> None: super().disconnect() Logger.log("i", "Disconnected from cluster %s", self.key) CuraApplication.getInstance().getBackend( ).backendStateChange.disconnect(self._onBackendStateChange)
def run(self) -> None: if self._build_plate_number is None: self.setResult(StartJobResult.Error) return stack = CuraApplication.getInstance().getGlobalContainerStack() if not stack: self.setResult(StartJobResult.Error) return # Don't slice if there is a setting with an error value. if CuraApplication.getInstance().getMachineManager().stacksHaveErrors: self.setResult(StartJobResult.SettingError) return if CuraApplication.getInstance().getBuildVolume().hasErrors(): self.setResult(StartJobResult.BuildPlateError) return # Wait for error checker to be done. while CuraApplication.getInstance().getMachineErrorChecker( ).needToWaitForResult: time.sleep(0.1) if CuraApplication.getInstance().getMachineErrorChecker().hasError: self.setResult(StartJobResult.SettingError) return # Don't slice if the buildplate or the nozzle type is incompatible with the materials if not CuraApplication.getInstance().getMachineManager().variantBuildplateCompatible and \ not CuraApplication.getInstance().getMachineManager().variantBuildplateUsable: self.setResult(StartJobResult.MaterialIncompatible) return for extruder_stack in stack.extruderList: material = extruder_stack.findContainer({"type": "material"}) if not extruder_stack.isEnabled: continue if material: if material.getMetaDataEntry("compatible") == False: self.setResult(StartJobResult.MaterialIncompatible) return # Don't slice if there is a per object setting with an error value. for node in DepthFirstIterator(self._scene.getRoot()): if not isinstance(node, CuraSceneNode) or not node.isSelectable(): continue if self._checkStackForErrors(node.callDecoration("getStack")): self.setResult(StartJobResult.ObjectSettingError) return with self._scene.getSceneLock(): # Remove old layer data. for node in DepthFirstIterator(self._scene.getRoot()): if node.callDecoration("getLayerData") and node.callDecoration( "getBuildPlateNumber") == self._build_plate_number: # Singe we walk through all nodes in the scene, they always have a parent. cast(SceneNode, node.getParent()).removeChild(node) break # Get the objects in their groups to print. object_groups = [] if stack.getProperty("print_sequence", "value") == "one_at_a_time": for node in OneAtATimeIterator(self._scene.getRoot()): temp_list = [] # Node can't be printed, so don't bother sending it. if getattr(node, "_outside_buildarea", False): continue # Filter on current build plate build_plate_number = node.callDecoration( "getBuildPlateNumber") if build_plate_number is not None and build_plate_number != self._build_plate_number: continue children = node.getAllChildren() children.append(node) for child_node in children: mesh_data = child_node.getMeshData() if mesh_data and mesh_data.getVertices() is not None: temp_list.append(child_node) if temp_list: object_groups.append(temp_list) Job.yieldThread() if len(object_groups) == 0: Logger.log( "w", "No objects suitable for one at a time found, or no correct order found" ) else: temp_list = [] has_printing_mesh = False for node in DepthFirstIterator(self._scene.getRoot()): mesh_data = node.getMeshData() if node.callDecoration( "isSliceable" ) and mesh_data and mesh_data.getVertices() is not None: is_non_printing_mesh = bool( node.callDecoration("isNonPrintingMesh")) # Find a reason not to add the node if node.callDecoration("getBuildPlateNumber" ) != self._build_plate_number: continue if getattr(node, "_outside_buildarea", False) and not is_non_printing_mesh: continue temp_list.append(node) if not is_non_printing_mesh: has_printing_mesh = True Job.yieldThread() # If the list doesn't have any model with suitable settings then clean the list # otherwise CuraEngine will crash if not has_printing_mesh: temp_list.clear() if temp_list: object_groups.append(temp_list) global_stack = CuraApplication.getInstance( ).getGlobalContainerStack() if not global_stack: return extruders_enabled = { position: stack.isEnabled for position, stack in global_stack.extruders.items() } filtered_object_groups = [] has_model_with_disabled_extruders = False associated_disabled_extruders = set() for group in object_groups: stack = global_stack skip_group = False for node in group: # Only check if the printing extruder is enabled for printing meshes is_non_printing_mesh = node.callDecoration( "evaluateIsNonPrintingMesh") extruder_position = node.callDecoration( "getActiveExtruderPosition") if not is_non_printing_mesh and not extruders_enabled[ extruder_position]: skip_group = True has_model_with_disabled_extruders = True associated_disabled_extruders.add(extruder_position) if not skip_group: filtered_object_groups.append(group) if has_model_with_disabled_extruders: self.setResult(StartJobResult.ObjectsWithDisabledExtruder) associated_disabled_extruders = { str(c) for c in sorted( [int(p) + 1 for p in associated_disabled_extruders]) } self.setMessage(", ".join(associated_disabled_extruders)) return # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being # able to find a possible sequence or because there are no objects on the build plate (or they are outside # the build volume) if not filtered_object_groups: self.setResult(StartJobResult.NothingToSlice) return self._buildGlobalSettingsMessage(stack) self._buildGlobalInheritsStackMessage(stack) # Build messages for extruder stacks for extruder_stack in global_stack.extruderList: self._buildExtruderMessage(extruder_stack) for group in filtered_object_groups: group_message = self._slice_message.addRepeatedMessage( "object_lists") parent = group[0].getParent() if parent is not None and parent.callDecoration("isGroup"): self._handlePerObjectSettings(cast(CuraSceneNode, parent), group_message) for object in group: mesh_data = object.getMeshData() if mesh_data is None: continue rot_scale = object.getWorldTransformation().getTransposed( ).getData()[0:3, 0:3] translate = object.getWorldTransformation().getData()[:3, 3] # This effectively performs a limited form of MeshData.getTransformed that ignores normals. verts = mesh_data.getVertices() verts = verts.dot(rot_scale) verts += translate # Convert from Y up axes to Z up axes. Equals a 90 degree rotation. verts[:, [1, 2]] = verts[:, [2, 1]] verts[:, 1] *= -1 obj = group_message.addRepeatedMessage("objects") obj.id = id(object) obj.name = object.getName() indices = mesh_data.getIndices() if indices is not None: flat_verts = numpy.take(verts, indices.flatten(), axis=0) else: flat_verts = numpy.array(verts) obj.vertices = flat_verts self._handlePerObjectSettings(cast(CuraSceneNode, object), obj) Job.yieldThread() self.setResult(StartJobResult.Finished)
def loadScripts(self, path: str) -> None: """Load all scripts from provided path. This should probably only be done on init. :param path: Path to check for scripts. """ if ApplicationMetadata.IsEnterpriseVersion: # Delete all __pycache__ not in installation folder, as it may present a security risk. # It prevents this very strange scenario (should already be prevented on enterprise because signed-fault): # - Copy an existing script from the postprocessing-script folder to the appdata scripts folder. # - Also copy the entire __pycache__ folder from the first to the last location. # - Leave the __pycache__ as is, but write malicious code just before the class begins. # - It'll execute, despite that the script has not been signed. # It's not known if these reproduction steps are minimal, but it does at least happen in this case. install_prefix = os.path.abspath( CuraApplication.getInstance().getInstallPrefix()) try: is_in_installation_path = os.path.commonpath( [install_prefix, path]).startswith(install_prefix) except ValueError: is_in_installation_path = False if not is_in_installation_path: TrustBasics.removeCached(path) scripts = pkgutil.iter_modules(path=[path]) """Load all scripts in the scripts folders""" for loader, script_name, ispkg in scripts: # Iterate over all scripts. if script_name not in sys.modules: try: file_path = os.path.join(path, script_name + ".py") if not self._isScriptAllowed(file_path): Logger.warning( "Skipped loading post-processing script {}: not trusted" .format(file_path)) continue spec = importlib.util.spec_from_file_location( __name__ + "." + script_name, file_path) loaded_script = importlib.util.module_from_spec(spec) if spec.loader is None: continue spec.loader.exec_module(loaded_script) # type: ignore sys.modules[ script_name] = loaded_script #TODO: This could be a security risk. Overwrite any module with a user-provided name? loaded_class = getattr(loaded_script, script_name) temp_object = loaded_class() Logger.log("d", "Begin loading of script: %s", script_name) try: setting_data = temp_object.getSettingData() if "name" in setting_data and "key" in setting_data: self._script_labels[ setting_data["key"]] = setting_data["name"] self._loaded_scripts[ setting_data["key"]] = loaded_class else: Logger.log("w", "Script %s.py has no name or key", script_name) self._script_labels[script_name] = script_name self._loaded_scripts[script_name] = loaded_class except AttributeError: Logger.log( "e", "Script %s.py is not a recognised script type. Ensure it inherits Script", script_name) except NotImplementedError: Logger.log("e", "Script %s.py has no implemented settings", script_name) except Exception as e: Logger.logException( "e", "Exception occurred while loading post processing plugin: {error_msg}" .format(error_msg=str(e)))
def _onWriteStarted(self, output_device): try: if not Preferences.getInstance().getValue("info/send_slice_info"): Logger.log("d", "'info/send_slice_info' is turned off.") return # Do nothing, user does not want to send data global_container_stack = Application.getInstance( ).getGlobalContainerStack() print_information = Application.getInstance().getPrintInformation() data = dict() # The data that we're going to submit. data["time_stamp"] = time.time() data["schema_version"] = 0 data["cura_version"] = Application.getInstance().getVersion() active_mode = Preferences.getInstance().getValue( "cura/active_mode") if active_mode == 0: data["active_mode"] = "recommended" else: data["active_mode"] = "custom" data[ "machine_settings_changed_by_user"] = global_container_stack.definitionChanges.getId( ) != "empty" data["language"] = Preferences.getInstance().getValue( "general/language") data["os"] = { "type": platform.system(), "version": platform.version() } data["active_machine"] = { "definition_id": global_container_stack.definition.getId(), "manufacturer": global_container_stack.definition.getMetaData().get( "manufacturer", "") } data["extruders"] = [] extruders = list(ExtruderManager.getInstance().getMachineExtruders( global_container_stack.getId())) extruders = sorted( extruders, key=lambda extruder: extruder.getMetaDataEntry("position")) if not extruders: extruders = [global_container_stack] for extruder in extruders: extruder_dict = dict() extruder_dict["active"] = ExtruderManager.getInstance( ).getActiveExtruderStack() == extruder extruder_dict["material"] = { "GUID": extruder.material.getMetaData().get("GUID", ""), "type": extruder.material.getMetaData().get("material", ""), "brand": extruder.material.getMetaData().get("brand", "") } extruder_dict[ "material_used"] = print_information.materialLengths[int( extruder.getMetaDataEntry("position", "0"))] extruder_dict["variant"] = extruder.variant.getName() extruder_dict["nozzle_size"] = extruder.getProperty( "machine_nozzle_size", "value") extruder_settings = dict() extruder_settings["wall_line_count"] = extruder.getProperty( "wall_line_count", "value") extruder_settings["retraction_enable"] = extruder.getProperty( "retraction_enable", "value") extruder_settings[ "infill_sparse_density"] = extruder.getProperty( "infill_sparse_density", "value") extruder_settings["infill_pattern"] = extruder.getProperty( "infill_pattern", "value") extruder_settings[ "gradual_infill_steps"] = extruder.getProperty( "gradual_infill_steps", "value") extruder_settings[ "default_material_print_temperature"] = extruder.getProperty( "default_material_print_temperature", "value") extruder_settings[ "material_print_temperature"] = extruder.getProperty( "material_print_temperature", "value") extruder_dict["extruder_settings"] = extruder_settings data["extruders"].append(extruder_dict) data[ "quality_profile"] = global_container_stack.quality.getMetaData( ).get("quality_type") data["models"] = [] # Listing all files placed on the build plate for node in DepthFirstIterator(CuraApplication.getInstance( ).getController().getScene().getRoot()): if node.callDecoration("isSliceable"): model = dict() model["hash"] = node.getMeshData().getHash() bounding_box = node.getBoundingBox() model["bounding_box"] = { "minimum": { "x": bounding_box.minimum.x, "y": bounding_box.minimum.y, "z": bounding_box.minimum.z }, "maximum": { "x": bounding_box.maximum.x, "y": bounding_box.maximum.y, "z": bounding_box.maximum.z } } model["transformation"] = { "data": str(node.getWorldTransformation().getData()).replace( "\n", "") } extruder_position = node.callDecoration( "getActiveExtruderPosition") model[ "extruder"] = 0 if extruder_position is None else int( extruder_position) model_settings = dict() model_stack = node.callDecoration("getStack") if model_stack: model_settings[ "support_enabled"] = model_stack.getProperty( "support_enable", "value") model_settings["support_extruder_nr"] = int( model_stack.getProperty("support_extruder_nr", "value")) # Mesh modifiers; model_settings[ "infill_mesh"] = model_stack.getProperty( "infill_mesh", "value") model_settings[ "cutting_mesh"] = model_stack.getProperty( "cutting_mesh", "value") model_settings[ "support_mesh"] = model_stack.getProperty( "support_mesh", "value") model_settings[ "anti_overhang_mesh"] = model_stack.getProperty( "anti_overhang_mesh", "value") model_settings[ "wall_line_count"] = model_stack.getProperty( "wall_line_count", "value") model_settings[ "retraction_enable"] = model_stack.getProperty( "retraction_enable", "value") # Infill settings model_settings[ "infill_sparse_density"] = model_stack.getProperty( "infill_sparse_density", "value") model_settings[ "infill_pattern"] = model_stack.getProperty( "infill_pattern", "value") model_settings[ "gradual_infill_steps"] = model_stack.getProperty( "gradual_infill_steps", "value") model["model_settings"] = model_settings data["models"].append(model) print_times = print_information.printTimesPerFeature data["print_times"] = { "travel": int(print_times["travel"].getDisplayString( DurationFormat.Format.Seconds)), "support": int(print_times["support"].getDisplayString( DurationFormat.Format.Seconds)), "infill": int(print_times["infill"].getDisplayString( DurationFormat.Format.Seconds)), "total": int( print_information.currentPrintTime.getDisplayString( DurationFormat.Format.Seconds)) } print_settings = dict() print_settings[ "layer_height"] = global_container_stack.getProperty( "layer_height", "value") # Support settings print_settings[ "support_enabled"] = global_container_stack.getProperty( "support_enable", "value") print_settings["support_extruder_nr"] = int( global_container_stack.getProperty("support_extruder_nr", "value")) # Platform adhesion settings print_settings[ "adhesion_type"] = global_container_stack.getProperty( "adhesion_type", "value") # Shell settings print_settings[ "wall_line_count"] = global_container_stack.getProperty( "wall_line_count", "value") print_settings[ "retraction_enable"] = global_container_stack.getProperty( "retraction_enable", "value") # Prime tower settings print_settings[ "prime_tower_enable"] = global_container_stack.getProperty( "prime_tower_enable", "value") # Infill settings print_settings[ "infill_sparse_density"] = global_container_stack.getProperty( "infill_sparse_density", "value") print_settings[ "infill_pattern"] = global_container_stack.getProperty( "infill_pattern", "value") print_settings[ "gradual_infill_steps"] = global_container_stack.getProperty( "gradual_infill_steps", "value") print_settings[ "print_sequence"] = global_container_stack.getProperty( "print_sequence", "value") data["print_settings"] = print_settings # Convert data to bytes binary_data = json.dumps(data).encode("utf-8") # Sending slice info non-blocking reportJob = SliceInfoJob(self.info_url, binary_data) reportJob.start() except Exception: # We really can't afford to have a mistake here, as this would break the sending of g-code to a device # (Either saving or directly to a printer). The functionality of the slice data is not *that* important. Logger.logException( "e", "Exception raised while sending slice info." ) # But we should be notified about these problems of course.
def __init__(self, parent=None) -> None: super().__init__(parent) self._max_layers = 0 self._current_layer_num = 0 self._minimum_layer_num = 0 self._current_layer_mesh = None self._current_layer_jumps = None self._top_layers_job = None # type: Optional["_CreateTopLayersJob"] self._activity = False self._old_max_layers = 0 self._max_paths = 0 self._current_path_num = 0 self._minimum_path_num = 0 self.currentLayerNumChanged.connect(self._onCurrentLayerNumChanged) self._busy = False self._simulation_running = False self._ghost_shader = None # type: Optional["ShaderProgram"] self._layer_pass = None # type: Optional[SimulationPass] self._composite_pass = None # type: Optional[CompositePass] self._old_layer_bindings = None # type: Optional[List[str]] self._simulationview_composite_shader = None # type: Optional["ShaderProgram"] self._old_composite_shader = None # type: Optional["ShaderProgram"] self._max_feedrate = sys.float_info.min self._min_feedrate = sys.float_info.max self._max_thickness = sys.float_info.min self._min_thickness = sys.float_info.max self._max_line_width = sys.float_info.min self._min_line_width = sys.float_info.max self._global_container_stack = None # type: Optional[ContainerStack] self._proxy = None self._resetSettings() self._legend_items = None self._show_travel_moves = False self._nozzle_node = None # type: Optional[NozzleNode] Application.getInstance().getPreferences().addPreference( "view/top_layer_count", 5) Application.getInstance().getPreferences().addPreference( "view/only_show_top_layers", False) Application.getInstance().getPreferences().addPreference( "view/force_layer_view_compatibility_mode", False) Application.getInstance().getPreferences().addPreference( "layerview/layer_view_type", 1) # Default to "Line Type". Application.getInstance().getPreferences().addPreference( "layerview/extruder_opacities", "") Application.getInstance().getPreferences().addPreference( "layerview/show_travel_moves", False) Application.getInstance().getPreferences().addPreference( "layerview/show_helpers", True) Application.getInstance().getPreferences().addPreference( "layerview/show_skin", True) Application.getInstance().getPreferences().addPreference( "layerview/show_infill", True) Application.getInstance().getPreferences().addPreference( "layerview/show_starts", True) self._updateWithPreferences() self._solid_layers = int( Application.getInstance().getPreferences().getValue( "view/top_layer_count")) self._only_show_top_layers = bool( Application.getInstance().getPreferences().getValue( "view/only_show_top_layers")) self._compatibility_mode = self._evaluateCompatibilityMode() self._wireprint_warning_message = Message(catalog.i18nc( "@info:status", "Cura does not accurately display layers when Wire Printing is enabled." ), title=catalog.i18nc( "@info:title", "Simulation View")) self._slice_first_warning_message = Message( catalog.i18nc("@info:status", "Nothing is shown because you need to slice first."), title=catalog.i18nc("@info:title", "No layers to show"), option_text=catalog.i18nc("@info:option_text", "Do not show this message again"), option_state=False) self._slice_first_warning_message.optionToggled.connect( self._onDontAskMeAgain) CuraApplication.getInstance().getPreferences().addPreference( self._no_layers_warning_preference, True) QtApplication.getInstance().engineCreatedSignal.connect( self._onEngineCreated)
def _onDontAskMeAgain(self, checked: bool) -> None: CuraApplication.getInstance().getPreferences().setValue( self._preference_key, checked)
def createExtruderStackWithDefaultSetup(cls, global_stack: "GlobalStack", extruder_position: int) -> None: """Create a default Extruder Stack :param global_stack: The global stack this extruder refers to. :param extruder_position: The position of the current extruder. """ from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() registry = application.getContainerRegistry() # Get the extruder definition. extruder_definition_dict = global_stack.getMetaDataEntry( "machine_extruder_trains") extruder_definition_id = extruder_definition_dict[str( extruder_position)] try: extruder_definition = registry.findDefinitionContainers( id=extruder_definition_id)[0] except IndexError: # It still needs to break, but we want to know what extruder ID made it break. msg = "Unable to find extruder definition with the id [%s]" % extruder_definition_id Logger.logException("e", msg) raise IndexError(msg) # Find out what filament diameter we need. approximate_diameter = round( extruder_definition.getProperty("material_diameter", "value") ) # Can't be modified by definition changes since we are just initialising the stack here. # Find the preferred containers. machine_node = ContainerTree.getInstance().machines[ global_stack.definition.getId()] extruder_variant_node = machine_node.variants.get( machine_node.preferred_variant_name) if not extruder_variant_node: Logger.log( "w", "Could not find preferred nozzle {nozzle_name}. Falling back to {fallback}." .format(nozzle_name=machine_node.preferred_variant_name, fallback=next(iter(machine_node.variants)))) extruder_variant_node = next(iter(machine_node.variants.values())) extruder_variant_container = extruder_variant_node.container material_node = extruder_variant_node.preferredMaterial( approximate_diameter) material_container = material_node.container quality_node = material_node.preferredQuality() new_extruder_id = registry.uniqueName(extruder_definition_id) new_extruder = cls.createExtruderStack( new_extruder_id, extruder_definition=extruder_definition, machine_definition_id=global_stack.definition.getId(), position=extruder_position, variant_container=extruder_variant_container, material_container=material_container, quality_container=quality_node.container) new_extruder.setNextStack(global_stack) registry.addContainer(new_extruder)
def execute(self, data): if not self.getSettingValueByKey("enable"): return data stream = io.StringIO() stream.write(";START_SETTINGS\n") machine_manager = CuraApplication.getInstance().getMachineManager() stack = CuraApplication.getInstance().getGlobalContainerStack() global_stack = machine_manager.activeMachine extruder_count = stack.getProperty("machine_extruder_count", "value") extruders = global_stack.extruderList print_information = CuraApplication.getInstance().getPrintInformation() self._write_kv(stream, translate(i18n_cura_catalog, "@label", "Job Name"), print_information.jobName) self._write_kv(stream, "Date", datetime.now().isoformat()) self._write_kv(stream, "Os", "{} {}".format(platform.system(), platform.version())) self._write_kv(stream, "Cura Version", CuraVersion) self._write_kv(stream, translate(i18n_cura_catalog, "@label", "Profile"), global_stack.qualityChanges.getMetaData().get("name", "")) self._write_kv(stream, translate(i18n_cura_catalog, "@label:table_header", "Quality"), global_stack.quality.getMetaData().get("name", "")) # Material for i, extruder in enumerate(extruders): M_Name = extruder.material.getMetaData().get("material", "") MaterialStr = "%s %s : %d" % (translate(i18n_cura_catalog, "@label", "Material"), translate(i18n_cura_catalog, "@label", "Extruder"), i) self._write_kv(stream, MaterialStr, M_Name) material_weight = sum(print_information.materialWeights, 0) if material_weight > 0: self._write_kv(stream, translate(i18n_cura_catalog, "@label", "Material estimation"), "{:.1f}g".format(material_weight)) self._write_kv(stream, translate(i18n_cura_catalog, "@label", "Printing Time"), print_information.currentPrintTime.getDisplayString()) # Define every section to get the same order as in the Cura Interface # Modification from global_stack to extruders[0] for i, extruder in enumerate(extruders): self._doTree(extruder, "resolution", stream, 0, i) self._doTree(extruder, "shell", stream, 0, i) self._doTree(extruder, "infill", stream, 0, i) self._doTree(extruder, "material", stream, 0, i) self._doTree(extruder, "speed", stream, 0, i) self._doTree(extruder, "travel", stream, 0, i) self._doTree(extruder, "cooling", stream, 0, i) # If single extruder doesn't export the data if extruder_count>1 : self._doTree(extruder, "dual", stream, 0, i) self._doTree(extruders[0],"support", stream, 0, 0) self._doTree(extruders[0],"platform_adhesion", stream, 0, 0) for i, extruder in enumerate(extruders): self._doTree(extruder, "meshfix", stream, 0, i) self._doTree(extruders[0], "blackmagic", stream, 0, 0) self._doTree(extruders[0], "experimental", stream, 0, 0) self._doTree(extruders[0], "machine_settings", stream, 0, 0) for i, extruder in enumerate(extruders): self._doTreeExtrud(extruder, "machine_settings", stream, 0, i) # set_trace(port=4444) stream.write(";END_SETTINGS\n") ## some characters, like 40°C and 800mm/s² aren't ascii-encodable and cause errors data.append("".join(filter(lambda x: x in string.printable, stream.getvalue()))) return data
def slice(self) -> None: Logger.log("d", "Starting to slice...") self._backend._slice_start_time = time() if not self._backend._build_plates_to_be_sliced: self._backend.processingProgress.emit(1.0) Logger.log( "w", "Slice unnecessary, nothing has changed that needs reslicing.") return if self._backend._process_layers_job: Logger.log("d", "Process layers job still busy, trying later.") return if not hasattr(self._backend._scene, "gcode_dict"): self._backend._scene.gcode_dict = { } #type: ignore #Because we are creating the missing attribute here. # see if we really have to slice application = CuraApplication.getInstance() active_build_plate = application.getMultiBuildPlateModel( ).activeBuildPlate build_plate_to_be_sliced = self._backend._build_plates_to_be_sliced.pop( 0) Logger.log( "d", "Going to slice build plate [%s]!" % build_plate_to_be_sliced) num_objects = self._backend._numObjectsPerBuildPlate() self._backend._stored_layer_data = [] self._backend._stored_optimized_layer_data[ build_plate_to_be_sliced] = [] if build_plate_to_be_sliced not in num_objects or num_objects[ build_plate_to_be_sliced] == 0: self._backend._scene.gcode_dict[build_plate_to_be_sliced] = [ ] #type: ignore #Because we created this attribute above. Logger.log("d", "Build plate %s has no objects to be sliced, skipping", build_plate_to_be_sliced) if self._backend._build_plates_to_be_sliced: self._backend.slice() return if application.getPrintInformation( ) and build_plate_to_be_sliced == active_build_plate: application.getPrintInformation().setToZeroPrintInformation( build_plate_to_be_sliced) if self._backend._process is None: # type: ignore self._backend._createSocket() self._backend.stopSlicing() self._backend._engine_is_fresh = False # Yes we're going to use the engine self._backend.processingProgress.emit(0.0) self._backend.backendStateChange.emit(BackendState.NotStarted) self._backend._scene.gcode_dict[build_plate_to_be_sliced] = [ ] #type: ignore #[] indexed by build plate number self._backend._slicing = True self._backend.slicingStarted.emit() self._backend.determineAutoSlicing( ) # Switch timer on or off if appropriate slice_message = self._backend._socket.createMessage("cura.proto.Slice") ## PATCH: local import self._backend._start_slice_job = StartSliceJob.StartSliceJob( slice_message) self._backend._start_slice_job_build_plate = build_plate_to_be_sliced self._backend._start_slice_job.setBuildPlate( self._backend._start_slice_job_build_plate) self._backend._start_slice_job.start() self._backend._start_slice_job.finished.connect( self._backend._onStartSliceCompleted)
def htmlComparePage(): # Current machine machine_manager = Application.getInstance().getMachineManager() global_stack = machine_manager.activeMachine stack = CuraApplication.getInstance().getGlobalContainerStack() machine_id = global_stack.quality.getMetaData().get('definition', '') Logger.log("d", "HtmlComparePage machine_id = " + machine_id) containers = ContainerRegistry.getInstance().findInstanceContainers( definition=machine_id, type='quality_changes') containers.sort(key=lambda x: x.getId()) containers.reverse() containers.sort(key=lambda x: x.getName()) Profil_List = [] Container_List = [] liste_keys = [] liste_keys_extruder = [] # Logger.log("d", "Before container") for container in containers: # type to detect Extruder or Global container analyse getMetaDataEntry('position') extruder_position = container.getMetaDataEntry('position') if extruder_position is None: # Logger.log("d", "global : " + container.getId()) Profil_List.append(container.getName()) Container_List.append(str(id(container))) if hasattr(container, 'getAllKeys'): keys = list(container.getAllKeys()) for key in keys: liste_keys.append(key) else: if hasattr(container, 'getAllKeys'): keys = list(container.getAllKeys()) for key in keys: liste_keys_extruder.append(key) liste_keys = list(dict.fromkeys(liste_keys)) liste_keys.sort() liste_keys_extruder = list(dict.fromkeys(liste_keys_extruder)) liste_keys_extruder.sort() html = getHtmlHeader() # Menu creation html += '<div class="menu">\n' html += '<h3><a href="#Top_page">Profile List ' + encode( machine_id) + '</a></h3>' html += '<ul>\n' for profil in Profil_List: # html += '<li><input type="checkbox" id="chk_' + str( Profil_List.index(profil) ) + '" checked onclick="toggleColumnVisibility()"/> <a href="#' + str( Container_List[Profil_List.index(profil)]) + '">' + encode( profil) + '</a></li>' html += '</ul>\n' # Java script filter function html += keyUnselectAllWidget() html += keyFilterWidget() html += toggleDifferencesWidget() html += toggleNullValueWidget() html += '</div>\n' # Contents creation html += '\n<div class="contents">\n' html += '<h2 id="Top_page">Profiles</h2>\n' short_value_properties = True html += '<table class=' 'key_value_table' '><thead>\n' html += '<tr><th>Key</th>\n' for profil in Profil_List: html += '<th>' + encode(profil) html += '<a id="' + str( Container_List[Profil_List.index(profil)]) + '" ></a>' html += '</th>\n' html += '</tr></thead><tbody>\n' html += '<tr class="metadata"><td class="key">definition</td>' for container in containers: # type to detect Extruder or Global container analyse getMetaDataEntry('position') extruder_position = container.getMetaDataEntry('position') if extruder_position is None: MetaData_definition = container.getMetaDataEntry('definition') if MetaData_definition is not None: html += '<td class="value">' + MetaData_definition + '</td>' html += '</tr>\n' html += '<tr class="metadata"><td class="key">quality_type</td>' for container in containers: # type to detect Extruder or Global container analyse getMetaDataEntry('position') extruder_position = container.getMetaDataEntry('position') if extruder_position is None: MetaData_quality_type = container.getMetaDataEntry('quality_type') if MetaData_quality_type is not None: html += '<td class="value">' + MetaData_quality_type + '</td>' html += '</tr>\n' for Lkey in liste_keys: try: untranslated_label = stack.getProperty(Lkey, 'label') definition_key = Lkey + ' label' translated_label = i18n_catalog.i18nc(definition_key, untranslated_label) untranslated_description = stack.getProperty(Lkey, 'description') description_key = Lkey + ' description' translated_description = i18n_catalog.i18nc( description_key, untranslated_description) except ValueError: continue html += '<tr class="" --data-key="' + translated_label + '"><td class="CellWithComment">🔑 ' + encode( translated_label ) + '<span class="CellComment">' + translated_description + '</span></td>' for container in containers: # type to detect Extruder or Global container analyse getMetaDataEntry('position') extruder_position = container.getMetaDataEntry('position') if extruder_position is None: # Html_td = '' key_properties = [ 'value', 'resolve' ] if short_value_properties else setting_prop_names key_properties.sort() # hasattr() method returns true if an object has the given named attribute and false if it does not if hasattr(container, 'getAllKeys'): keys = list(container.getAllKeys()) keys.sort() for key in keys: if key == Lkey: formatted_value = formatSettingCompareValue( container, key, key_properties).value formatted_key = encode(str(key)) Html_td = '<td class="value">' + formatted_value + '</td>' if Html_td == '': html += '<td class="value">-</td>' else: html += Html_td html += '</tr>\n' # html += tableFooter() extruder_count = int( CuraApplication.getInstance().getGlobalContainerStack().getProperty( 'machine_extruder_count', 'value')) # Logger.log("d", "extruder_count : %s",extruder_count) ind = 0 while ind < extruder_count: # Naw Table for Extruder settings # html +='<table class=''key_value_table_extruder''><thead>\n' html += '<thead><tr><th>' + encode('Extruder N° ' + str(ind)) + '</th>\n' for profil in Profil_List: html += '<th>' + encode(profil) html += '</th>\n' html += '</tr></thead><tbody>\n' try: for Lkey in liste_keys_extruder: # Logger.log("d", "Lkey : %s",Lkey) definition_key = Lkey + ' label' description_key = Lkey + ' description' try: untranslated_label = stack.getProperty(Lkey, 'label') translated_label = i18n_catalog.i18nc( definition_key, untranslated_label) untranslated_description = stack.getProperty( Lkey, 'description') translated_description = i18n_catalog.i18nc( description_key, untranslated_description) html += '<tr class="" --data-key="' + translated_label + '"><td class="CellWithComment">🔑 ' + encode( translated_label ) + '<span class="CellComment">' + translated_description + '</span></td>' except: Logger.log("d", "Translated_label ERROR on Lkey : %s", Lkey) translated_label = Lkey untranslated_label = Lkey translated_description = 'Description ' + Lkey + ' Not found' untranslated_description = 'Description ' + Lkey + ' Not found' html += '<tr class="" --data-key="' + translated_label + '"><td class="CellWithError">🔑 ' + encode( translated_label ) + '<span class="CellComment">' + translated_description + '</span></td>' html += '<td class="value_extruder">-</td>' continue for container in containers: # type to detect Extruder or Global container analyse getMetaDataEntry('position') # Could be none type extruder_position = container.getMetaDataEntry('position') if extruder_position is not None: # Logger.log("d", "extruder_position : %s",extruder_position) Extrud_Nb = int(extruder_position) if Extrud_Nb == ind: # Html_td = '' key_properties = [ 'value', 'resolve' ] if short_value_properties else setting_prop_names key_properties.sort() # hasattr() method returns true if an object has the given named attribute and false if it does not if hasattr(container, 'getAllKeys'): keys = list(container.getAllKeys()) keys.sort() for key in keys: if key == Lkey: # Logger.log("d", "Key -> Lkey : %s",key) formatted_value = formatSettingCompareValue( container, key, key_properties).value formatted_key = encode(str(key)) Html_td = '<td class="value_extruder">' + formatted_value + '</td>' if Html_td == '': html += '<td class="value_extruder">-</td>' else: html += Html_td html += '</tr>\n' except: Logger.log("d", "HtmlComparePage ERROR on Lkey : %s", Lkey) pass # html += tableFooter() ind += 1 html += tableFooter() html += '</div>' html += htmlFooter Logger.log("d", "HtmlComparePage : Fin") return html
def _convertSavitarNodeToUMNode( self, savitar_node: Savitar.SceneNode, file_name: str = "") -> Optional[SceneNode]: self._object_count += 1 node_name = "Object %s" % self._object_count active_build_plate = CuraApplication.getInstance( ).getMultiBuildPlateModel().activeBuildPlate um_node = CuraSceneNode() # This adds a SettingOverrideDecorator um_node.addDecorator(BuildPlateDecorator(active_build_plate)) um_node.setName(node_name) transformation = self._createMatrixFromTransformationString( savitar_node.getTransformation()) um_node.setTransformation(transformation) mesh_builder = MeshBuilder() data = numpy.fromstring( savitar_node.getMeshData().getFlatVerticesAsBytes(), dtype=numpy.float32) vertices = numpy.resize(data, (int(data.size / 3), 3)) mesh_builder.setVertices(vertices) mesh_builder.calculateNormals(fast=True) if file_name: # The filename is used to give the user the option to reload the file if it is changed on disk # It is only set for the root node of the 3mf file mesh_builder.setFileName(file_name) mesh_data = mesh_builder.build() if len(mesh_data.getVertices()): um_node.setMeshData(mesh_data) for child in savitar_node.getChildren(): child_node = self._convertSavitarNodeToUMNode(child) if child_node: um_node.addChild(child_node) if um_node.getMeshData() is None and len(um_node.getChildren()) == 0: return None settings = savitar_node.getSettings() # Add the setting override decorator, so we can add settings to this node. if settings: global_container_stack = CuraApplication.getInstance( ).getGlobalContainerStack() # Ensure the correct next container for the SettingOverride decorator is set. if global_container_stack: default_stack = ExtruderManager.getInstance().getExtruderStack( 0) if default_stack: um_node.callDecoration("setActiveExtruder", default_stack.getId()) # Get the definition & set it definition_id = ContainerTree.getInstance().machines[ global_container_stack.definition.getId( )].quality_definition um_node.callDecoration("getStack").getTop().setDefinition( definition_id) setting_container = um_node.callDecoration("getStack").getTop() for key in settings: setting_value = settings[key] # Extruder_nr is a special case. if key == "extruder_nr": extruder_stack = ExtruderManager.getInstance( ).getExtruderStack(int(setting_value)) if extruder_stack: um_node.callDecoration("setActiveExtruder", extruder_stack.getId()) else: Logger.log("w", "Unable to find extruder in position %s", setting_value) continue setting_container.setProperty(key, "value", setting_value) if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None: group_decorator = GroupDecorator() um_node.addDecorator(group_decorator) um_node.setSelectable(True) if um_node.getMeshData(): # Assuming that all nodes with mesh data are printable objects # affects (auto) slicing sliceable_decorator = SliceableObjectDecorator() um_node.addDecorator(sliceable_decorator) return um_node
def addExtruderStackForSingleExtrusionMachine( self, machine, extruder_id, new_global_quality_changes=None, create_new_ids=True): new_extruder_id = extruder_id application = CuraApplication.getInstance() extruder_definitions = self.findDefinitionContainers( id=new_extruder_id) if not extruder_definitions: Logger.log("w", "Could not find definition containers for extruder %s", new_extruder_id) return extruder_definition = extruder_definitions[0] unique_name = self.uniqueName( machine.getName() + " " + new_extruder_id ) if create_new_ids else machine.getName() + " " + new_extruder_id extruder_stack = ExtruderStack.ExtruderStack(unique_name) extruder_stack.setName(extruder_definition.getName()) extruder_stack.setDefinition(extruder_definition) extruder_stack.addMetaDataEntry( "position", extruder_definition.getMetaDataEntry("position")) # create a new definition_changes container for the extruder stack definition_changes_id = self.uniqueName( extruder_stack.getId() + "_settings" ) if create_new_ids else extruder_stack.getId() + "_settings" definition_changes_name = definition_changes_id definition_changes = InstanceContainer(definition_changes_id, parent=application) definition_changes.setName(definition_changes_name) definition_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) definition_changes.addMetaDataEntry("type", "definition_changes") definition_changes.addMetaDataEntry("definition", extruder_definition.getId()) # move definition_changes settings if exist for setting_key in definition_changes.getAllKeys(): if machine.definition.getProperty(setting_key, "settable_per_extruder"): setting_value = machine.definitionChanges.getProperty( setting_key, "value") if setting_value is not None: # move it to the extruder stack's definition_changes setting_definition = machine.getSettingDefinition( setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState( ) # Ensure that the state is not seen as a user state. definition_changes.addInstance(new_instance) definition_changes.setDirty(True) machine.definitionChanges.removeInstance( setting_key, postpone_emit=True) self.addContainer(definition_changes) extruder_stack.setDefinitionChanges(definition_changes) # create empty user changes container otherwise user_container_id = self.uniqueName( extruder_stack.getId() + "_user") if create_new_ids else extruder_stack.getId() + "_user" user_container_name = user_container_id user_container = InstanceContainer(user_container_id, parent=application) user_container.setName(user_container_name) user_container.addMetaDataEntry("type", "user") user_container.addMetaDataEntry("machine", machine.getId()) user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) user_container.setDefinition(machine.definition.getId()) user_container.setMetaDataEntry( "position", extruder_stack.getMetaDataEntry("position")) if machine.userChanges: # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes # container to the extruder stack. for user_setting_key in machine.userChanges.getAllKeys(): settable_per_extruder = machine.getProperty( user_setting_key, "settable_per_extruder") if settable_per_extruder: setting_value = machine.getProperty( user_setting_key, "value") setting_definition = machine.getSettingDefinition( user_setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState( ) # Ensure that the state is not seen as a user state. user_container.addInstance(new_instance) user_container.setDirty(True) machine.userChanges.removeInstance(user_setting_key, postpone_emit=True) self.addContainer(user_container) extruder_stack.setUserChanges(user_container) empty_variant = application.empty_variant_container empty_material = application.empty_material_container empty_quality = application.empty_quality_container if machine.variant.getId() not in ("empty", "empty_variant"): variant = machine.variant else: variant = empty_variant extruder_stack.variant = variant if machine.material.getId() not in ("empty", "empty_material"): material = machine.material else: material = empty_material extruder_stack.material = material if machine.quality.getId() not in ("empty", "empty_quality"): quality = machine.quality else: quality = empty_quality extruder_stack.quality = quality machine_quality_changes = machine.qualityChanges if new_global_quality_changes is not None: machine_quality_changes = new_global_quality_changes if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"): extruder_quality_changes_container = self.findInstanceContainers( name=machine_quality_changes.getName(), extruder=extruder_id) if extruder_quality_changes_container: extruder_quality_changes_container = extruder_quality_changes_container[ 0] quality_changes_id = extruder_quality_changes_container.getId() extruder_stack.qualityChanges = self.findInstanceContainers( id=quality_changes_id)[0] else: # Some extruder quality_changes containers can be created at runtime as files in the qualities # folder. Those files won't be loaded in the registry immediately. So we also need to search # the folder to see if the quality_changes exists. extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder( machine_quality_changes.getName()) if extruder_quality_changes_container: quality_changes_id = extruder_quality_changes_container.getId( ) extruder_quality_changes_container.addMetaDataEntry( "position", extruder_definition.getMetaDataEntry("position")) extruder_stack.qualityChanges = self.findInstanceContainers( id=quality_changes_id)[0] else: # if we still cannot find a quality changes container for the extruder, create a new one container_name = machine_quality_changes.getName() container_id = self.uniqueName(extruder_stack.getId() + "_qc_" + container_name) extruder_quality_changes_container = InstanceContainer( container_id, parent=application) extruder_quality_changes_container.setName(container_name) extruder_quality_changes_container.addMetaDataEntry( "type", "quality_changes") extruder_quality_changes_container.addMetaDataEntry( "setting_version", CuraApplication.SettingVersion) extruder_quality_changes_container.addMetaDataEntry( "position", extruder_definition.getMetaDataEntry("position")) extruder_quality_changes_container.addMetaDataEntry( "quality_type", machine_quality_changes.getMetaDataEntry( "quality_type")) extruder_quality_changes_container.setDefinition( machine_quality_changes.getDefinition().getId()) self.addContainer(extruder_quality_changes_container) extruder_stack.qualityChanges = extruder_quality_changes_container if not extruder_quality_changes_container: Logger.log( "w", "Could not find quality_changes named [%s] for extruder [%s]", machine_quality_changes.getName(), extruder_stack.getId()) else: # move all per-extruder settings to the extruder's quality changes for qc_setting_key in machine_quality_changes.getAllKeys(): settable_per_extruder = machine.getProperty( qc_setting_key, "settable_per_extruder") if settable_per_extruder: setting_value = machine_quality_changes.getProperty( qc_setting_key, "value") setting_definition = machine.getSettingDefinition( qc_setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState( ) # Ensure that the state is not seen as a user state. extruder_quality_changes_container.addInstance( new_instance) extruder_quality_changes_container.setDirty(True) machine_quality_changes.removeInstance( qc_setting_key, postpone_emit=True) else: extruder_stack.qualityChanges = self.findInstanceContainers( id="empty_quality_changes")[0] self.addContainer(extruder_stack) # Also need to fix the other qualities that are suitable for this machine. Those quality changes may still have # per-extruder settings in the container for the machine instead of the extruder. if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"): quality_changes_machine_definition_id = machine_quality_changes.getDefinition( ).getId() else: whole_machine_definition = machine.definition machine_entry = machine.definition.getMetaDataEntry("machine") if machine_entry is not None: container_registry = ContainerRegistry.getInstance() whole_machine_definition = container_registry.findDefinitionContainers( id=machine_entry)[0] quality_changes_machine_definition_id = "fdmprinter" if whole_machine_definition.getMetaDataEntry( "has_machine_quality"): quality_changes_machine_definition_id = machine.definition.getMetaDataEntry( "quality_definition", whole_machine_definition.getId()) qcs = self.findInstanceContainers( type="quality_changes", definition=quality_changes_machine_definition_id) qc_groups = {} # map of qc names -> qc containers for qc in qcs: qc_name = qc.getName() if qc_name not in qc_groups: qc_groups[qc_name] = [] qc_groups[qc_name].append(qc) # try to find from the quality changes cura directory too quality_changes_container = self._findQualityChangesContainerInCuraFolder( machine_quality_changes.getName()) if quality_changes_container: qc_groups[qc_name].append(quality_changes_container) for qc_name, qc_list in qc_groups.items(): qc_dict = {"global": None, "extruders": []} for qc in qc_list: extruder_position = qc.getMetaDataEntry("position") if extruder_position is not None: qc_dict["extruders"].append(qc) else: qc_dict["global"] = qc if qc_dict["global"] is not None and len( qc_dict["extruders"]) == 1: # move per-extruder settings for qc_setting_key in qc_dict["global"].getAllKeys(): settable_per_extruder = machine.getProperty( qc_setting_key, "settable_per_extruder") if settable_per_extruder: setting_value = qc_dict["global"].getProperty( qc_setting_key, "value") setting_definition = machine.getSettingDefinition( qc_setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState( ) # Ensure that the state is not seen as a user state. qc_dict["extruders"][0].addInstance(new_instance) qc_dict["extruders"][0].setDirty(True) qc_dict["global"].removeInstance(qc_setting_key, postpone_emit=True) # Set next stack at the end extruder_stack.setNextStack(machine) return extruder_stack
def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]: from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() variant_manager = application.getVariantManager() material_manager = application.getMaterialManager() quality_manager = application.getQualityManager() registry = application.getContainerRegistry() definitions = registry.findDefinitionContainers(id = definition_id) if not definitions: ConfigurationErrorMessage.getInstance().addFaultyContainers(definition_id) Logger.log("w", "Definition {definition} was not found!", definition = definition_id) return None machine_definition = definitions[0] # get variant container for the global stack global_variant_container = application.empty_variant_container global_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.BUILD_PLATE) if global_variant_node: global_variant_container = global_variant_node.getContainer() if not global_variant_container: global_variant_container = application.empty_variant_container # get variant container for extruders extruder_variant_container = application.empty_variant_container extruder_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.NOZZLE) extruder_variant_name = None if extruder_variant_node: extruder_variant_container = extruder_variant_node.getContainer() if not extruder_variant_container: extruder_variant_container = application.empty_variant_container extruder_variant_name = extruder_variant_container.getName() generated_name = registry.createUniqueName("machine", "", name, machine_definition.getName()) # Make sure the new name does not collide with any definition or (quality) profile # createUniqueName() only looks at other stacks, but not at definitions or quality profiles # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true if registry.findContainersMetadata(id = generated_name): generated_name = registry.uniqueName(generated_name) new_global_stack = cls.createGlobalStack( new_stack_id = generated_name, definition = machine_definition, variant_container = global_variant_container, material_container = application.empty_material_container, quality_container = application.empty_quality_container, ) new_global_stack.setName(generated_name) # Create ExtruderStacks extruder_dict = machine_definition.getMetaDataEntry("machine_extruder_trains") for position, extruder_definition_id in extruder_dict.items(): # Sanity check: make sure that the positions in the extruder definitions are same as in the machine # definition extruder_definition = registry.findDefinitionContainers(id = extruder_definition_id)[0] position_in_extruder_def = extruder_definition.getMetaDataEntry("position") if position_in_extruder_def != position: ConfigurationErrorMessage.getInstance().addFaultyContainers(extruder_definition_id) return None #Don't return any container stack then, not the rest of the extruders either. # get material container for extruders material_container = application.empty_material_container material_node = material_manager.getDefaultMaterial(new_global_stack, position, extruder_variant_name, extruder_definition = extruder_definition) if material_node and material_node.getContainer(): material_container = material_node.getContainer() new_extruder_id = registry.uniqueName(extruder_definition_id) new_extruder = cls.createExtruderStack( new_extruder_id, extruder_definition = extruder_definition, machine_definition_id = definition_id, position = position, variant_container = extruder_variant_container, material_container = material_container, quality_container = application.empty_quality_container ) new_extruder.setNextStack(new_global_stack) new_global_stack.addExtruder(new_extruder) for new_extruder in new_global_stack.extruders.values(): #Only register the extruders if we're sure that all of them are correct. registry.addContainer(new_extruder) preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type") quality_group_dict = quality_manager.getQualityGroups(new_global_stack) if not quality_group_dict: # There is no available quality group, set all quality containers to empty. new_global_stack.quality = application.empty_quality_container for extruder_stack in new_global_stack.extruders.values(): extruder_stack.quality = application.empty_quality_container else: # Set the quality containers to the preferred quality type if available, otherwise use the first quality # type that's available. if preferred_quality_type not in quality_group_dict: Logger.log("w", "The preferred quality {quality_type} doesn't exist for this set-up. Choosing a random one.".format(quality_type = preferred_quality_type)) preferred_quality_type = next(iter(quality_group_dict)) quality_group = quality_group_dict.get(preferred_quality_type) new_global_stack.quality = quality_group.node_for_global.getContainer() if not new_global_stack.quality: new_global_stack.quality = application.empty_quality_container for position, extruder_stack in new_global_stack.extruders.items(): if position in quality_group.nodes_for_extruders and quality_group.nodes_for_extruders[position].getContainer(): extruder_stack.quality = quality_group.nodes_for_extruders[position].getContainer() else: extruder_stack.quality = application.empty_quality_container # Register the global stack after the extruder stacks are created. This prevents the registry from adding another # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1). registry.addContainer(new_global_stack) return new_global_stack
def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: self.writeStarted.emit(self) self.sendMaterialProfiles() # Formats supported by this application (file types that we can actually write). if file_handler: file_formats = file_handler.getSupportedFileTypesWrite() else: file_formats = CuraApplication.getInstance().getMeshFileHandler( ).getSupportedFileTypesWrite() global_stack = CuraApplication.getInstance().getGlobalContainerStack() # Create a list from the supported file formats string. if not global_stack: Logger.log("e", "Missing global stack!") return machine_file_formats = global_stack.getMetaDataEntry( "file_formats").split(";") machine_file_formats = [ file_type.strip() for file_type in machine_file_formats ] # Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format. if "application/x-ufp" not in machine_file_formats and Version( self.firmwareVersion) >= Version("4.4"): machine_file_formats = ["application/x-ufp"] + machine_file_formats # Take the intersection between file_formats and machine_file_formats. format_by_mimetype = { format["mime_type"]: format for format in file_formats } file_formats = [ format_by_mimetype[mimetype] for mimetype in machine_file_formats ] #Keep them ordered according to the preference in machine_file_formats. if len(file_formats) == 0: Logger.log("e", "There are no file formats available to write with!") raise OutputDeviceError.WriteRequestFailedError( i18n_catalog.i18nc( "@info:status", "There are no file formats available to write with!")) preferred_format = file_formats[0] # Just take the first file format available. if file_handler is not None: writer = file_handler.getWriterByMimeType( cast(str, preferred_format["mime_type"])) else: writer = CuraApplication.getInstance().getMeshFileHandler( ).getWriterByMimeType(cast(str, preferred_format["mime_type"])) if not writer: Logger.log("e", "Unexpected error when trying to get the FileWriter") return # This function pauses with the yield, waiting on instructions on which printer it needs to print with. if not writer: Logger.log("e", "Missing file or mesh writer!") return self._sending_job = self._sendPrintJob(writer, preferred_format, nodes) if self._sending_job is not None: self._sending_job.send(None) # Start the generator. if len(self._printers) > 1: # We need to ask the user. self._spawnPrinterSelectionDialog() is_job_sent = True else: # Just immediately continue. self._sending_job.send("") # No specifically selected printer. is_job_sent = self._sending_job.send(None)
def __init__(self) -> None: super().__init__() # Find out where the engine is located, and how it is called. # This depends on how Cura is packaged and which OS we are running on. executable_name = "CuraEngine" if Platform.isWindows(): executable_name += ".exe" default_engine_location = executable_name search_path = [ os.path.abspath(os.path.dirname(sys.executable)), os.path.abspath( os.path.join(os.path.dirname(sys.executable), "bin")), os.path.abspath(os.path.join(os.path.dirname(sys.executable), "..")), os.path.join(CuraApplication.getInstallPrefix(), "bin"), os.path.dirname(os.path.abspath(sys.executable)), ] for path in search_path: engine_path = os.path.join(path, executable_name) if os.path.isfile(engine_path): default_engine_location = engine_path break if Platform.isLinux() and not default_engine_location: if not os.getenv("PATH"): raise OSError( "There is something wrong with your Linux installation.") for pathdir in cast(str, os.getenv("PATH")).split(os.pathsep): execpath = os.path.join(pathdir, executable_name) if os.path.exists(execpath): default_engine_location = execpath break self._application = CuraApplication.getInstance( ) #type: CuraApplication self._multi_build_plate_model = None #type: Optional[MultiBuildPlateModel] self._machine_error_checker = None #type: Optional[MachineErrorChecker] if not default_engine_location: raise EnvironmentError("Could not find CuraEngine") Logger.log("i", "Found CuraEngine at: %s", default_engine_location) default_engine_location = os.path.abspath(default_engine_location) self._application.getPreferences().addPreference( "backend/location", default_engine_location) # Workaround to disable layer view processing if layer view is not active. self._layer_view_active = False #type: bool self._onActiveViewChanged() self._stored_layer_data = [] # type: List[Arcus.PythonMessage] self._stored_optimized_layer_data = { } # type: Dict[int, List[Arcus.PythonMessage]] # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob self._scene = self._application.getController().getScene( ) #type: Scene self._scene.sceneChanged.connect(self._onSceneChanged) # Triggers for auto-slicing. Auto-slicing is triggered as follows: # - auto-slicing is started with a timer # - whenever there is a value change, we start the timer # - sometimes an error check can get scheduled for a value change, in that case, we ONLY want to start the # auto-slicing timer when that error check is finished # If there is an error check, stop the auto-slicing timer, and only wait for the error check to be finished # to start the auto-slicing timer again. # self._global_container_stack = None #type: Optional[ContainerStack] # Listeners for receiving messages from the back-end. self._message_handlers["cura.proto.Layer"] = self._onLayerMessage self._message_handlers[ "cura.proto.LayerOptimized"] = self._onOptimizedLayerMessage self._message_handlers["cura.proto.Progress"] = self._onProgressMessage self._message_handlers[ "cura.proto.GCodeLayer"] = self._onGCodeLayerMessage self._message_handlers[ "cura.proto.GCodePrefix"] = self._onGCodePrefixMessage self._message_handlers[ "cura.proto.PrintTimeMaterialEstimates"] = self._onPrintTimeMaterialEstimates self._message_handlers[ "cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage self._start_slice_job = None #type: Optional[StartSliceJob] self._start_slice_job_build_plate = None #type: Optional[int] self._slicing = False #type: bool # Are we currently slicing? self._restart = False #type: bool # Back-end is currently restarting? self._tool_active = False #type: bool # If a tool is active, some tasks do not have to do anything self._always_restart = True #type: bool # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness. self._process_layers_job = None #type: Optional[ProcessSlicedLayersJob] # The currently active job to process layers, or None if it is not processing layers. self._build_plates_to_be_sliced = [ ] #type: List[int] # what needs slicing? self._engine_is_fresh = True #type: bool # Is the newly started engine used before or not? self._backend_log_max_lines = 20000 #type: int # Maximum number of lines to buffer self._error_message = None #type: Optional[Message] # Pop-up message that shows errors. self._last_num_objects = defaultdict( int ) #type: Dict[int, int] # Count number of objects to see if there is something changed self._postponed_scene_change_sources = [ ] #type: List[SceneNode] # scene change is postponed (by a tool) self._slice_start_time = None #type: Optional[float] self._is_disabled = False #type: bool self._application.getPreferences().addPreference( "general/auto_slice", False) self._use_timer = False #type: bool # When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired. # This timer will group them up, and only slice for the last setting changed signal. # TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction. self._change_timer = QTimer() #type: QTimer self._change_timer.setSingleShot(True) self._change_timer.setInterval(500) self.determineAutoSlicing() self._application.getPreferences().preferenceChanged.connect( self._onPreferencesChanged) self._application.initializationFinished.connect(self.initialize)
def _onEngineCreated(self) -> None: CuraApplication.getInstance().getMachineManager( ).outputDevicesChanged.connect(self._onOutputDevicesChanged)