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 _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)
Example #3
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
    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 #5
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 #6
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 #7
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 #8
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 #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 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
                                          )
Example #13
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)
    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 #15
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()
 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)
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 _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 #20
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 #21
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 #22
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 #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 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 #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 _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 #27
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
    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)
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)
Example #30
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 #31
0
 def _storeBackupDate(self) -> None:
     backup_date = datetime.now().strftime(self.DATE_FORMAT)
     preferences = CuraApplication.getInstance().getPreferences()
     preferences.setValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY,
                          backup_date)
Example #32
0
    def event(self, event):
        if event.type == Event.ViewActivateEvent:
            # FIX: on Max OS X, somehow QOpenGLContext.currentContext() can become None during View switching.
            # This can happen when you do the following steps:
            #   1. Start Cura
            #   2. Load a model
            #   3. Switch to Custom mode
            #   4. Select the model and click on the per-object tool icon
            #   5. Switch view to Layer view or X-Ray
            #   6. Cura will very likely crash
            # It seems to be a timing issue that the currentContext can somehow be empty, but I have no clue why.
            # This fix tries to reschedule the view changing event call on the Qt thread again if the current OpenGL
            # context is None.
            if Platform.isOSX():
                if QOpenGLContext.currentContext() is None:
                    Logger.log(
                        "d",
                        "current context of OpenGL is empty on Mac OS X, will try to create shaders later"
                    )
                    CuraApplication.getInstance().callLater(
                        lambda e=event: self.event(e))
                    return

            if not self._xray_pass:
                # Currently the RenderPass constructor requires a size > 0
                # This should be fixed in RenderPass's constructor.
                self._xray_pass = XRayPass.XRayPass(1, 1)

            self.getRenderer().addRenderPass(self._xray_pass)

            if not self._xray_composite_shader:
                self._xray_composite_shader = OpenGL.getInstance(
                ).createShaderProgram(
                    os.path.join(
                        PluginRegistry.getInstance().getPluginPath("XRayView"),
                        "xray_composite.shader"))
                theme = Application.getInstance().getTheme()
                self._xray_composite_shader.setUniformValue(
                    "u_background_color",
                    Color(*theme.getColor("viewport_background").getRgb()))
                self._xray_composite_shader.setUniformValue(
                    "u_error_color",
                    Color(*theme.getColor("xray_error").getRgb()))
                self._xray_composite_shader.setUniformValue(
                    "u_outline_color",
                    Color(*theme.getColor("model_selection_outline").getRgb()))

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

            self._old_layer_bindings = self._composite_pass.getLayerBindings()
            self._composite_pass.setLayerBindings(
                ["default", "selection", "xray"])
            self._old_composite_shader = self._composite_pass.getCompositeShader(
            )
            self._composite_pass.setCompositeShader(
                self._xray_composite_shader)

        if event.type == Event.ViewDeactivateEvent:
            self.getRenderer().removeRenderPass(self._xray_pass)
            self._composite_pass.setLayerBindings(self._old_layer_bindings)
            self._composite_pass.setCompositeShader(self._old_composite_shader)
 def __init__(self) -> None:
     self._cura_api = CuraApplication.getInstance().getCuraAPI()
     self._json_cloud_scope = JsonDecoratorScope(
         UltimakerCloudScope(CuraApplication.getInstance()))
Example #34
0
 def __init__(self):
     self._application = CuraApplication.getInstance()
Example #35
0
 def _checkIfClusterHost(self):
     if len(self._printers) < 1 and self.isConnected():
         NotClusterHostMessage(self).show()
         self.close()
         CuraApplication.getInstance().getOutputDeviceManager().removeOutputDevice(self.key)
Example #36
0
    def __init__(self, parent=None) -> None:
        QObject.__init__(self, parent)
        Extension.__init__(self)

        #Inzialize varables
        self.userText = ""
        self._continueDialog = None

        # set the preferences to store the default value
        self._preferences = CuraApplication.getInstance().getPreferences()
        self._preferences.addPreference("calibrationshapes/size", 20)

        # convert as float to avoid further issue
        self._size = float(
            self._preferences.getValue("calibrationshapes/size"))

        # Suggested solution from fieldOfView . Unfortunatly it doesn't works
        # https://github.com/5axes/Calibration-Shapes/issues/1
        # Cura should be able to find the scripts from inside the plugin folder if the scripts are into a folder named resources
        Resources.addSearchPath(
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         "resources"))

        self._controller = CuraApplication.getInstance().getController()
        self._message = None

        self.setMenuName(catalog.i18nc("@item:inmenu", "Part for calibration"))
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a cube"),
                         self.addCube)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a cylinder"),
                         self.addCylinder)
        # self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a sphere"), self.addSphere)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a tube"),
                         self.addTube)
        self.addMenuItem("", lambda: None)
        self.addMenuItem(
            catalog.i18nc("@item:inmenu", "Add a Calibration Cube"),
            self.addCalibrationCube)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a PLA TempTower"),
                         self.addPLATempTower)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a PETG TempTower"),
                         self.addPETGTempTower)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add an ABS TempTower"),
                         self.addABSTempTower)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Retract Test"),
                         self.addRetractTest)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Retract Tower"),
                         self.addRetractTower)
        self.addMenuItem(
            catalog.i18nc("@item:inmenu", "Add a Junction Deviation Tower"),
            self.addJunctionDeviationTower)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Bridge Test"),
                         self.addBridgeTest)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Thin Wall Test"),
                         self.addThinWall)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add an Overhang Test"),
                         self.addOverhangTest)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Flow Test"),
                         self.addFlowTest)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Tolerance Test"),
                         self.addTolerance)
        self.addMenuItem(
            catalog.i18nc("@item:inmenu", "Add a MultiCube Calibration"),
            self.addMultiCube)
        self.addMenuItem(
            catalog.i18nc("@item:inmenu", "Add a Bed Level Calibration"),
            self.addBedLevelCalibration)
        self.addMenuItem(" ", lambda: None)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Copy Scripts"),
                         self.copyScript)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Define default size"),
                         self.defaultSize)
        self.addMenuItem("  ", lambda: None)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Help"), self.gotoHelp)

        #Inzialize varables
        self.userText = ""
        self._continueDialog = None
