Esempio n. 1
0
 def get_cura_dir_path():
     if Platform.isWindows():
         return os.path.expanduser("~/AppData/Roaming/" + CuraAppName)
     elif Platform.isLinux():
         return os.path.expanduser("~/.local/share/" + CuraAppName)
     elif Platform.isOSX():
         return os.path.expanduser("~/Library/Logs/" + CuraAppName)
Esempio n. 2
0
    def _createSocket(self, protocol_file):
        if self._socket:
            self._socket.stateChanged.disconnect(self._onSocketStateChanged)
            self._socket.messageReceived.disconnect(self._onMessageReceived)
            self._socket.error.disconnect(self._onSocketError)
            # If the error occured due to parsing, both connections believe that connection is okay. So we need to force a close.
            self._socket.close()

        self._socket = SignalSocket()
        self._socket.stateChanged.connect(self._onSocketStateChanged)
        self._socket.messageReceived.connect(self._onMessageReceived)
        self._socket.error.connect(self._onSocketError)
        
        if Platform.isWindows():
            # On Windows, the Protobuf DiskSourceTree does stupid things with paths.
            # So convert to forward slashes here so it finds the proto file properly.
            protocol_file = protocol_file.replace("\\", "/")

        if not self._socket.registerAllMessageTypes(protocol_file):
            Logger.log("e", "Could not register Cura protocol messages: %s", self._socket.getLastError())

        if Application.getInstance().getCommandLineOption("external-backend", False):
            Logger.log("i", "Listening for backend connections on %s", self._port)

        self._socket.listen("127.0.0.1", self._port)
Esempio n. 3
0
    def _createSocket(self, protocol_file):
        if self._socket:
            Logger.log("d", "Previous socket existed. Closing that first.") # temp debug logging
            self._socket.stateChanged.disconnect(self._onSocketStateChanged)
            self._socket.messageReceived.disconnect(self._onMessageReceived)
            self._socket.error.disconnect(self._onSocketError)
            # Hack for (at least) Linux. If the socket is connecting, the close will deadlock.
            while self._socket.getState() == Arcus.SocketState.Opening:
                sleep(0.1)
            # If the error occurred due to parsing, both connections believe that connection is okay.
            # So we need to force a close.
            self._socket.close()

        self._socket = SignalSocket()
        self._socket.stateChanged.connect(self._onSocketStateChanged)
        self._socket.messageReceived.connect(self._onMessageReceived)
        self._socket.error.connect(self._onSocketError)
        
        if Platform.isWindows():
            # On Windows, the Protobuf DiskSourceTree does stupid things with paths.
            # So convert to forward slashes here so it finds the proto file properly.
            # Using sys.getfilesystemencoding() avoid the application crashing if it is
            # installed on a path with non-ascii characters GitHub issue #3907
            protocol_file = protocol_file.replace("\\", "/").encode(sys.getfilesystemencoding())

        if not self._socket.registerAllMessageTypes(protocol_file):
            Logger.log("e", "Could not register Uranium protocol messages: %s", self._socket.getLastError())

        if UM.Application.Application.getInstance().getUseExternalBackend():
            Logger.log("i", "Listening for backend connections on %s", self._port)

        self._socket.listen("127.0.0.1", self._port)
Esempio n. 4
0
def get_cura_dir_path():
    if Platform.isWindows():
        return os.path.expanduser("~/AppData/Local/cura/")
    elif Platform.isLinux():
        return os.path.expanduser("~/.local/share/cura")
    elif Platform.isOSX():
        return os.path.expanduser("~/Library/Logs/cura")
