Esempio n. 1
0
    def _getAllBroadcast(self):
        ipconfig_process = subprocess.Popen('ifconfig' if Platform.isLinux() or Platform.isOSX() else 'ipconfig', stdout=subprocess.PIPE, shell=True)
        output = ipconfig_process.stdout.read().decode('utf-8', 'ignore')
        allIPlist = re.findall('\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}', output)
        allIP = ''
        for j in allIPlist:
            if j.count('255.255') < 1:
                allIP += j + '   '

        ipList = gethostbyname_ex(gethostname())
        if len(ipList) == 3:
            ipList = ipList[2]
        broadcast = []
        for i in range(len(allIPlist)):
            ipaddr = allIPlist[i]
            if ipaddr in ipList and ipaddr != '127.0.0.1' and i + 1 <= len(allIPlist):
                if Platform.isLinux() or Platform.isOSX():
                    broadcast.append(allIPlist[(i + 1)])
                else:
                    broadcast.append(self._generate_broad_addr(ipaddr, allIPlist[(i + 1)]))

        if not broadcast:
            Logger.log("w", "Cann't find valid boradcast,use all IP")
            broadcast = allIPlist
        return broadcast
Esempio n. 2
0
    def _updateContainerNameFilters(self) -> None:
        self._container_name_filters = {}
        plugin_registry = cura.CuraApplication.CuraApplication.getInstance(
        ).getPluginRegistry()
        container_registry = cura.CuraApplication.CuraApplication.getInstance(
        ).getContainerRegistry()
        for plugin_id, container_type in container_registry.getContainerTypes(
        ):
            # Ignore default container types since those are not plugins
            if container_type in (InstanceContainer, ContainerStack,
                                  DefinitionContainer, GlobalStack,
                                  ExtruderStack):
                continue

            serialize_type = ""
            try:
                plugin_metadata = plugin_registry.getMetaData(plugin_id)
                if plugin_metadata:
                    serialize_type = plugin_metadata["settings_container"][
                        "type"]
                else:
                    continue
            except KeyError as e:
                continue

            mime_type = container_registry.getMimeTypeForContainer(
                container_type)
            if mime_type is None:
                continue
            entry = {
                "type": serialize_type,
                "mime": mime_type,
                "container": container_type
            }

            suffix = mime_type.preferredSuffix
            if Platform.isOSX() and "." in suffix:
                # OSX's File dialog is stupid and does not allow selecting files with a . in its name
                suffix = suffix[suffix.index(".") + 1:]

            suffix_list = "*." + suffix
            for suffix in mime_type.suffixes:
                if suffix == mime_type.preferredSuffix:
                    continue

                if Platform.isOSX() and "." in suffix:
                    # OSX's File dialog is stupid and does not allow selecting files with a . in its name
                    suffix = suffix[suffix.index("."):]

                suffix_list += ", *." + suffix

            name_filter = "{0} ({1})".format(mime_type.comment, suffix_list)
            self._container_name_filters[name_filter] = entry
Esempio n. 3
0
def register(app):
    if Platform.isWindows() or Platform.isLinux() or Platform.isOSX(): 
        from . import OpenSCADReader # @UnresolvedImport
        return {"mesh_reader": OpenSCADReader.OpenSCADReader()}
    else:
        Logger.logException("i", "Unsupported OS!")
        return {}
Esempio n. 4
0
def getMetaData() -> Dict:
    # Workarround for osx not supporting double file extensions correclty.
    if Platform.isOSX():
        workspace_extension = "3mf"
    else:
        workspace_extension = "curaproject.3mf"

    metaData = {
        "plugin": {
            "name": catalog.i18nc("@label", "3MF Reader"),
            "author": "Ultimaker",
            "version": "1.0",
            "description": catalog.i18nc("@info:whatsthis", "Provides support for reading 3MF files."),
            "api": 3
        }
    }
    if "3MFReader.ThreeMFReader" in sys.modules:
        metaData["mesh_reader"] = [
            {
                "extension": "3mf",
                "description": catalog.i18nc("@item:inlistbox", "3MF File")
            }
        ]
        metaData["workspace_reader"] = [
            {
                "extension": workspace_extension,
                "description": catalog.i18nc("@item:inlistbox", "3MF File")
            }
        ]
    
    return metaData