Example #37
0
 def _getLastBackupDate(self) -> "datetime":
     preferences = CuraApplication.getInstance().getPreferences()
     last_backup_date = preferences.getValue(
         Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY)
     return datetime.strptime(last_backup_date, self.DATE_FORMAT)
Example #38
0
    def __init__(self,
                 serial_port: str,
                 baud_rate: Optional[int] = None) -> None:
        super().__init__(serial_port)
        self.setName(catalog.i18nc("@item:inmenu", "USB printing"))
        self.setShortDescription(
            catalog.i18nc("@action:button Preceded by 'Ready to'.",
                          "Print via USB"))
        self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB"))
        self.setIconName("print")

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

        self._timeout = 3

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

        self._use_auto_detect = True

        self._baud_rate = baud_rate

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

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

        self._update_firmware_thread = Thread(target=self._updateFirmware,
                                              daemon=True)

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

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

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

        self._accepts_commands = True

        self._paused = False

        self._firmware_view = None
        self._firmware_location = None
        self._firmware_progress = 0
        self._firmware_update_state = FirmwareUpdateState.idle

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

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

        CuraApplication.getInstance().getOnExitCallbackManager().addCallback(
            self._checkActivePrintingUponAppExit)
Example #39
0
 def toggleAutoBackup(self, enabled: bool) -> None:
     preferences = CuraApplication.getInstance().getPreferences()
     preferences.setValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY,
                          enabled)