Esempio n. 5
0
    def exportProfile(self, instance_ids, file_name, file_type):
        # Parse the fileType to deduce what plugin can save the file format.
        # fileType has the format "<description> (*.<extension>)"
        split = file_type.rfind(" (*.")  # Find where the description ends and the extension starts.
        if split < 0:  # Not found. Invalid format.
            Logger.log("e", "Invalid file format identifier %s", file_type)
            return
        description = file_type[:split]
        extension = file_type[split + 4:-1]  # Leave out the " (*." and ")".
        if not file_name.endswith("." + extension):  # Auto-fill the extension if the user did not provide any.
            file_name += "." + extension

        # On Windows, QML FileDialog properly asks for overwrite confirm, but not on other platforms, so handle those ourself.
        if not Platform.isWindows():
            if os.path.exists(file_name):
                result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"),
                                              catalog.i18nc("@label", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?").format(file_name))
                if result == QMessageBox.No:
                    return
        found_containers = []
        extruder_positions = []
        for instance_id in instance_ids:
            containers = ContainerRegistry.getInstance().findInstanceContainers(id=instance_id)
            if containers:
                found_containers.append(containers[0])

                # Determine the position of the extruder of this container
                extruder_id = containers[0].getMetaDataEntry("extruder", "")
                if extruder_id == "":
                    # Global stack
                    extruder_positions.append(-1)
                else:
                    extruder_containers = ContainerRegistry.getInstance().findDefinitionContainers(id=extruder_id)
                    if extruder_containers:
                        extruder_positions.append(int(extruder_containers[0].getMetaDataEntry("position", 0)))
                    else:
                        extruder_positions.append(0)
        # Ensure the profiles are always exported in order (global, extruder 0, extruder 1, ...)
        found_containers = [containers for (positions, containers) in sorted(zip(extruder_positions, found_containers))]

        profile_writer = self._findProfileWriter(extension, description)

        try:
            success = profile_writer.write(file_name, found_containers)
        except Exception as e:
            Logger.log("e", "Failed to export profile to %s: %s", file_name, str(e))
            m = Message(catalog.i18nc("@info:status", "Failed to export profile to <filename>{0}</filename>: <message>{1}</message>", file_name, str(e)), lifetime = 0)
            m.show()
            return
        if not success:
            Logger.log("w", "Failed to export profile to %s: Writer plugin reported failure.", file_name)
            m = Message(catalog.i18nc("@info:status", "Failed to export profile to <filename>{0}</filename>: Writer plugin reported failure.", file_name), lifetime = 0)
            m.show()
            return
        m = Message(catalog.i18nc("@info:status", "Exported profile to <filename>{0}</filename>", file_name))
        m.show()
Esempio n. 6
0
    def exportContainer(self, container_id: str, file_type: str, file_url_or_string: Union[QUrl, str]) -> Dict[str, str]:
        if not container_id or not file_type or not file_url_or_string:
            return { "status": "error", "message": "Invalid arguments"}

        if isinstance(file_url_or_string, QUrl):
            file_url = file_url_or_string.toLocalFile()
        else:
            file_url = file_url_or_string

        if not file_url:
            return { "status": "error", "message": "Invalid path"}

        mime_type = None
        if not file_type in self._container_name_filters:
            try:
                mime_type = MimeTypeDatabase.getMimeTypeForFile(file_url)
            except MimeTypeNotFoundError:
                return { "status": "error", "message": "Unknown File Type" }
        else:
            mime_type = self._container_name_filters[file_type]["mime"]

        containers = self._container_registry.findContainers(None, id = container_id)
        if not containers:
            return { "status": "error", "message": "Container not found"}
        container = containers[0]

        if Platform.isOSX() and "." in file_url:
            file_url = file_url[:file_url.rfind(".")]

        for suffix in mime_type.suffixes:
            if file_url.endswith(suffix):
                break
        else:
            file_url += "." + mime_type.preferredSuffix

        if not Platform.isWindows():
            if os.path.exists(file_url):
                result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"),
                                              catalog.i18nc("@label", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?").format(file_url))
                if result == QMessageBox.No:
                    return { "status": "cancelled", "message": "User cancelled"}

        try:
            contents = container.serialize()
        except NotImplementedError:
            return { "status": "error", "message": "Unable to serialize container"}

        if contents is None:
            return {"status": "error", "message": "Serialization returned None. Unable to write to file"}

        with SaveFile(file_url, "w") as f:
            f.write(contents)

        return { "status": "success", "message": "Succesfully exported container", "path": file_url}
Esempio n. 7
0
    def _getPossibleConfigStorageRootPathList(cls):
        # Returns all possible root paths for storing app configurations (in old and new versions)
        config_root_list = [Resources._getConfigStorageRootPath()]
        if Platform.isWindows():
            # it used to be in LOCALAPPDATA on Windows
            config_root_list.append(os.getenv("LOCALAPPDATA"))
        elif Platform.isOSX():
            config_root_list.append(os.path.expanduser("~"))

        config_root_list = [os.path.join(n, cls.ApplicationIdentifier) for n in config_root_list]
        return config_root_list
Esempio n. 8
0
def register(app):
    if Platform.isWindows():
        from . import WindowsRemovableDrivePlugin
        return { "output_device": WindowsRemovableDrivePlugin.WindowsRemovableDrivePlugin() }
    elif Platform.isOSX():
        from . import OSXRemovableDrivePlugin
        return { "output_device": OSXRemovableDrivePlugin.OSXRemovableDrivePlugin() }
    elif Platform.isLinux():
        from . import LinuxRemovableDrivePlugin
        return { "output_device": LinuxRemovableDrivePlugin.LinuxRemovableDrivePlugin() }
    else:
        Logger.log("e", "Unsupported system, thus no removable device hotplugging support available.")
        return { }
Esempio n. 9
0
    def _getCacheStorageRootPath(cls):
        # Returns the path where we store different versions of app configurations
        cache_path = None
        if Platform.isWindows():
            cache_path = os.getenv("LOCALAPPDATA")
        elif Platform.isOSX():
            cache_path = None
        elif Platform.isLinux():
            try:
                cache_path = os.environ["XDG_CACHE_HOME"]
            except KeyError:
                cache_path = os.path.expanduser("~/.cache")

        return cache_path
Esempio n. 10
0
    def _getConfigStorageRootPath(cls):
        # Returns the path where we store different versions of app configurations
        config_path = None
        if Platform.isWindows():
            config_path = os.getenv("APPDATA")
        elif Platform.isOSX():
            config_path = os.path.expanduser("~/Library/Application Support")
        elif Platform.isLinux():
            try:
                config_path = os.environ["XDG_CONFIG_HOME"]
            except KeyError:
                config_path = os.path.expanduser("~/.config")
        else:
            config_path = "."

        return config_path
Esempio n. 11
0
    def _getConfigStorageRootPath(cls) -> str:
        # Returns the path where we store different versions of app configurations
        if Platform.isWindows():
            config_path = os.getenv("APPDATA")
            if not config_path: # Protect if the getenv function returns None (it should never happen)
                config_path = "."
        elif Platform.isOSX():
            config_path = os.path.expanduser("~/Library/Application Support")
        elif Platform.isLinux():
            try:
                config_path = os.environ["XDG_CONFIG_HOME"]
            except KeyError:
                config_path = os.path.expanduser("~/.config")
        else:
            config_path = "."

        return config_path
Esempio n. 12
0
    def isReadOnly(self, container_id: str) -> bool:
        if container_id in self._is_read_only_cache:
            return self._is_read_only_cache[container_id]
        storage_path = os.path.realpath(Resources.getDataStoragePath())
        file_path = self._id_to_path[container_id]  # If KeyError: We don't know this ID.

        # The container is read-only if file_path is not a subdirectory of storage_path.
        if Platform.isWindows():
            # On Windows, if the paths provided to commonpath() don't come from the same drive,
            # a ValueError will be raised.
            try:
                result = os.path.commonpath([storage_path, os.path.realpath(file_path)]) != storage_path
            except ValueError:
                result = True
        else:
            result = os.path.commonpath([storage_path, os.path.realpath(file_path)]) != storage_path
        self._is_read_only_cache[container_id] = result
        return result
Esempio n. 13
0
    def exportProfile(self, instance_id, file_name, file_type):
        Logger.log('d', 'exportProfile instance_id: '+str(instance_id))

        # Parse the fileType to deduce what plugin can save the file format.
        # fileType has the format "<description> (*.<extension>)"
        split = file_type.rfind(" (*.")  # Find where the description ends and the extension starts.
        if split < 0:  # Not found. Invalid format.
            Logger.log("e", "Invalid file format identifier %s", file_type)
            return
        description = file_type[:split]
        extension = file_type[split + 4:-1]  # Leave out the " (*." and ")".
        if not file_name.endswith("." + extension):  # Auto-fill the extension if the user did not provide any.
            file_name += "." + extension

        # On Windows, QML FileDialog properly asks for overwrite confirm, but not on other platforms, so handle those ourself.
        if not Platform.isWindows():
            if os.path.exists(file_name):
                result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"),
                                              catalog.i18nc("@label", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?").format(file_name))
                if result == QMessageBox.No:
                    return

        containers = ContainerRegistry.getInstance().findInstanceContainers(id=instance_id)
        if not containers:
            return
        container = containers[0]

        profile_writer = self._findProfileWriter(extension, description)

        try:
            success = profile_writer.write(file_name, container)
        except Exception as e:
            Logger.log("e", "Failed to export profile to %s: %s", file_name, str(e))
            m = Message(catalog.i18nc("@info:status", "Failed to export profile to <filename>{0}</filename>: <message>{1}</message>", file_name, str(e)), lifetime = 0)
            m.show()
            return
        if not success:
            Logger.log("w", "Failed to export profile to %s: Writer plugin reported failure.", file_name)
            m = Message(catalog.i18nc("@info:status", "Failed to export profile to <filename>{0}</filename>: Writer plugin reported failure.", file_name), lifetime = 0)
            m.show()
            return
        m = Message(catalog.i18nc("@info:status", "Exported profile to <filename>{0}</filename>", file_name))
        m.show()
Esempio n. 14
0
    def exportQualityProfile(self, container_list, file_name, file_type) -> bool:
        # Parse the fileType to deduce what plugin can save the file format.
        # fileType has the format "<description> (*.<extension>)"
        split = file_type.rfind(" (*.")  # Find where the description ends and the extension starts.
        if split < 0:  # Not found. Invalid format.
            Logger.log("e", "Invalid file format identifier %s", file_type)
            return False
        description = file_type[:split]
        extension = file_type[split + 4:-1]  # Leave out the " (*." and ")".
        if not file_name.endswith("." + extension):  # Auto-fill the extension if the user did not provide any.
            file_name += "." + extension

        # On Windows, QML FileDialog properly asks for overwrite confirm, but not on other platforms, so handle those ourself.
        if not Platform.isWindows():
            if os.path.exists(file_name):
                result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"),
                                              catalog.i18nc("@label Don't translate the XML tag <filename>!", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?").format(file_name))
                if result == QMessageBox.No:
                    return False

        profile_writer = self._findProfileWriter(extension, description)
        try:
            success = profile_writer.write(file_name, container_list)
        except Exception as e:
            Logger.log("e", "Failed to export profile to %s: %s", file_name, str(e))
            m = Message(catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "Failed to export profile to <filename>{0}</filename>: <message>{1}</message>", file_name, str(e)),
                        lifetime = 0,
                        title = catalog.i18nc("@info:title", "Error"))
            m.show()
            return False
        if not success:
            Logger.log("w", "Failed to export profile to %s: Writer plugin reported failure.", file_name)
            m = Message(catalog.i18nc("@info:status Don't translate the XML tag <filename>!", "Failed to export profile to <filename>{0}</filename>: Writer plugin reported failure.", file_name),
                        lifetime = 0,
                        title = catalog.i18nc("@info:title", "Error"))
            m.show()
            return False
        m = Message(catalog.i18nc("@info:status Don't translate the XML tag <filename>!", "Exported profile to <filename>{0}</filename>", file_name),
                    title = catalog.i18nc("@info:title", "Export succeeded"))
        m.show()
        return True
Esempio n. 15
0
    def exportQualityProfile(self, container_list, file_name, file_type):
        # Parse the fileType to deduce what plugin can save the file format.
        # fileType has the format "<description> (*.<extension>)"
        split = file_type.rfind(" (*.")  # Find where the description ends and the extension starts.
        if split < 0:  # Not found. Invalid format.
            Logger.log("e", "Invalid file format identifier %s", file_type)
            return
        description = file_type[:split]
        extension = file_type[split + 4:-1]  # Leave out the " (*." and ")".
        if not file_name.endswith("." + extension):  # Auto-fill the extension if the user did not provide any.
            file_name += "." + extension

        # On Windows, QML FileDialog properly asks for overwrite confirm, but not on other platforms, so handle those ourself.
        if not Platform.isWindows():
            if os.path.exists(file_name):
                result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"),
                                              catalog.i18nc("@label Don't translate the XML tag <filename>!", "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?").format(file_name))
                if result == QMessageBox.No:
                    return

        profile_writer = self._findProfileWriter(extension, description)
        try:
            success = profile_writer.write(file_name, container_list)
        except Exception as e:
            Logger.log("e", "Failed to export profile to %s: %s", file_name, str(e))
            m = Message(catalog.i18nc("@info:status Don't translate the XML tags <filename> or <message>!", "Failed to export profile to <filename>{0}</filename>: <message>{1}</message>", file_name, str(e)),
                        lifetime = 0,
                        title = catalog.i18nc("@info:title", "Error"))
            m.show()
            return
        if not success:
            Logger.log("w", "Failed to export profile to %s: Writer plugin reported failure.", file_name)
            m = Message(catalog.i18nc("@info:status Don't translate the XML tag <filename>!", "Failed to export profile to <filename>{0}</filename>: Writer plugin reported failure.", file_name),
                        lifetime = 0,
                        title = catalog.i18nc("@info:title", "Error"))
            m.show()
            return
        m = Message(catalog.i18nc("@info:status Don't translate the XML tag <filename>!", "Exported profile to <filename>{0}</filename>", file_name),
                    title = catalog.i18nc("@info:title", "Export succeeded"))
        m.show()
Esempio n. 16
0
    def __compress_gcode(self):
        exePath = None
        if Platform.isWindows():
            exePath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'VC_compress_gcode.exe')
        elif Platform.isOSX():
            exePath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'VC_compress_gcode_MAC')
        else:
            self.__log("w", "Could not find gcode compression tool")

        if exePath is not None and os.path.exists(exePath):
            cmd = '"' + exePath + '"' + ' "' + self._localTempGcode + '" ' + self._config["x_mm_per_step"] + ' ' + self._config["y_mm_per_step"] + ' ' + self._config["z_mm_per_step"] + ' ' + \
                self._config["e_mm_per_step"] + ' "' + os.path.dirname(self._localTempGcode) + '" ' \
                + self._config["s_x_max"] + ' ' + self._config["s_y_max"] + ' ' + self._config["s_z_max"] + ' ' + self._config["s_machine_type"]
            self.__log("d", cmd)

            ret = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
            self.__log("d", ret.stdout.read().decode('utf-8', 'ignore').rstrip())

            if os.path.exists(self._localTempGcode + '.tz'):  # check whether the compression succedded
                return True
            else:
                return False
    def __init__(self):
        super().__init__()

        self._application = Application.getInstance()

        self._i18n_catalog = None

        settings_definition_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "arcwelder_settings.def.json")
        try:
            with open(settings_definition_path, "r", encoding = "utf-8") as f:
                self._settings_dict = json.load(f, object_pairs_hook = OrderedDict)
        except:
            Logger.logException("e", "Could not load arc welder settings definition")
            return

        if Platform.isWindows():
            arcwelder_executable = "bin/win64/ArcWelder.exe"
        elif Platform.isLinux():
            arcwelder_executable = "bin/linux/ArcWelder"
        elif Platform.isOSX():
            arcwelder_executable = "bin/osx/ArcWelder"

        self._arcwelder_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), arcwelder_executable)
        try:
            os.chmod(self._arcwelder_path, stat.S_IXUSR | stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR)  # Make sure we have the rights to run this.
        except:
            Logger.logException("e", "Could modify rights of ArcWelder executable")
            return
        version_output = subprocess.check_output([self._arcwelder_path, "--version"]).decode()
        match = re.search("version: (.*)", version_output)
        if match:
            Logger.log("d", "Using ArcWelder %s" % match.group(1))
        else:
            Logger.log("w", "Could not determine ArcWelder version")

        self._application.getPreferences().addPreference("arcwelderplugin/settings_made_visible", False)

        ContainerRegistry.getInstance().containerLoadComplete.connect(self._onContainerLoadComplete)
        self._application.getOutputDeviceManager().writeStarted.connect(self._filterGcode)
    def isReadOnly(self, container_id: str) -> bool:
        if container_id in self._is_read_only_cache:
            return self._is_read_only_cache[container_id]
        storage_path = os.path.realpath(Resources.getDataStoragePath())
        file_path = self._id_to_path[
            container_id]  # If KeyError: We don't know this ID.

        # The container is read-only if file_path is not a subdirectory of storage_path.
        if Platform.isWindows():
            # On Windows, if the paths provided to commonpath() don't come from the same drive,
            # a ValueError will be raised.
            try:
                result = os.path.commonpath([
                    storage_path, os.path.realpath(file_path)
                ]) != storage_path
            except ValueError:
                result = True
        else:
            result = os.path.commonpath(
                [storage_path, os.path.realpath(file_path)]) != storage_path
        self._is_read_only_cache[container_id] = result
        return result
