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 _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 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 __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 _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 __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 _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 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 _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 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 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 __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 _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 _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 _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 __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 _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 _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 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 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 _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 _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 _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 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 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 _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 _storeBackupDate(self) -> None: backup_date = datetime.now().strftime(self.DATE_FORMAT) preferences = CuraApplication.getInstance().getPreferences() preferences.setValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, backup_date)
def event(self, event): if event.type == Event.ViewActivateEvent: # FIX: on Max OS X, somehow QOpenGLContext.currentContext() can become None during View switching. # This can happen when you do the following steps: # 1. Start Cura # 2. Load a model # 3. Switch to Custom mode # 4. Select the model and click on the per-object tool icon # 5. Switch view to Layer view or X-Ray # 6. Cura will very likely crash # It seems to be a timing issue that the currentContext can somehow be empty, but I have no clue why. # This fix tries to reschedule the view changing event call on the Qt thread again if the current OpenGL # context is None. if Platform.isOSX(): if QOpenGLContext.currentContext() is None: Logger.log( "d", "current context of OpenGL is empty on Mac OS X, will try to create shaders later" ) CuraApplication.getInstance().callLater( lambda e=event: self.event(e)) return if not self._xray_pass: # Currently the RenderPass constructor requires a size > 0 # This should be fixed in RenderPass's constructor. self._xray_pass = XRayPass.XRayPass(1, 1) self.getRenderer().addRenderPass(self._xray_pass) if not self._xray_composite_shader: self._xray_composite_shader = OpenGL.getInstance( ).createShaderProgram( os.path.join( PluginRegistry.getInstance().getPluginPath("XRayView"), "xray_composite.shader")) theme = Application.getInstance().getTheme() self._xray_composite_shader.setUniformValue( "u_background_color", Color(*theme.getColor("viewport_background").getRgb())) self._xray_composite_shader.setUniformValue( "u_error_color", Color(*theme.getColor("xray_error").getRgb())) self._xray_composite_shader.setUniformValue( "u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb())) if not self._composite_pass: self._composite_pass = self.getRenderer().getRenderPass( "composite") self._old_layer_bindings = self._composite_pass.getLayerBindings() self._composite_pass.setLayerBindings( ["default", "selection", "xray"]) self._old_composite_shader = self._composite_pass.getCompositeShader( ) self._composite_pass.setCompositeShader( self._xray_composite_shader) if event.type == Event.ViewDeactivateEvent: self.getRenderer().removeRenderPass(self._xray_pass) self._composite_pass.setLayerBindings(self._old_layer_bindings) self._composite_pass.setCompositeShader(self._old_composite_shader)
def __init__(self) -> None: self._cura_api = CuraApplication.getInstance().getCuraAPI() self._json_cloud_scope = JsonDecoratorScope( UltimakerCloudScope(CuraApplication.getInstance()))
def __init__(self): self._application = CuraApplication.getInstance()
def _checkIfClusterHost(self): if len(self._printers) < 1 and self.isConnected(): NotClusterHostMessage(self).show() self.close() CuraApplication.getInstance().getOutputDeviceManager().removeOutputDevice(self.key)
def __init__(self, parent=None) -> None: QObject.__init__(self, parent) Extension.__init__(self) #Inzialize varables self.userText = "" self._continueDialog = None # set the preferences to store the default value self._preferences = CuraApplication.getInstance().getPreferences() self._preferences.addPreference("calibrationshapes/size", 20) # convert as float to avoid further issue self._size = float( self._preferences.getValue("calibrationshapes/size")) # Suggested solution from fieldOfView . Unfortunatly it doesn't works # https://github.com/5axes/Calibration-Shapes/issues/1 # Cura should be able to find the scripts from inside the plugin folder if the scripts are into a folder named resources Resources.addSearchPath( os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources")) self._controller = CuraApplication.getInstance().getController() self._message = None self.setMenuName(catalog.i18nc("@item:inmenu", "Part for calibration")) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a cube"), self.addCube) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a cylinder"), self.addCylinder) # self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a sphere"), self.addSphere) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a tube"), self.addTube) self.addMenuItem("", lambda: None) self.addMenuItem( catalog.i18nc("@item:inmenu", "Add a Calibration Cube"), self.addCalibrationCube) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a PLA TempTower"), self.addPLATempTower) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a PETG TempTower"), self.addPETGTempTower) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add an ABS TempTower"), self.addABSTempTower) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Retract Test"), self.addRetractTest) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Retract Tower"), self.addRetractTower) self.addMenuItem( catalog.i18nc("@item:inmenu", "Add a Junction Deviation Tower"), self.addJunctionDeviationTower) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Bridge Test"), self.addBridgeTest) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Thin Wall Test"), self.addThinWall) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add an Overhang Test"), self.addOverhangTest) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Flow Test"), self.addFlowTest) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Tolerance Test"), self.addTolerance) self.addMenuItem( catalog.i18nc("@item:inmenu", "Add a MultiCube Calibration"), self.addMultiCube) self.addMenuItem( catalog.i18nc("@item:inmenu", "Add a Bed Level Calibration"), self.addBedLevelCalibration) self.addMenuItem(" ", lambda: None) self.addMenuItem(catalog.i18nc("@item:inmenu", "Copy Scripts"), self.copyScript) self.addMenuItem(catalog.i18nc("@item:inmenu", "Define default size"), self.defaultSize) self.addMenuItem(" ", lambda: None) self.addMenuItem(catalog.i18nc("@item:inmenu", "Help"), self.gotoHelp) #Inzialize varables self.userText = "" self._continueDialog = None
def _getLastBackupDate(self) -> "datetime": preferences = CuraApplication.getInstance().getPreferences() last_backup_date = preferences.getValue( Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY) return datetime.strptime(last_backup_date, self.DATE_FORMAT)
def __init__(self, serial_port: str, baud_rate: Optional[int] = None) -> None: super().__init__(serial_port) 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, 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._update_firmware_thread = Thread(target=self._updateFirmware, daemon=True) self._last_temperature_request = None # type: Optional[int] 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._firmware_view = None self._firmware_location = None self._firmware_progress = 0 self._firmware_update_state = FirmwareUpdateState.idle 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() CuraApplication.getInstance().getOnExitCallbackManager().addCallback( self._checkActivePrintingUponAppExit)
def toggleAutoBackup(self, enabled: bool) -> None: preferences = CuraApplication.getInstance().getPreferences() preferences.setValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, enabled)
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 = [] remote_clusters_added = False host_guid_map = {machine.getMetaDataEntry(self.META_HOST_GUID): device_cluster_id for device_cluster_id, machine in self._um_cloud_printers.items() if machine.getMetaDataEntry(self.META_HOST_GUID)} machine_manager = CuraApplication.getInstance().getMachineManager() for cluster_data in clusters: device = CloudOutputDevice(self._api, cluster_data) # If the machine already existed before, it will be present in the host_guid_map if cluster_data.host_guid in host_guid_map: machine = machine_manager.getMachine(device.printerType, {self.META_HOST_GUID: cluster_data.host_guid}) if machine and machine.getMetaDataEntry(self.META_CLUSTER_ID) != device.key: # If the retrieved device has a different cluster_id than the existing machine, bring the existing # machine up-to-date. self._updateOutdatedMachine(outdated_machine = machine, new_cloud_output_device = device) # Create a machine if we don't already have it. Do not make it the active machine. # 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) elif device.getId() not in self._remote_clusters: self._remote_clusters[device.getId()] = device remote_clusters_added = True # If a printer that was removed from the account is re-added, change its metadata to mark it not removed # from the account elif not parseBool(self._um_cloud_printers[device.key].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")): self._um_cloud_printers[device.key].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True) # Inform the Cloud printers model about new devices. new_devices_list_of_dicts = [{ "key": d.getId(), "name": d.name, "machine_type": d.printerTypeName, "firmware_version": d.firmwareVersion} for d in new_devices] discovered_cloud_printers_model = CuraApplication.getInstance().getDiscoveredCloudPrintersModel() discovered_cloud_printers_model.addDiscoveredCloudPrinters(new_devices_list_of_dicts) if not new_devices: if remote_clusters_added: self._connectToActiveMachine() return # Sort new_devices on online status first, alphabetical second. # Since the first device might be activated in case there is no active printer yet, # it would be nice to prioritize online devices online_cluster_names = {c.friendly_name.lower() for c in clusters if c.is_online and not c.friendly_name is None} new_devices.sort(key = lambda x: ("a{}" if x.name.lower() in online_cluster_names else "b{}").format(x.name.lower())) 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, message_type = Message.MessageType.POSITIVE ) message.show() for idx, device in enumerate(new_devices): message_text = self.i18n_catalog.i18nc("info:status Filled in with printer name and printer model.", "Adding printer {name} ({model}) from your account").format(name = device.name, model = 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 # If there is no active machine, activate the first available cloud printer activate = not CuraApplication.getInstance().getMachineManager().activeMachine self._createMachineFromDiscoveredDevice(device.getId(), activate = activate) message.setProgress(None) max_disp_devices = 3 if len(new_devices) > max_disp_devices: num_hidden = len(new_devices) - max_disp_devices device_name_list = ["<li>{} ({})</li>".format(device.name, device.printerTypeName) for device in new_devices[0:max_disp_devices]] device_name_list.append("<li>" + self.i18n_catalog.i18ncp("info:{0} gets replaced by a number of printers", "... and {0} other", "... and {0} others", num_hidden) + "</li>") device_names = "".join(device_name_list) else: device_names = "".join(["<li>{} ({})</li>".format(device.name, device.printerTypeName) for device in new_devices]) message_text = self.i18n_catalog.i18nc("info:status", "Printers added from Digital Factory:") + "<ul>" + device_names + "</ul>" message.setText(message_text)
def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]: from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() variant_manager = application.getVariantManager() 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 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 in extruder_dict: cls.createExtruderStackWithDefaultSetup(new_global_stack, position) 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
faulthandler.enable(file = sys.stderr, all_threads = True) elif sys.stdout and not sys.stdout.closed: faulthandler.enable(file = sys.stdout, all_threads = True) from cura.CuraApplication import CuraApplication # WORKAROUND: CURA-6739 # The CTM file loading module in Trimesh requires the OpenCTM library to be dynamically loaded. It uses # ctypes.util.find_library() to find libopenctm.dylib, but this doesn't seem to look in the ".app" application folder # on Mac OS X. Adding the search path to environment variables such as DYLD_LIBRARY_PATH and DYLD_FALLBACK_LIBRARY_PATH # makes it work. The workaround here uses DYLD_FALLBACK_LIBRARY_PATH. if Platform.isOSX() and getattr(sys, "frozen", False): old_env = os.environ.get("DYLD_FALLBACK_LIBRARY_PATH", "") # This is where libopenctm.so is in the .app folder. search_path = os.path.join(CuraApplication.getInstallPrefix(), "MacOS") path_list = old_env.split(":") if search_path not in path_list: path_list.append(search_path) os.environ["DYLD_FALLBACK_LIBRARY_PATH"] = ":".join(path_list) import trimesh.exchange.load os.environ["DYLD_FALLBACK_LIBRARY_PATH"] = old_env # WORKAROUND: CURA-6739 # Similar CTM file loading fix for Linux, but NOTE THAT this doesn't work directly with Python 3.5.7. There's a fix # for ctypes.util.find_library() in Python 3.6 and 3.7. That fix makes sure that find_library() will check # LD_LIBRARY_PATH. With Python 3.5, that fix needs to be backported to make this workaround work. if Platform.isLinux() and getattr(sys, "frozen", False): old_env = os.environ.get("LD_LIBRARY_PATH", "") # This is where libopenctm.so is in the AppImage. search_path = os.path.join(CuraApplication.getInstallPrefix(), "bin")
def _devicesRemovedFromAccount(self, removed_device_ids: Set[str]) -> None: """ Removes the CloudOutputDevice from the received device ids and marks the specific printers as "removed from account". In addition, it generates a message to inform the user about the printers that are no longer linked to his/her account. The message is not generated if all the printers have been previously reported as not linked to the account. :param removed_device_ids: Set of device ids, whose CloudOutputDevice needs to be removed :return: None """ if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn: return # Do not report device ids which have been previously marked as non-linked to the account ignored_device_ids = set() for device_id in removed_device_ids: if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")): ignored_device_ids.add(device_id) # Keep the reported_device_ids list in a class variable, so that the message button actions can access it and # take the necessary steps to fulfill their purpose. self.reported_device_ids = removed_device_ids - ignored_device_ids if not self.reported_device_ids: return # Generate message self._removed_printers_message = Message( title = self.i18n_catalog.i18ncp( "info:status", "A cloud connection is not available for a printer", "A cloud connection is not available for some printers", len(self.reported_device_ids) ), message_type = Message.MessageType.WARNING ) device_names = "".join(["<li>{} ({})</li>".format(self._um_cloud_printers[device].name, self._um_cloud_printers[device].definition.name) for device in self.reported_device_ids]) message_text = self.i18n_catalog.i18ncp( "info:status", "This printer is not linked to the Digital Factory:", "These printers are not linked to the Digital Factory:", len(self.reported_device_ids) ) message_text += "<br/><ul>{}</ul><br/>".format(device_names) digital_factory_string = self.i18n_catalog.i18nc("info:name", "Ultimaker Digital Factory") message_text += self.i18n_catalog.i18nc( "info:status", "To establish a connection, please visit the {website_link}".format(website_link = "<a href='https://digitalfactory.ultimaker.com?utm_source=cura&utm_medium=software&utm_campaign=change-account-connect-printer'>{}</a>.".format(digital_factory_string)) ) self._removed_printers_message.setText(message_text) self._removed_printers_message.addAction("keep_printer_configurations_action", name = self.i18n_catalog.i18nc("@action:button", "Keep printer configurations"), icon = "", description = "Keep cloud printers in Ultimaker Cura when not connected to your account.", button_align = Message.ActionButtonAlignment.ALIGN_RIGHT) self._removed_printers_message.addAction("remove_printers_action", name = self.i18n_catalog.i18nc("@action:button", "Remove printers"), icon = "", description = "Remove cloud printer(s) which aren't linked to your account.", button_style = Message.ActionButtonStyle.SECONDARY, button_align = Message.ActionButtonAlignment.ALIGN_LEFT) self._removed_printers_message.actionTriggered.connect(self._onRemovedPrintersMessageActionTriggered) output_device_manager = CuraApplication.getInstance().getOutputDeviceManager() # Remove the output device from the printers for device_id in removed_device_ids: device = self._um_cloud_printers.get(device_id, None) # type: Optional[GlobalStack] if not device: continue if device_id in output_device_manager.getOutputDeviceIds(): output_device_manager.removeOutputDevice(device_id) if device_id in self._remote_clusters: del self._remote_clusters[device_id] # Update the printer's metadata to mark it as not linked to the account device.setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, False) self._removed_printers_message.show()
def autoBackupEnabled(self) -> bool: preferences = CuraApplication.getInstance().getPreferences() return bool( preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY))
def _onConfirmExitDialogResult(self, result: bool) -> None: if result: application = CuraApplication.getInstance() application.triggerNextExitCheck()
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) 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, device_id, address, properties, parent=None) -> None: super().__init__(device_id=device_id, address=address, properties=properties, connection_type=ConnectionType.NetworkConnection, parent=parent) self._api_prefix = "/cluster-api/v1/" self._application = CuraApplication.getInstance() self._number_of_extruders = 2 self._dummy_lambdas = ( "", {}, io.BytesIO() ) # type: Tuple[Optional[str], Dict[str, Union[str, int, bool]], Union[io.StringIO, io.BytesIO]] self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._received_print_jobs = False # type: bool if PluginRegistry.getInstance() is not None: self._monitor_view_qml_path = os.path.join( PluginRegistry.getInstance().getPluginPath( "UM3NetworkPrinting"), "resources", "qml", "MonitorStage.qml") # Trigger the printersChanged signal when the private signal is triggered self.printersChanged.connect(self._clusterPrintersChanged) self._accepts_commands = True # type: bool # Cluster does not have authentication, so default to authenticated self._authentication_state = AuthState.Authenticated self._error_message = None # type: Optional[Message] self._write_job_progress_message = None # type: Optional[Message] self._progress_message = None # type: Optional[Message] self._active_printer = None # type: Optional[PrinterOutputModel] self._printer_selection_dialog = None # type: QObject self.setPriority( 3 ) # Make sure the output device gets selected above local file output self.setName(self._id) self.setShortDescription( i18n_catalog.i18nc("@action:button Preceded by 'Ready to'.", "Print over network")) self.setDescription( i18n_catalog.i18nc("@properties:tooltip", "Print over network")) self.setConnectionText( i18n_catalog.i18nc("@info:status", "Connected over the network")) self._printer_uuid_to_unique_name_mapping = {} # type: Dict[str, str] self._finished_jobs = [] # type: List[UM3PrintJobOutputModel] self._cluster_size = int(properties.get(b"cluster_size", 0)) # type: int self._latest_reply_handler = None # type: Optional[QNetworkReply] self._sending_job = None self._active_camera_url = QUrl() # type: QUrl
class CalibrationShapes(QObject, Extension): #Create an api from cura.CuraApplication import CuraApplication api = CuraApplication.getInstance().getCuraAPI() # The QT signal, which signals an update for user information text userInfoTextChanged = pyqtSignal() userSizeChanged = pyqtSignal() def __init__(self, parent=None) -> None: QObject.__init__(self, parent) Extension.__init__(self) #Inzialize varables self.userText = "" self._continueDialog = None # set the preferences to store the default value self._preferences = CuraApplication.getInstance().getPreferences() self._preferences.addPreference("calibrationshapes/size", 20) # convert as float to avoid further issue self._size = float( self._preferences.getValue("calibrationshapes/size")) # Suggested solution from fieldOfView . Unfortunatly it doesn't works # https://github.com/5axes/Calibration-Shapes/issues/1 # Cura should be able to find the scripts from inside the plugin folder if the scripts are into a folder named resources Resources.addSearchPath( os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources")) self._controller = CuraApplication.getInstance().getController() self._message = None self.setMenuName(catalog.i18nc("@item:inmenu", "Part for calibration")) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a cube"), self.addCube) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a cylinder"), self.addCylinder) # self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a sphere"), self.addSphere) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a tube"), self.addTube) self.addMenuItem("", lambda: None) self.addMenuItem( catalog.i18nc("@item:inmenu", "Add a Calibration Cube"), self.addCalibrationCube) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a PLA TempTower"), self.addPLATempTower) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a PETG TempTower"), self.addPETGTempTower) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add an ABS TempTower"), self.addABSTempTower) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Retract Test"), self.addRetractTest) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Retract Tower"), self.addRetractTower) self.addMenuItem( catalog.i18nc("@item:inmenu", "Add a Junction Deviation Tower"), self.addJunctionDeviationTower) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Bridge Test"), self.addBridgeTest) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Thin Wall Test"), self.addThinWall) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add an Overhang Test"), self.addOverhangTest) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Flow Test"), self.addFlowTest) self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Tolerance Test"), self.addTolerance) self.addMenuItem( catalog.i18nc("@item:inmenu", "Add a MultiCube Calibration"), self.addMultiCube) self.addMenuItem( catalog.i18nc("@item:inmenu", "Add a Bed Level Calibration"), self.addBedLevelCalibration) self.addMenuItem(" ", lambda: None) self.addMenuItem(catalog.i18nc("@item:inmenu", "Copy Scripts"), self.copyScript) self.addMenuItem(catalog.i18nc("@item:inmenu", "Define default size"), self.defaultSize) self.addMenuItem(" ", lambda: None) self.addMenuItem(catalog.i18nc("@item:inmenu", "Help"), self.gotoHelp) #Inzialize varables self.userText = "" self._continueDialog = None # Define the default value pour the standard element def defaultSize(self) -> None: if self._continueDialog is None: self._continueDialog = self._createDialogue() self._continueDialog.show() #self.userSizeChanged.emit() #====User Input===================================================================================================== @pyqtProperty(str, notify=userSizeChanged) def sizeInput(self): return str(self._size) #The QT property, which is computed on demand from our userInfoText when the appropriate signal is emitted @pyqtProperty(str, notify=userInfoTextChanged) def userInfoText(self): return self.userText #This method builds the dialog from the qml file and registers this class #as the manager variable def _createDialogue(self): qml_file_path = os.path.join( PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "CalibrationShapes.qml") component_with_context = Application.getInstance().createQmlComponent( qml_file_path, {"manager": self}) return component_with_context def getSize(self) -> float: return self._size # is called when a key gets released in the size inputField (twice for some reason) @pyqtSlot(str) def sizeEntered(self, text): # Is the textfield empty ? Don't show a message then if text == "": #self.writeToLog("size-Textfield: Empty") self.userMessage("", "ok") return #Convert commas to points text = text.replace(",", ".") #self.writeToLog("Size-Textfield: read value "+text) #Is the entered Text a number? try: float(text) except ValueError: self.userMessage("Entered size invalid: " + text, "wrong") return self._size = float(text) #Check if positive if self._size <= 0: self.userMessage("Size value must be positive !", "wrong") self._size = 20 return self.writeToLog("Set calibrationshapes/size printFromHeight to : " + text) self._preferences.setValue("calibrationshapes/size", self._size) #clear the message Field self.userMessage("", "ok") #===== Text Output =================================================================================================== #writes the message to the log, includes timestamp, length is fixed def writeToLog(self, str): Logger.log("d", "Source calibration shapes = %s", str) #Sends an user message to the Info Textfield, color depends on status (prioritized feedback) # Red wrong for Errors and Warnings # Grey for details and messages that aren't interesting for advanced users def userMessage(self, message, status): if status is "wrong": #Red self.userText = "<font color='#a00000'>" + message + "</font>" else: # Grey if status is "ok": self.userText = "<font color='#9fa4b0'>" + message + "</font>" else: self.writeToLog("Error: Invalid status: " + status) return #self.writeToLog("User Message: "+message) self.userInfoTextChanged.emit() # Copy the scripts to the right directory ( Temporary solution) def copyScript(self) -> None: File_List = ['RetractTower.py', 'SpeedTower.py', 'TempFanTower.py'] plugPath = os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources") # Logger.log("d", "plugPath= %s", plugPath) stringMatch = re.split("plugins", plugPath) destPath = stringMatch[0] + "scripts" nbfile = 0 # LCopy the script for fl in File_List: script_definition_path = os.path.join(plugPath, fl) dest_definition_path = os.path.join(destPath, fl) # self.writeToLog("Dest_definition_path= "+dest_definition_path) if os.path.isfile(dest_definition_path) == False: copyfile(script_definition_path, dest_definition_path) nbfile += 1 txt_Message = str(nbfile) + " scripts copied in " txt_Message = txt_Message + os.path.join(destPath, "scripts") if nbfile > 0: txt_Message = txt_Message + "\nYou must now restart Cura to see the scripts in the postprocessing script list" self._message = Message(catalog.i18nc("@info:status", txt_Message), title=catalog.i18nc("@title", "Calibration Shapes")) self._message.show() def gotoHelp(self) -> None: QDesktopServices.openUrl( QUrl("https://github.com/5axes/Calibration-Shapes/wiki")) def addBedLevelCalibration(self) -> None: # Get the build plate Size machine_manager = CuraApplication.getInstance().getMachineManager() stack = CuraApplication.getInstance().getGlobalContainerStack() global_stack = machine_manager.activeMachine m_w = global_stack.getProperty("machine_width", "value") m_d = global_stack.getProperty("machine_depth", "value") if (m_w / m_d) > 1.15 or (m_d / m_w) > 1.15: factor_w = round((m_w / 100), 1) factor_d = round((m_d / 100), 1) else: factor_w = int(m_w / 100) factor_d = int(m_d / 100) Logger.log("d", "factor_w= %.1f", factor_w) Logger.log("d", "factor_d= %.1f", factor_d) model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "ParametricBedLevel.stl") mesh = trimesh.load(model_definition_path) origin = [0, 0, 0] DirX = [1, 0, 0] # DirY = [0, 1, 0] DirZ = [0, 0, 1] mesh.apply_transform( trimesh.transformations.scale_matrix(factor_w, origin, DirX)) mesh.apply_transform( trimesh.transformations.scale_matrix(factor_d, origin, DirZ)) self._addShape(self._toMeshData(mesh)) def addCalibrationCube(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "CalibrationCube.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addMultiCube(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "MultiCube.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addJunctionDeviationTower(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "JunctionDeviationTower.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addPLATempTower(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "TempTowerPLA.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addPETGTempTower(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "TempTowerPETG.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addABSTempTower(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "TempTowerABS.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addRetractTest(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "RetractTest.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addRetractTower(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "RetractTower.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addBridgeTest(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "BridgeTest.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addThinWall(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "ThinWall.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addOverhangTest(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "Overhang.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addFlowTest(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "FlowTest.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addTolerance(self) -> None: model_definition_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "models", "Tolerance.stl") self._addShape(self._toMeshData(trimesh.load(model_definition_path))) def addCube(self) -> None: Tz = trimesh.transformations.translation_matrix( [0, self._size * 0.5, 0]) self._addShape( self._toMeshData( trimesh.creation.box( extents=[self._size, self._size, self._size], transform=Tz))) def addCylinder(self) -> None: Rx = trimesh.transformations.rotation_matrix(math.radians(90), [1, 0, 0]) mesh = trimesh.creation.cylinder(radius=self._size / 2, height=self._size, sections=90, transform=Rx) mesh.apply_transform( trimesh.transformations.translation_matrix( [0, self._size * 0.5, 0])) self._addShape(self._toMeshData(mesh)) def addTube(self) -> None: #origin, xaxis, yaxis, zaxis = [0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1] # S = trimesh.transformations.scale_matrix(20, origin) xaxis = [1, 0, 0] Rx = trimesh.transformations.rotation_matrix(math.radians(90), xaxis) mesh = trimesh.creation.annulus(r_min=self._size / 4, r_max=self._size / 2, height=self._size, sections=90, transform=Rx) mesh.apply_transform( trimesh.transformations.translation_matrix( [0, self._size * 0.5, 0])) self._addShape(self._toMeshData(mesh)) # Sphere are not very usefull but I leave the code for the moment def addSphere(self) -> None: # subdivisions (int) – How many times to subdivide the mesh. Note that the number of faces will grow as function of 4 ** subdivisions, so you probably want to keep this under ~5 mesh = trimesh.creation.icosphere( subdivisions=4, radius=self._size / 2, ) mesh.apply_transform( trimesh.transformations.translation_matrix( [0, self._size * 0.5, 0])) self._addShape(self._toMeshData(mesh)) # Initial Source code from fieldOfView def _toMeshData(self, tri_node: trimesh.base.Trimesh) -> MeshData: tri_faces = tri_node.faces tri_vertices = tri_node.vertices indices = [] vertices = [] index_count = 0 face_count = 0 for tri_face in tri_faces: face = [] for tri_index in tri_face: vertices.append(tri_vertices[tri_index]) face.append(index_count) index_count += 1 indices.append(face) face_count += 1 vertices = numpy.asarray(vertices, dtype=numpy.float32) indices = numpy.asarray(indices, dtype=numpy.int32) normals = calculateNormalsFromIndexedVertices(vertices, indices, face_count) mesh_data = MeshData(vertices=vertices, indices=indices, normals=normals) return mesh_data # Initial Source code from fieldOfView def _addShape(self, mesh_data: MeshData) -> None: application = CuraApplication.getInstance() global_stack = application.getGlobalContainerStack() if not global_stack: return node = CuraSceneNode() node.setMeshData(mesh_data) node.setSelectable(True) node.setName("TestPart" + str(id(mesh_data))) scene = self._controller.getScene() op = AddSceneNodeOperation(node, scene.getRoot()) op.push() default_extruder_position = application.getMachineManager( ).defaultExtruderPosition default_extruder_id = global_stack.extruders[ default_extruder_position].getId() node.callDecoration("setActiveExtruder", default_extruder_id) active_build_plate = application.getMultiBuildPlateModel( ).activeBuildPlate node.addDecorator(BuildPlateDecorator(active_build_plate)) node.addDecorator(SliceableObjectDecorator()) application.getController().getScene().sceneChanged.emit(node)
def event(self, event): 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 # 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: self._simulationview_composite_shader = OpenGL.getInstance( ).createShaderProgram( os.path.join( PluginRegistry.getInstance().getPluginPath( "SimulationView"), "simulationview_composite.shader")) theme = Application.getInstance().getTheme() 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 = 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) self._nozzle_node.setParent(None) self.getRenderer().removeRenderPass(self._layer_pass) self._composite_pass.setLayerBindings(self._old_layer_bindings) self._composite_pass.setCompositeShader(self._old_composite_shader)
def _autoBackup(self) -> None: preferences = CuraApplication.getInstance().getPreferences() if preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY ) and self._isLastBackupTooLongAgo(): self.createBackup()
def _onGetRemoteClustersFinished( self, clusters: List[CloudClusterResponse]) -> None: """Callback for when the request for getting the clusters is successful and finished.""" self._um_cloud_printers = { m.getMetaDataEntry(self.META_CLUSTER_ID): m for m in CuraApplication.getInstance().getContainerRegistry(). findContainerStacks(type="machine") if m.getMetaDataEntry(self.META_CLUSTER_ID, None) } new_clusters = [] all_clusters = {c.cluster_id: c for c in clusters } # type: Dict[str, CloudClusterResponse] online_clusters = {c.cluster_id: c for c in clusters if c.is_online } # type: Dict[str, CloudClusterResponse] # Add the new printers in Cura. for device_id, cluster_data in all_clusters.items(): if device_id not in self._remote_clusters: new_clusters.append(cluster_data) if device_id in self._um_cloud_printers: # Existing cloud printers may not have the host_guid meta-data entry. If that's the case, add it. if not self._um_cloud_printers[device_id].getMetaDataEntry( self.META_HOST_GUID, None): self._um_cloud_printers[device_id].setMetaDataEntry( self.META_HOST_GUID, cluster_data.host_guid) # If a printer was previously not linked to the account and is rediscovered, mark the printer as linked # to the current account if not parseBool( self._um_cloud_printers[device_id].getMetaDataEntry( META_UM_LINKED_TO_ACCOUNT, "true")): self._um_cloud_printers[device_id].setMetaDataEntry( META_UM_LINKED_TO_ACCOUNT, True) self._onDevicesDiscovered(new_clusters) # Hide the current removed_printers_message, if there is any if self._removed_printers_message: self._removed_printers_message.actionTriggered.disconnect( self._onRemovedPrintersMessageActionTriggered) self._removed_printers_message.hide() # Remove the CloudOutput device for offline printers offline_device_keys = set(self._remote_clusters.keys()) - set( online_clusters.keys()) for device_id in offline_device_keys: self._onDiscoveredDeviceRemoved(device_id) # Handle devices that were previously added in Cura but do not exist in the account anymore (i.e. they were # removed from the account) removed_device_keys = set(self._um_cloud_printers.keys()) - set( all_clusters.keys()) if removed_device_keys: self._devicesRemovedFromAccount(removed_device_keys) if new_clusters or offline_device_keys or removed_device_keys: self.discoveredDevicesChanged.emit() if offline_device_keys: # If the removed device was active we should connect to the new active device self._connectToActiveMachine() self._syncing = False self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SUCCESS)
def existsKey(self, key: str) -> bool: return CuraApplication.getInstance().getMachineManager( ).existNetworkInstances(network_key=key)
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 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 # 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() ): #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 for node in DepthFirstIterator( self._scene.getRoot() ): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if node.callDecoration("isSliceable") 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: 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) extruders_enabled = { position: stack.isEnabled for position, stack in CuraApplication.getInstance(). getGlobalContainerStack().extruders.items() } filtered_object_groups = [] has_model_with_disabled_extruders = False associated_disabled_extruders = set() for group in object_groups: stack = CuraApplication.getInstance().getGlobalContainerStack() skip_group = False for node in group: extruder_position = node.callDecoration( "getActiveExtruderPosition") if 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 ExtruderManager.getInstance( ).getMachineExtruders(stack.getId()): self._buildExtruderMessage(extruder_stack) 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) for object in group: mesh_data = object.getMeshData() 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) 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(object, obj) Job.yieldThread() self.setResult(StartJobResult.Finished)
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 bellow 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 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 = ContainerRegistry.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] # 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) # get material container for extruders material_container = application.empty_material_container material_node = material_manager.getDefaultMaterial( new_global_stack, extruder_variant_name) if material_node and material_node.getContainer(): material_container = material_node.getContainer() # 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. 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, global_stack=new_global_stack, ) 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) 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 run(self) -> None: """Runs the job that initiates the slicing.""" 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 # 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 = [] # 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 = [stack.isEnabled for stack in global_stack.extruderList] 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 = int(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 = {p + 1 for p in associated_disabled_extruders} self.setMessage(", ".join(map(str, sorted(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 restart(self): CuraApplication.getInstance().windowClosed()
def __init__(self): super().__init__() self._zero_conf = None self._zero_conf_browser = None self._application = CuraApplication.getInstance() # Create a cloud output device manager that abstracts all cloud connection logic away. self._cloud_output_device_manager = CloudOutputDeviceManager() # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. self.addDeviceSignal.connect(self._onAddDevice) self.removeDeviceSignal.connect(self._onRemoveDevice) self._application.globalContainerStackChanged.connect( self.refreshConnections) self._discovered_devices = {} self._network_manager = QNetworkAccessManager() self._network_manager.finished.connect(self._onNetworkRequestFinished) self._min_cluster_version = Version("4.0.0") self._min_cloud_version = Version("5.2.0") self._api_version = "1" self._api_prefix = "/api/v" + self._api_version + "/" self._cluster_api_version = "1" self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/" # Get list of manual instances from preferences self._preferences = CuraApplication.getInstance().getPreferences() self._preferences.addPreference( "um3networkprinting/manual_instances", "") # A comma-separated list of ip adresses or hostnames self._manual_instances = self._preferences.getValue( "um3networkprinting/manual_instances").split(",") # Store the last manual entry key self._last_manual_entry_key = "" # type: str # The zero-conf service changed requests are handled in a separate thread, so we can re-schedule the requests # which fail to get detailed service info. # Any new or re-scheduled requests will be appended to the request queue, and the handling thread will pick # them up and process them. self._service_changed_request_queue = Queue() self._service_changed_request_event = Event() self._service_changed_request_thread = Thread( target=self._handleOnServiceChangedRequests, daemon=True) self._service_changed_request_thread.start() self._account = self._application.getCuraAPI().account # Check if cloud flow is possible when user logs in self._account.loginStateChanged.connect(self.checkCloudFlowIsPossible) # Check if cloud flow is possible when user switches machines self._application.globalContainerStackChanged.connect( self._onMachineSwitched) # Listen for when cloud flow is possible self.cloudFlowIsPossible.connect(self._onCloudFlowPossible) # Listen if cloud cluster was added self._cloud_output_device_manager.addedCloudCluster.connect( self._onCloudPrintingConfigured) # Listen if cloud cluster was removed self._cloud_output_device_manager.removedCloudCluster.connect( self.checkCloudFlowIsPossible) self._start_cloud_flow_message = None # type: Optional[Message] self._cloud_flow_complete_message = None # type: Optional[Message]
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 if os.path.exists( os.path.join(CuraApplication.getInstallPrefix(), "bin", executable_name)): default_engine_location = os.path.join( CuraApplication.getInstallPrefix(), "bin", executable_name) if hasattr(sys, "frozen"): default_engine_location = os.path.join( os.path.dirname(os.path.abspath(sys.executable)), executable_name) 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)