Example #40
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 = []
        remote_clusters_added = False
        host_guid_map = {machine.getMetaDataEntry(self.META_HOST_GUID): device_cluster_id
                         for device_cluster_id, machine in self._um_cloud_printers.items()
                         if machine.getMetaDataEntry(self.META_HOST_GUID)}
        machine_manager = CuraApplication.getInstance().getMachineManager()

        for cluster_data in clusters:
            device = CloudOutputDevice(self._api, cluster_data)
            # If the machine already existed before, it will be present in the host_guid_map
            if cluster_data.host_guid in host_guid_map:
                machine = machine_manager.getMachine(device.printerType, {self.META_HOST_GUID: cluster_data.host_guid})
                if machine and machine.getMetaDataEntry(self.META_CLUSTER_ID) != device.key:
                    # If the retrieved device has a different cluster_id than the existing machine, bring the existing
                    # machine up-to-date.
                    self._updateOutdatedMachine(outdated_machine = machine, new_cloud_output_device = device)

            # Create a machine if we don't already have it. Do not make it the active machine.
            # We only need to add it if it wasn't already added by "local" network or by cloud.
            if machine_manager.getMachine(device.printerType, {self.META_CLUSTER_ID: device.key}) is None \
                    and machine_manager.getMachine(device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None:  # The host name is part of the network key.
                new_devices.append(device)
            elif device.getId() not in self._remote_clusters:
                self._remote_clusters[device.getId()] = device
                remote_clusters_added = True
            # If a printer that was removed from the account is re-added, change its metadata to mark it not removed
            # from the account
            elif not parseBool(self._um_cloud_printers[device.key].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")):
                self._um_cloud_printers[device.key].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True)

        # Inform the Cloud printers model about new devices.
        new_devices_list_of_dicts = [{
                "key": d.getId(),
                "name": d.name,
                "machine_type": d.printerTypeName,
                "firmware_version": d.firmwareVersion} for d in new_devices]
        discovered_cloud_printers_model = CuraApplication.getInstance().getDiscoveredCloudPrintersModel()
        discovered_cloud_printers_model.addDiscoveredCloudPrinters(new_devices_list_of_dicts)

        if not new_devices:
            if remote_clusters_added:
                self._connectToActiveMachine()
            return

        # Sort new_devices on online status first, alphabetical second.
        # Since the first device might be activated in case there is no active printer yet,
        # it would be nice to prioritize online devices
        online_cluster_names = {c.friendly_name.lower() for c in clusters if c.is_online and not c.friendly_name is None}
        new_devices.sort(key = lambda x: ("a{}" if x.name.lower() in online_cluster_names else "b{}").format(x.name.lower()))

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

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

            # If there is no active machine, activate the first available cloud printer
            activate = not CuraApplication.getInstance().getMachineManager().activeMachine
            self._createMachineFromDiscoveredDevice(device.getId(), activate = activate)

        message.setProgress(None)

        max_disp_devices = 3
        if len(new_devices) > max_disp_devices:
            num_hidden = len(new_devices) - max_disp_devices
            device_name_list = ["<li>{} ({})</li>".format(device.name, device.printerTypeName) for device in new_devices[0:max_disp_devices]]
            device_name_list.append("<li>" + self.i18n_catalog.i18ncp("info:{0} gets replaced by a number of printers", "... and {0} other", "... and {0} others", num_hidden) + "</li>")
            device_names = "".join(device_name_list)
        else:
            device_names = "".join(["<li>{} ({})</li>".format(device.name, device.printerTypeName) for device in new_devices])

        message_text = self.i18n_catalog.i18nc("info:status", "Printers added from Digital Factory:") + "<ul>" + device_names + "</ul>"
        message.setText(message_text)
Example #41
0
    def createMachine(cls, name: str,
                      definition_id: str) -> Optional[GlobalStack]:
        from cura.CuraApplication import CuraApplication
        application = CuraApplication.getInstance()
        variant_manager = application.getVariantManager()
        quality_manager = application.getQualityManager()
        registry = application.getContainerRegistry()

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

        machine_definition = definitions[0]

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

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

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

        # Create ExtruderStacks
        extruder_dict = machine_definition.getMetaDataEntry(
            "machine_extruder_trains")
        for position in extruder_dict:
            cls.createExtruderStackWithDefaultSetup(new_global_stack, position)

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

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

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

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

        return new_global_stack
Example #42
0
    faulthandler.enable(file = sys.stderr, all_threads = True)
elif sys.stdout and not sys.stdout.closed:
    faulthandler.enable(file = sys.stdout, all_threads = True)

from cura.CuraApplication import CuraApplication


# WORKAROUND: CURA-6739
# The CTM file loading module in Trimesh requires the OpenCTM library to be dynamically loaded. It uses
# ctypes.util.find_library() to find libopenctm.dylib, but this doesn't seem to look in the ".app" application folder
# on Mac OS X. Adding the search path to environment variables such as DYLD_LIBRARY_PATH and DYLD_FALLBACK_LIBRARY_PATH
# makes it work. The workaround here uses DYLD_FALLBACK_LIBRARY_PATH.
if Platform.isOSX() and getattr(sys, "frozen", False):
    old_env = os.environ.get("DYLD_FALLBACK_LIBRARY_PATH", "")
    # This is where libopenctm.so is in the .app folder.
    search_path = os.path.join(CuraApplication.getInstallPrefix(), "MacOS")
    path_list = old_env.split(":")
    if search_path not in path_list:
        path_list.append(search_path)
    os.environ["DYLD_FALLBACK_LIBRARY_PATH"] = ":".join(path_list)
    import trimesh.exchange.load
    os.environ["DYLD_FALLBACK_LIBRARY_PATH"] = old_env

# WORKAROUND: CURA-6739
# Similar CTM file loading fix for Linux, but NOTE THAT this doesn't work directly with Python 3.5.7. There's a fix
# for ctypes.util.find_library() in Python 3.6 and 3.7. That fix makes sure that find_library() will check
# LD_LIBRARY_PATH. With Python 3.5, that fix needs to be backported to make this workaround work.
if Platform.isLinux() and getattr(sys, "frozen", False):
    old_env = os.environ.get("LD_LIBRARY_PATH", "")
    # This is where libopenctm.so is in the AppImage.
    search_path = os.path.join(CuraApplication.getInstallPrefix(), "bin")
Example #43
0
    def _devicesRemovedFromAccount(self, removed_device_ids: Set[str]) -> None:
        """
        Removes the CloudOutputDevice from the received device ids and marks the specific printers as "removed from
        account". In addition, it generates a message to inform the user about the printers that are no longer linked to
        his/her account. The message is not generated if all the printers have been previously reported as not linked
        to the account.

        :param removed_device_ids: Set of device ids, whose CloudOutputDevice needs to be removed
        :return: None
        """

        if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn:
            return

        # Do not report device ids which have been previously marked as non-linked to the account
        ignored_device_ids = set()
        for device_id in removed_device_ids:
            if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")):
                ignored_device_ids.add(device_id)
        # Keep the reported_device_ids list in a class variable, so that the message button actions can access it and
        # take the necessary steps to fulfill their purpose.
        self.reported_device_ids = removed_device_ids - ignored_device_ids
        if not self.reported_device_ids:
            return

        # Generate message
        self._removed_printers_message = Message(
                title = self.i18n_catalog.i18ncp(
                        "info:status",
                        "A cloud connection is not available for a printer",
                        "A cloud connection is not available for some printers",
                        len(self.reported_device_ids)
                ),
            message_type = Message.MessageType.WARNING
        )
        device_names = "".join(["<li>{} ({})</li>".format(self._um_cloud_printers[device].name, self._um_cloud_printers[device].definition.name) for device in self.reported_device_ids])
        message_text = self.i18n_catalog.i18ncp(
                "info:status",
                "This printer is not linked to the Digital Factory:",
                "These printers are not linked to the Digital Factory:",
                len(self.reported_device_ids)
        )
        message_text += "<br/><ul>{}</ul><br/>".format(device_names)
        digital_factory_string = self.i18n_catalog.i18nc("info:name", "Ultimaker Digital Factory")

        message_text += self.i18n_catalog.i18nc(
                "info:status",
                "To establish a connection, please visit the {website_link}".format(website_link = "<a href='https://digitalfactory.ultimaker.com?utm_source=cura&utm_medium=software&utm_campaign=change-account-connect-printer'>{}</a>.".format(digital_factory_string))
        )
        self._removed_printers_message.setText(message_text)
        self._removed_printers_message.addAction("keep_printer_configurations_action",
                                                 name = self.i18n_catalog.i18nc("@action:button", "Keep printer configurations"),
                                                 icon = "",
                                                 description = "Keep cloud printers in Ultimaker Cura when not connected to your account.",
                                                 button_align = Message.ActionButtonAlignment.ALIGN_RIGHT)
        self._removed_printers_message.addAction("remove_printers_action",
                                                 name = self.i18n_catalog.i18nc("@action:button", "Remove printers"),
                                                 icon = "",
                                                 description = "Remove cloud printer(s) which aren't linked to your account.",
                                                 button_style = Message.ActionButtonStyle.SECONDARY,
                                                 button_align = Message.ActionButtonAlignment.ALIGN_LEFT)
        self._removed_printers_message.actionTriggered.connect(self._onRemovedPrintersMessageActionTriggered)

        output_device_manager = CuraApplication.getInstance().getOutputDeviceManager()

        # Remove the output device from the printers
        for device_id in removed_device_ids:
            device = self._um_cloud_printers.get(device_id, None)  # type: Optional[GlobalStack]
            if not device:
                continue
            if device_id in output_device_manager.getOutputDeviceIds():
                output_device_manager.removeOutputDevice(device_id)
            if device_id in self._remote_clusters:
                del self._remote_clusters[device_id]

            # Update the printer's metadata to mark it as not linked to the account
            device.setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, False)

        self._removed_printers_message.show()