Esempio n. 19
0
    def isReadOnly(self, container_id: str) -> bool:
        """Returns whether a container is read-only or not.

        A container can only be modified if it is stored in the data directory.
        :return: Whether the specified container is read-only.
        """

        if container_id in self._is_read_only_cache:
            return self._is_read_only_cache[container_id]
        if self._storage_path == "":
            try:
                self._storage_path = os.path.realpath(
                    Resources.getDataStoragePath())
            except OSError:  # Directory can't be accessed.
                self._is_read_only_cache[container_id] = True
                return True
        storage_path = self._storage_path

        file_path = self._id_to_path[
            container_id]  # If KeyError: We don't know this ID.

        # The container is read-only if file_path is not a subdirectory of storage_path.
        if Platform.isWindows():
            # On Windows, if the paths provided to commonpath() don't come from the same drive,
            # a ValueError will be raised.
            try:
                result = os.path.commonpath([
                    storage_path, os.path.realpath(file_path)
                ]) != storage_path
            except ValueError:
                result = True
        else:
            result = os.path.commonpath(
                [storage_path, os.path.realpath(file_path)]) != storage_path

        result |= ContainerRegistry.getInstance().isExplicitReadOnly(
            container_id)
        self._is_read_only_cache[container_id] = result
        return result
Esempio n. 20
0
    def __initializeStoragePaths(cls):
        Logger.log("d", "Initializing storage paths")
        # use nested structure: <app-name>/<version>/...
        if cls.ApplicationVersion == "master" or cls.ApplicationVersion == "unknown":
            storage_dir_name = os.path.join(cls.ApplicationIdentifier, cls.ApplicationVersion)
        else:
            from UM.Version import Version
            version = Version(cls.ApplicationVersion)
            storage_dir_name = os.path.join(cls.ApplicationIdentifier, "%s.%s" % (version.getMajor(), version.getMinor()))
        Logger.log("d", "...StorageRootPath is %s", Resources._getConfigStorageRootPath())
        Logger.log("d", "...StorageDirName is %s", storage_dir_name )

        # config is saved in "<CONFIG_ROOT>/<storage_dir_name>"
        cls.__config_storage_path = os.path.join(Resources._getConfigStorageRootPath(), storage_dir_name)
        Logger.log("d", "Config storage path is %s", cls.__config_storage_path)

        # data is saved in
        #  - on Linux: "<DATA_ROOT>/<storage_dir_name>"
        #  - on other: "<CONFIG_DIR>" (in the config directory)
        data_root_path = Resources._getDataStorageRootPath()
        cls.__data_storage_path = cls.__config_storage_path if data_root_path is None else \
            os.path.join(data_root_path, storage_dir_name)
        Logger.log("d", "Data storage path is %s", cls.__data_storage_path)
        # cache is saved in
        #  - on Linux:   "<CACHE_DIR>/<storage_dir_name>"
        #  - on Windows: "<CACHE_DIR>/<storage_dir_name>/cache"
        #  - on Mac:     "<CONFIG_DIR>/cache" (in the config directory)
        cache_root_path = Resources._getCacheStorageRootPath()
        if cache_root_path is None:
            cls.__cache_storage_path = os.path.join(cls.__config_storage_path, "cache")
        else:
            cls.__cache_storage_path = os.path.join(cache_root_path, storage_dir_name)
            if Platform.isWindows():
                cls.__cache_storage_path = os.path.join(cls.__cache_storage_path, "cache")
        Logger.log("d", "Cache storage path is %s", cls.__cache_storage_path)
        if not os.path.exists(cls.__config_storage_path) or not os.path.exists(cls.__data_storage_path):
            cls._copyLatestDirsIfPresent()

        cls.__paths.insert(0, cls.__data_storage_path)
Esempio n. 21
0
    def _createSocket(self, protocol_file):
        """Creates a socket and attaches listeners."""

        if self._socket:
            Logger.log("d", "Previous socket existed. Closing that first."
                       )  # temp debug logging
            self._socket.stateChanged.disconnect(self._onSocketStateChanged)
            self._socket.messageReceived.disconnect(self._onMessageReceived)
            self._socket.error.disconnect(self._onSocketError)
            # Hack for (at least) Linux. If the socket is connecting, the close will deadlock.
            while self._socket.getState() == Arcus.SocketState.Opening:
                sleep(0.1)
            # If the error occurred due to parsing, both connections believe that connection is okay.
            # So we need to force a close.
            self._socket.close()

        self._socket = SignalSocket()
        self._socket.stateChanged.connect(self._onSocketStateChanged)
        self._socket.messageReceived.connect(self._onMessageReceived)
        self._socket.error.connect(self._onSocketError)

        if Platform.isWindows():
            # On Windows, the Protobuf DiskSourceTree does stupid things with paths.
            # So convert to forward slashes here so it finds the proto file properly.
            # Using sys.getfilesystemencoding() avoid the application crashing if it is
            # installed on a path with non-ascii characters GitHub issue #3907
            protocol_file = protocol_file.replace("\\", "/").encode(
                sys.getfilesystemencoding())

        if not self._socket.registerAllMessageTypes(protocol_file):
            Logger.log("e", "Could not register Uranium protocol messages: %s",
                       self._socket.getLastError())

        if UM.Application.Application.getInstance().getUseExternalBackend():
            Logger.log("i", "Listening for backend connections on %s",
                       self._port)

        self._socket.listen("127.0.0.1", self._port)