Esempio n. 5
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. 6
0
    def _sendCrashReport(self):
        # Before sending data, the user comments are stored
        self.data["user_info"] = self.user_description_text_area.toPlainText()

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

        # Submit data
        kwoptions = {"data": binary_data, "timeout": 5}

        if Platform.isOSX():
            kwoptions["context"] = ssl._create_unverified_context()

        Logger.log("i", "Sending crash report info to [%s]...", self.crash_url)

        try:
            f = urllib.request.urlopen(self.crash_url, **kwoptions)
            Logger.log("i", "Sent crash report info.")
            f.close()
        except urllib.error.HTTPError:
            Logger.logException(
                "e",
                "An HTTP error occurred while trying to send crash report")
        except Exception:  # We don't want any exception to cause problems
            Logger.logException(
                "e", "An exception occurred while trying to send crash report")

        os._exit(1)
Esempio n. 7
0
    def setBlenderPath(self, outdated=False):
        """Tries to set the path to blender automatically, if unsuccessful the user can set it manually.

        :param outdated: Flag if the found blender version is outdated.
        """

        # Stops here because blender path from settings file is correct.
        if not outdated_blender_version or outdated:
            # Supports multi-platform
            if Platform.isWindows():
                temp_blender_path = glob.glob(
                    'C:/Program Files/Blender Foundation/**/*.exe')
                blender_path = temp_blender_path[len(temp_blender_path) -
                                                 1].replace('\\', '/')
            elif Platform.isOSX():
                blender_path = '/Applications/Blender.app/Contents/MacOS/Blender'
            elif Platform.isLinux():
                blender_path = '/usr/bin/blender'
            else:
                blender_path = None

            # If unsuccessful the user can set it manually.
            if not os.path.exists(blender_path):
                self._openFileDialog()
            else:
                # Adds blender path in settings file.
                Application.getInstance().getPreferences().setValue(
                    'cura_blender/blender_path', blender_path)
                self.verifyBlenderPath(manual=False)
Esempio n. 8
0
    def run(self):
        if not self.url or not self.data:
            Logger.log("e", "URL or DATA for sending slice info was not set!")
            return

        # Submit data
        kwoptions = {"data": self.data, "timeout": 5}

        if Platform.isOSX():
            kwoptions["context"] = ssl._create_unverified_context()

        try:
            f = urllib.request.urlopen(self.url, **kwoptions)
            Logger.log("i", "Sent anonymous slice info to %s", self.url)
            f.close()
        except urllib.error.HTTPError as http_exception:
            Logger.log(
                "e",
                "An HTTP error occurred while trying to send slice information: %s"
                % http_exception)
        except Exception as e:  # We don't want any exception to cause problems
            Logger.log(
                "e",
                "An exception occurred while trying to send slice information: %s"
                % e)
Esempio n. 9
0
    def event(self, event):
        if event.type == Event.ViewActivateEvent:
            # FIX: on Max OS X, somehow QOpenGLContext.currentContext() can become None during View switching.
            # This can happen when you do the following steps:
            #   1. Start Cura
            #   2. Load a model
            #   3. Switch to Custom mode
            #   4. Select the model and click on the per-object tool icon
            #   5. Switch view to Layer view or X-Ray
            #   6. Cura will very likely crash
            # It seems to be a timing issue that the currentContext can somehow be empty, but I have no clue why.
            # This fix tries to reschedule the view changing event call on the Qt thread again if the current OpenGL
            # context is None.
            if Platform.isOSX():
                if QOpenGLContext.currentContext() is None:
                    Logger.log("d", "current context of OpenGL is empty on Mac OS X, will try to create shaders later")
                    Application.getInstance().callLater(lambda e = event: self.event(e))
                    return


        if event.type == Event.ViewDeactivateEvent:
            if self._composite_pass and 'xray' in self._composite_pass.getLayerBindings():
                self.getRenderer().removeRenderPass(self._xray_pass)
                self._composite_pass.setLayerBindings(self._old_layer_bindings)
                self._composite_pass.setCompositeShader(self._old_composite_shader)
                self._xray_warning_message.hide()
