Example #1
0
    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()
Example #2
0
    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)
Example #3
0
 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")
Example #4
0
    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
Example #5
0
    def _createEraserMesh(self, parent: CuraSceneNode, position: Vector):
        node = CuraSceneNode()

        node.setName("Eraser")
        node.setSelectable(True)
        node.setCalculateBoundingBox(True)
        mesh = self._createCube(10)
        node.setMeshData(mesh.build())
        node.calculateBoundingBoxMesh()

        active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
        node.addDecorator(BuildPlateDecorator(active_build_plate))
        node.addDecorator(SliceableObjectDecorator())

        stack = node.callDecoration("getStack") # created by SettingOverrideDecorator that is automatically added to CuraSceneNode
        settings = stack.getTop()

        definition = stack.getSettingDefinition("anti_overhang_mesh")
        new_instance = SettingInstance(definition, settings)
        new_instance.setProperty("value", True)
        new_instance.resetState()  # Ensure that the state is not seen as a user state.
        settings.addInstance(new_instance)

        op = GroupedOperation()
        # First add node to the scene at the correct position/scale, before parenting, so the eraser mesh does not get scaled with the parent
        op.addOperation(AddSceneNodeOperation(node, self._controller.getScene().getRoot()))
        op.addOperation(SetParentOperation(node, parent))
        op.push()
        node.setPosition(position, CuraSceneNode.TransformSpace.World)

        CuraApplication.getInstance().getController().getScene().sceneChanged.emit(node)
    def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], connection_type: ConnectionType = ConnectionType.NetworkConnection, parent: QObject = None) -> None:
        super().__init__(device_id = device_id, connection_type = connection_type, parent = parent)
        self._manager = None    # type: Optional[QNetworkAccessManager]
        self._last_manager_create_time = None       # type: Optional[float]
        self._recreate_network_manager_time = 30
        self._timeout_time = 10  # After how many seconds of no response should a timeout occur?

        self._last_response_time = None     # type: Optional[float]
        self._last_request_time = None      # type: Optional[float]

        self._api_prefix = ""
        self._address = address
        self._properties = properties
        self._user_agent = "%s/%s " % (CuraApplication.getInstance().getApplicationName(),
                                       CuraApplication.getInstance().getVersion())

        self._onFinishedCallbacks = {}      # type: Dict[str, Callable[[QNetworkReply], None]]
        self._authentication_state = AuthState.NotAuthenticated

        # QHttpMultiPart objects need to be kept alive and not garbage collected during the
        # HTTP which uses them. We hold references to these QHttpMultiPart objects here.
        self._kept_alive_multiparts = {}        # type: Dict[QNetworkReply, QHttpMultiPart]

        self._sending_gcode = False
        self._compressing_gcode = False
        self._gcode = []                    # type: List[str]
        self._connection_state_before_timeout = None    # type: Optional[ConnectionState]
Example #7
0
 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()
Example #8
0
    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)
Example #9
0
 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)
Example #11
0
    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)
Example #12
0
 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)
Example #13
0
 def materialHotendChangedMessage(self, callback):
     CuraApplication.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Sync with your printer"),
                                          i18n_catalog.i18nc("@label",
                                                             "Would you like to use your current printer configuration in Cura?"),
                                          i18n_catalog.i18nc("@label",
                                                             "The PrintCores and/or materials on your printer differ from those within your current project. For the best result, always slice for the PrintCores and materials that are inserted in your printer."),
                                          buttons=QMessageBox.Yes + QMessageBox.No,
                                          icon=QMessageBox.Question,
                                          callback=callback
                                          )
 def _checkCorrectGroupName(self, device_id: str, group_name: str) -> None:
     global_container_stack = CuraApplication.getInstance().getGlobalContainerStack()
     active_machine_network_name = CuraApplication.getInstance().getMachineManager().activeMachineNetworkKey()
     if global_container_stack and device_id == active_machine_network_name:
         # Check if the group_name is correct. If not, update all the containers connected to the same printer
         if CuraApplication.getInstance().getMachineManager().activeMachineNetworkGroupName != group_name:
             metadata_filter = {"um_network_key": active_machine_network_name}
             containers = CuraContainerRegistry.getInstance().findContainerStacks(type="machine",
                                                                                  **metadata_filter)
             for container in containers:
                 container.setMetaDataEntry("group_name", group_name)
    def __init__(self, serial_port: str, baud_rate: Optional[int] = None) -> None:
        super().__init__(serial_port, connection_type = ConnectionType.UsbConnection)
        self.setName(catalog.i18nc("@item:inmenu", "USB printing"))
        self.setShortDescription(catalog.i18nc("@action:button Preceded by 'Ready to'.", "Print via USB"))
        self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB"))
        self.setIconName("print")

        self._serial = None  # type: Optional[Serial]
        self._serial_port = serial_port
        self._address = serial_port

        self._timeout = 3

        # List of gcode lines to be printed
        self._gcode = [] # type: List[str]
        self._gcode_position = 0

        self._use_auto_detect = True

        self._baud_rate = baud_rate

        self._all_baud_rates = [115200, 250000, 500000, 230400, 57600, 38400, 19200, 9600]

        # Instead of using a timer, we really need the update to be as a thread, as reading from serial can block.
        self._update_thread = Thread(target = self._update, daemon = True)

        self._last_temperature_request = None  # type: Optional[int]
        self._firmware_idle_count = 0

        self._is_printing = False  # A print is being sent.

        ## Set when print is started in order to check running time.
        self._print_start_time = None  # type: Optional[float]
        self._print_estimated_time = None  # type: Optional[int]

        self._accepts_commands = True

        self._paused = False
        self._printer_busy = False  # When printer is preheating and waiting (M190/M109), or when waiting for action on the printer

        self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB"))

        # Queue for commands that need to be sent.
        self._command_queue = Queue()   # type: Queue
        # Event to indicate that an "ok" was received from the printer after sending a command.
        self._command_received = Event()
        self._command_received.set()

        self._firmware_name_requested = False
        self._firmware_updater = AvrFirmwareUpdater(self)

        self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "MonitorItem.qml")

        CuraApplication.getInstance().getOnExitCallbackManager().addCallback(self._checkActivePrintingUponAppExit)
Example #16
0
    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()
Example #17
0
    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)
Example #19
0
    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
Example #20
0
    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
Example #21
0
    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"))
Example #22
0
    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"))
Example #23
0
    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)
Example #24
0
    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()
Example #25
0
    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)
Example #26
0
    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
Example #27
0
    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
Example #28
0
    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
Example #29
0
 def connect(self):
     super().connect()
     self._setupMessages()
     global_container = CuraApplication.getInstance().getGlobalContainerStack()
     if global_container:
         self._authentication_id = global_container.getMetaDataEntry("network_authentication_id", None)
         self._authentication_key = global_container.getMetaDataEntry("network_authentication_key", None)
    def createOutputModel(self) -> MaterialOutputModel:
        material_manager = CuraApplication.getInstance().getMaterialManager()
        material_group_list = material_manager.getMaterialGroupListByGUID(self.guid) or []

        # Sort the material groups by "is_read_only = True" first, and then the name alphabetically.
        read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list))
        non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list))
        material_group = None
        if read_only_material_group_list:
            read_only_material_group_list = sorted(read_only_material_group_list, key = lambda x: x.name)
            material_group = read_only_material_group_list[0]
        elif non_read_only_material_group_list:
            non_read_only_material_group_list = sorted(non_read_only_material_group_list, key = lambda x: x.name)
            material_group = non_read_only_material_group_list[0]

        if material_group:
            container = material_group.root_material_node.getContainer()
            color = container.getMetaDataEntry("color_code")
            brand = container.getMetaDataEntry("brand")
            material_type = container.getMetaDataEntry("material")
            name = container.getName()
        else:
            Logger.log("w", "Unable to find material with guid {guid}. Using data as provided by cluster"
                       .format(guid = self.guid))
            color = self.color
            brand = self.brand
            material_type = self.material
            name = "Empty" if self.material == "empty" else "Unknown"

        return MaterialOutputModel(guid = self.guid, type = material_type, brand = brand, color = color, name = name)
    def __init__(self, parent: QObject = None) -> None:
        super().__init__("DiscoverOctoPrintAction", catalog.i18nc("@action", "Connect OctoPrint"))

        self._qml_url = os.path.join("qml", "DiscoverOctoPrintAction.qml")

        self._application = CuraApplication.getInstance()
        self._network_plugin = None # type: Optional[OctoPrintOutputDevicePlugin]

        #   QNetwork manager needs to be created in advance. If we don't it can happen that it doesn't correctly
        #   hook itself into the event loop, which results in events never being fired / done.
        self._network_manager = QNetworkAccessManager()
        self._network_manager.finished.connect(self._onRequestFinished)

        self._settings_reply = None # type: Optional[QNetworkReply]

        self._instance_supports_appkeys = False
        self._appkey_reply = None # type: Optional[QNetworkReply]
        self._appkey_request = None # type: Optional[QNetworkRequest]
        self._appkey_instance_id = ""

        self._appkey_poll_timer = QTimer()
        self._appkey_poll_timer.setInterval(500)
        self._appkey_poll_timer.setSingleShot(True)
        self._appkey_poll_timer.timeout.connect(self._pollApiKey)

        # Try to get version information from plugin.json
        plugin_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "plugin.json")
        try:
            with open(plugin_file_path) as plugin_file:
                plugin_info = json.load(plugin_file)
                self._plugin_version = plugin_info["version"]
        except:
            # The actual version info is not critical to have so we can continue
            self._plugin_version = "0.0"
            Logger.logException("w", "Could not get version information for the plugin")

        self._user_agent = ("%s/%s %s/%s" % (
            self._application.getApplicationName(),
            self._application.getVersion(),
            "OctoPrintPlugin",
            self._plugin_version
        )).encode()

        self._instance_responded = False
        self._instance_in_error = False
        self._instance_api_key_accepted = False
        self._instance_supports_sd = False
        self._instance_supports_camera = False
        self._instance_installed_plugins = [] # type: List[str]

        # Load keys cache from preferences
        self._preferences = self._application.getPreferences()
        self._preferences.addPreference("octoprint/keys_cache", "")

        try:
            self._keys_cache = json.loads(self._deobfuscateString(self._preferences.getValue("octoprint/keys_cache")))
        except ValueError:
            self._keys_cache = {} # type: Dict[str, Any]
        if not isinstance(self._keys_cache, dict):
            self._keys_cache = {} # type: Dict[str, Any]

        self._additional_components = None # type:Optional[QObject]

        ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded)
        self._application.engineCreatedSignal.connect(self._createAdditionalComponentsView)
Example #32
0
 def _getStoredManualAddresses(self) -> List[str]:
     preferences = CuraApplication.getInstance().getPreferences()
     preferences.addPreference(self.MANUAL_DEVICES_PREFERENCE_KEY, "")
     manual_instances = preferences.getValue(
         self.MANUAL_DEVICES_PREFERENCE_KEY).split(",")
     return manual_instances
Example #33
0
    def createMachine(
            cls,
            name: str,
            definition_id: str,
            machine_extruder_count: Optional[int] = None
    ) -> Optional[GlobalStack]:
        """Create a new instance of a machine.

        :param name: The name of the new machine.
        :param definition_id: The ID of the machine definition to use.
        :param machine_extruder_count: The number of extruders in the machine.

        :return: The new global stack or None if an error occurred.
        """

        from cura.CuraApplication import CuraApplication
        application = CuraApplication.getInstance()
        registry = application.getContainerRegistry()
        container_tree = ContainerTree.getInstance()

        definitions = registry.findDefinitionContainers(id=definition_id)
        if not definitions:
            ConfigurationErrorMessage.getInstance().addFaultyContainers(
                definition_id)
            Logger.log("w",
                       "Definition {definition} was not found!",
                       definition=definition_id)
            return None

        machine_definition = definitions[0]
        machine_node = container_tree.machines[machine_definition.getId()]

        generated_name = registry.createUniqueName(
            "machine", "", name, machine_definition.getName())
        # Make sure the new name does not collide with any definition or (quality) profile
        # createUniqueName() only looks at other stacks, but not at definitions or quality profiles
        # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true
        if registry.findContainersMetadata(id=generated_name):
            generated_name = registry.uniqueName(generated_name)

        new_global_stack = cls.createGlobalStack(
            new_stack_id=generated_name,
            definition=machine_definition,
            variant_container=application.empty_variant_container,
            material_container=application.empty_material_container,
            quality_container=machine_node.preferredGlobalQuality().container,
        )
        new_global_stack.setName(generated_name)

        # Create ExtruderStacks
        extruder_dict = machine_definition.getMetaDataEntry(
            "machine_extruder_trains")
        for position in extruder_dict:
            try:
                cls.createExtruderStackWithDefaultSetup(
                    new_global_stack, position)
            except IndexError as e:
                Logger.logException(
                    "e",
                    "Failed to create an extruder stack for position {pos}: {err}"
                    .format(pos=position, err=str(e)))
                return None

        # If given, set the machine_extruder_count when creating the machine, or else the extruderList used below will
        # not return the correct extruder list (since by default, the machine_extruder_count is 1) in machines with
        # settable number of extruders.
        if machine_extruder_count and 0 <= machine_extruder_count <= len(
                extruder_dict):
            new_global_stack.setProperty("machine_extruder_count", "value",
                                         machine_extruder_count)

        # Only register the extruders if we're sure that all of them are correct.
        for new_extruder in new_global_stack.extruderList:
            registry.addContainer(new_extruder)

        # Register the global stack after the extruder stacks are created. This prevents the registry from adding another
        # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).
        registry.addContainer(new_global_stack)

        return new_global_stack