Esempio n. 22
0
    def __initializeStoragePaths(cls):
        Logger.log("d", "Initializing storage paths")
        # use nested structure: <app-name>/<version>/...
        if cls.ApplicationVersion == "master" or cls.ApplicationVersion == "unknown":
            storage_dir_name = os.path.join(cls.ApplicationIdentifier, cls.ApplicationVersion)
        else:
            from UM.Version import Version
            version = Version(cls.ApplicationVersion)
            storage_dir_name = os.path.join(cls.ApplicationIdentifier, "%s.%s" % (version.getMajor(), version.getMinor()))

        # config is saved in "<CONFIG_ROOT>/<storage_dir_name>"
        cls.__config_storage_path = os.path.join(Resources._getConfigStorageRootPath(), storage_dir_name)
        Logger.log("d", "Config storage path is %s", cls.__config_storage_path)

        # data is saved in
        #  - on Linux: "<DATA_ROOT>/<storage_dir_name>"
        #  - on other: "<CONFIG_DIR>" (in the config directory)
        data_root_path = Resources._getDataStorageRootPath()
        cls.__data_storage_path = cls.__config_storage_path if data_root_path is None else \
            os.path.join(data_root_path, storage_dir_name)
        Logger.log("d", "Data storage path is %s", cls.__data_storage_path)
        # cache is saved in
        #  - on Linux:   "<CACHE_DIR>/<storage_dir_name>"
        #  - on Windows: "<CACHE_DIR>/<storage_dir_name>/cache"
        #  - on Mac:     "<CONFIG_DIR>/cache" (in the config directory)
        cache_root_path = Resources._getCacheStorageRootPath()
        if cache_root_path is None:
            cls.__cache_storage_path = os.path.join(cls.__config_storage_path, "cache")
        else:
            cls.__cache_storage_path = os.path.join(cache_root_path, storage_dir_name)
            if Platform.isWindows():
                cls.__cache_storage_path = os.path.join(cls.__cache_storage_path, "cache")
        Logger.log("d", "Cache storage path is %s", cls.__cache_storage_path)
        if not os.path.exists(cls.__config_storage_path):
            cls._copyLatestDirsIfPresent()

        cls.__paths.insert(0, cls.__data_storage_path)
Esempio n. 23
0
def register(app):
    if Platform.isWindows():
        from . import WindowsRemovableDrivePlugin
        return {
            "output_device":
            WindowsRemovableDrivePlugin.WindowsRemovableDrivePlugin()
        }
    elif Platform.isOSX():
        from . import OSXRemovableDrivePlugin
        return {
            "output_device": OSXRemovableDrivePlugin.OSXRemovableDrivePlugin()
        }
    elif Platform.isLinux():
        from . import LinuxRemovableDrivePlugin
        return {
            "output_device":
            LinuxRemovableDrivePlugin.LinuxRemovableDrivePlugin()
        }
    else:
        Logger.log(
            "e",
            "Unsupported system, thus no removable device hotplugging support available."
        )
        return {}
Esempio n. 24
0
    def __init__(self, type: int = Auto) -> None:
        # These collections must be treated as immutable otherwise we lose thread safety.
        self.__functions = WeakImmutableList()      # type: "WeakImmutableList"
        self.__methods = WeakImmutablePairList()    # type: "WeakImmutablePairList"
        self.__signals = WeakImmutableList()        # type: "WeakImmutableList"

        self.__lock = threading.Lock()  # Guards access to the fields above.
        self.__type = type

        self._postpone_emit = False
        self._postpone_thread = None    # type: threading.Thread
        self._compress_postpone = False
        self._postponed_emits = None    # type: Any

        if _recordSignalNames():
            try:
                if Platform.isWindows():
                    self.__name = inspect.stack()[1][0].f_locals["key"]
                else:
                    self.__name = inspect.stack()[1].frame.f_locals["key"]
            except KeyError:
                self.__name = "Signal"
        else:
            self.__name = "Anon"
Esempio n. 25
0
    def __init__(self, type: int = Auto) -> None:
        # These collections must be treated as immutable otherwise we lose thread safety.
        self.__functions = WeakImmutableList()      # type: "WeakImmutableList"
        self.__methods = WeakImmutablePairList()    # type: "WeakImmutablePairList"
        self.__signals = WeakImmutableList()        # type: "WeakImmutableList"

        self.__lock = threading.Lock()  # Guards access to the fields above.
        self.__type = type

        self._postpone_emit = False
        self._postpone_thread = None    # type: threading.Thread
        self._compress_postpone = False
        self._postponed_emits = None    # type: Any

        if _recordSignalNames():
            try:
                if Platform.isWindows():
                    self.__name = inspect.stack()[1][0].f_locals["key"]
                else:
                    self.__name = inspect.stack()[1].frame.f_locals["key"]
            except KeyError:
                self.__name = "Signal"
        else:
            self.__name = "Anon"
Esempio n. 26
0
 def __exit__(self, exc_type: type, exc_val: Exception, exc_tb: Any) -> None: #exc_tb is actually a traceback object which is not exposed in Python.
     if Platform.isWindows():
         self._deleteLockFileWindows()
     else:
         self._deleteLockFile()
Esempio n. 27
0
 def removeWatchedFile(self, file_path: str) -> None:
     # The QT 5.10.0 issue, only on Windows. Cura crashes after loading a stl file from USB/sd-card/Cloud-based drive
     if not Platform.isWindows():
         self._file_watcher.removePath(file_path)
    def _filterGcode(self, output_device):
        scene = self._application.getController().getScene()

        global_container_stack = self._application.getGlobalContainerStack()
        if not global_container_stack:
            return

        arcwelder_enable = global_container_stack.getProperty(
            "arcwelder_enable", "value")
        if not arcwelder_enable:
            Logger.log("d", "ArcWelder is not enabled")
            return

        maximum_radius = global_container_stack.getProperty(
            "arcwelder_maximum_radius", "value")
        path_tolerance = (global_container_stack.getProperty(
            "arcwelder_tolerance", "value") / 100)
        resolution = global_container_stack.getProperty(
            "arcwelder_resolution", "value")
        firmware_compensation = global_container_stack.getProperty(
            "arcwelder_firmware_compensation", "value")
        min_arc_segment = int(
            global_container_stack.getProperty("arcwelder_min_arc_segment",
                                               "value"))
        mm_per_arc_segment = global_container_stack.getProperty(
            "arcwelder_mm_per_arc_segment", "value")
        allow_3d_arcs = global_container_stack.getProperty(
            "arcwelder_allow_3d_arcs", "value")
        allow_dynamic_precision = global_container_stack.getProperty(
            "arcwelder_allow_dynamic_precision", "value")
        allow_travel_arcs = global_container_stack.getProperty(
            "arcwelder_allow_travel_arcs", "value")
        default_xyz_precision = int(
            global_container_stack.getProperty(
                "arcwelder_default_xyz_precision", "value"))
        default_e_precision = int(
            global_container_stack.getProperty("arcwelder_default_e_precision",
                                               "value"))
        g90_influences_extruder = global_container_stack.getProperty(
            "arcwelder_g90_influences_extruder", "value")
        extrusion_rate_variance = (global_container_stack.getProperty(
            "arcwelder_extrusion_rate_variance", "value") / 100)
        max_gcode_length = int(
            global_container_stack.getProperty("arcwelder_max_gcode_length",
                                               "value"))

        # If the scene does not have a gcode, do nothing
        gcode_dict = getattr(scene, "gcode_dict", {})
        if not gcode_dict:  # this also checks for an empty dict
            Logger.log("w", "Scene has no gcode to process")
            return

        dict_changed = False

        layer_separator = ";ARCWELDERPLUGIN_GCODELIST_SEPARATOR\n"
        processed_marker = ";ARCWELDERPROCESSED\n"

        for plate_id in gcode_dict:
            gcode_list = gcode_dict[plate_id]
            if len(gcode_list) < 2:
                Logger.log("w", "Plate %s does not contain any layers",
                           plate_id)
                continue

            if processed_marker in gcode_list[0]:
                Logger.log("d", "Plate %s has already been processed",
                           plate_id)
                continue

            if len(gcode_list) > 0:
                # remove header from gcode, so we can put it back in front after processing
                header = gcode_list.pop(0)
            else:
                header = ""
            joined_gcode = layer_separator.join(gcode_list)

            file_descriptor, temporary_path = tempfile.mkstemp()
            Logger.log("d", "Using temporary file %s", temporary_path)

            with os.fdopen(file_descriptor, "w",
                           encoding="utf-8") as temporary_file:
                temporary_file.write(joined_gcode)

            command_arguments = [
                self._arcwelder_path,
                "-m=%f" % maximum_radius,
                "-t=%f" % path_tolerance,
                "-r=%f" % resolution,
                "-x=%d" % default_xyz_precision,
                "-e=%d" % default_e_precision,
                "-v=%f" % extrusion_rate_variance,
                "-c=%d" % max_gcode_length
            ]

            if firmware_compensation:
                command_arguments.extend(
                    ["-s=%f" % mm_per_arc_segment,
                     "-a=%d" % min_arc_segment])

            if allow_3d_arcs:
                command_arguments.append("-z")

            if allow_dynamic_precision:
                command_arguments.append("-d")

            if allow_travel_arcs:
                command_arguments.append("-y")

            if g90_influences_extruder:
                command_arguments.append("-g")

            command_arguments.append(temporary_path)

            Logger.log(
                "d",
                "Running ArcWelder with the following options: %s" %
                command_arguments,
            )

            if Platform.isWindows():
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
            else:
                startupinfo = None
            process_output = subprocess.check_output(
                command_arguments,
                startupinfo=startupinfo).decode(locale.getpreferredencoding())

            Logger.log("d", process_output)

            with open(temporary_path, "r", encoding="utf-8") as temporary_file:
                result_gcode = temporary_file.read()
            os.remove(temporary_path)

            gcode_list = result_gcode.split(layer_separator)
            if header != "":
                gcode_list.insert(0, header)  # add header back in front
            gcode_list[0] += processed_marker
            gcode_dict[plate_id] = gcode_list
            dict_changed = True

        if dict_changed:
            setattr(scene, "gcode_dict", gcode_dict)