Esempio n. 10
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. 11
0
    def __init__(self, *args):
        super().__init__(*args)

        self._previous_extension = ""
        # Only do this on OS X
        if Platform.isOSX():
            self.filterSelected.connect(self._onFilterChanged)
Esempio n. 12
0
def getMetaData() -> Dict:
    # Workarround for osx not supporting double file extensions correclty.
    if Platform.isOSX():
        workspace_extension = "3mf"
    else:
        workspace_extension = "curaproject.3mf"
    return {
        "plugin": {
            "name":
            catalog.i18nc("@label", "3MF Reader"),
            "author":
            "Ultimaker",
            "version":
            "1.0",
            "description":
            catalog.i18nc("@info:whatsthis",
                          "Provides support for reading 3MF files."),
            "api":
            3
        },
        "mesh_reader": [{
            "extension":
            "3mf",
            "description":
            catalog.i18nc("@item:inlistbox", "3MF File")
        }],
        "workspace_reader": [{
            "extension":
            workspace_extension,
            "description":
            catalog.i18nc("@item:inlistbox", "3MF File")
        }]
    }
Esempio n. 13
0
 def get_steslicer_dir_path():
     if Platform.isWindows():
         return os.path.expanduser("~/AppData/Roaming/steslicer")
     elif Platform.isLinux():
         return os.path.expanduser("~/.local/share/steslicer")
     elif Platform.isOSX():
         return os.path.expanduser("~/Library/Logs/steslicer")
Esempio n. 14
0
def register(app):
    if Platform.isWindows():
        from . import BigtreeWindowsRemovableDrivePlugin
        return {
            "output_device":
            BigtreeWindowsRemovableDrivePlugin.
            BigtreeWindowsRemovableDrivePlugin()
        }
    elif Platform.isOSX():
        from . import BigtreeOSXRemovableDrivePlugin
        return {
            "output_device":
            BigtreeOSXRemovableDrivePlugin.BigtreeOSXRemovableDrivePlugin()
        }
    elif Platform.isLinux():
        from . import BigtreeLinuxRemovableDrivePlugin
        return {
            "output_device":
            BigtreeLinuxRemovableDrivePlugin.BigtreeLinuxRemovableDrivePlugin(
            )
        }
    else:
        Logger.log(
            "e",
            "Unsupported system, thus no removable device hotplugging support available."
        )
        return {}
Esempio n. 15
0
 def get_cura_dir_path():
     if Platform.isWindows():
         return os.path.expanduser("~/AppData/Roaming/continuum-test")
     elif Platform.isLinux():
         return os.path.expanduser("~/.local/share/continuum-test")
     elif Platform.isOSX():
         return os.path.expanduser("~/Library/Logs/continuum-test")
Esempio n. 16
0
def get_cura_dir_for_stdoutputs() -> str:
    if Platform.isWindows():
        return os.path.expanduser("~/AppData/Roaming/cura/")
    elif Platform.isLinux():
        return os.path.expanduser("~/.local/share/cura")
    elif Platform.isOSX():
        return os.path.expanduser("~/Library/Logs/cura")
Esempio n. 17
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. 18
0
def getMetaData():
    # Workarround for osx not supporting double file extensions correctly.
    if Platform.isOSX():
        workspace_extension = "3mf"
    else:
        workspace_extension = "curaproject.3mf"

    metaData = {}

    if "3MFWriter.ThreeMFWriter" in sys.modules:
        metaData["mesh_writer"] = {
            "output": [{
                "extension": "3mf",
                "description": i18n_catalog.i18nc("@item:inlistbox", "3MF file"),
                "mime_type": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml",
                "mode": ThreeMFWriter.ThreeMFWriter.OutputMode.BinaryMode
            }]
        }
        metaData["workspace_writer"] = {
            "output": [{
                "extension": workspace_extension,
                "description": i18n_catalog.i18nc("@item:inlistbox", "Cura Project 3MF file"),
                "mime_type": "application/x-curaproject+xml",
                "mode": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter.OutputMode.BinaryMode
            }]
        }

    return metaData