Example #44
0
 def autoBackupEnabled(self) -> bool:
     preferences = CuraApplication.getInstance().getPreferences()
     return bool(
         preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY))
Example #45
0
 def _onConfirmExitDialogResult(self, result: bool) -> None:
     if result:
         application = CuraApplication.getInstance()
         application.triggerNextExitCheck()
Example #46
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)
        self._sending_job.send(None)  #Start the generator.

        if len(self._printers) > 1:  #We need to ask the user.
            self._spawnPrinterSelectionDialog()
            is_job_sent = True
        else:  #Just immediately continue.
            self._sending_job.send("")  #No specifically selected printer.
            is_job_sent = self._sending_job.send(None)
    def __init__(self, device_id, address, properties, parent=None) -> None:
        super().__init__(device_id=device_id,
                         address=address,
                         properties=properties,
                         connection_type=ConnectionType.NetworkConnection,
                         parent=parent)
        self._api_prefix = "/cluster-api/v1/"

        self._application = CuraApplication.getInstance()

        self._number_of_extruders = 2

        self._dummy_lambdas = (
            "", {}, io.BytesIO()
        )  # type: Tuple[Optional[str], Dict[str, Union[str, int, bool]], Union[io.StringIO, io.BytesIO]]

        self._print_jobs = []  # type: List[UM3PrintJobOutputModel]
        self._received_print_jobs = False  # type: bool

        if PluginRegistry.getInstance() is not None:
            self._monitor_view_qml_path = os.path.join(
                PluginRegistry.getInstance().getPluginPath(
                    "UM3NetworkPrinting"), "resources", "qml",
                "MonitorStage.qml")

        # Trigger the printersChanged signal when the private signal is triggered
        self.printersChanged.connect(self._clusterPrintersChanged)

        self._accepts_commands = True  # type: bool

        # Cluster does not have authentication, so default to authenticated
        self._authentication_state = AuthState.Authenticated

        self._error_message = None  # type: Optional[Message]
        self._write_job_progress_message = None  # type: Optional[Message]
        self._progress_message = None  # type: Optional[Message]

        self._active_printer = None  # type: Optional[PrinterOutputModel]

        self._printer_selection_dialog = None  # type: QObject

        self.setPriority(
            3
        )  # Make sure the output device gets selected above local file output
        self.setName(self._id)
        self.setShortDescription(
            i18n_catalog.i18nc("@action:button Preceded by 'Ready to'.",
                               "Print over network"))
        self.setDescription(
            i18n_catalog.i18nc("@properties:tooltip", "Print over network"))

        self.setConnectionText(
            i18n_catalog.i18nc("@info:status", "Connected over the network"))

        self._printer_uuid_to_unique_name_mapping = {}  # type: Dict[str, str]

        self._finished_jobs = []  # type: List[UM3PrintJobOutputModel]

        self._cluster_size = int(properties.get(b"cluster_size",
                                                0))  # type: int

        self._latest_reply_handler = None  # type: Optional[QNetworkReply]
        self._sending_job = None

        self._active_camera_url = QUrl()  # type: QUrl