Esempio n. 29
0
# WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612
if Platform.isLinux(): # Needed for platform.linux_distribution, which is not available on Windows and OSX
    # For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
    # The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
    try:
        import ctypes
        from ctypes.util import find_library
        libGL = find_library("GL")
        ctypes.CDLL(libGL, ctypes.RTLD_GLOBAL)
    except:
        # GLES-only systems (e.g. ARM Mali) do not have libGL, ignore error
        pass

# When frozen, i.e. installer version, don't let PYTHONPATH mess up the search path for DLLs.
if Platform.isWindows() and hasattr(sys, "frozen"):
    try:
        del os.environ["PYTHONPATH"]
    except KeyError:
        pass

# WORKAROUND: GITHUB-704 GITHUB-708
# It looks like setuptools creates a .pth file in
# the default /usr/lib which causes the default site-packages
# to be inserted into sys.path before PYTHONPATH.
# This can cause issues such as having libsip loaded from
# the system instead of the one provided with Cura, which causes
# incompatibility issues with libArcus
if "PYTHONPATH" in os.environ.keys():                       # If PYTHONPATH is used
    PYTHONPATH = os.environ["PYTHONPATH"].split(os.pathsep) # Get the value, split it..
    PYTHONPATH.reverse()                                    # and reverse it, because we always insert at 1
Esempio n. 30
0
from UM.Platform import Platform

#WORKAROUND: GITHUB-88 GITHUB-385 GITHUB-612
if Platform.isLinux(
):  # Needed for platform.linux_distribution, which is not available on Windows and OSX
    # For Ubuntu: https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
    if platform.linux_distribution()[0] in (
            "debian", "Ubuntu", "LinuxMint"
    ):  # TODO: Needs a "if X11_GFX == 'nvidia'" here. The workaround is only needed on Ubuntu+NVidia drivers. Other drivers are not affected, but fine with this fix.
        import ctypes
        from ctypes.util import find_library
        libGL = find_library("GL")
        ctypes.CDLL(libGL, ctypes.RTLD_GLOBAL)

# When frozen, i.e. installer version, don't let PYTHONPATH mess up the search path for DLLs.
if Platform.isWindows() and hasattr(sys, "frozen"):
    try:
        del os.environ["PYTHONPATH"]
    except KeyError:
        pass

#WORKAROUND: GITHUB-704 GITHUB-708
# It looks like setuptools creates a .pth file in
# the default /usr/lib which causes the default site-packages
# to be inserted into sys.path before PYTHONPATH.
# This can cause issues such as having libsip loaded from
# the system instead of the one provided with Cura, which causes
# incompatibility issues with libArcus
if "PYTHONPATH" in os.environ.keys():  # If PYTHONPATH is used
    PYTHONPATH = os.environ["PYTHONPATH"].split(
        os.pathsep)  # Get the value, split it..
Esempio n. 31
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)
Esempio n. 32
0
    def __init__(self) -> None:
        """Starts the back-end plug-in.

        This registers all the signal listeners and prepares for communication
        with the back-end in general.
        CuraEngineBackend is exposed to qml as well.
        """

        super().__init__()
        # Find out where the engine is located, and how it is called.
        # This depends on how Cura is packaged and which OS we are running on.
        executable_name = "CuraEngine"
        if Platform.isWindows():
            executable_name += ".exe"
        default_engine_location = executable_name

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

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

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

        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)
        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 = 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

        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()
        application.getPreferences().preferenceChanged.connect(
            self._onPreferencesChanged)

        application.initializationFinished.connect(self.initialize)
Esempio n. 33
0
    def exportProfile(self, instance_ids, file_name, file_type):
        # Parse the fileType to deduce what plugin can save the file format.
        # fileType has the format "<description> (*.<extension>)"
        split = file_type.rfind(
            " (*."
        )  # Find where the description ends and the extension starts.
        if split < 0:  # Not found. Invalid format.
            Logger.log("e", "Invalid file format identifier %s", file_type)
            return
        description = file_type[:split]
        extension = file_type[split + 4:-1]  # Leave out the " (*." and ")".
        if not file_name.endswith(
                "." + extension
        ):  # Auto-fill the extension if the user did not provide any.
            file_name += "." + extension

        # On Windows, QML FileDialog properly asks for overwrite confirm, but not on other platforms, so handle those ourself.
        if not Platform.isWindows():
            if os.path.exists(file_name):
                result = QMessageBox.question(
                    None, catalog.i18nc("@title:window",
                                        "File Already Exists"),
                    catalog.i18nc(
                        "@label Don't translate the XML tag <filename>!",
                        "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?"
                    ).format(file_name))
                if result == QMessageBox.No:
                    return
        found_containers = []
        extruder_positions = []
        for instance_id in instance_ids:
            containers = ContainerRegistry.getInstance(
            ).findInstanceContainers(id=instance_id)
            if containers:
                found_containers.append(containers[0])

                # Determine the position of the extruder of this container
                extruder_id = containers[0].getMetaDataEntry("extruder", "")
                if extruder_id == "":
                    # Global stack
                    extruder_positions.append(-1)
                else:
                    extruder_containers = ContainerRegistry.getInstance(
                    ).findDefinitionContainersMetadata(id=extruder_id)
                    if extruder_containers:
                        extruder_positions.append(
                            int(extruder_containers[0].get("position", 0)))
                    else:
                        extruder_positions.append(0)
        # Ensure the profiles are always exported in order (global, extruder 0, extruder 1, ...)
        found_containers = [
            containers
            for (positions, containers
                 ) in sorted(zip(extruder_positions, found_containers))
        ]
        base_containers = []
        quality_manager = QualityManager.getInstance()

        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        global_machine_definition = quality_manager.getParentMachineDefinition(
            global_container_stack.getBottom())
        for container in found_containers:
            if container.getMetaDataEntry("type") == "quality_changes":
                material = ContainerRegistry.getInstance(
                ).findInstanceContainers(
                    id=container.getMetaDataEntry("material"))
                if len(material) > 0:
                    base_container = quality_manager.findQualityByQualityType(
                        container.getMetaDataEntry("quality_type"),
                        global_machine_definition, [material[0].getMetaData()])
                    if base_container:
                        c = copy.deepcopy(base_container)
                        c.setMetaDataEntry("id", "base/" + container.id)
                        base_containers.append(c)
        found_containers.extend(base_containers)

        profile_writer = self._findProfileWriter(extension, description)

        try:
            success = profile_writer.write(file_name, found_containers)
        except Exception as e:
            Logger.log("e", "Failed to export profile to %s: %s", file_name,
                       str(e))
            m = Message(catalog.i18nc(
                "@info:status Don't translate the XML tags <filename> or <message>!",
                "Failed to export profile to <filename>{0}</filename>: <message>{1}</message>",
                file_name, str(e)),
                        lifetime=0,
                        title=catalog.i18nc("@info:title", "Error"))
            m.show()
            return
        if not success:
            Logger.log(
                "w",
                "Failed to export profile to %s: Writer plugin reported failure.",
                file_name)
            m = Message(catalog.i18nc(
                "@info:status Don't translate the XML tag <filename>!",
                "Failed to export profile to <filename>{0}</filename>: Writer plugin reported failure.",
                file_name),
                        lifetime=0,
                        title=catalog.i18nc("@info:title", "Error"))
            m.show()
            return
        m = Message(catalog.i18nc(
            "@info:status Don't translate the XML tag <filename>!",
            "Exported profile to <filename>{0}</filename>", file_name),
                    title=catalog.i18nc("@info:title", "Export succeeded"))
        m.show()