Esempio n. 19
0
    def _sendCrashReport(self):
        # Before sending data, the user comments are stored
        self.data["user_info"] = self.user_description_text_area.toPlainText()

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

        # Submit data
        kwoptions = {"data": binary_data, "timeout": 5}

        if Platform.isOSX():
            kwoptions["context"] = ssl._create_unverified_context()

        Logger.log("i", "Sending crash report info to [%s]...", self.crash_url)
        if not self.has_started:
            print("Sending crash report info to [%s]...\n" % self.crash_url)

        try:
            f = urllib.request.urlopen(self.crash_url, **kwoptions)
            Logger.log("i", "Sent crash report info.")
            if not self.has_started:
                print("Sent crash report info.\n")
            f.close()
        except urllib.error.HTTPError as e:
            Logger.logException("e", "An HTTP error occurred while trying to send crash report")
            if not self.has_started:
                print("An HTTP error occurred while trying to send crash report: %s" % e)
        except Exception as e:  # We don't want any exception to cause problems
            Logger.logException("e", "An exception occurred while trying to send crash report")
            if not self.has_started:
                print("An exception occurred while trying to send crash report: %s" % e)

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

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

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

            if not self._xray_composite_shader:
                self._xray_composite_shader = OpenGL.getInstance(
                ).createShaderProgram(
                    Resources.getPath(Resources.Shaders,
                                      "xray_composite.shader"))
                theme = Application.getInstance().getTheme()
                self._xray_composite_shader.setUniformValue(
                    "u_background_color",
                    Color(*theme.getColor("viewport_background").getRgb()))
                self._xray_composite_shader.setUniformValue(
                    "u_outline_color",
                    Color(*theme.getColor("model_selection_outline").getRgb()))

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

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

        if event.type == Event.ViewDeactivateEvent:
            self.getRenderer().removeRenderPass(self._xray_pass)
            self._composite_pass.setLayerBindings(self._old_layer_bindings)
            self._composite_pass.setCompositeShader(self._old_composite_shader)
Esempio n. 22
0
    def _updateContainerNameFilters(self) -> None:
        self._container_name_filters = {}
        for plugin_id, container_type in self._container_registry.getContainerTypes():
            # Ignore default container types since those are not plugins
            if container_type in (InstanceContainer, ContainerStack, DefinitionContainer):
                continue

            serialize_type = ""
            try:
                plugin_metadata = self._plugin_registry.getMetaData(plugin_id)
                if plugin_metadata:
                    serialize_type = plugin_metadata["settings_container"]["type"]
                else:
                    continue
            except KeyError as e:
                continue

            mime_type = self._container_registry.getMimeTypeForContainer(container_type)
            if mime_type is None:
                continue
            entry = {
                "type": serialize_type,
                "mime": mime_type,
                "container": container_type
            }

            suffix = mime_type.preferredSuffix
            if Platform.isOSX() and "." in suffix:
                # OSX's File dialog is stupid and does not allow selecting files with a . in its name
                suffix = suffix[suffix.index(".") + 1:]

            suffix_list = "*." + suffix
            for suffix in mime_type.suffixes:
                if suffix == mime_type.preferredSuffix:
                    continue

                if Platform.isOSX() and "." in suffix:
                    # OSX's File dialog is stupid and does not allow selecting files with a . in its name
                    suffix = suffix[suffix.index("."):]

                suffix_list += ", *." + suffix

            name_filter = "{0} ({1})".format(mime_type.comment, suffix_list)
            self._container_name_filters[name_filter] = entry
Esempio n. 23
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"}

        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}

        return {"status": "success", "message": "Successfully exported container", "path": file_url}
Esempio n. 24
0
def getMetaData():
    file_extension = "gz" if Platform.isOSX() else "gcode.gz"
    return {
        "mesh_reader": [
            {
                "extension": file_extension,
                "description": i18n_catalog.i18nc("@item:inlistbox", "Compressed G-code File")
            }
        ]
    }
Esempio n. 25
0
def getMetaData():
    file_extension = "gz" if Platform.isOSX() else "gcode.gz"
    return {
        "mesh_reader": [{
            "extension":
            file_extension,
            "description":
            i18n_catalog.i18nc("@item:inlistbox", "Compressed G-code File")
        }]
    }