Example #34
0
    def run(self) -> None:
        if self._build_plate_number is None:
            self.setResult(StartJobResult.Error)
            return

        stack = CuraApplication.getInstance().getGlobalContainerStack()
        if not stack:
            self.setResult(StartJobResult.Error)
            return

        # Don't slice if there is a setting with an error value.
        if CuraApplication.getInstance().getMachineManager().stacksHaveErrors:
            self.setResult(StartJobResult.SettingError)
            return

        if CuraApplication.getInstance().getBuildVolume().hasErrors():
            self.setResult(StartJobResult.BuildPlateError)
            return

        # Don't slice if the buildplate or the nozzle type is incompatible with the materials
        if not CuraApplication.getInstance().getMachineManager().variantBuildplateCompatible and \
                not CuraApplication.getInstance().getMachineManager().variantBuildplateUsable:
            self.setResult(StartJobResult.MaterialIncompatible)
            return

        for position, extruder_stack in stack.extruders.items():
            material = extruder_stack.findContainer({"type": "material"})
            if not extruder_stack.isEnabled:
                continue
            if material:
                if material.getMetaDataEntry("compatible") == False:
                    self.setResult(StartJobResult.MaterialIncompatible)
                    return

        # Don't slice if there is a per object setting with an error value.
        for node in DepthFirstIterator(
                self._scene.getRoot()
        ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
            if not isinstance(node, CuraSceneNode) or not node.isSelectable():
                continue

            if self._checkStackForErrors(node.callDecoration("getStack")):
                self.setResult(StartJobResult.ObjectSettingError)
                return

        with self._scene.getSceneLock():
            # Remove old layer data.
            for node in DepthFirstIterator(
                    self._scene.getRoot()
            ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                if node.callDecoration("getLayerData") and node.callDecoration(
                        "getBuildPlateNumber") == self._build_plate_number:
                    node.getParent().removeChild(node)
                    break

            global_enable_support = stack.getProperty("support_enable",
                                                      "value")

            # Get the objects in their groups to print.
            object_groups = []
            if stack.getProperty("print_sequence", "value") == "one_at_a_time":
                # note that one_at_a_time printing is disabled on belt printers due to collission risk
                for node in OneAtATimeIterator(
                        self._scene.getRoot()
                ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                    temp_list = []

                    # Node can't be printed, so don't bother sending it.
                    if getattr(node, "_outside_buildarea", False):
                        continue

                    # Filter on current build plate
                    build_plate_number = node.callDecoration(
                        "getBuildPlateNumber")
                    if build_plate_number is not None and build_plate_number != self._build_plate_number:
                        continue

                    children = node.getAllChildren()
                    children.append(node)
                    for child_node in children:
                        if child_node.getMeshData() and child_node.getMeshData(
                        ).getVertices() is not None:
                            temp_list.append(child_node)

                    if temp_list:
                        object_groups.append(temp_list)
                    Job.yieldThread()
                if len(object_groups) == 0:
                    Logger.log(
                        "w",
                        "No objects suitable for one at a time found, or no correct order found"
                    )
            else:
                temp_list = []
                has_printing_mesh = False
                # print convex hull nodes as "faux-raft"
                print_convex_hulls = stack.getProperty("blackbelt_raft",
                                                       "value")
                for node in DepthFirstIterator(
                        self._scene.getRoot()
                ):  #type: ignore #Ignore type error because iter() should get called automatically by Python syntax.
                    slice_node = (print_convex_hulls
                                  and type(node) is ConvexHullNode
                                  ) or node.callDecoration("isSliceable")
                    if slice_node and node.getMeshData() and node.getMeshData(
                    ).getVertices() is not None:
                        per_object_stack = node.callDecoration("getStack")
                        is_non_printing_mesh = False
                        if per_object_stack:
                            is_non_printing_mesh = any(
                                per_object_stack.getProperty(key, "value")
                                for key in NON_PRINTING_MESH_SETTINGS)

                        # Find a reason not to add the node
                        if node.callDecoration(
                                "getBuildPlateNumber"
                        ) != self._build_plate_number and type(
                                node) is not ConvexHullNode:
                            # NB: ConvexHullNodes get none of the usual decorators, so skip checking for them
                            continue
                        if getattr(node, "_outside_buildarea",
                                   False) and not is_non_printing_mesh:
                            continue

                        temp_list.append(node)
                        if not is_non_printing_mesh:
                            has_printing_mesh = True

                    Job.yieldThread()

                #If the list doesn't have any model with suitable settings then clean the list
                # otherwise CuraEngine will crash
                if not has_printing_mesh:
                    temp_list.clear()

                if temp_list:
                    object_groups.append(temp_list)

            global_stack = CuraApplication.getInstance(
            ).getGlobalContainerStack()
            if not global_stack:
                return
            extruders_enabled = {
                position: stack.isEnabled
                for position, stack in global_stack.extruders.items()
            }
            filtered_object_groups = []
            has_model_with_disabled_extruders = False
            associated_disabled_extruders = set()
            for group in object_groups:
                stack = global_stack
                skip_group = False
                for node in group:
                    # Only check if the printing extruder is enabled for printing meshes
                    is_non_printing_mesh = node.callDecoration(
                        "evaluateIsNonPrintingMesh")
                    extruder_position = node.callDecoration(
                        "getActiveExtruderPosition")
                    if extruder_position is None:  # raft meshes may not have an extruder position (yet)
                        extruder_position = "0"
                    if not is_non_printing_mesh and not extruders_enabled[
                            extruder_position]:
                        skip_group = True
                        has_model_with_disabled_extruders = True
                        associated_disabled_extruders.add(extruder_position)
                if not skip_group:
                    filtered_object_groups.append(group)

            if has_model_with_disabled_extruders:
                self.setResult(StartJobResult.ObjectsWithDisabledExtruder)
                associated_disabled_extruders = {
                    str(c)
                    for c in sorted(
                        [int(p) + 1 for p in associated_disabled_extruders])
                }
                self.setMessage(", ".join(associated_disabled_extruders))
                return

            # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
            # able to find a possible sequence or because there are no objects on the build plate (or they are outside
            # the build volume)
            if not filtered_object_groups:
                self.setResult(StartJobResult.NothingToSlice)
                return

            container_registry = ContainerRegistry.getInstance()
            stack_id = stack.getId()

            # Adapt layer_height and material_flow for a slanted gantry
            gantry_angle = self._scene.getRoot().callDecoration(
                "getGantryAngle")
            if gantry_angle:  # not 0 or None
                # Act on a copy of the stack, so these changes don't cause a reslice
                _stack = CuraContainerStack(stack_id + "_temp")
                for index, container in enumerate(stack.getContainers()):
                    if container_registry.isReadOnly(container.getId()):
                        _stack.replaceContainer(index, container)
                    else:
                        _stack.replaceContainer(index,
                                                copy.deepcopy(container))
                stack = _stack

                # Make sure CuraEngine does not create any supports
                # support_enable is set in the frontend so support options are settable,
                # but CuraEngine support structures don't work for slanted gantry
                stack.setProperty("support_enable", "value", False)
                # Make sure CuraEngine does not create a raft (we create one manually)
                # Adhesion type is used in the frontend to show the raft in the viewport
                stack.setProperty("adhesion_type", "value", "none")

                for key in ["layer_height", "layer_height_0"]:
                    current_value = stack.getProperty(key, "value")
                    stack.setProperty(key, "value",
                                      current_value / math.sin(gantry_angle))

            self._buildGlobalSettingsMessage(stack)
            self._buildGlobalInheritsStackMessage(stack)

            # Build messages for extruder stacks
            # Send the extruder settings in the order of extruder positions. Somehow, if you send e.g. extruder 3 first,
            # then CuraEngine can slice with the wrong settings. This I think should be fixed in CuraEngine as well.
            extruder_stack_list = sorted(list(global_stack.extruders.items()),
                                         key=lambda item: int(item[0]))
            for _, extruder_stack in extruder_stack_list:
                if gantry_angle:  # not 0 or None
                    # Act on a copy of the stack, so these changes don't cause a reslice
                    _extruder_stack = CuraContainerStack(
                        extruder_stack.getId() + "_temp")
                    for index, container in enumerate(
                            extruder_stack.getContainers()):
                        if container_registry.isReadOnly(container.getId()):
                            _extruder_stack.replaceContainer(index, container)
                        else:
                            _extruder_stack.replaceContainer(
                                index, copy.deepcopy(container))
                    extruder_stack = _extruder_stack
                    extruder_stack.setNextStack(stack)
                    for key in [
                            "material_flow", "prime_tower_flow",
                            "spaghetti_flow"
                    ]:
                        if extruder_stack.hasProperty(key, "value"):
                            current_value = extruder_stack.getProperty(
                                key, "value")
                            extruder_stack.setProperty(
                                key, "value",
                                current_value * math.sin(gantry_angle))
                self._buildExtruderMessage(extruder_stack)

            bottom_cutting_meshes = []
            raft_meshes = []
            support_meshes = []
            if gantry_angle:  # not 0 or None
                for group in filtered_object_groups:
                    added_meshes = []
                    for object in group:

                        is_non_printing_mesh = False
                        per_object_stack = object.callDecoration("getStack")

                        # ConvexHullNodes get none of the usual decorators. If it made it here, it is meant to be printed
                        if type(object) is ConvexHullNode:
                            raft_thickness = stack.getProperty(
                                "blackbelt_raft_thickness", "value")
                            raft_margin = stack.getProperty(
                                "blackbelt_raft_margin", "value")

                            mb = MeshBuilder()
                            hull_polygon = object.getHull()
                            if raft_margin > 0:
                                hull_polygon = hull_polygon.getMinkowskiHull(
                                    Polygon.approximatedCircle(raft_margin))
                            mb.addConvexPolygonExtrusion(
                                hull_polygon.getPoints()[::-1], 0,
                                raft_thickness)

                            new_node = self._addMeshFromBuilder(mb, "raftMesh")
                            added_meshes.append(new_node)
                            raft_meshes.append(new_node.getName())

                        elif not is_non_printing_mesh:
                            # add support mesh if needed
                            blackbelt_support_gantry_angle_bias = None
                            blackbelt_support_minimum_island_area = None
                            if per_object_stack:
                                is_non_printing_mesh = any(
                                    per_object_stack.getProperty(key, "value")
                                    for key in NON_PRINTING_MESH_SETTINGS)

                                node_enable_support = per_object_stack.getProperty(
                                    "support_enable", "value")
                                if per_object_stack.getProperty(
                                        "support_mesh", "value"):
                                    node_enable_support = node_enable_support or per_object_stack.getProperty(
                                        "support_mesh_drop_down", "value")
                                add_support_mesh = node_enable_support if node_enable_support is not None else global_enable_support

                                blackbelt_support_gantry_angle_bias = per_object_stack.getProperty(
                                    "blackbelt_support_gantry_angle_bias",
                                    "value")
                                blackbelt_support_minimum_island_area = per_object_stack.getProperty(
                                    "blackbelt_support_minimum_island_area",
                                    "value")
                            else:
                                add_support_mesh = global_enable_support

                            if add_support_mesh:
                                if blackbelt_support_gantry_angle_bias is None:
                                    blackbelt_support_gantry_angle_bias = global_stack.getProperty(
                                        "blackbelt_support_gantry_angle_bias",
                                        "value")
                                biased_down_angle = math.radians(
                                    blackbelt_support_gantry_angle_bias)
                                if blackbelt_support_minimum_island_area is None:
                                    blackbelt_support_minimum_island_area = global_stack.getProperty(
                                        "blackbelt_support_minimum_island_area",
                                        "value")
                                support_mesh_data = SupportMeshCreator(
                                    down_vector=numpy.array([
                                        0, -math.cos(
                                            math.radians(biased_down_angle)),
                                        -math.sin(biased_down_angle)
                                    ]),
                                    bottom_cut_off=stack.getProperty(
                                        "wall_line_width_0", "value") / 2,
                                    minimum_island_area=
                                    blackbelt_support_minimum_island_area
                                ).createSupportMeshForNode(object)
                                if support_mesh_data:
                                    new_node = self._addMeshFromData(
                                        support_mesh_data,
                                        "generatedSupportMesh")
                                    added_meshes.append(new_node)
                                    support_meshes.append(new_node.getName())

                            # check if the bottom needs to be cut off
                            aabb = object.getBoundingBox()

                            if aabb.bottom < 0:
                                # mesh extends below the belt; add a cutting mesh to cut off the part below the bottom
                                height = -aabb.bottom
                                center = Vector(aabb.center.x, -height / 2,
                                                aabb.center.z)

                                mb = MeshBuilder()
                                mb.addCube(width=aabb.width,
                                           height=height,
                                           depth=aabb.depth,
                                           center=center)

                                new_node = self._addMeshFromBuilder(
                                    mb, "bottomCuttingMesh")
                                added_meshes.append(new_node)
                                bottom_cutting_meshes.append(
                                    new_node.getName())

                    if added_meshes:
                        group += added_meshes

            transform_matrix = self._scene.getRoot().callDecoration(
                "getTransformMatrix")
            front_offset = None

            raft_offset = 0
            raft_speed = None
            raft_flow = 1.0

            if stack.getProperty("blackbelt_raft", "value"):
                raft_offset = stack.getProperty(
                    "blackbelt_raft_thickness", "value") + stack.getProperty(
                        "blackbelt_raft_gap", "value")
                raft_speed = stack.getProperty("blackbelt_raft_speed", "value")
                raft_flow = stack.getProperty("blackbelt_raft_flow",
                                              "value") * math.sin(gantry_angle)

            adhesion_extruder_nr = stack.getProperty("adhesion_extruder_nr",
                                                     "value")
            support_extruder_nr = stack.getProperty("support_extruder_nr",
                                                    "value")

            for group in filtered_object_groups:
                group_message = self._slice_message.addRepeatedMessage(
                    "object_lists")
                if group[0].getParent() is not None and group[0].getParent(
                ).callDecoration("isGroup"):
                    self._handlePerObjectSettings(group[0].getParent(),
                                                  group_message)

                if transform_matrix:
                    scene_front = None
                    for object in group:
                        if type(object) is ConvexHullNode:
                            continue

                        is_non_printing_mesh = object.getName(
                        ) in bottom_cutting_meshes or object.getName(
                        ) in raft_meshes
                        if not is_non_printing_mesh:
                            per_object_stack = object.callDecoration(
                                "getStack")
                            if per_object_stack:
                                is_non_printing_mesh = any(
                                    per_object_stack.getProperty(key, "value")
                                    for key in NON_PRINTING_MESH_SETTINGS)

                        if not is_non_printing_mesh:
                            _front = object.getBoundingBox().back
                            if scene_front is None or _front < scene_front:
                                scene_front = _front

                    if scene_front is not None:
                        front_offset = transformVertices(
                            numpy.array([[0, 0, scene_front]]),
                            transform_matrix)[0][1]

                for object in group:
                    if type(object) is ConvexHullNode:
                        continue

                    mesh_data = object.getMeshData()
                    rot_scale = object.getWorldTransformation().getTransposed(
                    ).getData()[0:3, 0:3]
                    translate = object.getWorldTransformation().getData()[:3,
                                                                          3]
                    # offset all non-raft objects if rafts are enabled
                    # air gap is applied here to vertically offset objects from the raft
                    if object.getName() not in raft_meshes:
                        translate[1] += raft_offset
                    if front_offset:
                        translate[2] -= front_offset

                    # This effectively performs a limited form of MeshData.getTransformed that ignores normals.
                    verts = mesh_data.getVertices()
                    verts = verts.dot(rot_scale)
                    verts += translate

                    if transform_matrix:
                        verts = transformVertices(verts, transform_matrix)

                    # Convert from Y up axes to Z up axes. Equals a 90 degree rotation.
                    verts[:, [1, 2]] = verts[:, [2, 1]]
                    verts[:, 1] *= -1

                    obj = group_message.addRepeatedMessage("objects")
                    obj.id = id(object)

                    obj.name = object.getName()
                    indices = mesh_data.getIndices()
                    if indices is not None:
                        flat_verts = numpy.take(verts,
                                                indices.flatten(),
                                                axis=0)
                    else:
                        flat_verts = numpy.array(verts)

                    obj.vertices = flat_verts

                    if object.getName() in raft_meshes:
                        self._addSettingsMessage(
                            obj, {
                                "wall_line_count": 99999999,
                                "speed_wall_0": raft_speed,
                                "speed_wall_x": raft_speed,
                                "material_flow": raft_flow,
                                "extruder_nr": adhesion_extruder_nr
                            })

                    elif object.getName() in support_meshes:
                        self._addSettingsMessage(
                            obj, {
                                "support_mesh": "True",
                                "support_mesh_drop_down": "False",
                                "extruder_nr": support_extruder_nr
                            })

                    elif object.getName() in bottom_cutting_meshes:
                        self._addSettingsMessage(
                            obj, {
                                "cutting_mesh": True,
                                "wall_line_count": 0,
                                "top_layers": 0,
                                "bottom_layers": 0,
                                "infill_line_distance": 0,
                                "extruder_nr": 0
                            })

                    else:
                        self._handlePerObjectSettings(object, obj)
                    Job.yieldThread()

                # Store the front-most coordinate of the scene so the scene can be moved back into place post slicing
                # TODO: this should be handled per mesh-group instead of per scene
                # One-at-a-time printing should be disabled for slanted gantry printers for now

                self._scene.getRoot().callDecoration("setSceneFrontOffset",
                                                     front_offset)

        self.setResult(StartJobResult.Finished)
Example #35
0
 def _onDontAskMeAgain(self, checked: bool) -> None:
     CuraApplication.getInstance().getPreferences().setValue(
         self._no_layers_warning_preference, not checked)
Example #36
0
 def changestage(self):
     CuraApplication.getInstance().getController().setActiveStage(
         "MonitorStage")
Example #37
0
 def _onConfirmExitDialogResult(self, result: bool) -> None:
     if result:
         application = CuraApplication.getInstance()
         application.triggerNextExitCheck()
Example #38
0
 def __init__(self) -> None:
     self._cura_api = CuraApplication.getInstance().getCuraAPI()
Example #39
0
    def write(self, stream, nodes, mode=MeshWriter.OutputMode.BinaryMode):
        archive = VirtualFile()
        archive.openStream(stream, "application/x-ufp", OpenMode.WriteOnly)

        #Store the g-code from the scene.
        archive.addContentType(extension="gcode", mime_type="text/x-gcode")
        gcode_textio = StringIO()  #We have to convert the g-code into bytes.
        gcode_writer = cast(
            MeshWriter,
            PluginRegistry.getInstance().getPluginObject("GCodeWriter"))
        success = gcode_writer.write(gcode_textio, None)
        if not success:  #Writing the g-code failed. Then I can also not write the gzipped g-code.
            self.setInformation(gcode_writer.getInformation())
            return False
        gcode = archive.getStream("/3D/model.gcode")
        gcode.write(gcode_textio.getvalue().encode("UTF-8"))
        archive.addRelation(
            virtual_path="/3D/model.gcode",
            relation_type=
            "http://schemas.ultimaker.org/package/2018/relationships/gcode")

        self._createSnapshot()

        #Store the thumbnail.
        if self._snapshot:
            archive.addContentType(extension="png", mime_type="image/png")
            thumbnail = archive.getStream("/Metadata/thumbnail.png")

            thumbnail_buffer = QBuffer()
            thumbnail_buffer.open(QBuffer.ReadWrite)
            thumbnail_image = self._snapshot
            thumbnail_image.save(thumbnail_buffer, "PNG")

            thumbnail.write(thumbnail_buffer.data())
            archive.addRelation(
                virtual_path="/Metadata/thumbnail.png",
                relation_type=
                "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail",
                origin="/3D/model.gcode")
        else:
            Logger.log("d", "Thumbnail not created, cannot save it")

        # Store the material.
        application = CuraApplication.getInstance()
        machine_manager = application.getMachineManager()
        container_registry = application.getContainerRegistry()
        global_stack = machine_manager.activeMachine

        material_extension = "xml.fdm_material"
        material_mime_type = "application/x-ultimaker-material-profile"

        try:
            archive.addContentType(extension=material_extension,
                                   mime_type=material_mime_type)
        except:
            Logger.log("w", "The material extension: %s was already added",
                       material_extension)

        added_materials = []
        for extruder_stack in global_stack.extruders.values():
            material = extruder_stack.material
            try:
                material_file_name = material.getMetaData(
                )["base_file"] + ".xml.fdm_material"
            except KeyError:
                Logger.log("w", "Unable to get base_file for the material %s",
                           material.getId())
                continue
            material_file_name = "/Materials/" + material_file_name

            # The same material should not be added again.
            if material_file_name in added_materials:
                continue

            material_root_id = material.getMetaDataEntry("base_file")
            material_root_query = container_registry.findContainers(
                id=material_root_id)
            if not material_root_query:
                Logger.log(
                    "e",
                    "Cannot find material container with root id {root_id}".
                    format(root_id=material_root_id))
                return False
            material_container = material_root_query[0]

            try:
                serialized_material = material_container.serialize()
            except NotImplementedError:
                Logger.log(
                    "e",
                    "Unable serialize material container with root id: %s",
                    material_root_id)
                return False

            material_file = archive.getStream(material_file_name)
            material_file.write(serialized_material.encode("UTF-8"))
            archive.addRelation(
                virtual_path=material_file_name,
                relation_type=
                "http://schemas.ultimaker.org/package/2018/relationships/material",
                origin="/3D/model.gcode")

            added_materials.append(material_file_name)

        try:
            archive.close()
        except OSError as e:
            error_msg = catalog.i18nc(
                "@info:error", "Can't write to UFP file:") + " " + str(e)
            self.setInformation(error_msg)
            Logger.error(error_msg)
            return False
        return True
Example #40
0
    def __init__(self,
                 serial_port: str,
                 baud_rate: Optional[int] = None) -> None:
        super().__init__(serial_port,
                         connection_type=ConnectionType.UsbConnection)
        self.setName(catalog.i18nc("@item:inmenu", "USB printing"))
        self.setShortDescription(
            catalog.i18nc("@action:button Preceded by 'Ready to'.",
                          "Print via USB"))
        self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB"))
        self.setIconName("print")

        self._serial = None  # type: Optional[Serial]
        self._serial_port = serial_port
        self._address = serial_port

        self._timeout = 3

        # List of gcode lines to be printed
        self._gcode = []  # type: List[str]
        self._gcode_position = 0

        self._use_auto_detect = True

        self._baud_rate = baud_rate

        self._all_baud_rates = [
            115200, 250000, 500000, 230400, 76800, 57600, 38400, 19200, 9600
        ]

        # Instead of using a timer, we really need the update to be as a thread, as reading from serial can block.
        self._update_thread = Thread(target=self._update,
                                     daemon=True,
                                     name="USBPrinterUpdate")

        self._last_temperature_request = None  # type: Optional[int]
        self._firmware_idle_count = 0

        self._is_printing = False  # A print is being sent.

        ## Set when print is started in order to check running time.
        self._print_start_time = None  # type: Optional[float]
        self._print_estimated_time = None  # type: Optional[int]

        self._accepts_commands = True

        self._paused = False
        self._printer_busy = False  # When printer is preheating and waiting (M190/M109), or when waiting for action on the printer

        self.setConnectionText(
            catalog.i18nc("@info:status", "Connected via USB"))

        # Queue for commands that need to be sent.
        self._command_queue = Queue()  # type: Queue
        # Event to indicate that an "ok" was received from the printer after sending a command.
        self._command_received = Event()
        self._command_received.set()

        self._firmware_name_requested = False
        self._firmware_updater = AvrFirmwareUpdater(self)

        plugin_path = PluginRegistry.getInstance().getPluginPath("USBPrinting")
        if plugin_path:
            self._monitor_view_qml_path = os.path.join(plugin_path,
                                                       "MonitorItem.qml")
        else:
            Logger.log(
                "e",
                "Cannot create Monitor QML view: cannot find plugin path for plugin [USBPrinting]"
            )
            self._monitor_view_qml_path = ""

        CuraApplication.getInstance().getOnExitCallbackManager().addCallback(
            self._checkActivePrintingUponAppExit)
Example #41
0
    def event(self, event) -> bool:
        modifiers = QApplication.keyboardModifiers()
        ctrl_is_active = modifiers & Qt.ControlModifier
        shift_is_active = modifiers & Qt.ShiftModifier
        if event.type == Event.KeyPressEvent and ctrl_is_active:
            amount = 10 if shift_is_active else 1
            if event.key == KeyEvent.UpKey:
                self.setLayer(self._current_layer_num + amount)
                return True
            if event.key == KeyEvent.DownKey:
                self.setLayer(self._current_layer_num - amount)
                return True

        if event.type == Event.ViewActivateEvent:
            # FIX: on Max OS X, somehow QOpenGLContext.currentContext() can become None during View switching.
            # This can happen when you do the following steps:
            #   1. Start Cura
            #   2. Load a model
            #   3. Switch to Custom mode
            #   4. Select the model and click on the per-object tool icon
            #   5. Switch view to Layer view or X-Ray
            #   6. Cura will very likely crash
            # It seems to be a timing issue that the currentContext can somehow be empty, but I have no clue why.
            # This fix tries to reschedule the view changing event call on the Qt thread again if the current OpenGL
            # context is None.
            if Platform.isOSX():
                if QOpenGLContext.currentContext() is None:
                    Logger.log(
                        "d",
                        "current context of OpenGL is empty on Mac OS X, will try to create shaders later"
                    )
                    CuraApplication.getInstance().callLater(
                        lambda e=event: self.event(e))
                    return False

            # Make sure the SimulationPass is created
            layer_pass = self.getSimulationPass()
            self.getRenderer().addRenderPass(layer_pass)

            # Make sure the NozzleNode is add to the root
            nozzle = self.getNozzleNode()
            nozzle.setParent(self.getController().getScene().getRoot())
            nozzle.setVisible(False)

            Application.getInstance().globalContainerStackChanged.connect(
                self._onGlobalStackChanged)
            self._onGlobalStackChanged()

            if not self._simulationview_composite_shader:
                plugin_path = cast(
                    str,
                    PluginRegistry.getInstance().getPluginPath(
                        "SimulationView"))
                self._simulationview_composite_shader = OpenGL.getInstance(
                ).createShaderProgram(
                    os.path.join(plugin_path,
                                 "simulationview_composite.shader"))
                theme = CuraApplication.getInstance().getTheme()
                if theme is not None:
                    self._simulationview_composite_shader.setUniformValue(
                        "u_background_color",
                        Color(*theme.getColor("viewport_background").getRgb()))
                    self._simulationview_composite_shader.setUniformValue(
                        "u_outline_color",
                        Color(*theme.getColor(
                            "model_selection_outline").getRgb()))

            if not self._composite_pass:
                self._composite_pass = cast(
                    CompositePass,
                    self.getRenderer().getRenderPass("composite"))

            self._old_layer_bindings = self._composite_pass.getLayerBindings(
            )[:]  # make a copy so we can restore to it later
            self._composite_pass.getLayerBindings().append("simulationview")
            self._old_composite_shader = self._composite_pass.getCompositeShader(
            )
            self._composite_pass.setCompositeShader(
                self._simulationview_composite_shader)

        elif event.type == Event.ViewDeactivateEvent:
            self._wireprint_warning_message.hide()
            Application.getInstance().globalContainerStackChanged.disconnect(
                self._onGlobalStackChanged)
            if self._global_container_stack:
                self._global_container_stack.propertyChanged.disconnect(
                    self._onPropertyChanged)
            if self._nozzle_node:
                self._nozzle_node.setParent(None)
            self.getRenderer().removeRenderPass(self._layer_pass)
            if self._composite_pass:
                self._composite_pass.setLayerBindings(
                    cast(List[str], self._old_layer_bindings))
                self._composite_pass.setCompositeShader(
                    cast(ShaderProgram, self._old_composite_shader))

        return False
Example #42
0
 def _hasHumanReadableMachineTypeName(self, machine_type_name: str) -> bool:
     from cura.CuraApplication import CuraApplication
     results = CuraApplication.getInstance().getContainerRegistry(
     ).findDefinitionContainersMetadata(name=machine_type_name)
     return len(results) > 0
Example #43
0
    def _read(self, file_name: str) -> Union[SceneNode, List[SceneNode]]:
        result = []
        self._object_count = 0  # Used to name objects as there is no node name yet.
        # The base object of 3mf is a zipped archive.
        try:
            archive = zipfile.ZipFile(file_name, "r")
            self._base_name = os.path.basename(file_name)
            parser = Savitar.ThreeMFParser()
            scene_3mf = parser.parse(archive.open("3D/3dmodel.model").read())
            self._unit = scene_3mf.getUnit()
            for node in scene_3mf.getSceneNodes():
                um_node = self._convertSavitarNodeToUMNode(node, file_name)
                if um_node is None:
                    continue
                # compensate for original center position, if object(s) is/are not around its zero position

                transform_matrix = Matrix()
                mesh_data = um_node.getMeshData()
                if mesh_data is not None:
                    extents = mesh_data.getExtents()
                    if extents is not None:
                        center_vector = Vector(extents.center.x,
                                               extents.center.y,
                                               extents.center.z)
                        transform_matrix.setByTranslation(center_vector)
                transform_matrix.multiply(um_node.getLocalTransformation())
                um_node.setTransformation(transform_matrix)

                global_container_stack = CuraApplication.getInstance(
                ).getGlobalContainerStack()

                # Create a transformation Matrix to convert from 3mf worldspace into ours.
                # First step: flip the y and z axis.
                transformation_matrix = Matrix()
                transformation_matrix._data[1, 1] = 0
                transformation_matrix._data[1, 2] = 1
                transformation_matrix._data[2, 1] = -1
                transformation_matrix._data[2, 2] = 0

                # Second step: 3MF defines the left corner of the machine as center, whereas cura uses the center of the
                # build volume.
                if global_container_stack:
                    translation_vector = Vector(
                        x=-global_container_stack.getProperty(
                            "machine_width", "value") / 2,
                        y=-global_container_stack.getProperty(
                            "machine_depth", "value") / 2,
                        z=0)
                    translation_matrix = Matrix()
                    translation_matrix.setByTranslation(translation_vector)
                    transformation_matrix.multiply(translation_matrix)

                # Third step: 3MF also defines a unit, whereas Cura always assumes mm.
                scale_matrix = Matrix()
                scale_matrix.setByScaleVector(
                    self._getScaleFromUnit(self._unit))
                transformation_matrix.multiply(scale_matrix)

                # Pre multiply the transformation with the loaded transformation, so the data is handled correctly.
                um_node.setTransformation(
                    um_node.getLocalTransformation().preMultiply(
                        transformation_matrix))

                # Check if the model is positioned below the build plate and honor that when loading project files.
                node_meshdata = um_node.getMeshData()
                if node_meshdata is not None:
                    aabb = node_meshdata.getExtents(
                        um_node.getWorldTransformation())
                    if aabb is not None:
                        minimum_z_value = aabb.minimum.y  # y is z in transformation coordinates
                        if minimum_z_value < 0:
                            um_node.addDecorator(ZOffsetDecorator())
                            um_node.callDecoration("setZOffset",
                                                   minimum_z_value)

                result.append(um_node)

        except Exception:
            Logger.logException("e", "An exception occurred in 3mf reader.")
            return []

        return result
Example #44
0
    def _onDevicesDiscovered(self,
                             clusters: List[CloudClusterResponse]) -> None:
        """**Synchronously** create machines for discovered devices

        Any new machines are made available to the user.
        May take a long time to complete. As this code needs access to the Application
        and blocks the GIL, creating a Job for this would not make sense.
        Shows a Message informing the user of progress.
        """
        new_devices = []
        for cluster_data in clusters:
            device = CloudOutputDevice(self._api, cluster_data)
            # Create a machine if we don't already have it. Do not make it the active machine.
            machine_manager = CuraApplication.getInstance().getMachineManager()

            # We only need to add it if it wasn't already added by "local" network or by cloud.
            if machine_manager.getMachine(device.printerType, {self.META_CLUSTER_ID: device.key}) is None \
                    and machine_manager.getMachine(device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None:  # The host name is part of the network key.
                new_devices.append(device)

        if not new_devices:
            return

        new_devices.sort(key=lambda x: x.name.lower())

        image_path = os.path.join(
            CuraApplication.getInstance().getPluginRegistry().getPluginPath(
                "UM3NetworkPrinting") or "", "resources", "svg",
            "cloud-flow-completed.svg")

        message = Message(title=self.I18N_CATALOG.i18ncp(
            "info:status", "New printer detected from your Ultimaker account",
            "New printers detected from your Ultimaker account",
            len(new_devices)),
                          progress=0,
                          lifetime=0,
                          image_source=image_path)
        message.show()

        for idx, device in enumerate(new_devices):
            message_text = self.I18N_CATALOG.i18nc(
                "info:status", "Adding printer {} ({}) from your account",
                device.name, device.printerTypeName)
            message.setText(message_text)
            if len(new_devices) > 1:
                message.setProgress((idx / len(new_devices)) * 100)
            CuraApplication.getInstance().processEvents()
            self._remote_clusters[device.getId()] = device
            self._createMachineFromDiscoveredDevice(device.getId(),
                                                    activate=False)

        message.setProgress(None)

        max_disp_devices = 3
        if len(new_devices) > max_disp_devices:
            num_hidden = len(new_devices) - max_disp_devices + 1
            device_name_list = [
                "- {} ({})".format(device.name, device.printerTypeName)
                for device in new_devices[0:num_hidden]
            ]
            device_name_list.append(
                self.I18N_CATALOG.i18nc("info:hidden list items",
                                        "- and {} others", num_hidden))
            device_names = "\n".join(device_name_list)
        else:
            device_names = "\n".join([
                "- {} ({})".format(device.name, device.printerTypeName)
                for device in new_devices
            ])

        message_text = self.I18N_CATALOG.i18nc(
            "info:status", "Cloud printers added from your account:\n{}",
            device_names)
        message.setText(message_text)
Example #45
0
 def disconnect(self) -> None:
     super().disconnect()
     Logger.log("i", "Disconnected from cluster %s", self.key)
     CuraApplication.getInstance().getBackend(
     ).backendStateChange.disconnect(self._onBackendStateChange)
Example #46
0
    def run(self) -> None:
        if self._build_plate_number is None:
            self.setResult(StartJobResult.Error)
            return

        stack = CuraApplication.getInstance().getGlobalContainerStack()
        if not stack:
            self.setResult(StartJobResult.Error)
            return

        # Don't slice if there is a setting with an error value.
        if CuraApplication.getInstance().getMachineManager().stacksHaveErrors:
            self.setResult(StartJobResult.SettingError)
            return

        if CuraApplication.getInstance().getBuildVolume().hasErrors():
            self.setResult(StartJobResult.BuildPlateError)
            return

        # Wait for error checker to be done.
        while CuraApplication.getInstance().getMachineErrorChecker(
        ).needToWaitForResult:
            time.sleep(0.1)

        if CuraApplication.getInstance().getMachineErrorChecker().hasError:
            self.setResult(StartJobResult.SettingError)
            return

        # Don't slice if the buildplate or the nozzle type is incompatible with the materials
        if not CuraApplication.getInstance().getMachineManager().variantBuildplateCompatible and \
                not CuraApplication.getInstance().getMachineManager().variantBuildplateUsable:
            self.setResult(StartJobResult.MaterialIncompatible)
            return

        for extruder_stack in stack.extruderList:
            material = extruder_stack.findContainer({"type": "material"})
            if not extruder_stack.isEnabled:
                continue
            if material:
                if material.getMetaDataEntry("compatible") == False:
                    self.setResult(StartJobResult.MaterialIncompatible)
                    return

        # Don't slice if there is a per object setting with an error value.
        for node in DepthFirstIterator(self._scene.getRoot()):
            if not isinstance(node, CuraSceneNode) or not node.isSelectable():
                continue

            if self._checkStackForErrors(node.callDecoration("getStack")):
                self.setResult(StartJobResult.ObjectSettingError)
                return

        with self._scene.getSceneLock():
            # Remove old layer data.
            for node in DepthFirstIterator(self._scene.getRoot()):
                if node.callDecoration("getLayerData") and node.callDecoration(
                        "getBuildPlateNumber") == self._build_plate_number:
                    # Singe we walk through all nodes in the scene, they always have a parent.
                    cast(SceneNode, node.getParent()).removeChild(node)
                    break

            # Get the objects in their groups to print.
            object_groups = []
            if stack.getProperty("print_sequence", "value") == "one_at_a_time":
                for node in OneAtATimeIterator(self._scene.getRoot()):
                    temp_list = []

                    # Node can't be printed, so don't bother sending it.
                    if getattr(node, "_outside_buildarea", False):
                        continue

                    # Filter on current build plate
                    build_plate_number = node.callDecoration(
                        "getBuildPlateNumber")
                    if build_plate_number is not None and build_plate_number != self._build_plate_number:
                        continue

                    children = node.getAllChildren()
                    children.append(node)
                    for child_node in children:
                        mesh_data = child_node.getMeshData()
                        if mesh_data and mesh_data.getVertices() is not None:
                            temp_list.append(child_node)

                    if temp_list:
                        object_groups.append(temp_list)
                    Job.yieldThread()
                if len(object_groups) == 0:
                    Logger.log(
                        "w",
                        "No objects suitable for one at a time found, or no correct order found"
                    )
            else:
                temp_list = []
                has_printing_mesh = False
                for node in DepthFirstIterator(self._scene.getRoot()):
                    mesh_data = node.getMeshData()
                    if node.callDecoration(
                            "isSliceable"
                    ) and mesh_data and mesh_data.getVertices() is not None:
                        is_non_printing_mesh = bool(
                            node.callDecoration("isNonPrintingMesh"))

                        # Find a reason not to add the node
                        if node.callDecoration("getBuildPlateNumber"
                                               ) != self._build_plate_number:
                            continue
                        if getattr(node, "_outside_buildarea",
                                   False) and not is_non_printing_mesh:
                            continue

                        temp_list.append(node)
                        if not is_non_printing_mesh:
                            has_printing_mesh = True

                    Job.yieldThread()

                # If the list doesn't have any model with suitable settings then clean the list
                # otherwise CuraEngine will crash
                if not has_printing_mesh:
                    temp_list.clear()

                if temp_list:
                    object_groups.append(temp_list)

            global_stack = CuraApplication.getInstance(
            ).getGlobalContainerStack()
            if not global_stack:
                return
            extruders_enabled = {
                position: stack.isEnabled
                for position, stack in global_stack.extruders.items()
            }
            filtered_object_groups = []
            has_model_with_disabled_extruders = False
            associated_disabled_extruders = set()
            for group in object_groups:
                stack = global_stack
                skip_group = False
                for node in group:
                    # Only check if the printing extruder is enabled for printing meshes
                    is_non_printing_mesh = node.callDecoration(
                        "evaluateIsNonPrintingMesh")
                    extruder_position = node.callDecoration(
                        "getActiveExtruderPosition")
                    if not is_non_printing_mesh and not extruders_enabled[
                            extruder_position]:
                        skip_group = True
                        has_model_with_disabled_extruders = True
                        associated_disabled_extruders.add(extruder_position)
                if not skip_group:
                    filtered_object_groups.append(group)

            if has_model_with_disabled_extruders:
                self.setResult(StartJobResult.ObjectsWithDisabledExtruder)
                associated_disabled_extruders = {
                    str(c)
                    for c in sorted(
                        [int(p) + 1 for p in associated_disabled_extruders])
                }
                self.setMessage(", ".join(associated_disabled_extruders))
                return

            # There are cases when there is nothing to slice. This can happen due to one at a time slicing not being
            # able to find a possible sequence or because there are no objects on the build plate (or they are outside
            # the build volume)
            if not filtered_object_groups:
                self.setResult(StartJobResult.NothingToSlice)
                return

            self._buildGlobalSettingsMessage(stack)
            self._buildGlobalInheritsStackMessage(stack)

            # Build messages for extruder stacks
            for extruder_stack in global_stack.extruderList:
                self._buildExtruderMessage(extruder_stack)

            for group in filtered_object_groups:
                group_message = self._slice_message.addRepeatedMessage(
                    "object_lists")
                parent = group[0].getParent()
                if parent is not None and parent.callDecoration("isGroup"):
                    self._handlePerObjectSettings(cast(CuraSceneNode, parent),
                                                  group_message)

                for object in group:
                    mesh_data = object.getMeshData()
                    if mesh_data is None:
                        continue
                    rot_scale = object.getWorldTransformation().getTransposed(
                    ).getData()[0:3, 0:3]
                    translate = object.getWorldTransformation().getData()[:3,
                                                                          3]

                    # This effectively performs a limited form of MeshData.getTransformed that ignores normals.
                    verts = mesh_data.getVertices()
                    verts = verts.dot(rot_scale)
                    verts += translate

                    # Convert from Y up axes to Z up axes. Equals a 90 degree rotation.
                    verts[:, [1, 2]] = verts[:, [2, 1]]
                    verts[:, 1] *= -1

                    obj = group_message.addRepeatedMessage("objects")
                    obj.id = id(object)
                    obj.name = object.getName()
                    indices = mesh_data.getIndices()
                    if indices is not None:
                        flat_verts = numpy.take(verts,
                                                indices.flatten(),
                                                axis=0)
                    else:
                        flat_verts = numpy.array(verts)

                    obj.vertices = flat_verts

                    self._handlePerObjectSettings(cast(CuraSceneNode, object),
                                                  obj)

                    Job.yieldThread()

        self.setResult(StartJobResult.Finished)
Example #47
0
    def loadScripts(self, path: str) -> None:
        """Load all scripts from provided path.

        This should probably only be done on init.
        :param path: Path to check for scripts.
        """

        if ApplicationMetadata.IsEnterpriseVersion:
            # Delete all __pycache__ not in installation folder, as it may present a security risk.
            # It prevents this very strange scenario (should already be prevented on enterprise because signed-fault):
            #  - Copy an existing script from the postprocessing-script folder to the appdata scripts folder.
            #  - Also copy the entire __pycache__ folder from the first to the last location.
            #  - Leave the __pycache__ as is, but write malicious code just before the class begins.
            #  - It'll execute, despite that the script has not been signed.
            # It's not known if these reproduction steps are minimal, but it does at least happen in this case.
            install_prefix = os.path.abspath(
                CuraApplication.getInstance().getInstallPrefix())
            try:
                is_in_installation_path = os.path.commonpath(
                    [install_prefix, path]).startswith(install_prefix)
            except ValueError:
                is_in_installation_path = False
            if not is_in_installation_path:
                TrustBasics.removeCached(path)

        scripts = pkgutil.iter_modules(path=[path])
        """Load all scripts in the scripts folders"""
        for loader, script_name, ispkg in scripts:
            # Iterate over all scripts.
            if script_name not in sys.modules:
                try:
                    file_path = os.path.join(path, script_name + ".py")
                    if not self._isScriptAllowed(file_path):
                        Logger.warning(
                            "Skipped loading post-processing script {}: not trusted"
                            .format(file_path))
                        continue

                    spec = importlib.util.spec_from_file_location(
                        __name__ + "." + script_name, file_path)
                    loaded_script = importlib.util.module_from_spec(spec)
                    if spec.loader is None:
                        continue
                    spec.loader.exec_module(loaded_script)  # type: ignore
                    sys.modules[
                        script_name] = loaded_script  #TODO: This could be a security risk. Overwrite any module with a user-provided name?

                    loaded_class = getattr(loaded_script, script_name)
                    temp_object = loaded_class()
                    Logger.log("d", "Begin loading of script: %s", script_name)
                    try:
                        setting_data = temp_object.getSettingData()
                        if "name" in setting_data and "key" in setting_data:
                            self._script_labels[
                                setting_data["key"]] = setting_data["name"]
                            self._loaded_scripts[
                                setting_data["key"]] = loaded_class
                        else:
                            Logger.log("w", "Script %s.py has no name or key",
                                       script_name)
                            self._script_labels[script_name] = script_name
                            self._loaded_scripts[script_name] = loaded_class
                    except AttributeError:
                        Logger.log(
                            "e",
                            "Script %s.py is not a recognised script type. Ensure it inherits Script",
                            script_name)
                    except NotImplementedError:
                        Logger.log("e",
                                   "Script %s.py has no implemented settings",
                                   script_name)
                except Exception as e:
                    Logger.logException(
                        "e",
                        "Exception occurred while loading post processing plugin: {error_msg}"
                        .format(error_msg=str(e)))
Example #48
0
    def _onWriteStarted(self, output_device):
        try:
            if not Preferences.getInstance().getValue("info/send_slice_info"):
                Logger.log("d", "'info/send_slice_info' is turned off.")
                return  # Do nothing, user does not want to send data

            global_container_stack = Application.getInstance(
            ).getGlobalContainerStack()
            print_information = Application.getInstance().getPrintInformation()

            data = dict()  # The data that we're going to submit.
            data["time_stamp"] = time.time()
            data["schema_version"] = 0
            data["cura_version"] = Application.getInstance().getVersion()

            active_mode = Preferences.getInstance().getValue(
                "cura/active_mode")
            if active_mode == 0:
                data["active_mode"] = "recommended"
            else:
                data["active_mode"] = "custom"

            data[
                "machine_settings_changed_by_user"] = global_container_stack.definitionChanges.getId(
                ) != "empty"
            data["language"] = Preferences.getInstance().getValue(
                "general/language")
            data["os"] = {
                "type": platform.system(),
                "version": platform.version()
            }

            data["active_machine"] = {
                "definition_id":
                global_container_stack.definition.getId(),
                "manufacturer":
                global_container_stack.definition.getMetaData().get(
                    "manufacturer", "")
            }

            data["extruders"] = []
            extruders = list(ExtruderManager.getInstance().getMachineExtruders(
                global_container_stack.getId()))
            extruders = sorted(
                extruders,
                key=lambda extruder: extruder.getMetaDataEntry("position"))

            if not extruders:
                extruders = [global_container_stack]

            for extruder in extruders:
                extruder_dict = dict()
                extruder_dict["active"] = ExtruderManager.getInstance(
                ).getActiveExtruderStack() == extruder
                extruder_dict["material"] = {
                    "GUID": extruder.material.getMetaData().get("GUID", ""),
                    "type":
                    extruder.material.getMetaData().get("material", ""),
                    "brand": extruder.material.getMetaData().get("brand", "")
                }
                extruder_dict[
                    "material_used"] = print_information.materialLengths[int(
                        extruder.getMetaDataEntry("position", "0"))]
                extruder_dict["variant"] = extruder.variant.getName()
                extruder_dict["nozzle_size"] = extruder.getProperty(
                    "machine_nozzle_size", "value")

                extruder_settings = dict()
                extruder_settings["wall_line_count"] = extruder.getProperty(
                    "wall_line_count", "value")
                extruder_settings["retraction_enable"] = extruder.getProperty(
                    "retraction_enable", "value")
                extruder_settings[
                    "infill_sparse_density"] = extruder.getProperty(
                        "infill_sparse_density", "value")
                extruder_settings["infill_pattern"] = extruder.getProperty(
                    "infill_pattern", "value")
                extruder_settings[
                    "gradual_infill_steps"] = extruder.getProperty(
                        "gradual_infill_steps", "value")
                extruder_settings[
                    "default_material_print_temperature"] = extruder.getProperty(
                        "default_material_print_temperature", "value")
                extruder_settings[
                    "material_print_temperature"] = extruder.getProperty(
                        "material_print_temperature", "value")
                extruder_dict["extruder_settings"] = extruder_settings
                data["extruders"].append(extruder_dict)

            data[
                "quality_profile"] = global_container_stack.quality.getMetaData(
                ).get("quality_type")

            data["models"] = []
            # Listing all files placed on the build plate
            for node in DepthFirstIterator(CuraApplication.getInstance(
            ).getController().getScene().getRoot()):
                if node.callDecoration("isSliceable"):
                    model = dict()
                    model["hash"] = node.getMeshData().getHash()
                    bounding_box = node.getBoundingBox()
                    model["bounding_box"] = {
                        "minimum": {
                            "x": bounding_box.minimum.x,
                            "y": bounding_box.minimum.y,
                            "z": bounding_box.minimum.z
                        },
                        "maximum": {
                            "x": bounding_box.maximum.x,
                            "y": bounding_box.maximum.y,
                            "z": bounding_box.maximum.z
                        }
                    }
                    model["transformation"] = {
                        "data":
                        str(node.getWorldTransformation().getData()).replace(
                            "\n", "")
                    }
                    extruder_position = node.callDecoration(
                        "getActiveExtruderPosition")
                    model[
                        "extruder"] = 0 if extruder_position is None else int(
                            extruder_position)

                    model_settings = dict()
                    model_stack = node.callDecoration("getStack")
                    if model_stack:
                        model_settings[
                            "support_enabled"] = model_stack.getProperty(
                                "support_enable", "value")
                        model_settings["support_extruder_nr"] = int(
                            model_stack.getProperty("support_extruder_nr",
                                                    "value"))

                        # Mesh modifiers;
                        model_settings[
                            "infill_mesh"] = model_stack.getProperty(
                                "infill_mesh", "value")
                        model_settings[
                            "cutting_mesh"] = model_stack.getProperty(
                                "cutting_mesh", "value")
                        model_settings[
                            "support_mesh"] = model_stack.getProperty(
                                "support_mesh", "value")
                        model_settings[
                            "anti_overhang_mesh"] = model_stack.getProperty(
                                "anti_overhang_mesh", "value")

                        model_settings[
                            "wall_line_count"] = model_stack.getProperty(
                                "wall_line_count", "value")
                        model_settings[
                            "retraction_enable"] = model_stack.getProperty(
                                "retraction_enable", "value")

                        # Infill settings
                        model_settings[
                            "infill_sparse_density"] = model_stack.getProperty(
                                "infill_sparse_density", "value")
                        model_settings[
                            "infill_pattern"] = model_stack.getProperty(
                                "infill_pattern", "value")
                        model_settings[
                            "gradual_infill_steps"] = model_stack.getProperty(
                                "gradual_infill_steps", "value")

                    model["model_settings"] = model_settings

                    data["models"].append(model)

            print_times = print_information.printTimesPerFeature
            data["print_times"] = {
                "travel":
                int(print_times["travel"].getDisplayString(
                    DurationFormat.Format.Seconds)),
                "support":
                int(print_times["support"].getDisplayString(
                    DurationFormat.Format.Seconds)),
                "infill":
                int(print_times["infill"].getDisplayString(
                    DurationFormat.Format.Seconds)),
                "total":
                int(
                    print_information.currentPrintTime.getDisplayString(
                        DurationFormat.Format.Seconds))
            }

            print_settings = dict()
            print_settings[
                "layer_height"] = global_container_stack.getProperty(
                    "layer_height", "value")

            # Support settings
            print_settings[
                "support_enabled"] = global_container_stack.getProperty(
                    "support_enable", "value")
            print_settings["support_extruder_nr"] = int(
                global_container_stack.getProperty("support_extruder_nr",
                                                   "value"))

            # Platform adhesion settings
            print_settings[
                "adhesion_type"] = global_container_stack.getProperty(
                    "adhesion_type", "value")

            # Shell settings
            print_settings[
                "wall_line_count"] = global_container_stack.getProperty(
                    "wall_line_count", "value")
            print_settings[
                "retraction_enable"] = global_container_stack.getProperty(
                    "retraction_enable", "value")

            # Prime tower settings
            print_settings[
                "prime_tower_enable"] = global_container_stack.getProperty(
                    "prime_tower_enable", "value")

            # Infill settings
            print_settings[
                "infill_sparse_density"] = global_container_stack.getProperty(
                    "infill_sparse_density", "value")
            print_settings[
                "infill_pattern"] = global_container_stack.getProperty(
                    "infill_pattern", "value")
            print_settings[
                "gradual_infill_steps"] = global_container_stack.getProperty(
                    "gradual_infill_steps", "value")

            print_settings[
                "print_sequence"] = global_container_stack.getProperty(
                    "print_sequence", "value")

            data["print_settings"] = print_settings

            # Convert data to bytes
            binary_data = json.dumps(data).encode("utf-8")

            # Sending slice info non-blocking
            reportJob = SliceInfoJob(self.info_url, binary_data)
            reportJob.start()
        except Exception:
            # We really can't afford to have a mistake here, as this would break the sending of g-code to a device
            # (Either saving or directly to a printer). The functionality of the slice data is not *that* important.
            Logger.logException(
                "e", "Exception raised while sending slice info."
            )  # But we should be notified about these problems of course.
Example #49
0
    def __init__(self, parent=None) -> None:
        super().__init__(parent)

        self._max_layers = 0
        self._current_layer_num = 0
        self._minimum_layer_num = 0
        self._current_layer_mesh = None
        self._current_layer_jumps = None
        self._top_layers_job = None  # type: Optional["_CreateTopLayersJob"]
        self._activity = False
        self._old_max_layers = 0

        self._max_paths = 0
        self._current_path_num = 0
        self._minimum_path_num = 0
        self.currentLayerNumChanged.connect(self._onCurrentLayerNumChanged)

        self._busy = False
        self._simulation_running = False

        self._ghost_shader = None  # type: Optional["ShaderProgram"]
        self._layer_pass = None  # type: Optional[SimulationPass]
        self._composite_pass = None  # type: Optional[CompositePass]
        self._old_layer_bindings = None  # type: Optional[List[str]]
        self._simulationview_composite_shader = None  # type: Optional["ShaderProgram"]
        self._old_composite_shader = None  # type: Optional["ShaderProgram"]

        self._max_feedrate = sys.float_info.min
        self._min_feedrate = sys.float_info.max
        self._max_thickness = sys.float_info.min
        self._min_thickness = sys.float_info.max
        self._max_line_width = sys.float_info.min
        self._min_line_width = sys.float_info.max

        self._global_container_stack = None  # type: Optional[ContainerStack]
        self._proxy = None

        self._resetSettings()
        self._legend_items = None
        self._show_travel_moves = False
        self._nozzle_node = None  # type: Optional[NozzleNode]

        Application.getInstance().getPreferences().addPreference(
            "view/top_layer_count", 5)
        Application.getInstance().getPreferences().addPreference(
            "view/only_show_top_layers", False)
        Application.getInstance().getPreferences().addPreference(
            "view/force_layer_view_compatibility_mode", False)

        Application.getInstance().getPreferences().addPreference(
            "layerview/layer_view_type", 1)  # Default to "Line Type".
        Application.getInstance().getPreferences().addPreference(
            "layerview/extruder_opacities", "")

        Application.getInstance().getPreferences().addPreference(
            "layerview/show_travel_moves", False)
        Application.getInstance().getPreferences().addPreference(
            "layerview/show_helpers", True)
        Application.getInstance().getPreferences().addPreference(
            "layerview/show_skin", True)
        Application.getInstance().getPreferences().addPreference(
            "layerview/show_infill", True)
        Application.getInstance().getPreferences().addPreference(
            "layerview/show_starts", True)

        self._updateWithPreferences()

        self._solid_layers = int(
            Application.getInstance().getPreferences().getValue(
                "view/top_layer_count"))
        self._only_show_top_layers = bool(
            Application.getInstance().getPreferences().getValue(
                "view/only_show_top_layers"))
        self._compatibility_mode = self._evaluateCompatibilityMode()

        self._wireprint_warning_message = Message(catalog.i18nc(
            "@info:status",
            "Cura does not accurately display layers when Wire Printing is enabled."
        ),
                                                  title=catalog.i18nc(
                                                      "@info:title",
                                                      "Simulation View"))
        self._slice_first_warning_message = Message(
            catalog.i18nc("@info:status",
                          "Nothing is shown because you need to slice first."),
            title=catalog.i18nc("@info:title", "No layers to show"),
            option_text=catalog.i18nc("@info:option_text",
                                      "Do not show this message again"),
            option_state=False)
        self._slice_first_warning_message.optionToggled.connect(
            self._onDontAskMeAgain)
        CuraApplication.getInstance().getPreferences().addPreference(
            self._no_layers_warning_preference, True)

        QtApplication.getInstance().engineCreatedSignal.connect(
            self._onEngineCreated)
Example #50
0
 def _onDontAskMeAgain(self, checked: bool) -> None:
     CuraApplication.getInstance().getPreferences().setValue(
         self._preference_key, checked)
Example #51
0
    def createExtruderStackWithDefaultSetup(cls, global_stack: "GlobalStack",
                                            extruder_position: int) -> None:
        """Create a default Extruder Stack

        :param global_stack: The global stack this extruder refers to.
        :param extruder_position: The position of the current extruder.
        """

        from cura.CuraApplication import CuraApplication
        application = CuraApplication.getInstance()
        registry = application.getContainerRegistry()

        # Get the extruder definition.
        extruder_definition_dict = global_stack.getMetaDataEntry(
            "machine_extruder_trains")
        extruder_definition_id = extruder_definition_dict[str(
            extruder_position)]
        try:
            extruder_definition = registry.findDefinitionContainers(
                id=extruder_definition_id)[0]
        except IndexError:
            # It still needs to break, but we want to know what extruder ID made it break.
            msg = "Unable to find extruder definition with the id [%s]" % extruder_definition_id
            Logger.logException("e", msg)
            raise IndexError(msg)

        # Find out what filament diameter we need.
        approximate_diameter = round(
            extruder_definition.getProperty("material_diameter", "value")
        )  # Can't be modified by definition changes since we are just initialising the stack here.

        # Find the preferred containers.
        machine_node = ContainerTree.getInstance().machines[
            global_stack.definition.getId()]
        extruder_variant_node = machine_node.variants.get(
            machine_node.preferred_variant_name)
        if not extruder_variant_node:
            Logger.log(
                "w",
                "Could not find preferred nozzle {nozzle_name}. Falling back to {fallback}."
                .format(nozzle_name=machine_node.preferred_variant_name,
                        fallback=next(iter(machine_node.variants))))
            extruder_variant_node = next(iter(machine_node.variants.values()))
        extruder_variant_container = extruder_variant_node.container
        material_node = extruder_variant_node.preferredMaterial(
            approximate_diameter)
        material_container = material_node.container
        quality_node = material_node.preferredQuality()

        new_extruder_id = registry.uniqueName(extruder_definition_id)
        new_extruder = cls.createExtruderStack(
            new_extruder_id,
            extruder_definition=extruder_definition,
            machine_definition_id=global_stack.definition.getId(),
            position=extruder_position,
            variant_container=extruder_variant_container,
            material_container=material_container,
            quality_container=quality_node.container)
        new_extruder.setNextStack(global_stack)

        registry.addContainer(new_extruder)
    def execute(self, data):
        if not self.getSettingValueByKey("enable"):
            return data

        stream = io.StringIO()
        stream.write(";START_SETTINGS\n")

        machine_manager = CuraApplication.getInstance().getMachineManager()
        stack = CuraApplication.getInstance().getGlobalContainerStack()

        global_stack = machine_manager.activeMachine

        extruder_count = stack.getProperty("machine_extruder_count", "value")
        extruders = global_stack.extruderList

        print_information = CuraApplication.getInstance().getPrintInformation()

        self._write_kv(stream, translate(i18n_cura_catalog, "@label", "Job Name"), print_information.jobName)
        self._write_kv(stream, "Date", datetime.now().isoformat())
        self._write_kv(stream, "Os", "{} {}".format(platform.system(), platform.version()))
        self._write_kv(stream, "Cura Version", CuraVersion)

        self._write_kv(stream, translate(i18n_cura_catalog, "@label", "Profile"), global_stack.qualityChanges.getMetaData().get("name", ""))
        self._write_kv(stream, translate(i18n_cura_catalog, "@label:table_header", "Quality"), global_stack.quality.getMetaData().get("name", ""))

        # Material
        for i, extruder in enumerate(extruders):
            M_Name = extruder.material.getMetaData().get("material", "")
            MaterialStr = "%s %s : %d" % (translate(i18n_cura_catalog, "@label", "Material"), translate(i18n_cura_catalog, "@label", "Extruder"), i)
            self._write_kv(stream, MaterialStr, M_Name)

        material_weight = sum(print_information.materialWeights, 0)
        if material_weight > 0:
            self._write_kv(stream, translate(i18n_cura_catalog, "@label", "Material estimation"), "{:.1f}g".format(material_weight))

        self._write_kv(stream, translate(i18n_cura_catalog, "@label", "Printing Time"), print_information.currentPrintTime.getDisplayString())

        # Define every section to get the same order as in the Cura Interface
        # Modification from global_stack to extruders[0]
        for i, extruder in enumerate(extruders):
            self._doTree(extruder, "resolution", stream, 0, i)
            self._doTree(extruder, "shell",      stream, 0, i)
            self._doTree(extruder, "infill",     stream, 0, i)
            self._doTree(extruder, "material",   stream, 0, i)
            self._doTree(extruder, "speed",      stream, 0, i)
            self._doTree(extruder, "travel",     stream, 0, i)
            self._doTree(extruder, "cooling",    stream, 0, i)

            # If single extruder doesn't export the data
            if extruder_count>1 :
                self._doTree(extruder, "dual", stream, 0, i)

        self._doTree(extruders[0],"support", stream, 0, 0)
        self._doTree(extruders[0],"platform_adhesion", stream, 0, 0)

        for i, extruder in enumerate(extruders):
            self._doTree(extruder, "meshfix", stream, 0, i)

        self._doTree(extruders[0], "blackmagic", stream, 0, 0)
        self._doTree(extruders[0], "experimental", stream, 0, 0)
        self._doTree(extruders[0], "machine_settings", stream, 0, 0)

        for i, extruder in enumerate(extruders):
            self._doTreeExtrud(extruder, "machine_settings", stream, 0, i)

        # set_trace(port=4444)

        stream.write(";END_SETTINGS\n")

        ## some characters, like 40°C and 800mm/s² aren't ascii-encodable and cause errors
        data.append("".join(filter(lambda x: x in string.printable, stream.getvalue())))

        return data
Example #53
0
    def slice(self) -> None:
        Logger.log("d", "Starting to slice...")
        self._backend._slice_start_time = time()
        if not self._backend._build_plates_to_be_sliced:
            self._backend.processingProgress.emit(1.0)
            Logger.log(
                "w",
                "Slice unnecessary, nothing has changed that needs reslicing.")
            return

        if self._backend._process_layers_job:
            Logger.log("d", "Process layers job still busy, trying later.")
            return

        if not hasattr(self._backend._scene, "gcode_dict"):
            self._backend._scene.gcode_dict = {
            }  #type: ignore #Because we are creating the missing attribute here.

        # see if we really have to slice
        application = CuraApplication.getInstance()
        active_build_plate = application.getMultiBuildPlateModel(
        ).activeBuildPlate
        build_plate_to_be_sliced = self._backend._build_plates_to_be_sliced.pop(
            0)
        Logger.log(
            "d", "Going to slice build plate [%s]!" % build_plate_to_be_sliced)
        num_objects = self._backend._numObjectsPerBuildPlate()

        self._backend._stored_layer_data = []
        self._backend._stored_optimized_layer_data[
            build_plate_to_be_sliced] = []

        if build_plate_to_be_sliced not in num_objects or num_objects[
                build_plate_to_be_sliced] == 0:
            self._backend._scene.gcode_dict[build_plate_to_be_sliced] = [
            ]  #type: ignore #Because we created this attribute above.
            Logger.log("d",
                       "Build plate %s has no objects to be sliced, skipping",
                       build_plate_to_be_sliced)
            if self._backend._build_plates_to_be_sliced:
                self._backend.slice()
            return

        if application.getPrintInformation(
        ) and build_plate_to_be_sliced == active_build_plate:
            application.getPrintInformation().setToZeroPrintInformation(
                build_plate_to_be_sliced)

        if self._backend._process is None:  # type: ignore
            self._backend._createSocket()
        self._backend.stopSlicing()
        self._backend._engine_is_fresh = False  # Yes we're going to use the engine

        self._backend.processingProgress.emit(0.0)
        self._backend.backendStateChange.emit(BackendState.NotStarted)

        self._backend._scene.gcode_dict[build_plate_to_be_sliced] = [
        ]  #type: ignore #[] indexed by build plate number
        self._backend._slicing = True
        self._backend.slicingStarted.emit()

        self._backend.determineAutoSlicing(
        )  # Switch timer on or off if appropriate

        slice_message = self._backend._socket.createMessage("cura.proto.Slice")
        ## PATCH: local import
        self._backend._start_slice_job = StartSliceJob.StartSliceJob(
            slice_message)
        self._backend._start_slice_job_build_plate = build_plate_to_be_sliced
        self._backend._start_slice_job.setBuildPlate(
            self._backend._start_slice_job_build_plate)
        self._backend._start_slice_job.start()
        self._backend._start_slice_job.finished.connect(
            self._backend._onStartSliceCompleted)
Example #54
0
def htmlComparePage():

    # Current machine
    machine_manager = Application.getInstance().getMachineManager()
    global_stack = machine_manager.activeMachine
    stack = CuraApplication.getInstance().getGlobalContainerStack()

    machine_id = global_stack.quality.getMetaData().get('definition', '')
    Logger.log("d", "HtmlComparePage machine_id = " + machine_id)

    containers = ContainerRegistry.getInstance().findInstanceContainers(
        definition=machine_id, type='quality_changes')

    containers.sort(key=lambda x: x.getId())
    containers.reverse()
    containers.sort(key=lambda x: x.getName())

    Profil_List = []
    Container_List = []
    liste_keys = []
    liste_keys_extruder = []

    # Logger.log("d", "Before container")
    for container in containers:
        # type to detect Extruder or Global container analyse getMetaDataEntry('position')
        extruder_position = container.getMetaDataEntry('position')
        if extruder_position is None:
            # Logger.log("d", "global : " + container.getId())
            Profil_List.append(container.getName())
            Container_List.append(str(id(container)))

            if hasattr(container, 'getAllKeys'):
                keys = list(container.getAllKeys())
                for key in keys:
                    liste_keys.append(key)
        else:
            if hasattr(container, 'getAllKeys'):
                keys = list(container.getAllKeys())
                for key in keys:
                    liste_keys_extruder.append(key)

    liste_keys = list(dict.fromkeys(liste_keys))
    liste_keys.sort()
    liste_keys_extruder = list(dict.fromkeys(liste_keys_extruder))
    liste_keys_extruder.sort()

    html = getHtmlHeader()

    # Menu creation
    html += '<div class="menu">\n'

    html += '<h3><a href="#Top_page">Profile List ' + encode(
        machine_id) + '</a></h3>'

    html += '<ul>\n'
    for profil in Profil_List:
        #
        html += '<li><input type="checkbox" id="chk_' + str(
            Profil_List.index(profil)
        ) + '" checked onclick="toggleColumnVisibility()"/> <a href="#' + str(
            Container_List[Profil_List.index(profil)]) + '">' + encode(
                profil) + '</a></li>'

    html += '</ul>\n'

    # Java script filter function
    html += keyUnselectAllWidget()
    html += keyFilterWidget()
    html += toggleDifferencesWidget()
    html += toggleNullValueWidget()
    html += '</div>\n'

    # Contents creation
    html += '\n<div class="contents">\n'
    html += '<h2 id="Top_page">Profiles</h2>\n'
    short_value_properties = True

    html += '<table class=' 'key_value_table' '><thead>\n'
    html += '<tr><th>Key</th>\n'
    for profil in Profil_List:
        html += '<th>' + encode(profil)
        html += '<a id="' + str(
            Container_List[Profil_List.index(profil)]) + '" ></a>'
        html += '</th>\n'
    html += '</tr></thead><tbody>\n'

    html += '<tr class="metadata"><td class="key">definition</td>'
    for container in containers:
        # type to detect Extruder or Global container analyse getMetaDataEntry('position')
        extruder_position = container.getMetaDataEntry('position')
        if extruder_position is None:
            MetaData_definition = container.getMetaDataEntry('definition')
            if MetaData_definition is not None:
                html += '<td class="value">' + MetaData_definition + '</td>'
    html += '</tr>\n'

    html += '<tr class="metadata"><td class="key">quality_type</td>'
    for container in containers:
        # type to detect Extruder or Global container analyse getMetaDataEntry('position')
        extruder_position = container.getMetaDataEntry('position')
        if extruder_position is None:
            MetaData_quality_type = container.getMetaDataEntry('quality_type')
            if MetaData_quality_type is not None:
                html += '<td class="value">' + MetaData_quality_type + '</td>'
    html += '</tr>\n'

    for Lkey in liste_keys:
        try:
            untranslated_label = stack.getProperty(Lkey, 'label')
            definition_key = Lkey + ' label'
            translated_label = i18n_catalog.i18nc(definition_key,
                                                  untranslated_label)
            untranslated_description = stack.getProperty(Lkey, 'description')
            description_key = Lkey + ' description'
            translated_description = i18n_catalog.i18nc(
                description_key, untranslated_description)
        except ValueError:
            continue

        html += '<tr class="" --data-key="' + translated_label + '"><td class="CellWithComment">&#x1f511; ' + encode(
            translated_label
        ) + '<span class="CellComment">' + translated_description + '</span></td>'
        for container in containers:
            # type to detect Extruder or Global container analyse getMetaDataEntry('position')
            extruder_position = container.getMetaDataEntry('position')
            if extruder_position is None:
                #
                Html_td = ''
                key_properties = [
                    'value', 'resolve'
                ] if short_value_properties else setting_prop_names
                key_properties.sort()

                # hasattr() method returns true if an object has the given named attribute and false if it does not
                if hasattr(container, 'getAllKeys'):
                    keys = list(container.getAllKeys())
                    keys.sort()
                    for key in keys:
                        if key == Lkey:
                            formatted_value = formatSettingCompareValue(
                                container, key, key_properties).value
                            formatted_key = encode(str(key))
                            Html_td = '<td class="value">' + formatted_value + '</td>'

                if Html_td == '':
                    html += '<td class="value">-</td>'
                else:
                    html += Html_td

        html += '</tr>\n'
    # html += tableFooter()

    extruder_count = int(
        CuraApplication.getInstance().getGlobalContainerStack().getProperty(
            'machine_extruder_count', 'value'))
    # Logger.log("d", "extruder_count : %s",extruder_count)
    ind = 0
    while ind < extruder_count:
        # Naw Table for Extruder settings
        # html +='<table class=''key_value_table_extruder''><thead>\n'
        html += '<thead><tr><th>' + encode('Extruder N° ' +
                                           str(ind)) + '</th>\n'
        for profil in Profil_List:
            html += '<th>' + encode(profil)
            html += '</th>\n'
        html += '</tr></thead><tbody>\n'
        try:
            for Lkey in liste_keys_extruder:
                # Logger.log("d", "Lkey : %s",Lkey)
                definition_key = Lkey + ' label'
                description_key = Lkey + ' description'
                try:
                    untranslated_label = stack.getProperty(Lkey, 'label')
                    translated_label = i18n_catalog.i18nc(
                        definition_key, untranslated_label)
                    untranslated_description = stack.getProperty(
                        Lkey, 'description')
                    translated_description = i18n_catalog.i18nc(
                        description_key, untranslated_description)
                    html += '<tr class="" --data-key="' + translated_label + '"><td class="CellWithComment">&#x1f511; ' + encode(
                        translated_label
                    ) + '<span class="CellComment">' + translated_description + '</span></td>'
                except:
                    Logger.log("d", "Translated_label ERROR on Lkey : %s",
                               Lkey)
                    translated_label = Lkey
                    untranslated_label = Lkey
                    translated_description = 'Description ' + Lkey + ' Not found'
                    untranslated_description = 'Description ' + Lkey + ' Not found'
                    html += '<tr class="" --data-key="' + translated_label + '"><td class="CellWithError">&#x1f511; ' + encode(
                        translated_label
                    ) + '<span class="CellComment">' + translated_description + '</span></td>'
                    html += '<td class="value_extruder">-</td>'

                    continue

                for container in containers:
                    # type to detect Extruder or Global container analyse getMetaDataEntry('position')
                    # Could be none type
                    extruder_position = container.getMetaDataEntry('position')
                    if extruder_position is not None:
                        # Logger.log("d", "extruder_position : %s",extruder_position)
                        Extrud_Nb = int(extruder_position)
                        if Extrud_Nb == ind:
                            #
                            Html_td = ''
                            key_properties = [
                                'value', 'resolve'
                            ] if short_value_properties else setting_prop_names
                            key_properties.sort()

                            # hasattr() method returns true if an object has the given named attribute and false if it does not
                            if hasattr(container, 'getAllKeys'):
                                keys = list(container.getAllKeys())
                                keys.sort()
                                for key in keys:
                                    if key == Lkey:
                                        # Logger.log("d", "Key -> Lkey : %s",key)
                                        formatted_value = formatSettingCompareValue(
                                            container, key,
                                            key_properties).value
                                        formatted_key = encode(str(key))
                                        Html_td = '<td class="value_extruder">' + formatted_value + '</td>'

                            if Html_td == '':
                                html += '<td class="value_extruder">-</td>'
                            else:
                                html += Html_td

                html += '</tr>\n'
        except:
            Logger.log("d", "HtmlComparePage ERROR on Lkey : %s", Lkey)
            pass

        # html += tableFooter()
        ind += 1

    html += tableFooter()
    html += '</div>'

    html += htmlFooter

    Logger.log("d", "HtmlComparePage : Fin")
    return html
Example #55
0
    def _convertSavitarNodeToUMNode(
            self,
            savitar_node: Savitar.SceneNode,
            file_name: str = "") -> Optional[SceneNode]:
        self._object_count += 1
        node_name = "Object %s" % self._object_count

        active_build_plate = CuraApplication.getInstance(
        ).getMultiBuildPlateModel().activeBuildPlate

        um_node = CuraSceneNode()  # This adds a SettingOverrideDecorator
        um_node.addDecorator(BuildPlateDecorator(active_build_plate))
        um_node.setName(node_name)
        transformation = self._createMatrixFromTransformationString(
            savitar_node.getTransformation())
        um_node.setTransformation(transformation)
        mesh_builder = MeshBuilder()

        data = numpy.fromstring(
            savitar_node.getMeshData().getFlatVerticesAsBytes(),
            dtype=numpy.float32)

        vertices = numpy.resize(data, (int(data.size / 3), 3))
        mesh_builder.setVertices(vertices)
        mesh_builder.calculateNormals(fast=True)
        if file_name:
            # The filename is used to give the user the option to reload the file if it is changed on disk
            # It is only set for the root node of the 3mf file
            mesh_builder.setFileName(file_name)
        mesh_data = mesh_builder.build()

        if len(mesh_data.getVertices()):
            um_node.setMeshData(mesh_data)

        for child in savitar_node.getChildren():
            child_node = self._convertSavitarNodeToUMNode(child)
            if child_node:
                um_node.addChild(child_node)

        if um_node.getMeshData() is None and len(um_node.getChildren()) == 0:
            return None

        settings = savitar_node.getSettings()

        # Add the setting override decorator, so we can add settings to this node.
        if settings:
            global_container_stack = CuraApplication.getInstance(
            ).getGlobalContainerStack()

            # Ensure the correct next container for the SettingOverride decorator is set.
            if global_container_stack:
                default_stack = ExtruderManager.getInstance().getExtruderStack(
                    0)

                if default_stack:
                    um_node.callDecoration("setActiveExtruder",
                                           default_stack.getId())

                # Get the definition & set it
                definition_id = ContainerTree.getInstance().machines[
                    global_container_stack.definition.getId(
                    )].quality_definition
                um_node.callDecoration("getStack").getTop().setDefinition(
                    definition_id)

            setting_container = um_node.callDecoration("getStack").getTop()

            for key in settings:
                setting_value = settings[key]

                # Extruder_nr is a special case.
                if key == "extruder_nr":
                    extruder_stack = ExtruderManager.getInstance(
                    ).getExtruderStack(int(setting_value))
                    if extruder_stack:
                        um_node.callDecoration("setActiveExtruder",
                                               extruder_stack.getId())
                    else:
                        Logger.log("w",
                                   "Unable to find extruder in position %s",
                                   setting_value)
                    continue
                setting_container.setProperty(key, "value", setting_value)

        if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None:
            group_decorator = GroupDecorator()
            um_node.addDecorator(group_decorator)
        um_node.setSelectable(True)
        if um_node.getMeshData():
            # Assuming that all nodes with mesh data are printable objects
            # affects (auto) slicing
            sliceable_decorator = SliceableObjectDecorator()
            um_node.addDecorator(sliceable_decorator)
        return um_node
Example #56
0
    def addExtruderStackForSingleExtrusionMachine(
            self,
            machine,
            extruder_id,
            new_global_quality_changes=None,
            create_new_ids=True):
        new_extruder_id = extruder_id

        application = CuraApplication.getInstance()

        extruder_definitions = self.findDefinitionContainers(
            id=new_extruder_id)
        if not extruder_definitions:
            Logger.log("w",
                       "Could not find definition containers for extruder %s",
                       new_extruder_id)
            return

        extruder_definition = extruder_definitions[0]
        unique_name = self.uniqueName(
            machine.getName() + " " + new_extruder_id
        ) if create_new_ids else machine.getName() + " " + new_extruder_id

        extruder_stack = ExtruderStack.ExtruderStack(unique_name)
        extruder_stack.setName(extruder_definition.getName())
        extruder_stack.setDefinition(extruder_definition)
        extruder_stack.addMetaDataEntry(
            "position", extruder_definition.getMetaDataEntry("position"))

        # create a new definition_changes container for the extruder stack
        definition_changes_id = self.uniqueName(
            extruder_stack.getId() + "_settings"
        ) if create_new_ids else extruder_stack.getId() + "_settings"
        definition_changes_name = definition_changes_id
        definition_changes = InstanceContainer(definition_changes_id,
                                               parent=application)
        definition_changes.setName(definition_changes_name)
        definition_changes.addMetaDataEntry("setting_version",
                                            CuraApplication.SettingVersion)
        definition_changes.addMetaDataEntry("type", "definition_changes")
        definition_changes.addMetaDataEntry("definition",
                                            extruder_definition.getId())

        # move definition_changes settings if exist
        for setting_key in definition_changes.getAllKeys():
            if machine.definition.getProperty(setting_key,
                                              "settable_per_extruder"):
                setting_value = machine.definitionChanges.getProperty(
                    setting_key, "value")
                if setting_value is not None:
                    # move it to the extruder stack's definition_changes
                    setting_definition = machine.getSettingDefinition(
                        setting_key)
                    new_instance = SettingInstance(setting_definition,
                                                   definition_changes)
                    new_instance.setProperty("value", setting_value)
                    new_instance.resetState(
                    )  # Ensure that the state is not seen as a user state.
                    definition_changes.addInstance(new_instance)
                    definition_changes.setDirty(True)

                    machine.definitionChanges.removeInstance(
                        setting_key, postpone_emit=True)

        self.addContainer(definition_changes)
        extruder_stack.setDefinitionChanges(definition_changes)

        # create empty user changes container otherwise
        user_container_id = self.uniqueName(
            extruder_stack.getId() +
            "_user") if create_new_ids else extruder_stack.getId() + "_user"
        user_container_name = user_container_id
        user_container = InstanceContainer(user_container_id,
                                           parent=application)
        user_container.setName(user_container_name)
        user_container.addMetaDataEntry("type", "user")
        user_container.addMetaDataEntry("machine", machine.getId())
        user_container.addMetaDataEntry("setting_version",
                                        CuraApplication.SettingVersion)
        user_container.setDefinition(machine.definition.getId())
        user_container.setMetaDataEntry(
            "position", extruder_stack.getMetaDataEntry("position"))

        if machine.userChanges:
            # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes
            # container to the extruder stack.
            for user_setting_key in machine.userChanges.getAllKeys():
                settable_per_extruder = machine.getProperty(
                    user_setting_key, "settable_per_extruder")
                if settable_per_extruder:
                    setting_value = machine.getProperty(
                        user_setting_key, "value")

                    setting_definition = machine.getSettingDefinition(
                        user_setting_key)
                    new_instance = SettingInstance(setting_definition,
                                                   definition_changes)
                    new_instance.setProperty("value", setting_value)
                    new_instance.resetState(
                    )  # Ensure that the state is not seen as a user state.
                    user_container.addInstance(new_instance)
                    user_container.setDirty(True)

                    machine.userChanges.removeInstance(user_setting_key,
                                                       postpone_emit=True)

        self.addContainer(user_container)
        extruder_stack.setUserChanges(user_container)

        empty_variant = application.empty_variant_container
        empty_material = application.empty_material_container
        empty_quality = application.empty_quality_container

        if machine.variant.getId() not in ("empty", "empty_variant"):
            variant = machine.variant
        else:
            variant = empty_variant
        extruder_stack.variant = variant

        if machine.material.getId() not in ("empty", "empty_material"):
            material = machine.material
        else:
            material = empty_material
        extruder_stack.material = material

        if machine.quality.getId() not in ("empty", "empty_quality"):
            quality = machine.quality
        else:
            quality = empty_quality
        extruder_stack.quality = quality

        machine_quality_changes = machine.qualityChanges
        if new_global_quality_changes is not None:
            machine_quality_changes = new_global_quality_changes

        if machine_quality_changes.getId() not in ("empty",
                                                   "empty_quality_changes"):
            extruder_quality_changes_container = self.findInstanceContainers(
                name=machine_quality_changes.getName(), extruder=extruder_id)
            if extruder_quality_changes_container:
                extruder_quality_changes_container = extruder_quality_changes_container[
                    0]

                quality_changes_id = extruder_quality_changes_container.getId()
                extruder_stack.qualityChanges = self.findInstanceContainers(
                    id=quality_changes_id)[0]
            else:
                # Some extruder quality_changes containers can be created at runtime as files in the qualities
                # folder. Those files won't be loaded in the registry immediately. So we also need to search
                # the folder to see if the quality_changes exists.
                extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(
                    machine_quality_changes.getName())
                if extruder_quality_changes_container:
                    quality_changes_id = extruder_quality_changes_container.getId(
                    )
                    extruder_quality_changes_container.addMetaDataEntry(
                        "position",
                        extruder_definition.getMetaDataEntry("position"))
                    extruder_stack.qualityChanges = self.findInstanceContainers(
                        id=quality_changes_id)[0]
                else:
                    # if we still cannot find a quality changes container for the extruder, create a new one
                    container_name = machine_quality_changes.getName()
                    container_id = self.uniqueName(extruder_stack.getId() +
                                                   "_qc_" + container_name)
                    extruder_quality_changes_container = InstanceContainer(
                        container_id, parent=application)
                    extruder_quality_changes_container.setName(container_name)
                    extruder_quality_changes_container.addMetaDataEntry(
                        "type", "quality_changes")
                    extruder_quality_changes_container.addMetaDataEntry(
                        "setting_version", CuraApplication.SettingVersion)
                    extruder_quality_changes_container.addMetaDataEntry(
                        "position",
                        extruder_definition.getMetaDataEntry("position"))
                    extruder_quality_changes_container.addMetaDataEntry(
                        "quality_type",
                        machine_quality_changes.getMetaDataEntry(
                            "quality_type"))
                    extruder_quality_changes_container.setDefinition(
                        machine_quality_changes.getDefinition().getId())

                    self.addContainer(extruder_quality_changes_container)
                    extruder_stack.qualityChanges = extruder_quality_changes_container

            if not extruder_quality_changes_container:
                Logger.log(
                    "w",
                    "Could not find quality_changes named [%s] for extruder [%s]",
                    machine_quality_changes.getName(), extruder_stack.getId())
            else:
                # move all per-extruder settings to the extruder's quality changes
                for qc_setting_key in machine_quality_changes.getAllKeys():
                    settable_per_extruder = machine.getProperty(
                        qc_setting_key, "settable_per_extruder")
                    if settable_per_extruder:
                        setting_value = machine_quality_changes.getProperty(
                            qc_setting_key, "value")

                        setting_definition = machine.getSettingDefinition(
                            qc_setting_key)
                        new_instance = SettingInstance(setting_definition,
                                                       definition_changes)
                        new_instance.setProperty("value", setting_value)
                        new_instance.resetState(
                        )  # Ensure that the state is not seen as a user state.
                        extruder_quality_changes_container.addInstance(
                            new_instance)
                        extruder_quality_changes_container.setDirty(True)

                        machine_quality_changes.removeInstance(
                            qc_setting_key, postpone_emit=True)
        else:
            extruder_stack.qualityChanges = self.findInstanceContainers(
                id="empty_quality_changes")[0]

        self.addContainer(extruder_stack)

        # Also need to fix the other qualities that are suitable for this machine. Those quality changes may still have
        # per-extruder settings in the container for the machine instead of the extruder.
        if machine_quality_changes.getId() not in ("empty",
                                                   "empty_quality_changes"):
            quality_changes_machine_definition_id = machine_quality_changes.getDefinition(
            ).getId()
        else:
            whole_machine_definition = machine.definition
            machine_entry = machine.definition.getMetaDataEntry("machine")
            if machine_entry is not None:
                container_registry = ContainerRegistry.getInstance()
                whole_machine_definition = container_registry.findDefinitionContainers(
                    id=machine_entry)[0]

            quality_changes_machine_definition_id = "fdmprinter"
            if whole_machine_definition.getMetaDataEntry(
                    "has_machine_quality"):
                quality_changes_machine_definition_id = machine.definition.getMetaDataEntry(
                    "quality_definition", whole_machine_definition.getId())
        qcs = self.findInstanceContainers(
            type="quality_changes",
            definition=quality_changes_machine_definition_id)
        qc_groups = {}  # map of qc names -> qc containers
        for qc in qcs:
            qc_name = qc.getName()
            if qc_name not in qc_groups:
                qc_groups[qc_name] = []
            qc_groups[qc_name].append(qc)
            # try to find from the quality changes cura directory too
            quality_changes_container = self._findQualityChangesContainerInCuraFolder(
                machine_quality_changes.getName())
            if quality_changes_container:
                qc_groups[qc_name].append(quality_changes_container)

        for qc_name, qc_list in qc_groups.items():
            qc_dict = {"global": None, "extruders": []}
            for qc in qc_list:
                extruder_position = qc.getMetaDataEntry("position")
                if extruder_position is not None:
                    qc_dict["extruders"].append(qc)
                else:
                    qc_dict["global"] = qc
            if qc_dict["global"] is not None and len(
                    qc_dict["extruders"]) == 1:
                # move per-extruder settings
                for qc_setting_key in qc_dict["global"].getAllKeys():
                    settable_per_extruder = machine.getProperty(
                        qc_setting_key, "settable_per_extruder")
                    if settable_per_extruder:
                        setting_value = qc_dict["global"].getProperty(
                            qc_setting_key, "value")

                        setting_definition = machine.getSettingDefinition(
                            qc_setting_key)
                        new_instance = SettingInstance(setting_definition,
                                                       definition_changes)
                        new_instance.setProperty("value", setting_value)
                        new_instance.resetState(
                        )  # Ensure that the state is not seen as a user state.
                        qc_dict["extruders"][0].addInstance(new_instance)
                        qc_dict["extruders"][0].setDirty(True)

                        qc_dict["global"].removeInstance(qc_setting_key,
                                                         postpone_emit=True)

        # Set next stack at the end
        extruder_stack.setNextStack(machine)

        return extruder_stack
Example #57
0
    def createMachine(cls, name: str, definition_id: str) -> Optional[GlobalStack]:
        from cura.CuraApplication import CuraApplication
        application = CuraApplication.getInstance()
        variant_manager = application.getVariantManager()
        material_manager = application.getMaterialManager()
        quality_manager = application.getQualityManager()
        registry = application.getContainerRegistry()

        definitions = registry.findDefinitionContainers(id = definition_id)
        if not definitions:
            ConfigurationErrorMessage.getInstance().addFaultyContainers(definition_id)
            Logger.log("w", "Definition {definition} was not found!", definition = definition_id)
            return None

        machine_definition = definitions[0]

        # get variant container for the global stack
        global_variant_container = application.empty_variant_container
        global_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.BUILD_PLATE)
        if global_variant_node:
            global_variant_container = global_variant_node.getContainer()
        if not global_variant_container:
            global_variant_container = application.empty_variant_container

        # get variant container for extruders
        extruder_variant_container = application.empty_variant_container
        extruder_variant_node = variant_manager.getDefaultVariantNode(machine_definition, VariantType.NOZZLE)
        extruder_variant_name = None
        if extruder_variant_node:
            extruder_variant_container = extruder_variant_node.getContainer()
            if not extruder_variant_container:
                extruder_variant_container = application.empty_variant_container
            extruder_variant_name = extruder_variant_container.getName()

        generated_name = registry.createUniqueName("machine", "", name, machine_definition.getName())
        # Make sure the new name does not collide with any definition or (quality) profile
        # createUniqueName() only looks at other stacks, but not at definitions or quality profiles
        # Note that we don't go for uniqueName() immediately because that function matches with ignore_case set to true
        if registry.findContainersMetadata(id = generated_name):
            generated_name = registry.uniqueName(generated_name)

        new_global_stack = cls.createGlobalStack(
            new_stack_id = generated_name,
            definition = machine_definition,
            variant_container = global_variant_container,
            material_container = application.empty_material_container,
            quality_container = application.empty_quality_container,
        )
        new_global_stack.setName(generated_name)

        # Create ExtruderStacks
        extruder_dict = machine_definition.getMetaDataEntry("machine_extruder_trains")

        for position, extruder_definition_id in extruder_dict.items():
            # Sanity check: make sure that the positions in the extruder definitions are same as in the machine
            # definition
            extruder_definition = registry.findDefinitionContainers(id = extruder_definition_id)[0]
            position_in_extruder_def = extruder_definition.getMetaDataEntry("position")
            if position_in_extruder_def != position:
                ConfigurationErrorMessage.getInstance().addFaultyContainers(extruder_definition_id)
                return None #Don't return any container stack then, not the rest of the extruders either.

            # get material container for extruders
            material_container = application.empty_material_container
            material_node = material_manager.getDefaultMaterial(new_global_stack, position, extruder_variant_name, extruder_definition = extruder_definition)
            if material_node and material_node.getContainer():
                material_container = material_node.getContainer()

            new_extruder_id = registry.uniqueName(extruder_definition_id)
            new_extruder = cls.createExtruderStack(
                new_extruder_id,
                extruder_definition = extruder_definition,
                machine_definition_id = definition_id,
                position = position,
                variant_container = extruder_variant_container,
                material_container = material_container,
                quality_container = application.empty_quality_container
            )
            new_extruder.setNextStack(new_global_stack)
            new_global_stack.addExtruder(new_extruder)

        for new_extruder in new_global_stack.extruders.values(): #Only register the extruders if we're sure that all of them are correct.
            registry.addContainer(new_extruder)

        preferred_quality_type = machine_definition.getMetaDataEntry("preferred_quality_type")
        quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
        if not quality_group_dict:
            # There is no available quality group, set all quality containers to empty.
            new_global_stack.quality = application.empty_quality_container
            for extruder_stack in new_global_stack.extruders.values():
                extruder_stack.quality = application.empty_quality_container
        else:
            # Set the quality containers to the preferred quality type if available, otherwise use the first quality
            # type that's available.
            if preferred_quality_type not in quality_group_dict:
                Logger.log("w", "The preferred quality {quality_type} doesn't exist for this set-up. Choosing a random one.".format(quality_type = preferred_quality_type))
                preferred_quality_type = next(iter(quality_group_dict))
            quality_group = quality_group_dict.get(preferred_quality_type)

            new_global_stack.quality = quality_group.node_for_global.getContainer()
            if not new_global_stack.quality:
                new_global_stack.quality = application.empty_quality_container
            for position, extruder_stack in new_global_stack.extruders.items():
                if position in quality_group.nodes_for_extruders and quality_group.nodes_for_extruders[position].getContainer():
                    extruder_stack.quality = quality_group.nodes_for_extruders[position].getContainer()
                else:
                    extruder_stack.quality = application.empty_quality_container

        # Register the global stack after the extruder stacks are created. This prevents the registry from adding another
        # extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).
        registry.addContainer(new_global_stack)

        return new_global_stack
Example #58
0
    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)
Example #59
0
    def __init__(self) -> None:
        super().__init__()
        # Find out where the engine is located, and how it is called.
        # This depends on how Cura is packaged and which OS we are running on.
        executable_name = "CuraEngine"
        if Platform.isWindows():
            executable_name += ".exe"
        default_engine_location = executable_name

        search_path = [
            os.path.abspath(os.path.dirname(sys.executable)),
            os.path.abspath(
                os.path.join(os.path.dirname(sys.executable), "bin")),
            os.path.abspath(os.path.join(os.path.dirname(sys.executable),
                                         "..")),
            os.path.join(CuraApplication.getInstallPrefix(), "bin"),
            os.path.dirname(os.path.abspath(sys.executable)),
        ]

        for path in search_path:
            engine_path = os.path.join(path, executable_name)
            if os.path.isfile(engine_path):
                default_engine_location = engine_path
                break

        if Platform.isLinux() and not default_engine_location:
            if not os.getenv("PATH"):
                raise OSError(
                    "There is something wrong with your Linux installation.")
            for pathdir in cast(str, os.getenv("PATH")).split(os.pathsep):
                execpath = os.path.join(pathdir, executable_name)
                if os.path.exists(execpath):
                    default_engine_location = execpath
                    break

        self._application = CuraApplication.getInstance(
        )  #type: CuraApplication
        self._multi_build_plate_model = None  #type: Optional[MultiBuildPlateModel]
        self._machine_error_checker = None  #type: Optional[MachineErrorChecker]

        if not default_engine_location:
            raise EnvironmentError("Could not find CuraEngine")

        Logger.log("i", "Found CuraEngine at: %s", default_engine_location)

        default_engine_location = os.path.abspath(default_engine_location)
        self._application.getPreferences().addPreference(
            "backend/location", default_engine_location)

        # Workaround to disable layer view processing if layer view is not active.
        self._layer_view_active = False  #type: bool
        self._onActiveViewChanged()

        self._stored_layer_data = []  # type: List[Arcus.PythonMessage]
        self._stored_optimized_layer_data = {
        }  # type: Dict[int, List[Arcus.PythonMessage]] # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob

        self._scene = self._application.getController().getScene(
        )  #type: Scene
        self._scene.sceneChanged.connect(self._onSceneChanged)

        # Triggers for auto-slicing. Auto-slicing is triggered as follows:
        #  - auto-slicing is started with a timer
        #  - whenever there is a value change, we start the timer
        #  - sometimes an error check can get scheduled for a value change, in that case, we ONLY want to start the
        #    auto-slicing timer when that error check is finished
        # If there is an error check, stop the auto-slicing timer, and only wait for the error check to be finished
        # to start the auto-slicing timer again.
        #
        self._global_container_stack = None  #type: Optional[ContainerStack]

        # Listeners for receiving messages from the back-end.
        self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
        self._message_handlers[
            "cura.proto.LayerOptimized"] = self._onOptimizedLayerMessage
        self._message_handlers["cura.proto.Progress"] = self._onProgressMessage
        self._message_handlers[
            "cura.proto.GCodeLayer"] = self._onGCodeLayerMessage
        self._message_handlers[
            "cura.proto.GCodePrefix"] = self._onGCodePrefixMessage
        self._message_handlers[
            "cura.proto.PrintTimeMaterialEstimates"] = self._onPrintTimeMaterialEstimates
        self._message_handlers[
            "cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage

        self._start_slice_job = None  #type: Optional[StartSliceJob]
        self._start_slice_job_build_plate = None  #type: Optional[int]
        self._slicing = False  #type: bool # Are we currently slicing?
        self._restart = False  #type: bool # Back-end is currently restarting?
        self._tool_active = False  #type: bool # If a tool is active, some tasks do not have to do anything
        self._always_restart = True  #type: bool # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
        self._process_layers_job = None  #type: Optional[ProcessSlicedLayersJob] # The currently active job to process layers, or None if it is not processing layers.
        self._build_plates_to_be_sliced = [
        ]  #type: List[int] # what needs slicing?
        self._engine_is_fresh = True  #type: bool # Is the newly started engine used before or not?

        self._backend_log_max_lines = 20000  #type: int # Maximum number of lines to buffer
        self._error_message = None  #type: Optional[Message] # Pop-up message that shows errors.
        self._last_num_objects = defaultdict(
            int
        )  #type: Dict[int, int] # Count number of objects to see if there is something changed
        self._postponed_scene_change_sources = [
        ]  #type: List[SceneNode] # scene change is postponed (by a tool)

        self._slice_start_time = None  #type: Optional[float]
        self._is_disabled = False  #type: bool

        self._application.getPreferences().addPreference(
            "general/auto_slice", False)

        self._use_timer = False  #type: bool
        # When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired.
        # This timer will group them up, and only slice for the last setting changed signal.
        # TODO: Properly group propertyChanged signals by whether they are triggered by the same user interaction.
        self._change_timer = QTimer()  #type: QTimer
        self._change_timer.setSingleShot(True)
        self._change_timer.setInterval(500)
        self.determineAutoSlicing()
        self._application.getPreferences().preferenceChanged.connect(
            self._onPreferencesChanged)

        self._application.initializationFinished.connect(self.initialize)
 def _onEngineCreated(self) -> None:
     CuraApplication.getInstance().getMachineManager(
     ).outputDevicesChanged.connect(self._onOutputDevicesChanged)