Esempio n. 34
0
    def __init__(self) -> None:
        super().__init__()
        executable_name = "CliParser"
        if Platform.isWindows():
            executable_name += ".exe"
        default_engine_location = executable_name
        if os.path.exists(os.path.join(SteSlicerApplication.getInstallPrefix(), "bin", executable_name)):
            default_engine_location = os.path.join(SteSlicerApplication.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 = SteSlicerApplication.getInstance() #type: SteSlicerApplication
        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 CliParser")

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

        default_engine_location = os.path.abspath(default_engine_location)
        self._application.getPreferences().addPreference("cli_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_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)

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

        self._start_slice_job = None  # type: Optional[StartSliceJob]
        self._process_cli_job = None # type: Optional[ProcessCliJob]
        self._start_slice_job_build_plate = None  # type: Optional[int]
        self._glicer_process = None
        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)
Esempio n. 35
0
import os.path  # To watch files for changes.
import threading
from typing import Callable, List, Optional, Set, Any, Dict

from PyQt6.QtCore import QFileSystemWatcher  # To watch files for changes.

from UM.Logger import Logger
from UM.Mesh.ReadMeshJob import ReadMeshJob  # To reload a mesh when its file was changed.
from UM.Message import Message  # To display a message for reloading files that were changed.
from UM.Scene.Camera import Camera
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Scene.SceneNode import SceneNode
from UM.Signal import Signal, signalemitter
from UM.i18n import i18nCatalog
from UM.Platform import Platform
if Platform.isWindows():
    from PyQt6.QtCore import QEventLoop  # Windows fix for using file watcher on removable devices.

i18n_catalog = i18nCatalog("uranium")


@signalemitter
class Scene:
    """Container object for the scene graph

    The main purpose of this class is to provide the root SceneNode.
    """
    def __init__(self) -> None:
        super().__init__()

        from UM.Scene.SceneNode import SceneNode
Esempio n. 36
0
 def __enter__(self) -> None:
     if Platform.isWindows():
         self._createLockFileWindows()
     else:
         self._createLockFile()
Esempio n. 37
0
 def removeWatchedFile(self, file_path: str) -> None:
     # The QT 5.10.0 issue, only on Windows. Cura crashes after loading a stl file from USB/sd-card/Cloud-based drive
     if not Platform.isWindows():
         self._file_watcher.removePath(file_path)
Esempio n. 38
0
    def __init__(self):
        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(Application.getInstallPrefix(), "bin", executable_name)):
            default_engine_location = os.path.join(Application.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 os.getenv("PATH").split(os.pathsep):
                execpath = os.path.join(pathdir, executable_name)
                if os.path.exists(execpath):
                    default_engine_location = execpath
                    break

        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)
        Preferences.getInstance().addPreference("backend/location", default_engine_location)

        self._scene = Application.getInstance().getController().getScene()
        self._scene.sceneChanged.connect(self._onSceneChanged)

        # Workaround to disable layer view processing if layer view is not active.
        self._layer_view_active = False
        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
        self._onActiveViewChanged()
        self._stored_layer_data = []
        self._stored_optimized_layer_data = []

        # Triggers for when to (re)start slicing:
        self._global_container_stack = None
        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
        self._onGlobalStackChanged()

        self._active_extruder_stack = None
        cura.Settings.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
        self._onActiveExtruderChanged()

        # 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()
        self._change_timer.setInterval(500)
        self._change_timer.setSingleShot(True)
        self._change_timer.timeout.connect(self.slice)

        # 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
        self._slicing = False  # Are we currently slicing?
        self._restart = False  # Back-end is currently restarting?
        self._enabled = True  # Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around.
        self._always_restart = True  # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
        self._process_layers_job = None  # The currently active job to process layers, or None if it is not processing layers.

        self._backend_log_max_lines = 20000  # Maximum number of lines to buffer
        self._error_message = None  # Pop-up message that shows errors.

        self.backendQuit.connect(self._onBackendQuit)
        self.backendConnected.connect(self._onBackendConnected)

        # When a tool operation is in progress, don't slice. So we need to listen for tool operations.
        Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
        Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)

        self._slice_start_time = None
Esempio n. 39
0
    def __init__(self, parent=None):
        super().__init__(parent=parent)
        # 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(Application.getInstallPrefix(), "bin",
                             executable_name)):
            default_engine_location = os.path.join(
                Application.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 os.getenv("PATH").split(os.pathsep):
                execpath = os.path.join(pathdir, executable_name)
                if os.path.exists(execpath):
                    default_engine_location = execpath
                    break

        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)
        Preferences.getInstance().addPreference("backend/location",
                                                default_engine_location)

        # Workaround to disable layer view processing if layer view is not active.
        self._layer_view_active = False
        Application.getInstance().getController().activeViewChanged.connect(
            self._onActiveViewChanged)
        Application.getInstance().getBuildPlateModel(
        ).activeBuildPlateChanged.connect(self._onActiveViewChanged)
        self._onActiveViewChanged()
        self._stored_layer_data = []
        self._stored_optimized_layer_data = {
        }  # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob

        self._scene = Application.getInstance().getController().getScene()
        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, it will set the "_is_error_check_scheduled" flag, 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
        Application.getInstance().globalContainerStackChanged.connect(
            self._onGlobalStackChanged)
        Application.getInstance().getExtruderManager().extrudersAdded.connect(
            self._onGlobalStackChanged)
        self._onGlobalStackChanged()

        Application.getInstance().stacksValidationFinished.connect(
            self._onStackErrorCheckFinished)

        # A flag indicating if an error check was scheduled
        # If so, we will stop the auto-slice timer and start upon the error check
        self._is_error_check_scheduled = False

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

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

        self.backendQuit.connect(self._onBackendQuit)
        self.backendConnected.connect(self._onBackendConnected)

        # When a tool operation is in progress, don't slice. So we need to listen for tool operations.
        Application.getInstance().getController().toolOperationStarted.connect(
            self._onToolOperationStarted)
        Application.getInstance().getController().toolOperationStopped.connect(
            self._onToolOperationStopped)

        self._slice_start_time = None

        Preferences.getInstance().addPreference("general/auto_slice", True)

        self._use_timer = False
        # 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()
        self._change_timer.setSingleShot(True)
        self._change_timer.setInterval(500)
        self.determineAutoSlicing()
        Preferences.getInstance().preferenceChanged.connect(
            self._onPreferencesChanged)
Esempio n. 40
0
    def exportProfile(self, instance_ids, file_name, file_type):
        # Parse the fileType to deduce what plugin can save the file format.
        # fileType has the format "<description> (*.<extension>)"
        split = file_type.rfind(
            " (*."
        )  # Find where the description ends and the extension starts.
        if split < 0:  # Not found. Invalid format.
            Logger.log("e", "Invalid file format identifier %s", file_type)
            return
        description = file_type[:split]
        extension = file_type[split + 4:-1]  # Leave out the " (*." and ")".
        if not file_name.endswith(
                "." + extension
        ):  # Auto-fill the extension if the user did not provide any.
            file_name += "." + extension

        # On Windows, QML FileDialog properly asks for overwrite confirm, but not on other platforms, so handle those ourself.
        if not Platform.isWindows():
            if os.path.exists(file_name):
                result = QMessageBox.question(
                    None, catalog.i18nc("@title:window",
                                        "File Already Exists"),
                    catalog.i18nc(
                        "@label",
                        "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?"
                    ).format(file_name))
                if result == QMessageBox.No:
                    return
        found_containers = []
        extruder_positions = []
        for instance_id in instance_ids:
            containers = ContainerRegistry.getInstance(
            ).findInstanceContainers(id=instance_id)
            if containers:
                found_containers.append(containers[0])

                # Determine the position of the extruder of this container
                extruder_id = containers[0].getMetaDataEntry("extruder", "")
                if extruder_id == "":
                    # Global stack
                    extruder_positions.append(-1)
                else:
                    extruder_containers = ContainerRegistry.getInstance(
                    ).findDefinitionContainers(id=extruder_id)
                    if extruder_containers:
                        extruder_positions.append(
                            int(extruder_containers[0].getMetaDataEntry(
                                "position", 0)))
                    else:
                        extruder_positions.append(0)
        # Ensure the profiles are always exported in order (global, extruder 0, extruder 1, ...)
        found_containers = [
            containers
            for (positions, containers
                 ) in sorted(zip(extruder_positions, found_containers))
        ]

        profile_writer = self._findProfileWriter(extension, description)

        try:
            success = profile_writer.write(file_name, found_containers)
        except Exception as e:
            Logger.log("e", "Failed to export profile to %s: %s", file_name,
                       str(e))
            m = Message(catalog.i18nc(
                "@info:status",
                "Failed to export profile to <filename>{0}</filename>: <message>{1}</message>",
                file_name, str(e)),
                        lifetime=0)
            m.show()
            return
        if not success:
            Logger.log(
                "w",
                "Failed to export profile to %s: Writer plugin reported failure.",
                file_name)
            m = Message(catalog.i18nc(
                "@info:status",
                "Failed to export profile to <filename>{0}</filename>: Writer plugin reported failure.",
                file_name),
                        lifetime=0)
            m.show()
            return
        m = Message(
            catalog.i18nc("@info:status",
                          "Exported profile to <filename>{0}</filename>",
                          file_name))
        m.show()