Esempio n. 26
0
 def get_cura_dir_path():
     if Platform.isWindows():
         appdata_path = os.getenv("APPDATA")
         if not appdata_path:  #Defensive against the environment variable missing (should never happen).
             appdata_path = "."
         return os.path.join(appdata_path, CuraAppName)
     elif Platform.isLinux():
         return os.path.expanduser("~/.local/share/" + CuraAppName)
     elif Platform.isOSX():
         return os.path.expanduser("~/Library/Logs/" + CuraAppName)
Esempio n. 27
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. 28
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. 29
0
    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(locale.getpreferredencoding())
        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)
Esempio n. 30
0
def getMetaData():
    file_extension = "gz" if Platform.isOSX() else "gcode.gz"
    return {
        "mesh_writer": {
            "output": [{
                "extension": file_extension,
                "description": catalog.i18nc("@item:inlistbox", "Compressed G-code File"),
                "mime_type": "application/gzip",
                "mode": GCodeGzWriter.GCodeGzWriter.OutputMode.BinaryMode
            }]
        }
    }
Esempio n. 31
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. 32
0
def getMetaData():
    # Workarround for osx not supporting double file extensions correctly.
    if Platform.isOSX():
        workspace_extension = "3mf"
    else:
        workspace_extension = "curaproject.3mf"

    metaData = {
        "plugin": {
            "name":
            i18n_catalog.i18nc("@label", "3MF Writer"),
            "author":
            "Ultimaker",
            "version":
            "1.0",
            "description":
            i18n_catalog.i18nc("@info:whatsthis",
                               "Provides support for writing 3MF files."),
            "api":
            3
        }
    }

    if "3MFWriter.ThreeMFWriter" in sys.modules:
        metaData["mesh_writer"] = {
            "output": [{
                "extension":
                "3mf",
                "description":
                i18n_catalog.i18nc("@item:inlistbox", "3MF file"),
                "mime_type":
                "application/vnd.ms-package.3dmanufacturing-3dmodel+xml",
                "mode":
                ThreeMFWriter.ThreeMFWriter.OutputMode.BinaryMode
            }]
        }
        metaData["workspace_writer"] = {
            "output": [{
                "extension":
                workspace_extension,
                "description":
                i18n_catalog.i18nc("@item:inlistbox", "Cura Project 3MF file"),
                "mime_type":
                "application/x-curaproject+xml",
                "mode":
                ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter.OutputMode.
                BinaryMode
            }]
        }

    return metaData
Esempio n. 33
0
    def _getCacheStorageRootPath(cls) -> Optional[str]:
        # 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. 34
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. 35
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. 36
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. 37
0
    def _openFileDialog(self):
        """The user can set the path to blender manually. Gets called when blender isn't found in the expected place."""

        global verified_blender_path
        message = Message(text=catalog.i18nc(
            '@info', 'Set your blender path manually.'),
                          title=catalog.i18nc('@info:title',
                                              'Blender not found'))
        message.show()

        dialog = QFileDialog()
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        # Supports multi-platform
        if Platform.isWindows():
            dialog.setDirectory('C:/Program Files')
            dialog.setNameFilters(["Blender (*.exe)"])
        elif Platform.isOSX():
            dialog.setDirectory('/Applications')
        elif Platform.isLinux():
            dialog.setDirectory('/usr/bin')
        else:
            dialog.setDirectory('')

        dialog.setFileMode(QFileDialog.ExistingFile)
        dialog.setViewMode(QFileDialog.Detail)
        # Opens the file explorer and checks if file is selected.
        if dialog.exec_():
            message.hide()
            # Gets the selected blender path from file explorer.
            Application.getInstance().getPreferences().setValue(
                'cura_blender/blender_path', ''.join(dialog.selectedFiles()))
            verified_blender_path = False
            self.verifyBlenderPath(manual=True)
            message = Message(text=catalog.i18nc(
                '@info',
                Application.getInstance().getPreferences().getValue(
                    'cura_blender/blender_path')),
                              title=catalog.i18nc('@info:title',
                                                  'New Blenderpath set'))
            message.show()
        else:
            message.hide()
            message = Message(text=catalog.i18nc(
                '@info', 'No blender path was selected.'),
                              title=catalog.i18nc('@info:title',
                                                  'Blender not found'))
            message.show()