Example #48
0
class CalibrationShapes(QObject, Extension):
    #Create an api
    from cura.CuraApplication import CuraApplication
    api = CuraApplication.getInstance().getCuraAPI()

    # The QT signal, which signals an update for user information text
    userInfoTextChanged = pyqtSignal()
    userSizeChanged = pyqtSignal()

    def __init__(self, parent=None) -> None:
        QObject.__init__(self, parent)
        Extension.__init__(self)

        #Inzialize varables
        self.userText = ""
        self._continueDialog = None

        # set the preferences to store the default value
        self._preferences = CuraApplication.getInstance().getPreferences()
        self._preferences.addPreference("calibrationshapes/size", 20)

        # convert as float to avoid further issue
        self._size = float(
            self._preferences.getValue("calibrationshapes/size"))

        # Suggested solution from fieldOfView . Unfortunatly it doesn't works
        # https://github.com/5axes/Calibration-Shapes/issues/1
        # Cura should be able to find the scripts from inside the plugin folder if the scripts are into a folder named resources
        Resources.addSearchPath(
            os.path.join(os.path.dirname(os.path.abspath(__file__)),
                         "resources"))

        self._controller = CuraApplication.getInstance().getController()
        self._message = None

        self.setMenuName(catalog.i18nc("@item:inmenu", "Part for calibration"))
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a cube"),
                         self.addCube)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a cylinder"),
                         self.addCylinder)
        # self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a sphere"), self.addSphere)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a tube"),
                         self.addTube)
        self.addMenuItem("", lambda: None)
        self.addMenuItem(
            catalog.i18nc("@item:inmenu", "Add a Calibration Cube"),
            self.addCalibrationCube)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a PLA TempTower"),
                         self.addPLATempTower)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a PETG TempTower"),
                         self.addPETGTempTower)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add an ABS TempTower"),
                         self.addABSTempTower)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Retract Test"),
                         self.addRetractTest)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Retract Tower"),
                         self.addRetractTower)
        self.addMenuItem(
            catalog.i18nc("@item:inmenu", "Add a Junction Deviation Tower"),
            self.addJunctionDeviationTower)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Bridge Test"),
                         self.addBridgeTest)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Thin Wall Test"),
                         self.addThinWall)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add an Overhang Test"),
                         self.addOverhangTest)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Flow Test"),
                         self.addFlowTest)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Add a Tolerance Test"),
                         self.addTolerance)
        self.addMenuItem(
            catalog.i18nc("@item:inmenu", "Add a MultiCube Calibration"),
            self.addMultiCube)
        self.addMenuItem(
            catalog.i18nc("@item:inmenu", "Add a Bed Level Calibration"),
            self.addBedLevelCalibration)
        self.addMenuItem(" ", lambda: None)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Copy Scripts"),
                         self.copyScript)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Define default size"),
                         self.defaultSize)
        self.addMenuItem("  ", lambda: None)
        self.addMenuItem(catalog.i18nc("@item:inmenu", "Help"), self.gotoHelp)

        #Inzialize varables
        self.userText = ""
        self._continueDialog = None

    # Define the default value pour the standard element
    def defaultSize(self) -> None:

        if self._continueDialog is None:
            self._continueDialog = self._createDialogue()
        self._continueDialog.show()
        #self.userSizeChanged.emit()

    #====User Input=====================================================================================================
    @pyqtProperty(str, notify=userSizeChanged)
    def sizeInput(self):
        return str(self._size)

    #The QT property, which is computed on demand from our userInfoText when the appropriate signal is emitted
    @pyqtProperty(str, notify=userInfoTextChanged)
    def userInfoText(self):
        return self.userText

    #This method builds the dialog from the qml file and registers this class
    #as the manager variable
    def _createDialogue(self):
        qml_file_path = os.path.join(
            PluginRegistry.getInstance().getPluginPath(self.getPluginId()),
            "CalibrationShapes.qml")
        component_with_context = Application.getInstance().createQmlComponent(
            qml_file_path, {"manager": self})
        return component_with_context

    def getSize(self) -> float:

        return self._size

    # is called when a key gets released in the size inputField (twice for some reason)
    @pyqtSlot(str)
    def sizeEntered(self, text):
        # Is the textfield empty ? Don't show a message then
        if text == "":
            #self.writeToLog("size-Textfield: Empty")
            self.userMessage("", "ok")
            return

        #Convert commas to points
        text = text.replace(",", ".")

        #self.writeToLog("Size-Textfield: read value "+text)

        #Is the entered Text a number?
        try:
            float(text)
        except ValueError:
            self.userMessage("Entered size invalid: " + text, "wrong")
            return
        self._size = float(text)

        #Check if positive
        if self._size <= 0:
            self.userMessage("Size value must be positive !", "wrong")
            self._size = 20
            return

        self.writeToLog("Set calibrationshapes/size printFromHeight to : " +
                        text)
        self._preferences.setValue("calibrationshapes/size", self._size)

        #clear the message Field
        self.userMessage("", "ok")

    #===== Text Output ===================================================================================================
    #writes the message to the log, includes timestamp, length is fixed
    def writeToLog(self, str):
        Logger.log("d", "Source calibration shapes = %s", str)

    #Sends an user message to the Info Textfield, color depends on status (prioritized feedback)
    # Red wrong for Errors and Warnings
    # Grey for details and messages that aren't interesting for advanced users
    def userMessage(self, message, status):
        if status is "wrong":
            #Red
            self.userText = "<font color='#a00000'>" + message + "</font>"
        else:
            # Grey
            if status is "ok":
                self.userText = "<font color='#9fa4b0'>" + message + "</font>"
            else:
                self.writeToLog("Error: Invalid status: " + status)
                return
        #self.writeToLog("User Message: "+message)
        self.userInfoTextChanged.emit()

    # Copy the scripts to the right directory ( Temporary solution)
    def copyScript(self) -> None:
        File_List = ['RetractTower.py', 'SpeedTower.py', 'TempFanTower.py']

        plugPath = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                "resources")
        # Logger.log("d", "plugPath= %s", plugPath)

        stringMatch = re.split("plugins", plugPath)
        destPath = stringMatch[0] + "scripts"
        nbfile = 0
        # LCopy the script
        for fl in File_List:
            script_definition_path = os.path.join(plugPath, fl)
            dest_definition_path = os.path.join(destPath, fl)
            # self.writeToLog("Dest_definition_path= "+dest_definition_path)
            if os.path.isfile(dest_definition_path) == False:
                copyfile(script_definition_path, dest_definition_path)
                nbfile += 1

        txt_Message = str(nbfile) + " scripts copied in "
        txt_Message = txt_Message + os.path.join(destPath, "scripts")
        if nbfile > 0:
            txt_Message = txt_Message + "\nYou must now restart Cura to see the scripts in the postprocessing script list"

        self._message = Message(catalog.i18nc("@info:status", txt_Message),
                                title=catalog.i18nc("@title",
                                                    "Calibration Shapes"))
        self._message.show()

    def gotoHelp(self) -> None:
        QDesktopServices.openUrl(
            QUrl("https://github.com/5axes/Calibration-Shapes/wiki"))

    def addBedLevelCalibration(self) -> None:
        # Get the build plate Size
        machine_manager = CuraApplication.getInstance().getMachineManager()
        stack = CuraApplication.getInstance().getGlobalContainerStack()

        global_stack = machine_manager.activeMachine
        m_w = global_stack.getProperty("machine_width", "value")
        m_d = global_stack.getProperty("machine_depth", "value")
        if (m_w / m_d) > 1.15 or (m_d / m_w) > 1.15:
            factor_w = round((m_w / 100), 1)
            factor_d = round((m_d / 100), 1)
        else:
            factor_w = int(m_w / 100)
            factor_d = int(m_d / 100)

        Logger.log("d", "factor_w= %.1f", factor_w)
        Logger.log("d", "factor_d= %.1f", factor_d)

        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "ParametricBedLevel.stl")
        mesh = trimesh.load(model_definition_path)
        origin = [0, 0, 0]
        DirX = [1, 0, 0]
        # DirY = [0, 1, 0]
        DirZ = [0, 0, 1]
        mesh.apply_transform(
            trimesh.transformations.scale_matrix(factor_w, origin, DirX))
        mesh.apply_transform(
            trimesh.transformations.scale_matrix(factor_d, origin, DirZ))
        self._addShape(self._toMeshData(mesh))

    def addCalibrationCube(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "CalibrationCube.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addMultiCube(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "MultiCube.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addJunctionDeviationTower(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "JunctionDeviationTower.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addPLATempTower(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "TempTowerPLA.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addPETGTempTower(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "TempTowerPETG.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addABSTempTower(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "TempTowerABS.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addRetractTest(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "RetractTest.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addRetractTower(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "RetractTower.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addBridgeTest(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "BridgeTest.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addThinWall(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "ThinWall.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addOverhangTest(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "Overhang.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addFlowTest(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "FlowTest.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addTolerance(self) -> None:
        model_definition_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), "models",
            "Tolerance.stl")
        self._addShape(self._toMeshData(trimesh.load(model_definition_path)))

    def addCube(self) -> None:
        Tz = trimesh.transformations.translation_matrix(
            [0, self._size * 0.5, 0])
        self._addShape(
            self._toMeshData(
                trimesh.creation.box(
                    extents=[self._size, self._size,
                             self._size], transform=Tz)))

    def addCylinder(self) -> None:
        Rx = trimesh.transformations.rotation_matrix(math.radians(90),
                                                     [1, 0, 0])
        mesh = trimesh.creation.cylinder(radius=self._size / 2,
                                         height=self._size,
                                         sections=90,
                                         transform=Rx)
        mesh.apply_transform(
            trimesh.transformations.translation_matrix(
                [0, self._size * 0.5, 0]))
        self._addShape(self._toMeshData(mesh))

    def addTube(self) -> None:
        #origin, xaxis, yaxis, zaxis = [0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1]
        # S = trimesh.transformations.scale_matrix(20, origin)
        xaxis = [1, 0, 0]
        Rx = trimesh.transformations.rotation_matrix(math.radians(90), xaxis)
        mesh = trimesh.creation.annulus(r_min=self._size / 4,
                                        r_max=self._size / 2,
                                        height=self._size,
                                        sections=90,
                                        transform=Rx)
        mesh.apply_transform(
            trimesh.transformations.translation_matrix(
                [0, self._size * 0.5, 0]))
        self._addShape(self._toMeshData(mesh))

    # Sphere are not very usefull but I leave the code for the moment
    def addSphere(self) -> None:
        # subdivisions (int) – How many times to subdivide the mesh. Note that the number of faces will grow as function of 4 ** subdivisions, so you probably want to keep this under ~5
        mesh = trimesh.creation.icosphere(
            subdivisions=4,
            radius=self._size / 2,
        )
        mesh.apply_transform(
            trimesh.transformations.translation_matrix(
                [0, self._size * 0.5, 0]))
        self._addShape(self._toMeshData(mesh))

    # Initial Source code from  fieldOfView
    def _toMeshData(self, tri_node: trimesh.base.Trimesh) -> MeshData:
        tri_faces = tri_node.faces
        tri_vertices = tri_node.vertices

        indices = []
        vertices = []

        index_count = 0
        face_count = 0
        for tri_face in tri_faces:
            face = []
            for tri_index in tri_face:
                vertices.append(tri_vertices[tri_index])
                face.append(index_count)
                index_count += 1
            indices.append(face)
            face_count += 1

        vertices = numpy.asarray(vertices, dtype=numpy.float32)
        indices = numpy.asarray(indices, dtype=numpy.int32)
        normals = calculateNormalsFromIndexedVertices(vertices, indices,
                                                      face_count)

        mesh_data = MeshData(vertices=vertices,
                             indices=indices,
                             normals=normals)

        return mesh_data

    # Initial Source code from  fieldOfView
    def _addShape(self, mesh_data: MeshData) -> None:
        application = CuraApplication.getInstance()
        global_stack = application.getGlobalContainerStack()
        if not global_stack:
            return

        node = CuraSceneNode()

        node.setMeshData(mesh_data)
        node.setSelectable(True)
        node.setName("TestPart" + str(id(mesh_data)))

        scene = self._controller.getScene()
        op = AddSceneNodeOperation(node, scene.getRoot())
        op.push()

        default_extruder_position = application.getMachineManager(
        ).defaultExtruderPosition
        default_extruder_id = global_stack.extruders[
            default_extruder_position].getId()
        node.callDecoration("setActiveExtruder", default_extruder_id)

        active_build_plate = application.getMultiBuildPlateModel(
        ).activeBuildPlate
        node.addDecorator(BuildPlateDecorator(active_build_plate))

        node.addDecorator(SliceableObjectDecorator())

        application.getController().getScene().sceneChanged.emit(node)
Example #49
0
    def event(self, event):
        modifiers = QApplication.keyboardModifiers()
        ctrl_is_active = modifiers & Qt.ControlModifier
        shift_is_active = modifiers & Qt.ShiftModifier
        if event.type == Event.KeyPressEvent and ctrl_is_active:
            amount = 10 if shift_is_active else 1
            if event.key == KeyEvent.UpKey:
                self.setLayer(self._current_layer_num + amount)
                return True
            if event.key == KeyEvent.DownKey:
                self.setLayer(self._current_layer_num - amount)
                return True

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

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

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

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

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

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

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

        elif event.type == Event.ViewDeactivateEvent:
            self._wireprint_warning_message.hide()
            Application.getInstance().globalContainerStackChanged.disconnect(
                self._onGlobalStackChanged)
            if self._global_container_stack:
                self._global_container_stack.propertyChanged.disconnect(
                    self._onPropertyChanged)

            self._nozzle_node.setParent(None)
            self.getRenderer().removeRenderPass(self._layer_pass)
            self._composite_pass.setLayerBindings(self._old_layer_bindings)
            self._composite_pass.setCompositeShader(self._old_composite_shader)
Example #50
0
 def _autoBackup(self) -> None:
     preferences = CuraApplication.getInstance().getPreferences()
     if preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY
                             ) and self._isLastBackupTooLongAgo():
         self.createBackup()
Example #51
0
    def _onGetRemoteClustersFinished(
            self, clusters: List[CloudClusterResponse]) -> None:
        """Callback for when the request for getting the clusters is successful and finished."""

        self._um_cloud_printers = {
            m.getMetaDataEntry(self.META_CLUSTER_ID): m
            for m in CuraApplication.getInstance().getContainerRegistry().
            findContainerStacks(type="machine")
            if m.getMetaDataEntry(self.META_CLUSTER_ID, None)
        }
        new_clusters = []
        all_clusters = {c.cluster_id: c
                        for c in clusters
                        }  # type: Dict[str, CloudClusterResponse]
        online_clusters = {c.cluster_id: c
                           for c in clusters if c.is_online
                           }  # type: Dict[str, CloudClusterResponse]

        # Add the new printers in Cura.
        for device_id, cluster_data in all_clusters.items():
            if device_id not in self._remote_clusters:
                new_clusters.append(cluster_data)
            if device_id in self._um_cloud_printers:
                # Existing cloud printers may not have the host_guid meta-data entry. If that's the case, add it.
                if not self._um_cloud_printers[device_id].getMetaDataEntry(
                        self.META_HOST_GUID, None):
                    self._um_cloud_printers[device_id].setMetaDataEntry(
                        self.META_HOST_GUID, cluster_data.host_guid)
                # If a printer was previously not linked to the account and is rediscovered, mark the printer as linked
                # to the current account
                if not parseBool(
                        self._um_cloud_printers[device_id].getMetaDataEntry(
                            META_UM_LINKED_TO_ACCOUNT, "true")):
                    self._um_cloud_printers[device_id].setMetaDataEntry(
                        META_UM_LINKED_TO_ACCOUNT, True)
        self._onDevicesDiscovered(new_clusters)

        # Hide the current removed_printers_message, if there is any
        if self._removed_printers_message:
            self._removed_printers_message.actionTriggered.disconnect(
                self._onRemovedPrintersMessageActionTriggered)
            self._removed_printers_message.hide()

        # Remove the CloudOutput device for offline printers
        offline_device_keys = set(self._remote_clusters.keys()) - set(
            online_clusters.keys())
        for device_id in offline_device_keys:
            self._onDiscoveredDeviceRemoved(device_id)

        # Handle devices that were previously added in Cura but do not exist in the account anymore (i.e. they were
        # removed from the account)
        removed_device_keys = set(self._um_cloud_printers.keys()) - set(
            all_clusters.keys())
        if removed_device_keys:
            self._devicesRemovedFromAccount(removed_device_keys)

        if new_clusters or offline_device_keys or removed_device_keys:
            self.discoveredDevicesChanged.emit()
        if offline_device_keys:
            # If the removed device was active we should connect to the new active device
            self._connectToActiveMachine()

        self._syncing = False
        self._account.setSyncState(self.SYNC_SERVICE_NAME, SyncState.SUCCESS)
Example #52
0
 def existsKey(self, key: str) -> bool:
     return CuraApplication.getInstance().getMachineManager(
     ).existNetworkInstances(network_key=key)
Example #53
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)
Example #54
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

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

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

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

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

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

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

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

                    Job.yieldThread()

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

                if temp_list:
                    object_groups.append(temp_list)

            extruders_enabled = {
                position: stack.isEnabled
                for position, stack in CuraApplication.getInstance().
                getGlobalContainerStack().extruders.items()
            }
            filtered_object_groups = []
            has_model_with_disabled_extruders = False
            associated_disabled_extruders = set()
            for group in object_groups:
                stack = CuraApplication.getInstance().getGlobalContainerStack()
                skip_group = False
                for node in group:
                    extruder_position = node.callDecoration(
                        "getActiveExtruderPosition")
                    if not extruders_enabled[extruder_position]:
                        skip_group = True
                        has_model_with_disabled_extruders = True
                        associated_disabled_extruders.add(extruder_position)
                if not skip_group:
                    filtered_object_groups.append(group)

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

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

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

            # Build messages for extruder stacks
            for extruder_stack in ExtruderManager.getInstance(
            ).getMachineExtruders(stack.getId()):
                self._buildExtruderMessage(extruder_stack)

            for group in filtered_object_groups:
                group_message = self._slice_message.addRepeatedMessage(
                    "object_lists")
                if group[0].getParent() is not None and group[0].getParent(
                ).callDecoration("isGroup"):
                    self._handlePerObjectSettings(group[0].getParent(),
                                                  group_message)
                for object in group:
                    mesh_data = object.getMeshData()
                    rot_scale = object.getWorldTransformation().getTransposed(
                    ).getData()[0:3, 0:3]
                    translate = object.getWorldTransformation().getData()[:3,
                                                                          3]

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

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

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

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

                    obj.vertices = flat_verts

                    self._handlePerObjectSettings(object, obj)

                    Job.yieldThread()

        self.setResult(StartJobResult.Finished)
Example #55
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 bellow will
        # not return the correct extruder list (since by default, the machine_extruder_count is 1) in machines with
        # settable number of extruders.
        if machine_extruder_count and 0 <= machine_extruder_count <= len(
                extruder_dict):
            new_global_stack.setProperty("machine_extruder_count", "value",
                                         machine_extruder_count)

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

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

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

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

        machine_definition = definitions[0]

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

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

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

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

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

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

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

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

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

        preferred_quality_type = machine_definition.getMetaDataEntry(
            "preferred_quality_type")
        quality_group_dict = quality_manager.getQualityGroups(new_global_stack)
        quality_group = quality_group_dict.get(preferred_quality_type)

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

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

        return new_global_stack
Example #57
0
    def run(self) -> None:
        """Runs the job that initiates the slicing."""

        if self._build_plate_number is None:
            self.setResult(StartJobResult.Error)
            return

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                Job.yieldThread()

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

            if temp_list:
                object_groups.append(temp_list)

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

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

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

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

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

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

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

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

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

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

                obj.vertices = flat_verts

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

                Job.yieldThread()

        self.setResult(StartJobResult.Finished)
Example #58
0
 def restart(self):
     CuraApplication.getInstance().windowClosed()
Example #59
0
    def __init__(self):
        super().__init__()

        self._zero_conf = None
        self._zero_conf_browser = None

        self._application = CuraApplication.getInstance()

        # Create a cloud output device manager that abstracts all cloud connection logic away.
        self._cloud_output_device_manager = CloudOutputDeviceManager()

        # Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
        self.addDeviceSignal.connect(self._onAddDevice)
        self.removeDeviceSignal.connect(self._onRemoveDevice)

        self._application.globalContainerStackChanged.connect(
            self.refreshConnections)

        self._discovered_devices = {}

        self._network_manager = QNetworkAccessManager()
        self._network_manager.finished.connect(self._onNetworkRequestFinished)

        self._min_cluster_version = Version("4.0.0")
        self._min_cloud_version = Version("5.2.0")

        self._api_version = "1"
        self._api_prefix = "/api/v" + self._api_version + "/"
        self._cluster_api_version = "1"
        self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/"

        # Get list of manual instances from preferences
        self._preferences = CuraApplication.getInstance().getPreferences()
        self._preferences.addPreference(
            "um3networkprinting/manual_instances",
            "")  # A comma-separated list of ip adresses or hostnames

        self._manual_instances = self._preferences.getValue(
            "um3networkprinting/manual_instances").split(",")

        # Store the last manual entry key
        self._last_manual_entry_key = ""  # type: str

        # The zero-conf service changed requests are handled in a separate thread, so we can re-schedule the requests
        # which fail to get detailed service info.
        # Any new or re-scheduled requests will be appended to the request queue, and the handling thread will pick
        # them up and process them.
        self._service_changed_request_queue = Queue()
        self._service_changed_request_event = Event()
        self._service_changed_request_thread = Thread(
            target=self._handleOnServiceChangedRequests, daemon=True)
        self._service_changed_request_thread.start()

        self._account = self._application.getCuraAPI().account

        # Check if cloud flow is possible when user logs in
        self._account.loginStateChanged.connect(self.checkCloudFlowIsPossible)

        # Check if cloud flow is possible when user switches machines
        self._application.globalContainerStackChanged.connect(
            self._onMachineSwitched)

        # Listen for when cloud flow is possible
        self.cloudFlowIsPossible.connect(self._onCloudFlowPossible)

        # Listen if cloud cluster was added
        self._cloud_output_device_manager.addedCloudCluster.connect(
            self._onCloudPrintingConfigured)

        # Listen if cloud cluster was removed
        self._cloud_output_device_manager.removedCloudCluster.connect(
            self.checkCloudFlowIsPossible)

        self._start_cloud_flow_message = None  # type: Optional[Message]
        self._cloud_flow_complete_message = None  # type: Optional[Message]
Example #60
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
        if os.path.exists(
                os.path.join(CuraApplication.getInstallPrefix(), "bin",
                             executable_name)):
            default_engine_location = os.path.join(
                CuraApplication.getInstallPrefix(), "bin", executable_name)
        if hasattr(sys, "frozen"):
            default_engine_location = os.path.join(
                os.path.dirname(os.path.abspath(sys.executable)),
                executable_name)
        if Platform.isLinux() and not default_engine_location:
            if not os.getenv("PATH"):
                raise OSError(
                    "There is something wrong with your Linux installation.")
            for pathdir in cast(str, os.getenv("PATH")).split(os.pathsep):
                execpath = os.path.join(pathdir, executable_name)
                if os.path.exists(execpath):
                    default_engine_location = execpath
                    break

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self._application.initializationFinished.connect(self.initialize)