Esempio n. 41
0
    def exportProfile(self, id, name, url, fileType):
        #Input checking.
        path = url.toLocalFile()
        if not path:
            error_str = "Not a valid path. If this problem persists, please report a bug."
            error_str = i18nCatalog.i18nc("@info:status", error_str)
            return {"status": "error", "message": error_str}

        # On Windows, QML FileDialog properly asks for overwrite confirm, but not on other platforms, so handle those ourself.
        if not Platform.isWindows():
            if os.path.exists(path):
                result = QMessageBox.question(
                    None, catalog.i18nc("@title:window",
                                        "File Already Exists"),
                    catalog.i18nc(
                        "@label",
                        "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?"
                    ).format(path))
                if result == QMessageBox.No:
                    return

        if id == -1:
            #id -1 references the "Current settings"/working profile
            profile = copy.deepcopy(self._working_profile)
            profile.setType(None)
            profile.setMachineTypeId(self._manager.getActiveMachineInstance(
            ).getMachineDefinition().getProfilesMachineId())
        else:
            profile = self._manager.findProfile(
                name, instance=self._manager.getActiveMachineInstance())
        if not profile:
            error_str = "Profile not found. If this problem persists, please report a bug."
            error_str = i18nCatalog.i18nc("@info:status", error_str)
            return {"status": "error", "message": error_str}

        #Parse the fileType to deduce what plugin can save the file format.
        #TODO: This parsing can be made unnecessary by storing for each plugin what the fileType string is in complete (in addition to the {(description,extension)} dict).
        #fileType has the format "<description> (*.<extension>)"
        split = fileType.rfind(
            " (*.")  #Find where the description ends and the extension starts.
        if split < 0:  #Not found. Invalid format.
            Logger.log("e", "Invalid file format identifier %s", fileType)
            error_str = catalog.i18nc("@info:status",
                                      "Invalid file format: {0}", fileType)
            return {"status": "error", "message": error_str}
        description = fileType[:split]
        extension = fileType[split + 4:-1]  #Leave out the " (*." and ")".
        if not path.endswith(
                "." + extension
        ):  #Auto-fill the extension if the user did not provide any.
            path += "." + extension

        good_profile_writer = None
        for profile_writer_id, profile_writer in self._manager.getProfileWriters(
        ):  #Find which profile writer can write this file type.
            meta_data = PluginRegistry.getInstance().getMetaData(
                profile_writer_id)
            if "profile_writer" in meta_data:
                for supported_type in meta_data[
                        "profile_writer"]:  #All file types this plugin can supposedly write.
                    supported_extension = supported_type.get("extension", None)
                    if supported_extension == extension:  #This plugin supports a file type with the same extension.
                        supported_description = supported_type.get(
                            "description", None)
                        if supported_description == description:  #The description is also identical. Assume it's the same file type.
                            good_profile_writer = profile_writer
                            break
            if good_profile_writer:  #If we found a writer in this iteration, break the loop.
                break

        success = False
        try:
            success = good_profile_writer.write(path, profile)
        except Exception as e:
            Logger.log("e", "Failed to export profile to %s: %s", path, str(e))
            error_str = catalog.i18nc(
                "@info:status",
                "Failed to export profile to <filename>{0}</filename>: <message>{1}</message>",
                path, str(e))
            return {"status": "error", "message": error_str}
        if not success:
            Logger.log(
                "w",
                "Failed to export profile to %s: Writer plugin reported failure.",
                path)
            error_str = catalog.i18nc(
                "@info:status",
                "Failed to export profile to <filename>{0}</filename>: Writer plugin reported failure.",
                path)
            return {"status": "error", "message": error_str}
        m = Message(
            catalog.i18nc("@info:status",
                          "Exported profile to <filename>{0}</filename>",
                          path))
        m.show()
Esempio n. 42
0
    def __init__(self):
        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.
        default_engine_location = os.path.join(Application.getInstallPrefix(), "bin", "CuraEngine")
        if hasattr(sys, "frozen"):
            default_engine_location = os.path.join(os.path.dirname(os.path.abspath(sys.executable)), "CuraEngine")
        if Platform.isWindows():
            default_engine_location += ".exe"
        default_engine_location = os.path.abspath(default_engine_location)
        Preferences.getInstance().addPreference("backend/location", default_engine_location)

        self._scene = Application.getInstance().getController().getScene()
        self._scene.sceneChanged.connect(self._onSceneChanged)

        # Workaround to disable layer view processing if layer view is not active.
        self._layer_view_active = False
        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
        self._onActiveViewChanged()
        self._stored_layer_data = []

        #Triggers for when to (re)start slicing:
        self._global_container_stack = None
        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
        self._onGlobalStackChanged()

        self._active_extruder_stack = None
        ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
        self._onActiveExtruderChanged()

        #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()
        self._change_timer.setInterval(500)
        self._change_timer.setSingleShot(True)
        self._change_timer.timeout.connect(self.slice)

        #Listeners for receiving messages from the back-end.
        self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
        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.ObjectPrintTime"] = self._onObjectPrintTimeMessage
        self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage

        self._start_slice_job = None
        self._slicing = False #Are we currently slicing?
        self._restart = False #Back-end is currently restarting?
        self._enabled = True #Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around.
        self._always_restart = True #Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
        self._process_layers_job = None #The currently active job to process layers, or None if it is not processing layers.

        self._error_message = None #Pop-up message that shows errors.

        self.backendQuit.connect(self._onBackendQuit)
        self.backendConnected.connect(self._onBackendConnected)

        #When a tool operation is in progress, don't slice. So we need to listen for tool operations.
        Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
        Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)
Esempio n. 43
0
    def __init__(self, parent = None):
        super().__init__(parent = parent)
        # 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(Application.getInstallPrefix(), "bin", executable_name)):
            default_engine_location = os.path.join(Application.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 os.getenv("PATH").split(os.pathsep):
                execpath = os.path.join(pathdir, executable_name)
                if os.path.exists(execpath):
                    default_engine_location = execpath
                    break

        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)
        Preferences.getInstance().addPreference("backend/location", default_engine_location)

        # Workaround to disable layer view processing if layer view is not active.
        self._layer_view_active = False
        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
        self._onActiveViewChanged()
        self._stored_layer_data = []
        self._stored_optimized_layer_data = []

        self._scene = Application.getInstance().getController().getScene()
        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, it will set the "_is_error_check_scheduled" flag, 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
        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
        self._onGlobalStackChanged()

        Application.getInstance().stacksValidationFinished.connect(self._onStackErrorCheckFinished)

        # A flag indicating if an error check was scheduled
        # If so, we will stop the auto-slice timer and start upon the error check
        self._is_error_check_scheduled = False

        # 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
        self._slicing = False  # Are we currently slicing?
        self._restart = False  # Back-end is currently restarting?
        self._tool_active = False  # If a tool is active, some tasks do not have to do anything
        self._always_restart = True  # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
        self._process_layers_job = None  # The currently active job to process layers, or None if it is not processing layers.
        self._need_slicing = False
        self._engine_is_fresh = True  # Is the newly started engine used before or not?

        self._backend_log_max_lines = 20000  # Maximum number of lines to buffer
        self._error_message = None  # Pop-up message that shows errors.
        self._last_num_objects = 0  # Count number of objects to see if there is something changed
        self._postponed_scene_change_sources = []  # scene change is postponed (by a tool)

        self.backendQuit.connect(self._onBackendQuit)
        self.backendConnected.connect(self._onBackendConnected)

        # When a tool operation is in progress, don't slice. So we need to listen for tool operations.
        Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
        Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)

        self._slice_start_time = None

        Preferences.getInstance().addPreference("general/auto_slice", True)

        self._use_timer = False
        # 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()
        self._change_timer.setSingleShot(True)
        self._change_timer.setInterval(500)
        self.determineAutoSlicing()
        Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged)
Esempio n. 44
0
import numpy
import math
#from . import trimesh

from UM.Platform import Platform
from UM.Extension import Extension
from UM.Application import Application
from UM.Logger import Logger

from UM.Mesh.MeshData import MeshData, calculateNormalsFromIndexedVertices
from UM.Math.Vector import Vector

from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")

if (Platform.isWindows()):
    from .ThirdParty.win import trimesh
elif (Platform.isOSX()):
    from .ThirdParty.mac import trimesh