Esempio n. 38
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. 39
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. 40
0
    def event(self, event):
        if event.type == Event.ViewActivateEvent:
            # FIX: on Max OS X, somehow QOpenGLContext.currentContext() can become None during View switching.
            # This can happen when you do the following steps:
            #   1. Start Cura
            #   2. Load a model
            #   3. Switch to Custom mode
            #   4. Select the model and click on the per-object tool icon
            #   5. Switch view to Layer view or X-Ray
            #   6. Cura will very likely crash
            # It seems to be a timing issue that the currentContext can somehow be empty, but I have no clue why.
            # This fix tries to reschedule the view changing event call on the Qt thread again if the current OpenGL
            # context is None.
            if Platform.isOSX():
                if QOpenGLContext.currentContext() is None:
                    Logger.log("d", "current context of OpenGL is empty on Mac OS X, will try to create shaders later")
                    CuraApplication.getInstance().callLater(lambda e = event: self.event(e))
                    return

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

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

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

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

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

        if event.type == Event.ViewDeactivateEvent:
            self.getRenderer().removeRenderPass(self._xray_pass)
            self._composite_pass.setLayerBindings(self._old_layer_bindings)
            self._composite_pass.setCompositeShader(self._old_composite_shader)
Esempio n. 41
0
    def run(self):
        if not self.url or not self.data:
            Logger.log("e", "URL or DATA for sending slice info was not set!")
            return

        # Submit data
        kwoptions = {"data" : self.data,
                     "timeout" : 5
                     }

        if Platform.isOSX():
            kwoptions["context"] = ssl._create_unverified_context()

        try:
            f = urllib.request.urlopen(self.url, **kwoptions)
            Logger.log("i", "Sent anonymous slice info to %s", self.url)
            f.close()
        except urllib.error.HTTPError as http_exception:
            Logger.log("e", "An HTTP error occurred while trying to send slice information: %s" % http_exception)
        except Exception as e: # We don't want any exception to cause problems
            Logger.log("e", "An exception occurred while trying to send slice information: %s" % e)
Esempio n. 42
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
Esempio n. 43
0
def getMetaData() -> Dict:
    # Workarround for osx not supporting double file extensions correctly.
    if Platform.isOSX():
        workspace_extension = "3mf"
    else:
        workspace_extension = "curaproject.3mf"

    metaData = {}
    if "3MFReader.ThreeMFReader" in sys.modules:
        metaData["mesh_reader"] = [
            {
                "extension": "3mf",
                "description": catalog.i18nc("@item:inlistbox", "3MF File")
            }
        ]
        metaData["workspace_reader"] = [
            {
                "extension": workspace_extension,
                "description": catalog.i18nc("@item:inlistbox", "3MF File")
            }
        ]
    
    return metaData
Esempio n. 44
0
def getMetaData() -> Dict:
    # Workaround for osx not supporting double file extensions correctly.
    if Platform.isOSX():
        workspace_extension = "3mf"
    else:
        workspace_extension = "curaproject.3mf"

    metaData = {}
    if "3MFReader.ThreeMFReader" in sys.modules:
        metaData["mesh_reader"] = [
            {
                "extension": "3mf",
                "description": catalog.i18nc("@item:inlistbox", "3MF File")
            }
        ]
        metaData["workspace_reader"] = [
            {
                "extension": workspace_extension,
                "description": catalog.i18nc("@item:inlistbox", "3MF File")
            }
        ]
    
    return metaData
Esempio n. 45
0
    def event(self, event):
        modifiers = QApplication.keyboardModifiers()
        ctrl_is_active = modifiers & Qt.ControlModifier
        shift_is_active = modifiers & Qt.ShiftModifier
        if event.type == Event.KeyPressEvent and ctrl_is_active:
            amount = 10 if shift_is_active else 1
            if event.key == KeyEvent.UpKey:
                self.setLayer(self._current_layer_num + amount)
                return True
            if event.key == KeyEvent.DownKey:
                self.setLayer(self._current_layer_num - amount)
                return True

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

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

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

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

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

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

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

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

            self._nozzle_node.setParent(None)
            self.getRenderer().removeRenderPass(self._layer_pass)
            self._composite_pass.setLayerBindings(self._old_layer_bindings)
            self._composite_pass.setCompositeShader(self._old_composite_shader)
Esempio n. 46
0
    def _onWriteStarted(self, output_device):
        try:
            if not Preferences.getInstance().getValue("info/send_slice_info"):
                Logger.log("d", "'info/send_slice_info' is turned off.")
                return # Do nothing, user does not want to send data

            global_container_stack = Application.getInstance().getGlobalContainerStack()

            # Get total material used (in mm^3)
            print_information = Application.getInstance().getPrintInformation()
            material_radius = 0.5 * global_container_stack.getProperty("material_diameter", "value")

            # TODO: Send material per extruder instead of mashing it on a pile
            material_used = math.pi * material_radius * material_radius * sum(print_information.materialLengths) #Volume of all materials used

            # Get model information (bounding boxes, hashes and transformation matrix)
            models_info = []
            for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
                if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
                    if not getattr(node, "_outside_buildarea", False):
                        model_info = {}
                        model_info["hash"] = node.getMeshData().getHash()
                        model_info["bounding_box"] = {}
                        model_info["bounding_box"]["minimum"] = {}
                        model_info["bounding_box"]["minimum"]["x"] = node.getBoundingBox().minimum.x
                        model_info["bounding_box"]["minimum"]["y"] = node.getBoundingBox().minimum.y
                        model_info["bounding_box"]["minimum"]["z"] = node.getBoundingBox().minimum.z

                        model_info["bounding_box"]["maximum"] = {}
                        model_info["bounding_box"]["maximum"]["x"] = node.getBoundingBox().maximum.x
                        model_info["bounding_box"]["maximum"]["y"] = node.getBoundingBox().maximum.y
                        model_info["bounding_box"]["maximum"]["z"] = node.getBoundingBox().maximum.z
                        model_info["transformation"] = str(node.getWorldTransformation().getData())

                        models_info.append(model_info)

            # Bundle the collected data
            submitted_data = {
                "processor": platform.processor(),
                "machine": platform.machine(),
                "platform": platform.platform(),
                "settings": global_container_stack.serialize(), # global_container with references on used containers
                "version": Application.getInstance().getVersion(),
                "modelhash": "None",
                "printtime": print_information.currentPrintTime.getDisplayString(),
                "filament": material_used,
                "language": Preferences.getInstance().getValue("general/language"),
            }
            for container in global_container_stack.getContainers():
                container_id = container.getId()
                try:
                    container_serialized = container.serialize()
                except NotImplementedError:
                    Logger.log("w", "Container %s could not be serialized!", container_id)
                    continue

                if container_serialized:
                    submitted_data["settings_%s" %(container_id)] = container_serialized # This can be anything, eg. INI, JSON, etc.
                else:
                    Logger.log("i", "No data found in %s to be serialized!", container_id)

            # Convert data to bytes
            submitted_data = urllib.parse.urlencode(submitted_data)
            binary_data = submitted_data.encode("utf-8")

            # Submit data
            kwoptions = {"data" : binary_data,
                         "timeout" : 1
                         }
            if Platform.isOSX():
                kwoptions["context"] = ssl._create_unverified_context()
            try:
                f = urllib.request.urlopen(self.info_url, **kwoptions)
                Logger.log("i", "Sent anonymous slice info to %s", self.info_url)
                f.close()
            except Exception as e:
                Logger.logException("e", "An exception occurred while trying to send slice information")
        except:
            # We really can't afford to have a mistake here, as this would break the sending of g-code to a device
            # (Either saving or directly to a printer). The functionality of the slice data is not *that* important.
            pass
Esempio n. 47
0
    def event(self, event):
        modifiers = QApplication.keyboardModifiers()
        ctrl_is_active = modifiers & Qt.ControlModifier
        shift_is_active = modifiers & Qt.ShiftModifier
        if event.type == Event.KeyPressEvent and ctrl_is_active:
            amount = 10 if shift_is_active else 1
            if event.key == KeyEvent.UpKey:
                self.setLayer(self._current_layer_num + amount)
                return True
            if event.key == KeyEvent.DownKey:
                self.setLayer(self._current_layer_num - amount)
                return True

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

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

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

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

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

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

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

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

            self._nozzle_node.setParent(None)
            self.getRenderer().removeRenderPass(self._layer_pass)
            self._composite_pass.setLayerBindings(self._old_layer_bindings)
            self._composite_pass.setCompositeShader(self._old_composite_shader)