class SupportMeshCreator():
    def __init__(self,
                 support_angle=None,
                 filter_upwards_facing_faces=True,
                 down_vector=numpy.array([0, -1, 0]),
                 bottom_cut_off=0,
                 minimum_island_area=0):
        self._support_angle = support_angle
        if self._support_angle is None:
            global_container_stack = Application.getInstance(
Esempio n. 45
0
    def __init__(self):
        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(Application.getInstallPrefix(), "bin", executable_name)):
            default_engine_location = os.path.join(Application.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 os.getenv("PATH").split(os.pathsep):
                execpath = os.path.join(pathdir, executable_name)
                if os.path.exists(execpath):
                    default_engine_location = execpath
                    break

        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)
        Preferences.getInstance().addPreference("backend/location", default_engine_location)

        self._scene = Application.getInstance().getController().getScene()
        self._scene.sceneChanged.connect(self._onSceneChanged)

        self._pause_slicing = False

        # Workaround to disable layer view processing if layer view is not active.
        self._layer_view_active = False
        Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
        self._onActiveViewChanged()
        self._stored_layer_data = []
        self._stored_optimized_layer_data = []

        # Triggers for when to (re)start slicing:
        self._global_container_stack = None
        Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
        self._onGlobalStackChanged()

        self._active_extruder_stack = None
        cura.Settings.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged)
        self._onActiveExtruderChanged()

        # 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()
        self._change_timer.setInterval(500)
        self._change_timer.setSingleShot(True)
        self._change_timer.timeout.connect(self.slice)

        # 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
        self._slicing = False  # Are we currently slicing?
        self._restart = False  # Back-end is currently restarting?
        self._enabled = True  # Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around.
        self._always_restart = True  # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
        self._process_layers_job = None  # The currently active job to process layers, or None if it is not processing layers.

        self._backend_log_max_lines = 20000  # Maximum number of lines to buffer
        self._error_message = None  # Pop-up message that shows errors.

        self.backendQuit.connect(self._onBackendQuit)
        self.backendConnected.connect(self._onBackendConnected)

        # When a tool operation is in progress, don't slice. So we need to listen for tool operations.
        Application.getInstance().getController().toolOperationStarted.connect(self._onToolOperationStarted)
        Application.getInstance().getController().toolOperationStopped.connect(self._onToolOperationStopped)

        self._slice_start_time = None
Esempio n. 46
0
    def __init__(self):
        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.
        default_engine_location = os.path.join(Application.getInstallPrefix(),
                                               "bin", "CuraEngine")
        if hasattr(sys, "frozen"):
            default_engine_location = os.path.join(
                os.path.dirname(os.path.abspath(sys.executable)), "CuraEngine")
        if Platform.isWindows():
            default_engine_location += ".exe"
        default_engine_location = os.path.abspath(default_engine_location)
        Preferences.getInstance().addPreference("backend/location",
                                                default_engine_location)

        self._scene = Application.getInstance().getController().getScene()
        self._scene.sceneChanged.connect(self._onSceneChanged)

        # Workaround to disable layer view processing if layer view is not active.
        self._layer_view_active = False
        Application.getInstance().getController().activeViewChanged.connect(
            self._onActiveViewChanged)
        self._onActiveViewChanged()
        self._stored_layer_data = []

        #Triggers for when to (re)start slicing:
        self._global_container_stack = None
        Application.getInstance().globalContainerStackChanged.connect(
            self._onGlobalStackChanged)
        self._onGlobalStackChanged()

        self._active_extruder_stack = None
        ExtruderManager.getInstance().activeExtruderChanged.connect(
            self._onActiveExtruderChanged)
        self._onActiveExtruderChanged()

        #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()
        self._change_timer.setInterval(500)
        self._change_timer.setSingleShot(True)
        self._change_timer.timeout.connect(self.slice)

        #Listeners for receiving messages from the back-end.
        self._message_handlers["cura.proto.Layer"] = self._onLayerMessage
        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.ObjectPrintTime"] = self._onObjectPrintTimeMessage
        self._message_handlers[
            "cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage

        self._start_slice_job = None
        self._slicing = False  #Are we currently slicing?
        self._restart = False  #Back-end is currently restarting?
        self._enabled = True  #Should we be slicing? Slicing might be paused when, for instance, the user is dragging the mesh around.
        self._always_restart = True  #Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
        self._process_layers_job = None  #The currently active job to process layers, or None if it is not processing layers.

        self._error_message = None  #Pop-up message that shows errors.

        self.backendQuit.connect(self._onBackendQuit)
        self.backendConnected.connect(self._onBackendConnected)

        #When a tool operation is in progress, don't slice. So we need to listen for tool operations.
        Application.getInstance().getController().toolOperationStarted.connect(
            self._onToolOperationStarted)
        Application.getInstance().getController().toolOperationStopped.connect(
            self._onToolOperationStopped)
Esempio n. 47
0
    def exportContainer(
            self, container_id: str, file_type: str,
            file_url_or_string: Union[QUrl, str]) -> Dict[str, str]:
        if not container_id or not file_type or not file_url_or_string:
            return {"status": "error", "message": "Invalid arguments"}

        if isinstance(file_url_or_string, QUrl):
            file_url = file_url_or_string.toLocalFile()
        else:
            file_url = file_url_or_string

        if not file_url:
            return {"status": "error", "message": "Invalid path"}

        if file_type not in self._container_name_filters:
            try:
                mime_type = MimeTypeDatabase.getMimeTypeForFile(file_url)
            except MimeTypeNotFoundError:
                return {"status": "error", "message": "Unknown File Type"}
        else:
            mime_type = self._container_name_filters[file_type]["mime"]

        containers = cura.CuraApplication.CuraApplication.getInstance(
        ).getContainerRegistry().findContainers(id=container_id)
        if not containers:
            return {"status": "error", "message": "Container not found"}
        container = containers[0]

        if Platform.isOSX() and "." in file_url:
            file_url = file_url[:file_url.rfind(".")]

        for suffix in mime_type.suffixes:
            if file_url.endswith(suffix):
                break
        else:
            file_url += "." + mime_type.preferredSuffix

        if not Platform.isWindows():
            if os.path.exists(file_url):
                result = QMessageBox.question(
                    None, catalog.i18nc("@title:window",
                                        "File Already Exists"),
                    catalog.i18nc(
                        "@label Don't translate the XML tag <filename>!",
                        "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?"
                    ).format(file_url))
                if result == QMessageBox.No:
                    return {"status": "cancelled", "message": "User cancelled"}

        try:
            contents = container.serialize()
        except NotImplementedError:
            return {
                "status": "error",
                "message": "Unable to serialize container"
            }

        if contents is None:
            return {
                "status": "error",
                "message":
                "Serialization returned None. Unable to write to file"
            }

        with SaveFile(file_url, "w") as f:
            f.write(contents)

        return {
            "status": "success",
            "message": "Successfully exported container",
            "path": file_url
        }
Esempio n. 48
0
    def exportContainer(
            self, container_id: str, file_type: str,
            file_url_or_string: Union[QUrl, str]) -> Dict[str, str]:
        """Export a container to a file

        :param container_id: The ID of the container to export
        :param file_type: The type of file to save as. Should be in the form of "description (*.extension, *.ext)"
        :param file_url_or_string: The URL where to save the file.

        :return: A dictionary containing a key "status" with a status code and a key "message" with a message
        explaining the status. The status code can be one of "error", "cancelled", "success"
        """

        if not container_id or not file_type or not file_url_or_string:
            return {"status": "error", "message": "Invalid arguments"}

        if isinstance(file_url_or_string, QUrl):
            file_url = file_url_or_string.toLocalFile()
        else:
            file_url = file_url_or_string

        if not file_url:
            return {"status": "error", "message": "Invalid path"}

        if file_type not in self._container_name_filters:
            try:
                mime_type = MimeTypeDatabase.getMimeTypeForFile(file_url)
            except MimeTypeNotFoundError:
                return {"status": "error", "message": "Unknown File Type"}
        else:
            mime_type = self._container_name_filters[file_type]["mime"]

        containers = cura.CuraApplication.CuraApplication.getInstance(
        ).getContainerRegistry().findContainers(id=container_id)
        if not containers:
            return {"status": "error", "message": "Container not found"}
        container = containers[0]

        if Platform.isOSX() and "." in file_url:
            file_url = file_url[:file_url.rfind(".")]

        for suffix in mime_type.suffixes:
            if file_url.endswith(suffix):
                break
        else:
            file_url += "." + mime_type.preferredSuffix

        if not Platform.isWindows():
            if os.path.exists(file_url):
                result = QMessageBox.question(
                    None, catalog.i18nc("@title:window",
                                        "File Already Exists"),
                    catalog.i18nc(
                        "@label Don't translate the XML tag <filename>!",
                        "The file <filename>{0}</filename> already exists. Are you sure you want to overwrite it?"
                    ).format(file_url))
                if result == QMessageBox.No:
                    return {"status": "cancelled", "message": "User cancelled"}

        try:
            contents = container.serialize()
        except NotImplementedError:
            return {
                "status": "error",
                "message": "Unable to serialize container"
            }

        if contents is None:
            return {
                "status": "error",
                "message":
                "Serialization returned None. Unable to write to file"
            }

        try:
            with SaveFile(file_url, "w") as f:
                f.write(contents)
        except OSError:
            return {
                "status": "error",
                "message": "Unable to write to this location.",
                "path": file_url
            }

        Logger.info(
            "Successfully exported container to {path}".format(path=file_url))
        return {
            "status": "success",
            "message": "Successfully exported container",
            "path": file_url
        }