Ejemplo n.º 1
0
    def __init__(self):
        super().__init__()
        self._workspace_readers = []
        self._workspace_writers = []

        PluginRegistry.addType("workspace_reader", self.addReader)
        PluginRegistry.addType("workspace_writer", self.addWriter)
Ejemplo n.º 2
0
    def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode):
        archive = VirtualFile()
        archive.openStream(stream, "application/x-ufp", OpenMode.WriteOnly)

        #Store the g-code from the scene.
        archive.addContentType(extension = "gcode", mime_type = "text/x-gcode")
        gcode_textio = StringIO() #We have to convert the g-code into bytes.
        PluginRegistry.getInstance().getPluginObject("GCodeWriter").write(gcode_textio, None)
        gcode = archive.getStream("/3D/model.gcode")
        gcode.write(gcode_textio.getvalue().encode("UTF-8"))
        archive.addRelation(virtual_path = "/3D/model.gcode", relation_type = "http://schemas.ultimaker.org/package/2018/relationships/gcode")

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

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

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

        archive.close()
        return True
Ejemplo n.º 3
0
    def __init__(self, application: "QtApplication") -> None:
        if ContainerRegistry.__instance is not None:
            raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__)
        ContainerRegistry.__instance = self

        super().__init__()

        self._application = application  # type: QtApplication

        self._emptyInstanceContainer = empty_container  # type: InstanceContainer

        # Sorted list of container providers (keep it sorted by sorting each time you add one!).
        self._providers = []  # type: List[ContainerProvider]
        PluginRegistry.addType("container_provider", self.addProvider)

        self.metadata = {}  # type: Dict[str, Dict[str, Any]]
        self._containers = {}  # type: Dict[str, ContainerInterface]
        self._wrong_container_ids = set() # type: Set[str]  # Set of already known wrong containers that must be skipped
        self.source_provider = {}  # type: Dict[str, Optional[ContainerProvider]]  # Where each container comes from.
        # Ensure that the empty container is added to the ID cache.
        self.metadata["empty"] = self._emptyInstanceContainer.getMetaData()
        self._containers["empty"] = self._emptyInstanceContainer
        self.source_provider["empty"] = None
        self._resource_types = {"definition": Resources.DefinitionContainers}  # type: Dict[str, int]
        self._query_cache = collections.OrderedDict()  # type: collections.OrderedDict # This should really be an ordered set but that does not exist...

        #Since queries are based on metadata, we need to make sure to clear the cache when a container's metadata changes.
        self.containerMetaDataChanged.connect(self._clearQueryCache)
Ejemplo n.º 4
0
 def _createMonitorViewFromQML(self) -> None:
     if self._monitor_view_qml_path is None and PluginRegistry.getInstance() is not None:
         self._monitor_view_qml_path = os.path.join(
             PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"),
             "resources", "qml", "MonitorStage.qml"
         )
     super()._createMonitorViewFromQML()
Ejemplo n.º 5
0
def container_registry(application):
    MimeTypeDatabase.addMimeType(
        MimeType(
            name = "application/x-uranium-definitioncontainer",
            comment = "Uranium Definition Container",
            suffixes = ["def.json"]
        )
    )

    MimeTypeDatabase.addMimeType(
        MimeType(
            name = "application/x-uranium-instancecontainer",
            comment = "Uranium Instance Container",
            suffixes = [ "inst.cfg" ]
        )
    )

    MimeTypeDatabase.addMimeType(
        MimeType(
            name = "application/x-uranium-containerstack",
            comment = "Uranium Container Stack",
            suffixes = [ "stack.cfg" ]
        )
    )

    Resources.addSearchPath(os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..", "Settings")))
    ContainerRegistry._ContainerRegistry__instance = None # Reset the private instance variable every time
    PluginRegistry.getInstance().removeType("settings_container")

    ContainerRegistry.getInstance().load()

    return ContainerRegistry.getInstance()
Ejemplo n.º 6
0
    def __init__(self):
        super().__init__()
        self._mesh_readers = {}
        self._mesh_writers = {}

        PluginRegistry.addType("mesh_writer", self.addWriter)
        PluginRegistry.addType("mesh_reader", self.addReader)
Ejemplo n.º 7
0
    def _read(self, file_name):
        with open(file_name, "rb") as file:
            file_data = file.read()
        uncompressed_gcode = gzip.decompress(file_data).decode("utf-8")
        PluginRegistry.getInstance().getPluginObject("GCodeReader").preReadFromStream(uncompressed_gcode)
        result = PluginRegistry.getInstance().getPluginObject("GCodeReader").readFromStream(uncompressed_gcode)

        return result
Ejemplo n.º 8
0
    def __init__(self, current_versions):
        self._version_upgrades = {} #For each config type and each version, gives a set of upgrade plug-ins that can convert them to something else.
        self._get_version_functions = {} #For each config type, gives a function with which to get the version number from those files.
        self._storage_paths = {} #For each config type, a set of storage paths to search for old config files.
        self._current_versions = current_versions #To know which preference versions and types to upgrade to.

        self._registry = PluginRegistry.getInstance()
        PluginRegistry.addType("version_upgrade", self._addVersionUpgrade)
Ejemplo n.º 9
0
    def __init__(self):
        super().__init__()

        self._output_devices = {}
        self._plugins = {}
        self._active_device = None
        self._active_device_override = False
        self._write_in_progress = False
        PluginRegistry.addType("output_device", self.addOutputDevicePlugin)
Ejemplo n.º 10
0
    def __init__(self):
        self._version_upgrades = {} #For each config type and each version, gives a set of upgrade plug-ins that can convert them to something else.
        self._get_version_functions = {} #For each config type, gives a function with which to get the version number from those files.
        self._storage_paths = {} #For each config type, a set of storage paths to search for old config files.
        self._current_versions = {} #To know which preference versions and types to upgrade to.
        self._upgrade_tasks = collections.deque() #The files that we still have to upgrade.
        self._upgrade_routes = {} #How to upgrade from one version to another. Needs to be pre-computed after all version upgrade plug-ins are registered.

        self._registry = PluginRegistry.getInstance()
        PluginRegistry.addType("version_upgrade", self._addVersionUpgrade)
Ejemplo n.º 11
0
 def _spawnPrinterSelectionDialog(self):
     if self._printer_selection_dialog is None:
         if PluginRegistry.getInstance() is not None:
             path = os.path.join(
                 PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"),
                 "resources", "qml", "PrintWindow.qml"
             )
             self._printer_selection_dialog = self._application.createQmlComponent(path, {"OutputDevice": self})
     if self._printer_selection_dialog is not None:
         self._printer_selection_dialog.show()
Ejemplo n.º 12
0
    def activeToolPanel(self):
        if not self._active_tool:
            return QUrl()

        try:
            panel_file = PluginRegistry.getInstance().getMetaData(self._active_tool.getPluginId())["tool"]["tool_panel"]
        except KeyError:
            return QUrl()

        return QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath(self._active_tool.getPluginId()), panel_file))
    def _createView(self):
        ## Load all scripts in the scripts folder
        self.loadAllScripts(os.path.join(PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), "scripts"))
        
        path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), "PostProcessingPlugin.qml"))
        self._component = QQmlComponent(Application.getInstance()._engine, path)

        self._context = QQmlContext(Application.getInstance()._engine.rootContext())
        self._context.setContextProperty("manager", self)
        self._view = self._component.create(self._context)
Ejemplo n.º 14
0
    def __init__(self, device_id, address, properties, parent = None) -> None:
        super().__init__(device_id = device_id, address = address, properties=properties, connection_type = ConnectionType.NetworkConnection, parent = parent)
        self._api_prefix = "/cluster-api/v1/"

        self._application = CuraApplication.getInstance()

        self._number_of_extruders = 2

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

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

        if PluginRegistry.getInstance() is not None:
            plugin_path = PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting")
            if plugin_path is None:
                Logger.log("e", "Cloud not find plugin path for plugin UM3NetworkPrnting")
                raise RuntimeError("Cloud not find plugin path for plugin UM3NetworkPrnting")
            self._monitor_view_qml_path = os.path.join(plugin_path, "resources", "qml", "MonitorStage.qml")

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

        self._accepts_commands = True  # type: bool

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

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

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

        self._printer_selection_dialog = None  # type: QObject

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

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

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

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

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

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

        self._active_camera_url = QUrl()  # type: QUrl
Ejemplo n.º 15
0
    def __init__(self) -> None:
        super().__init__()

        self._output_devices = {}  # type: Dict[str, OutputDevice]
        self._plugins = {}  # type: Dict[str, OutputDevicePlugin]
        self._active_device = None  # type: Optional[OutputDevice]
        self._active_device_override = False
        self._write_in_progress = False
        PluginRegistry.addType("output_device", self.addOutputDevicePlugin)

        self._is_running = False
Ejemplo n.º 16
0
    def __init__(self, writer_type, reader_type, parent = None):
        super().__init__(parent)

        self._readers = {}
        self._writers = {}

        self._writer_type = writer_type
        self._reader_type = reader_type

        PluginRegistry.addType(self._writer_type, self.addWriter)
        PluginRegistry.addType(self._reader_type, self.addReader)
Ejemplo n.º 17
0
    def __init__(self, api_client: CloudApiClient, cluster: CloudClusterResponse, parent: QObject = None) -> None:

        # The following properties are expected on each networked output device.
        # Because the cloud connection does not off all of these, we manually construct this version here.
        # An example of why this is needed is the selection of the compatible file type when exporting the tool path.
        properties = {
            b"address": b"",
            b"name": cluster.host_name.encode() if cluster.host_name else b"",
            b"firmware_version": cluster.host_version.encode() if cluster.host_version else b"",
            b"printer_type": b""
        }

        super().__init__(device_id = cluster.cluster_id, address = "",
                         connection_type = ConnectionType.CloudConnection, properties = properties, parent = parent)
        self._api = api_client
        self._cluster = cluster

        self._setInterfaceElements()

        self._account = api_client.account

        # We use the Cura Connect monitor tab to get most functionality right away.
        if PluginRegistry.getInstance() is not None:
            self._monitor_view_qml_path = os.path.join(
                PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"),
                "resources", "qml", "MonitorStage.qml"
            )

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

        # We keep track of which printer is visible in the monitor page.
        self._active_printer = None  # type: Optional[PrinterOutputModel]

        # Properties to populate later on with received cloud data.
        self._print_jobs = []  # type: List[UM3PrintJobOutputModel]
        self._number_of_extruders = 2  # All networked printers are dual-extrusion Ultimaker machines.

        # We only allow a single upload at a time.
        self._progress = CloudProgressMessage()

        # Keep server string of the last generated time to avoid updating models more than once for the same response
        self._received_printers = None  # type: Optional[List[CloudClusterPrinterStatus]]
        self._received_print_jobs = None  # type: Optional[List[CloudClusterPrintJobStatus]]

        # A set of the user's job IDs that have finished
        self._finished_jobs = set()  # type: Set[str]

        # Reference to the uploaded print job / mesh
        self._tool_path = None  # type: Optional[bytes]
        self._uploaded_print_job = None  # type: Optional[CloudPrintJobResponse]
Ejemplo n.º 18
0
    def __init__(self, application: "Application") -> None:
        super().__init__()  # Call super to make multiple inheritance work.

        self._scene = Scene()
        self._application = application

        self._active_view = None  # type: Optional[View]
        self._views = {}  # type: Dict[str, View]

        self._active_tool = None  # type: Optional[Tool]
        self._tool_operation_active = False
        self._tools = {}  # type: Dict[str, Tool]
        self._camera_tool = None #type: Optional[Tool]
        self._selection_tool = None #type: Optional[Tool]
        self._tools_enabled = True #type: bool

        self._active_stage = None #type: Optional[Stage]
        self._stages = {} #type: Dict[str, Stage]

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

        PluginRegistry.addType("stage", self.addStage)
        PluginRegistry.addType("view", self.addView)
        PluginRegistry.addType("tool", self.addTool)
        PluginRegistry.addType("input_device", self.addInputDevice)
Ejemplo n.º 19
0
 def _createViewFromQML(self):
     path = QUrl.fromLocalFile(
         os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), self._qml_url))
     self._component = QQmlComponent(Application.getInstance()._engine, path)
     self._context = QQmlContext(Application.getInstance()._engine.rootContext())
     self._context.setContextProperty("manager", self)
     self._view = self._component.create(self._context)
Ejemplo n.º 20
0
 def _onEngineCreated(self) -> None:
     plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
     if plugin_path:
         self.addDisplayComponent("main", os.path.join(plugin_path, "SimulationViewMainComponent.qml"))
         self.addDisplayComponent("menu", os.path.join(plugin_path, "SimulationViewMenuComponent.qml"))
     else:
         Logger.log("e", "Unable to find the path for %s", self.getPluginId())
Ejemplo n.º 21
0
 def loadChangeLogs(self):
     self._change_logs = collections.OrderedDict()
     with open(
         os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "ChangeLog.txt"),
         "r",
         -1,
         "utf-8",
     ) as f:
         open_version = None
         open_header = ""  # Initialise to an empty header in case there is no "*" in the first line of the changelog
         for line in f:
             line = line.replace("\n", "")
             if "[" in line and "]" in line:
                 line = line.replace("[", "")
                 line = line.replace("]", "")
                 open_version = Version(line)
                 open_header = ""
                 self._change_logs[open_version] = collections.OrderedDict()
             elif line.startswith("*"):
                 open_header = line.replace("*", "")
                 self._change_logs[open_version][open_header] = []
             elif line != "":
                 if open_header not in self._change_logs[open_version]:
                     self._change_logs[open_version][open_header] = []
                 self._change_logs[open_version][open_header].append(line)
Ejemplo n.º 22
0
    def __init__(self, application: "QtApplication", writer_type: str = "unknown_file_writer", reader_type: str = "unknown_file_reader", parent: QObject = None) -> None:
        if cast(FileHandler, self.__class__).__instance is not None:
            raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__)
        cast(FileHandler, self.__class__).__instance = self

        super().__init__(parent)

        self._application = application
        self._readers = {} # type: Dict[str, FileReader]
        self._writers = {} # type: Dict[str, FileWriter]

        self._writer_type = writer_type # type: str
        self._reader_type = reader_type # type: str

        PluginRegistry.addType(self._writer_type, self.addWriter)
        PluginRegistry.addType(self._reader_type, self.addReader)
Ejemplo n.º 23
0
 def qmlPath(self) -> "QUrl":
     plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
     if plugin_path is None:
         Logger.log("e", "Cannot create QML view: cannot find plugin path for plugin [%s]", self.getPluginId())
         return QUrl("")
     path = os.path.join(plugin_path, self._qml_url)
     return QUrl.fromLocalFile(path)
Ejemplo n.º 24
0
    def importProfile(self, file_name):
        if not file_name:
            return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, "Invalid path")}

        plugin_registry = PluginRegistry.getInstance()
        for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
            profile_reader = plugin_registry.getPluginObject(plugin_id)
            try:
                profile_or_list = profile_reader.read(file_name) # Try to open the file with the profile reader.
            except Exception as e:
                #Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
                Logger.log("e", "Failed to import profile from %s: %s", file_name, str(e))
                return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, str(e))}
            if profile_or_list: # Success!
                name_seed = os.path.splitext(os.path.basename(file_name))[0]
                if type(profile_or_list) is not list:
                    profile = profile_or_list
                    self._configureProfile(profile, name_seed)
                    return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) }
                else:
                    for profile in profile_or_list:
                        self._configureProfile(profile, name_seed)

                    if len(profile_or_list) == 1:
                        return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())}
                    else:
                        profile_names = ", ".join([profile.getName() for profile in profile_or_list])
                        return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profiles {0}", profile_names) }

        #If it hasn't returned by now, none of the plugins loaded the profile successfully.
        return { "status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type.", file_name)}
Ejemplo n.º 25
0
    def _update(self) -> None:
        items = []
        views = self._controller.getAllViews()
        current_view = self._controller.getActiveView()
        if current_view is None:
            return

        for view_id in views:
            view_meta_data = PluginRegistry.getInstance().getMetaData(view_id).get("view", {})

            # Skip view modes that are marked as not visible
            if "visible" in view_meta_data and not view_meta_data["visible"]:
                continue

            # Metadata elements
            name = view_meta_data.get("name", view_id)
            description = view_meta_data.get("description", "")
            icon_name = view_meta_data.get("icon", "")
            weight = view_meta_data.get("weight", 0)

            items.append({
                "id": view_id,
                "name": name,
                "active": view_id == current_view.getPluginId(),
                "description": description,
                "icon": icon_name,
                "weight": weight
            })

        items.sort(key = lambda t: t["weight"])
        self.setItems(items)
Ejemplo n.º 26
0
    def createChangelogWindow(self):
        path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "ChangeLog.qml"))

        component = QQmlComponent(Application.getInstance()._engine, path)
        self._changelog_context = QQmlContext(Application.getInstance()._engine.rootContext())
        self._changelog_context.setContextProperty("manager", self)
        self._changelog_window = component.create(self._changelog_context)
Ejemplo n.º 27
0
    def _onToolsChanged(self):
        items = []

        tools = self._controller.getAllTools()
        for name in tools:
            toolMetaData = PluginRegistry.getInstance().getMetaData(name).get("tool", {})

            # Skip tools that are marked as not visible
            if "visible" in toolMetaData and not toolMetaData["visible"]:
                continue

            # Optional metadata elements
            description = toolMetaData.get("description", "")
            iconName = toolMetaData.get("icon", "default.png")
            weight = toolMetaData.get("weight", 0)

            enabled = self._controller.getTool(name).getEnabled()

            items.append({
                "id": name,
                "name": toolMetaData.get("name", name),
                "icon": iconName,
                "active": False,
                "enabled": enabled,
                "description": description,
                "weight": weight
            })

        items.sort(key = lambda t: t["weight"])
        self.setItems(items)
Ejemplo n.º 28
0
    def importProfile(self, file_name):
        if not file_name:
            return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, "Invalid path")}

        plugin_registry = PluginRegistry.getInstance()
        for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
            profile_reader = plugin_registry.getPluginObject(plugin_id)
            try:
                profile = profile_reader.read(file_name) #Try to open the file with the profile reader.
            except Exception as e:
                #Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
                Logger.log("e", "Failed to import profile from %s: %s", file_name, str(e))
                return { "status": "error", "message": catalog.i18nc("@info:status", "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>", file_name, str(e))}
            if profile: #Success!
                profile.setReadOnly(False)

                new_name = self.createUniqueName("quality", "", os.path.splitext(os.path.basename(file_name))[0],
                                                 catalog.i18nc("@label", "Custom profile"))
                profile.setName(new_name)
                profile._id = new_name

                if self._machineHasOwnQualities():
                    profile.setDefinition(self._activeDefinition())
                    if self._machineHasOwnMaterials():
                        profile.addMetaDataEntry("material", self._activeMaterialId())
                else:
                    profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0])
                ContainerRegistry.getInstance().addContainer(profile)

                return { "status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile.getName()) }

        #If it hasn't returned by now, none of the plugins loaded the profile successfully.
        return { "status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type.", file_name)}
Ejemplo n.º 29
0
 def _createConfigUI(self):
     if self._ui_view is None:
         Logger.log("d", "Creating ImageReader config UI")
         path = os.path.join(PluginRegistry.getInstance().getPluginPath("ImageReader"), "ConfigUI.qml")
         self._ui_view = Application.getInstance().createQmlComponent(path, {"manager": self})
         self._ui_view.setFlags(self._ui_view.flags() & ~Qt.WindowCloseButtonHint & ~Qt.WindowMinimizeButtonHint & ~Qt.WindowMaximizeButtonHint);
         self._disable_size_callbacks = False
Ejemplo n.º 30
0
    def getWriterByMimeType(self, mime):
        writer_data = PluginRegistry.getInstance().getAllMetaData(filter = {"mesh_writer": {}}, active_only = True)
        for entry in writer_data:
            for output in entry["mesh_writer"].get("output", []):
                if mime == output["mime_type"]:
                    return self._mesh_writers[entry["id"]]

        return None
 def SaveTokenRegistry(self):
     path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
     path = os.path.join(path, 'tokens.cfg')
     with open(path, 'w') as file:
         json.dump(self._tokenregistry, file)
     Logger.debug("TokensSaved")
Ejemplo n.º 32
0
 def openMacroAndIconDirectory(self):
     plugin_dir = os.path.join(PluginRegistry.getInstance().getPluginPath(
         self.getPluginId()))
     macro_dir = os.path.join(plugin_dir, "macro")
     os.system("explorer.exe \"%s\"" % macro_dir)
Ejemplo n.º 33
0
 def _createSocket(self):
     super()._createSocket(os.path.abspath(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "Cura.proto")))
     self._engine_is_fresh = True
Ejemplo n.º 34
0
    def importProfile(self, file_name):
        Logger.log("d", "Attempting to import profile %s", file_name)
        if not file_name:
            return {
                "status":
                "error",
                "message":
                catalog.i18nc(
                    "@info:status Don't translate the XML tags <filename> or <message>!",
                    "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                    file_name, "Invalid path")
            }

        plugin_registry = PluginRegistry.getInstance()
        extension = file_name.split(".")[-1]

        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            return

        machine_extruders = list(
            ExtruderManager.getInstance().getMachineExtruders(
                global_container_stack.getId()))
        machine_extruders.sort(key=lambda k: k.getMetaDataEntry("position"))

        for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
            if meta_data["profile_reader"][0]["extension"] != extension:
                continue

            profile_reader = plugin_registry.getPluginObject(plugin_id)
            try:
                profile_or_list = profile_reader.read(
                    file_name)  # Try to open the file with the profile reader.
            except Exception as e:
                # Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
                Logger.log(
                    "e",
                    "Failed to import profile from %s: %s while using profile reader. Got exception %s",
                    file_name, profile_reader.getPluginId(), str(e))
                return {
                    "status":
                    "error",
                    "message":
                    catalog.i18nc(
                        "@info:status Don't translate the XML tags <filename> or <message>!",
                        "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                        file_name, str(e))
                }
            if profile_or_list:  # Success!
                name_seed = os.path.splitext(os.path.basename(file_name))[0]
                new_name = self.uniqueName(name_seed)
                if type(profile_or_list) is not list:
                    profile = profile_or_list

                    result = self._configureProfile(profile, name_seed,
                                                    new_name)
                    if result is not None:
                        return {
                            "status":
                            "error",
                            "message":
                            catalog.i18nc(
                                "@info:status Don't translate the XML tags <filename> or <message>!",
                                "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                                file_name, result)
                        }

                    return {
                        "status":
                        "ok",
                        "message":
                        catalog.i18nc("@info:status",
                                      "Successfully imported profile {0}",
                                      profile.getName())
                    }
                else:
                    profile_index = -1
                    global_profile = None

                    for profile in profile_or_list:
                        if profile_index >= 0:
                            if len(machine_extruders) > profile_index:
                                extruder_id = Application.getInstance(
                                ).getMachineManager().getQualityDefinitionId(
                                    machine_extruders[profile_index].getBottom(
                                    ))
                                # Ensure the extruder profiles get non-conflicting names
                                # NB: these are not user-facing
                                if "extruder" in profile.getMetaData():
                                    profile.setMetaDataEntry(
                                        "extruder", extruder_id)
                                else:
                                    profile.addMetaDataEntry(
                                        "extruder", extruder_id)
                                profile_id = (extruder_id + "_" +
                                              name_seed).lower().replace(
                                                  " ", "_")
                            elif profile_index == 0:
                                # Importing a multiextrusion profile into a single extrusion machine; merge 1st extruder profile into global profile
                                profile._id = self.uniqueName(
                                    "temporary_profile")
                                self.addContainer(profile)
                                ContainerManager.getInstance().mergeContainers(
                                    global_profile.getId(), profile.getId())
                                self.removeContainer(profile.getId())
                                break
                            else:
                                # The imported composite profile has a profile for an extruder that this machine does not have. Ignore this extruder-profile
                                break
                        else:
                            global_profile = profile
                            profile_id = (
                                global_container_stack.getBottom().getId() +
                                "_" + name_seed).lower().replace(" ", "_")

                        result = self._configureProfile(
                            profile, profile_id, new_name)
                        if result is not None:
                            return {
                                "status":
                                "error",
                                "message":
                                catalog.i18nc(
                                    "@info:status Don't translate the XML tags <filename> or <message>!",
                                    "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                                    file_name, result)
                            }

                        profile_index += 1

                    return {
                        "status":
                        "ok",
                        "message":
                        catalog.i18nc("@info:status",
                                      "Successfully imported profile {0}",
                                      profile_or_list[0].getName())
                    }

        # If it hasn't returned by now, none of the plugins loaded the profile successfully.
        return {
            "status":
            "error",
            "message":
            catalog.i18nc(
                "@info:status",
                "Profile {0} has an unknown file type or is corrupted.",
                file_name)
        }
Ejemplo n.º 35
0
def plugin_registry(application):
    PluginRegistry._PluginRegistry__instance = None
    plugin_registry = PluginRegistry(application)
    plugin_registry._plugin_locations = [] # Clear pre-defined plugin locations
    return plugin_registry
Ejemplo n.º 36
0
    def write(self, stream, nodes, mode=MeshWriter.OutputMode.TextMode):
        #Find an unused file name to temporarily write the g-code to.
        file_name = stream.name
        if not file_name:  #Not a file stream.
            Logger.log("e", "X3G writer can only write to local files.")
            return False
        file_directory = os.path.dirname(os.path.realpath(
            file_name))  #Save the tempfile next to the real output file.
        i = 0
        temp_file = os.path.join(file_directory, "output" + str(i) + ".gcode")
        while os.path.isfile(temp_file):
            i += 1
            temp_file = os.path.join(file_directory,
                                     "output" + str(i) + ".gcode")

        #Write the g-code to the temporary file.
        try:
            with open(temp_file, "w", -1, "utf-8") as f:
                PluginRegistry.getInstance(
                ).getPluginObject("GCodeWriter").write(
                    f, None
                )  #Let the g-code writer write its g-code into the temp file.
        except:
            Logger.log("e", "Error writing temporary g-code file %s",
                       temp_file)
            _removeTemporary(temp_file)
            return False

        #Call the converter application to convert it to X3G.
        Logger.log("d", "App path: %s", os.getcwd())
        Logger.log("d", "File name: %s", file_name)
        binary_path = os.path.dirname(os.path.realpath(__file__))
        binary_filename = os.path.join(binary_path, "gpx")
        if UM.Platform.Platform.isWindows():
            binary_filename += ".exe"
        if UM.Platform.Platform.isOSX(
        ):  #For the cross-platform release, we need to disambiguate between MacOS and Linux.
            if os.path.isfile(
                    binary_filename + "_macos"
            ):  #Still fall back to the default name if the MacOS-specific file doesn't exist.
                binary_filename += "_macos"

        command = [
            binary_filename, "-p", "-m", "r1d", "-c",
            os.path.join(binary_path, "cfg.ini"), temp_file, file_name
        ]
        safes = [os.path.expandvars(p) for p in command]
        Logger.log("d",
                   "Calling GPX: {command}".format(command=" ".join(command)))
        stream.close()  #Close the file so that the binary can write to it.
        try:
            process = subprocess.Popen(safes)
            process.wait()
            output = process.communicate(b"y")
            Logger.log("d", str(output))
        except Exception as e:
            Logger.log("e",
                       "System call to X3G converter application failed: %s",
                       str(e))
            _removeTemporary(temp_file)
            return False

        _removeTemporary(temp_file)
        return True
Ejemplo n.º 37
0
 def createUserAgreementWindow(self):
     path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "UserAgreement.qml")
     self._user_agreement_window = Application.getInstance().createQmlComponent(path, {"manager": self})
Ejemplo n.º 38
0
 def registerObjects(
     self, engine
 ) -> None:  #type: ignore #Don't type engine, because the type depends on the platform you're running on so it always gives an error somewhere.
     engine.rootContext().setContextProperty("PluginRegistry",
                                             PluginRegistry.getInstance())
Ejemplo n.º 39
0
    def __init__(self,
                 name: str,
                 version: str,
                 build_type: str = "",
                 is_debug_mode=False,
                 **kwargs):
        if Application._instance != None:
            raise ValueError("Duplicate singleton creation")

        # If the constructor is called and there is no instance, set the instance to self.
        # This is done because we can't make constructor private
        Application._instance = self

        self._application_name = name
        self._version = version
        self._build_type = build_type
        self._is_debug_mode = is_debug_mode

        os.putenv(
            "UBUNTU_MENUPROXY", "0"
        )  # For Ubuntu Unity this makes Qt use its own menu bar rather than pass it on to Unity.

        Signal._app = self
        Signal._signalQueue = self
        Resources.ApplicationIdentifier = name
        Resources.ApplicationVersion = version

        Resources.addSearchPath(
            os.path.join(os.path.dirname(sys.executable), "resources"))
        Resources.addSearchPath(
            os.path.join(Application.getInstallPrefix(), "share", "uranium",
                         "resources"))
        Resources.addSearchPath(
            os.path.join(Application.getInstallPrefix(), "Resources",
                         "uranium", "resources"))
        Resources.addSearchPath(
            os.path.join(Application.getInstallPrefix(), "Resources",
                         self.getApplicationName(), "resources"))

        if not hasattr(sys, "frozen"):
            Resources.addSearchPath(
                os.path.join(os.path.abspath(os.path.dirname(__file__)), "..",
                             "resources"))

        self._main_thread = threading.current_thread()

        super().__init__()  # Call super to make multiple inheritance work.
        i18nCatalog.setApplication(self)

        self._renderer = None

        PluginRegistry.addType("backend", self.setBackend)
        PluginRegistry.addType("logger", Logger.addLogger)
        PluginRegistry.addType("extension", self.addExtension)

        self.default_theme = self.getApplicationName()

        preferences = Preferences.getInstance()
        preferences.addPreference("general/language", "en_US")
        preferences.addPreference("general/visible_settings", "")

        try:
            preferences.readFromFile(
                Resources.getPath(Resources.Preferences,
                                  self._application_name + ".cfg"))
        except FileNotFoundError:
            pass

        self._controller = Controller(self)
        self._mesh_file_handler = MeshFileHandler.getInstance()
        self._mesh_file_handler.setApplication(self)
        self._workspace_file_handler = WorkspaceFileHandler.getInstance()
        self._workspace_file_handler.setApplication(self)
        self._extensions = []
        self._backend = None
        self._output_device_manager = OutputDeviceManager()

        self._required_plugins = []

        self._operation_stack = OperationStack(self.getController())

        self._plugin_registry = PluginRegistry.getInstance()

        self._plugin_registry.addPluginLocation(
            os.path.join(Application.getInstallPrefix(), "lib", "uranium"))
        self._plugin_registry.addPluginLocation(
            os.path.join(os.path.dirname(sys.executable), "plugins"))
        self._plugin_registry.addPluginLocation(
            os.path.join(Application.getInstallPrefix(), "Resources",
                         "uranium", "plugins"))
        self._plugin_registry.addPluginLocation(
            os.path.join(Application.getInstallPrefix(), "Resources",
                         self.getApplicationName(), "plugins"))
        # Locally installed plugins
        local_path = os.path.join(
            Resources.getStoragePath(Resources.Resources), "plugins")
        # Ensure the local plugins directory exists
        try:
            os.makedirs(local_path)
        except OSError:
            pass
        self._plugin_registry.addPluginLocation(local_path)

        if not hasattr(sys, "frozen"):
            self._plugin_registry.addPluginLocation(
                os.path.join(os.path.abspath(os.path.dirname(__file__)), "..",
                             "plugins"))

        self._plugin_registry.setApplication(self)

        ContainerRegistry.setApplication(self)
        UM.Settings.InstanceContainer.setContainerRegistry(
            self.getContainerRegistry())
        UM.Settings.ContainerStack.setContainerRegistry(
            self.getContainerRegistry())

        self._parsed_command_line = None
        self.parseCommandLine()

        self._visible_messages = []
        self._message_lock = threading.Lock()
        self.showMessageSignal.connect(self.showMessage)
        self.hideMessageSignal.connect(self.hideMessage)

        self._global_container_stack = None
Ejemplo n.º 40
0
    def render(self):
        if not self._layer_shader:
            if self._compatibility_mode:
                shader_filename = "layers.shader"
                shadow_shader_filename = "layers_shadow.shader"
            else:
                shader_filename = "layers3d.shader"
                shadow_shader_filename = "layers3d_shadow.shader"
            self._layer_shader = OpenGL.getInstance().createShaderProgram(
                os.path.join(
                    PluginRegistry.getInstance().getPluginPath(
                        "SimulationView"), shader_filename))
            self._layer_shadow_shader = OpenGL.getInstance(
            ).createShaderProgram(
                os.path.join(
                    PluginRegistry.getInstance().getPluginPath(
                        "SimulationView"), shadow_shader_filename))
            self._current_shader = self._layer_shader
        # Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers)
        self._layer_shader.setUniformValue(
            "u_active_extruder",
            float(max(0, self._extruder_manager.activeExtruderIndex)))
        if self._layer_view:
            self._layer_shader.setUniformValue(
                "u_max_feedrate", self._layer_view.getMaxFeedrate())
            self._layer_shader.setUniformValue(
                "u_min_feedrate", self._layer_view.getMinFeedrate())
            self._layer_shader.setUniformValue(
                "u_max_thickness", self._layer_view.getMaxThickness())
            self._layer_shader.setUniformValue(
                "u_min_thickness", self._layer_view.getMinThickness())
            self._layer_shader.setUniformValue(
                "u_layer_view_type", self._layer_view.getSimulationViewType())
            self._layer_shader.setUniformValue(
                "u_extruder_opacity", self._layer_view.getExtruderOpacities())
            self._layer_shader.setUniformValue(
                "u_show_travel_moves", self._layer_view.getShowTravelMoves())
            self._layer_shader.setUniformValue(
                "u_show_helpers", self._layer_view.getShowHelpers())
            self._layer_shader.setUniformValue("u_show_skin",
                                               self._layer_view.getShowSkin())
            self._layer_shader.setUniformValue(
                "u_show_infill", self._layer_view.getShowInfill())
        else:
            #defaults
            self._layer_shader.setUniformValue("u_max_feedrate", 1)
            self._layer_shader.setUniformValue("u_min_feedrate", 0)
            self._layer_shader.setUniformValue("u_max_thickness", 1)
            self._layer_shader.setUniformValue("u_min_thickness", 0)
            self._layer_shader.setUniformValue("u_layer_view_type", 1)
            self._layer_shader.setUniformValue(
                "u_extruder_opacity",
                [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]])
            self._layer_shader.setUniformValue("u_show_travel_moves", 0)
            self._layer_shader.setUniformValue("u_show_helpers", 1)
            self._layer_shader.setUniformValue("u_show_skin", 1)
            self._layer_shader.setUniformValue("u_show_infill", 1)

        if not self._tool_handle_shader:
            self._tool_handle_shader = OpenGL.getInstance(
            ).createShaderProgram(
                Resources.getPath(Resources.Shaders, "toolhandle.shader"))

        if not self._nozzle_shader:
            self._nozzle_shader = OpenGL.getInstance().createShaderProgram(
                Resources.getPath(Resources.Shaders, "color.shader"))
            self._nozzle_shader.setUniformValue(
                "u_color",
                Color(*Application.getInstance().getTheme().getColor(
                    "layerview_nozzle").getRgb()))

        self.bind()

        tool_handle_batch = RenderBatch(self._tool_handle_shader,
                                        type=RenderBatch.RenderType.Overlay,
                                        backface_cull=True)
        head_position = None  # Indicates the current position of the print head
        nozzle_node = None

        for node in DepthFirstIterator(self._scene.getRoot()):

            if isinstance(node, ToolHandle):
                tool_handle_batch.addItem(node.getWorldTransformation(),
                                          mesh=node.getSolidMesh())

            elif isinstance(node, NozzleNode):
                nozzle_node = node
                nozzle_node.setVisible(False)

            elif isinstance(node, SceneNode) and (node.getMeshData(
            ) or node.callDecoration("isBlockSlicing")) and node.isVisible():
                layer_data = node.callDecoration("getLayerData")
                if not layer_data:
                    continue

                # Render all layers below a certain number as line mesh instead of vertices.
                if self._layer_view._current_layer_num > -1 and (
                    (not self._layer_view._only_show_top_layers) or
                    (not self._layer_view.getCompatibilityMode())):
                    start = 0
                    end = 0
                    element_counts = layer_data.getElementCounts()
                    for layer in sorted(element_counts.keys()):
                        # In the current layer, we show just the indicated paths
                        if layer == self._layer_view._current_layer_num:
                            # We look for the position of the head, searching the point of the current path
                            index = self._layer_view._current_path_num
                            offset = 0
                            for polygon in layer_data.getLayer(layer).polygons:
                                # The size indicates all values in the two-dimension array, and the second dimension is
                                # always size 3 because we have 3D points.
                                if index >= polygon.data.size // 3 - offset:
                                    index -= polygon.data.size // 3 - offset
                                    offset = 1  # This is to avoid the first point when there is more than one polygon, since has the same value as the last point in the previous polygon
                                    continue
                                # The head position is calculated and translated
                                head_position = Vector(
                                    polygon.data[index + offset][0],
                                    polygon.data[index + offset][1],
                                    polygon.data[index + offset]
                                    [2]) + node.getWorldPosition()
                                break
                            break
                        if self._layer_view._minimum_layer_num > layer:
                            start += element_counts[layer]
                        end += element_counts[layer]

                    # Calculate the range of paths in the last layer
                    current_layer_start = end
                    current_layer_end = end + self._layer_view._current_path_num * 2  # Because each point is used twice

                    # This uses glDrawRangeElements internally to only draw a certain range of lines.
                    # All the layers but the current selected layer are rendered first
                    if self._old_current_path != self._layer_view._current_path_num:
                        self._current_shader = self._layer_shadow_shader
                        self._switching_layers = False
                    if not self._layer_view.isSimulationRunning(
                    ) and self._old_current_layer != self._layer_view._current_layer_num:
                        self._current_shader = self._layer_shader
                        self._switching_layers = True

                    layers_batch = RenderBatch(
                        self._current_shader,
                        type=RenderBatch.RenderType.Solid,
                        mode=RenderBatch.RenderMode.Lines,
                        range=(start, end),
                        backface_cull=True)
                    layers_batch.addItem(node.getWorldTransformation(),
                                         layer_data)
                    layers_batch.render(self._scene.getActiveCamera())

                    # Current selected layer is rendered
                    current_layer_batch = RenderBatch(
                        self._layer_shader,
                        type=RenderBatch.RenderType.Solid,
                        mode=RenderBatch.RenderMode.Lines,
                        range=(current_layer_start, current_layer_end))
                    current_layer_batch.addItem(node.getWorldTransformation(),
                                                layer_data)
                    current_layer_batch.render(self._scene.getActiveCamera())

                    self._old_current_layer = self._layer_view._current_layer_num
                    self._old_current_path = self._layer_view._current_path_num

                # Create a new batch that is not range-limited
                batch = RenderBatch(self._layer_shader,
                                    type=RenderBatch.RenderType.Solid)

                if self._layer_view.getCurrentLayerMesh():
                    batch.addItem(node.getWorldTransformation(),
                                  self._layer_view.getCurrentLayerMesh())

                if self._layer_view.getCurrentLayerJumps():
                    batch.addItem(node.getWorldTransformation(),
                                  self._layer_view.getCurrentLayerJumps())

                if len(batch.items) > 0:
                    batch.render(self._scene.getActiveCamera())

        # The nozzle is drawn when once we know the correct position of the head,
        # but the user is not using the layer slider, and the compatibility mode is not enabled
        if not self._switching_layers and not self._compatibility_mode and self._layer_view.getActivity(
        ) and nozzle_node is not None:
            if head_position is not None:
                nozzle_node.setVisible(True)
                nozzle_node.setPosition(head_position)
                nozzle_batch = RenderBatch(
                    self._nozzle_shader,
                    type=RenderBatch.RenderType.Transparent)
                nozzle_batch.addItem(nozzle_node.getWorldTransformation(),
                                     mesh=nozzle_node.getMeshData())
                nozzle_batch.render(self._scene.getActiveCamera())

        # Render toolhandles on top of the layerview
        if len(tool_handle_batch.items) > 0:
            tool_handle_batch.render(self._scene.getActiveCamera())

        self.release()
Ejemplo n.º 41
0
 def _setMainOverlay(self):
     main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("MonitorStage"), "MonitorMainView.qml")
     self.addDisplayComponent("main", main_component_path)
Ejemplo n.º 42
0
    def importProfile(self, file_name: str) -> Dict[str, str]:
        Logger.log("d", "Attempting to import profile %s", file_name)
        if not file_name:
            return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "Failed to import profile from <filename>{0}</filename>: {1}", file_name, "Invalid path")}

        global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack()
        if not global_stack:
            return {"status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "Can't import profile from <filename>{0}</filename> before a printer is added.", file_name)}
        container_tree = ContainerTree.getInstance()

        machine_extruders = global_stack.extruderList

        plugin_registry = PluginRegistry.getInstance()
        extension = file_name.split(".")[-1]

        for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
            if meta_data["profile_reader"][0]["extension"] != extension:
                continue
            profile_reader = cast(ProfileReader, plugin_registry.getPluginObject(plugin_id))
            try:
                profile_or_list = profile_reader.read(file_name)  # Try to open the file with the profile reader.
            except NoProfileException:
                return { "status": "ok", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "No custom profile to import in file <filename>{0}</filename>", file_name)}
            except Exception as e:
                # Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
                Logger.log("e", "Failed to import profile from %s: %s while using profile reader. Got exception %s", file_name, profile_reader.getPluginId(), str(e))
                return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "Failed to import profile from <filename>{0}</filename>:", file_name) + "\n<message>" + str(e) + "</message>"}

            if profile_or_list:
                # Ensure it is always a list of profiles
                if not isinstance(profile_or_list, list):
                    profile_or_list = [profile_or_list]

                # First check if this profile is suitable for this machine
                global_profile = None
                extruder_profiles = []
                if len(profile_or_list) == 1:
                    global_profile = profile_or_list[0]
                else:
                    for profile in profile_or_list:
                        if not profile.getMetaDataEntry("position"):
                            global_profile = profile
                        else:
                            extruder_profiles.append(profile)
                extruder_profiles = sorted(extruder_profiles, key = lambda x: int(x.getMetaDataEntry("position")))
                profile_or_list = [global_profile] + extruder_profiles

                if not global_profile:
                    Logger.log("e", "Incorrect profile [%s]. Could not find global profile", file_name)
                    return { "status": "error",
                             "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "This profile <filename>{0}</filename> contains incorrect data, could not import it.", file_name)}
                profile_definition = global_profile.getMetaDataEntry("definition")

                # Make sure we have a profile_definition in the file:
                if profile_definition is None:
                    break
                machine_definitions = self.findContainers(id = profile_definition)
                if not machine_definitions:
                    Logger.log("e", "Incorrect profile [%s]. Unknown machine type [%s]", file_name, profile_definition)
                    return {"status": "error",
                            "message": catalog.i18nc("@info:status Don't translate the XML tags <filename>!", "This profile <filename>{0}</filename> contains incorrect data, could not import it.", file_name)
                            }
                machine_definition = machine_definitions[0]

                # Get the expected machine definition.
                # i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode...
                has_machine_quality = parseBool(machine_definition.getMetaDataEntry("has_machine_quality", "false"))
                profile_definition = machine_definition.getMetaDataEntry("quality_definition", machine_definition.getId()) if has_machine_quality else "fdmprinter"
                expected_machine_definition = container_tree.machines[global_stack.definition.getId()].quality_definition

                # And check if the profile_definition matches either one (showing error if not):
                if profile_definition != expected_machine_definition:
                    Logger.log("d", "Profile {file_name} is for machine {profile_definition}, but the current active machine is {expected_machine_definition}. Changing profile's definition.".format(file_name = file_name, profile_definition = profile_definition, expected_machine_definition = expected_machine_definition))
                    global_profile.setMetaDataEntry("definition", expected_machine_definition)
                    for extruder_profile in extruder_profiles:
                        extruder_profile.setMetaDataEntry("definition", expected_machine_definition)

                quality_name = global_profile.getName()
                quality_type = global_profile.getMetaDataEntry("quality_type")

                name_seed = os.path.splitext(os.path.basename(file_name))[0]
                new_name = self.uniqueName(name_seed)

                # Ensure it is always a list of profiles
                if type(profile_or_list) is not list:
                    profile_or_list = [profile_or_list]

                # Make sure that there are also extruder stacks' quality_changes, not just one for the global stack
                if len(profile_or_list) == 1:
                    global_profile = profile_or_list[0]
                    extruder_profiles = []
                    for idx, extruder in enumerate(global_stack.extruderList):
                        profile_id = ContainerRegistry.getInstance().uniqueName(global_stack.getId() + "_extruder_" + str(idx + 1))
                        profile = InstanceContainer(profile_id)
                        profile.setName(quality_name)
                        profile.setMetaDataEntry("setting_version", cura.CuraApplication.CuraApplication.SettingVersion)
                        profile.setMetaDataEntry("type", "quality_changes")
                        profile.setMetaDataEntry("definition", expected_machine_definition)
                        profile.setMetaDataEntry("quality_type", quality_type)
                        profile.setDirty(True)
                        if idx == 0:
                            # Move all per-extruder settings to the first extruder's quality_changes
                            for qc_setting_key in global_profile.getAllKeys():
                                settable_per_extruder = global_stack.getProperty(qc_setting_key, "settable_per_extruder")
                                if settable_per_extruder:
                                    setting_value = global_profile.getProperty(qc_setting_key, "value")

                                    setting_definition = global_stack.getSettingDefinition(qc_setting_key)
                                    if setting_definition is not None:
                                        new_instance = SettingInstance(setting_definition, profile)
                                        new_instance.setProperty("value", setting_value)
                                        new_instance.resetState()  # Ensure that the state is not seen as a user state.
                                        profile.addInstance(new_instance)
                                        profile.setDirty(True)

                                    global_profile.removeInstance(qc_setting_key, postpone_emit = True)
                        extruder_profiles.append(profile)

                    for profile in extruder_profiles:
                        profile_or_list.append(profile)

                # Import all profiles
                profile_ids_added = []  # type: List[str]
                for profile_index, profile in enumerate(profile_or_list):
                    if profile_index == 0:
                        # This is assumed to be the global profile
                        profile_id = (cast(ContainerInterface, global_stack.getBottom()).getId() + "_" + name_seed).lower().replace(" ", "_")

                    elif profile_index < len(machine_extruders) + 1:
                        # This is assumed to be an extruder profile
                        extruder_id = machine_extruders[profile_index - 1].definition.getId()
                        extruder_position = str(profile_index - 1)
                        if not profile.getMetaDataEntry("position"):
                            profile.setMetaDataEntry("position", extruder_position)
                        else:
                            profile.setMetaDataEntry("position", extruder_position)
                        profile_id = (extruder_id + "_" + name_seed).lower().replace(" ", "_")

                    else:  # More extruders in the imported file than in the machine.
                        continue  # Delete the additional profiles.

                    result = self._configureProfile(profile, profile_id, new_name, expected_machine_definition)
                    if result is not None:
                        # Remove any profiles that did got added.
                        for profile_id in profile_ids_added:
                            self.removeContainer(profile_id)

                        return {"status": "error", "message": catalog.i18nc(
                            "@info:status Don't translate the XML tag <filename>!",
                            "Failed to import profile from <filename>{0}</filename>:",
                            file_name) + " " + result}
                    profile_ids_added.append(profile.getId())
                return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())}

            # This message is throw when the profile reader doesn't find any profile in the file
            return {"status": "error", "message": catalog.i18nc("@info:status", "File {0} does not contain any valid profile.", file_name)}

        # If it hasn't returned by now, none of the plugins loaded the profile successfully.
        return {"status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type or is corrupted.", file_name)}
Ejemplo n.º 43
0
    def _onRequestFinished(self, reply: QNetworkReply) -> None:
        if reply.error() == QNetworkReply.TimeoutError:
            Logger.log("w", "Received a timeout on a request to the instance")
            self._connection_state_before_timeout = self._connection_state
            self.setConnectionState(
                cast(ConnectionState, UnifiedConnectionState.Error))
            return

        if self._connection_state_before_timeout and reply.error(
        ) == QNetworkReply.NoError:  #  There was a timeout, but we got a correct answer again.
            if self._last_response_time:
                Logger.log(
                    "d",
                    "We got a response from the instance after %s of silence",
                    time() - self._last_response_time)
            self.setConnectionState(self._connection_state_before_timeout)
            self._connection_state_before_timeout = None

        if reply.error() == QNetworkReply.NoError:
            self._last_response_time = time()

        http_status_code = reply.attribute(
            QNetworkRequest.HttpStatusCodeAttribute)
        if not http_status_code:
            # Received no or empty reply
            return

        error_handled = False

        if reply.operation() == QNetworkAccessManager.GetOperation:
            if self._api_prefix + "printer" in reply.url().toString(
            ):  # Status update from /printer.
                if not self._printers:
                    self._createPrinterList()

                # An OctoPrint instance has a single printer.
                printer = self._printers[0]
                update_pace = self._update_slow_interval

                if http_status_code == 200:
                    update_pace = self._update_fast_interval

                    if not self.acceptsCommands:
                        self._setAcceptsCommands(True)
                        self.setConnectionText(
                            i18n_catalog.i18nc(
                                "@info:status",
                                "Connected to OctoPrint on {0}").format(
                                    self._id))

                    if self._connection_state == UnifiedConnectionState.Connecting:
                        self.setConnectionState(
                            cast(ConnectionState,
                                 UnifiedConnectionState.Connected))
                    try:
                        json_data = json.loads(
                            bytes(reply.readAll()).decode("utf-8"))
                    except json.decoder.JSONDecodeError:
                        Logger.log(
                            "w",
                            "Received invalid JSON from octoprint instance.")
                        json_data = {}

                    if "temperature" in json_data:
                        if not self._number_of_extruders_set:
                            self._number_of_extruders = 0
                            while "tool%d" % self._number_of_extruders in json_data[
                                    "temperature"]:
                                self._number_of_extruders += 1

                            if self._number_of_extruders > 1:
                                # Recreate list of printers to match the new _number_of_extruders
                                self._createPrinterList()
                                printer = self._printers[0]

                            if self._number_of_extruders > 0:
                                self._number_of_extruders_set = True

                        # Check for hotend temperatures
                        for index in range(0, self._number_of_extruders):
                            extruder = printer.extruders[index]
                            if ("tool%d" % index) in json_data["temperature"]:
                                hotend_temperatures = json_data["temperature"][
                                    "tool%d" % index]
                                target_temperature = hotend_temperatures[
                                    "target"] if hotend_temperatures[
                                        "target"] is not None else -1
                                actual_temperature = hotend_temperatures[
                                    "actual"] if hotend_temperatures[
                                        "actual"] is not None else -1
                                extruder.updateTargetHotendTemperature(
                                    target_temperature)
                                extruder.updateHotendTemperature(
                                    actual_temperature)
                            else:
                                extruder.updateTargetHotendTemperature(0)
                                extruder.updateHotendTemperature(0)

                        if "bed" in json_data["temperature"]:
                            bed_temperatures = json_data["temperature"]["bed"]
                            actual_temperature = bed_temperatures[
                                "actual"] if bed_temperatures[
                                    "actual"] is not None else -1
                            printer.updateBedTemperature(actual_temperature)
                            target_temperature = bed_temperatures[
                                "target"] if bed_temperatures[
                                    "target"] is not None else -1
                            printer.updateTargetBedTemperature(
                                target_temperature)
                        else:
                            printer.updateBedTemperature(-1)
                            printer.updateTargetBedTemperature(0)

                    printer_state = "offline"
                    if "state" in json_data:
                        flags = json_data["state"]["flags"]
                        if flags["error"] or flags["closedOrError"]:
                            printer_state = "error"
                        elif flags["paused"] or flags["pausing"]:
                            printer_state = "paused"
                        elif flags["printing"]:
                            printer_state = "printing"
                        elif flags["cancelling"]:
                            printer_state = "aborted"
                        elif flags["ready"] or flags["operational"]:
                            printer_state = "idle"
                    printer.updateState(printer_state)

                elif http_status_code == 401:
                    printer.updateState("offline")
                    if printer.activePrintJob:
                        printer.activePrintJob.updateState("offline")
                    self.setConnectionText(
                        i18n_catalog.i18nc(
                            "@info:status",
                            "OctoPrint on {0} does not allow access to print").
                        format(self._id))
                    error_handled = True

                elif http_status_code == 409:
                    if self._connection_state == UnifiedConnectionState.Connecting:
                        self.setConnectionState(
                            cast(ConnectionState,
                                 UnifiedConnectionState.Connected))

                    printer.updateState("offline")
                    if printer.activePrintJob:
                        printer.activePrintJob.updateState("offline")
                    self.setConnectionText(
                        i18n_catalog.i18nc(
                            "@info:status",
                            "The printer connected to OctoPrint on {0} is not operational"
                        ).format(self._id))
                    error_handled = True

                elif http_status_code == 502 or http_status_code == 503:
                    printer.updateState("offline")
                    if printer.activePrintJob:
                        printer.activePrintJob.updateState("offline")
                    self.setConnectionText(
                        i18n_catalog.i18nc(
                            "@info:status",
                            "OctoPrint on {0} is not running").format(
                                self._id))
                    error_handled = True

                else:
                    printer.updateState("offline")
                    if printer.activePrintJob:
                        printer.activePrintJob.updateState("offline")
                    Logger.log("w", "Received an unexpected returncode: %d",
                               http_status_code)

                if update_pace != self._update_timer.interval():
                    self._update_timer.setInterval(update_pace)

            elif self._api_prefix + "job" in reply.url().toString(
            ):  # Status update from /job:
                if not self._printers:
                    return  # Ignore the data for now, we don't have info about a printer yet.
                printer = self._printers[0]

                if http_status_code == 200:
                    try:
                        json_data = json.loads(
                            bytes(reply.readAll()).decode("utf-8"))
                    except json.decoder.JSONDecodeError:
                        Logger.log(
                            "w",
                            "Received invalid JSON from octoprint instance.")
                        json_data = {}

                    if printer.activePrintJob is None:
                        print_job = PrintJobOutputModel(
                            output_controller=self._output_controller)
                        printer.updateActivePrintJob(print_job)
                    else:
                        print_job = printer.activePrintJob

                    print_job_state = "offline"
                    if "state" in json_data:
                        if json_data["state"] == "Error":
                            print_job_state = "error"
                        elif json_data["state"] == "Pausing":
                            print_job_state = "pausing"
                        elif json_data["state"] == "Paused":
                            print_job_state = "paused"
                        elif json_data["state"] == "Printing":
                            print_job_state = "printing"
                        elif json_data["state"] == "Cancelling":
                            print_job_state = "abort"
                        elif json_data["state"] == "Operational":
                            print_job_state = "ready"
                            printer.updateState("idle")
                    print_job.updateState(print_job_state)

                    print_time = json_data["progress"]["printTime"]
                    if print_time:
                        print_job.updateTimeElapsed(print_time)

                        print_time_left = json_data["progress"][
                            "printTimeLeft"]
                        completion = json_data["progress"]["completion"]
                        if print_time_left:  # not 0 or None or ""
                            print_job.updateTimeTotal(print_time +
                                                      print_time_left)
                        elif completion:  # not 0 or None or ""
                            print_job.updateTimeTotal(print_time /
                                                      (completion / 100))
                        else:
                            print_job.updateTimeTotal(0)
                    else:
                        print_job.updateTimeElapsed(0)
                        print_job.updateTimeTotal(0)

                    print_job.updateName(json_data["job"]["file"]["name"])
                else:
                    pass  # See generic error handler below

            elif self._api_prefix + "settings" in reply.url().toString(
            ):  # OctoPrint settings dump from /settings:
                if http_status_code == 200:
                    try:
                        json_data = json.loads(
                            bytes(reply.readAll()).decode("utf-8"))
                    except json.decoder.JSONDecodeError:
                        Logger.log(
                            "w",
                            "Received invalid JSON from octoprint instance.")
                        json_data = {}

                    if "feature" in json_data and "sdSupport" in json_data[
                            "feature"]:
                        self._sd_supported = json_data["feature"]["sdSupport"]

                    if "webcam" in json_data and "streamUrl" in json_data[
                            "webcam"]:
                        self._camera_shares_proxy = False
                        stream_url = json_data["webcam"]["streamUrl"]
                        if not stream_url:  #empty string or None
                            self._camera_url = ""
                        elif stream_url[:4].lower() == "http":  # absolute uri
                            self._camera_url = stream_url
                        elif stream_url[:2] == "//":  # protocol-relative
                            self._camera_url = "%s:%s" % (self._protocol,
                                                          stream_url)
                        elif stream_url[:
                                        1] == ":":  # domain-relative (on another port)
                            self._camera_url = "%s://%s%s" % (
                                self._protocol, self._address, stream_url)
                        elif stream_url[:
                                        1] == "/":  # domain-relative (on same port)
                            self._camera_url = "%s://%s:%d%s" % (
                                self._protocol, self._address, self._port,
                                stream_url)
                            self._camera_shares_proxy = True
                        else:
                            Logger.log("w", "Unusable stream url received: %s",
                                       stream_url)
                            self._camera_url = ""

                        Logger.log("d", "Set OctoPrint camera url to %s",
                                   self._camera_url)
                        self.cameraUrlChanged.emit()

                        if "rotate90" in json_data["webcam"]:
                            self._camera_rotation = -90 if json_data["webcam"][
                                "rotate90"] else 0
                            if json_data["webcam"]["flipH"] and json_data[
                                    "webcam"]["flipV"]:
                                self._camera_mirror = False
                                self._camera_rotation += 180
                            elif json_data["webcam"]["flipH"]:
                                self._camera_mirror = True
                                self._camera_rotation += 180
                            elif json_data["webcam"]["flipV"]:
                                self._camera_mirror = True
                            else:
                                self._camera_mirror = False
                            self.cameraOrientationChanged.emit()

                    if "plugins" in json_data:
                        self._plugin_data = json_data["plugins"]

                        if "UltimakerFormatPackage" in self._plugin_data:
                            try:
                                ufp_writer_plugin = PluginRegistry.getInstance(
                                ).getPluginObject("UFPWriter")
                                self._ufp_supported = True
                                Logger.log("d", "Instance supports UFP files")
                            except PluginNotFoundError:
                                Logger.log(
                                    "w",
                                    "Instance supports UFP files, but UFPWriter is not available"
                                )

        elif reply.operation() == QNetworkAccessManager.PostOperation:
            if self._api_prefix + "files" in reply.url().toString(
            ):  # Result from /files command:
                if http_status_code == 201:
                    Logger.log(
                        "d", "Resource created on OctoPrint instance: %s",
                        reply.header(
                            QNetworkRequest.LocationHeader).toString())
                else:
                    pass  # See generic error handler below

                reply.uploadProgress.disconnect(self._onUploadProgress)
                if self._progress_message:
                    self._progress_message.hide()

                if self._forced_queue or not self._auto_print:
                    location = reply.header(QNetworkRequest.LocationHeader)
                    if location:
                        file_name = QUrl(
                            reply.header(QNetworkRequest.LocationHeader).
                            toString()).fileName()
                        message = Message(
                            i18n_catalog.i18nc(
                                "@info:status",
                                "Saved to OctoPrint as {0}").format(file_name))
                    else:
                        message = Message(
                            i18n_catalog.i18nc("@info:status",
                                               "Saved to OctoPrint"))
                    message.addAction(
                        "open_browser",
                        i18n_catalog.i18nc("@action:button", "OctoPrint..."),
                        "globe",
                        i18n_catalog.i18nc("@info:tooltip",
                                           "Open the OctoPrint web interface"))
                    message.actionTriggered.connect(self._openOctoPrint)
                    message.show()

            elif self._api_prefix + "job" in reply.url().toString(
            ):  # Result from /job command (eg start/pause):
                if http_status_code == 204:
                    Logger.log("d", "Octoprint job command accepted")
                else:
                    pass  # See generic error handler below

            elif self._api_prefix + "printer/command" in reply.url().toString(
            ):  # Result from /printer/command (gcode statements):
                if http_status_code == 204:
                    Logger.log("d", "Octoprint gcode command(s) accepted")
                else:
                    pass  # See generic error handler below

        else:
            Logger.log("d",
                       "OctoPrintOutputDevice got an unhandled operation %s",
                       reply.operation())

        if not error_handled and http_status_code >= 400:
            # Received an error reply
            error_string = bytes(reply.readAll()).decode("utf-8")
            if not error_string:
                error_string = reply.attribute(
                    QNetworkRequest.HttpReasonPhraseAttribute)
            if self._error_message:
                self._error_message.hide()
            self._error_message = Message(error_string,
                                          title=i18n_catalog.i18nc(
                                              "@label", "OctoPrint error"))
            self._error_message.show()
            return
Ejemplo n.º 44
0
 def addContainerType(cls, container):
     plugin_id = container.getPluginId()
     metadata = PluginRegistry.getInstance().getMetaData(plugin_id)
     if "settings_container" not in metadata or "mimetype" not in metadata["settings_container"]:
         raise Exception("Plugin {plugin} has incorrect metadata: Expected a 'settings_container' block with a 'mimetype' entry".format(plugin = plugin_id))
     cls.addContainerTypeByName(container.__class__, plugin_id, metadata["settings_container"]["mimetype"])
Ejemplo n.º 45
0
    def __init__(self,
                 api_client: CloudApiClient,
                 cluster: CloudClusterResponse,
                 parent: QObject = None) -> None:

        # The following properties are expected on each networked output device.
        # Because the cloud connection does not off all of these, we manually construct this version here.
        # An example of why this is needed is the selection of the compatible file type when exporting the tool path.
        properties = {
            b"address":
            cluster.host_internal_ip.encode()
            if cluster.host_internal_ip else b"",
            b"name":
            cluster.friendly_name.encode() if cluster.friendly_name else b"",
            b"firmware_version":
            cluster.host_version.encode() if cluster.host_version else b"",
            b"printer_type":
            cluster.printer_type.encode() if cluster.printer_type else b"",
            b"cluster_size":
            b"1"  # cloud devices are always clusters of at least one
        }

        super().__init__(device_id=cluster.cluster_id,
                         address="",
                         connection_type=ConnectionType.CloudConnection,
                         properties=properties,
                         parent=parent)
        self._api = api_client
        self._cluster = cluster

        self._setInterfaceElements()

        self._account = api_client.account

        # We use the Cura Connect monitor tab to get most functionality right away.
        if PluginRegistry.getInstance() is not None:
            plugin_path = PluginRegistry.getInstance().getPluginPath(
                "UM3NetworkPrinting")
            if plugin_path is None:
                Logger.log(
                    "e",
                    "Cloud not find plugin path for plugin UM3NetworkPrnting")
                raise RuntimeError(
                    "Cloud not find plugin path for plugin UM3NetworkPrnting")
            self._monitor_view_qml_path = os.path.join(plugin_path,
                                                       "resources", "qml",
                                                       "MonitorStage.qml")

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

        # We keep track of which printer is visible in the monitor page.
        self._active_printer = None  # type: Optional[PrinterOutputModel]

        # Properties to populate later on with received cloud data.
        self._print_jobs = []  # type: List[UM3PrintJobOutputModel]
        self._number_of_extruders = 2  # All networked printers are dual-extrusion Ultimaker machines.

        # We only allow a single upload at a time.
        self._progress = CloudProgressMessage()

        # Keep server string of the last generated time to avoid updating models more than once for the same response
        self._received_printers = None  # type: Optional[List[CloudClusterPrinterStatus]]
        self._received_print_jobs = None  # type: Optional[List[CloudClusterPrintJobStatus]]

        # A set of the user's job IDs that have finished
        self._finished_jobs = set()  # type: Set[str]

        # Reference to the uploaded print job / mesh
        self._tool_path = None  # type: Optional[bytes]
        self._uploaded_print_job = None  # type: Optional[CloudPrintJobResponse]
Ejemplo n.º 46
0
    def read(self, file_name):
        if file_name.split(".")[-1] != "ini":
            return None
        global_container_stack = Application.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            return None

        multi_extrusion = global_container_stack.getProperty(
            "machine_extruder_count", "value") > 1
        if multi_extrusion:
            Logger.log(
                "e",
                "Unable to import legacy profile %s. Multi extrusion is not supported",
                file_name)
            raise Exception(
                "Unable to import legacy profile. Multi extrusion is not supported"
            )

        Logger.log("i",
                   "Importing legacy profile from file " + file_name + ".")
        container_registry = ContainerRegistry.getInstance()
        profile_id = container_registry.uniqueName("Imported Legacy Profile")
        profile = InstanceContainer(profile_id)  # Create an empty profile.

        parser = configparser.ConfigParser(interpolation=None)
        try:
            parser.read([file_name])  # Parse the INI file.
        except Exception as e:
            Logger.log("e", "Unable to open legacy profile %s: %s", file_name,
                       str(e))
            return None

        # Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile".
        # Since importing multiple machine profiles is out of scope, just import the first section we find.
        section = ""
        for found_section in parser.sections():
            if found_section.startswith("profile"):
                section = found_section
                break
        if not section:  # No section starting with "profile" was found. Probably not a proper INI file.
            return None

        try:
            with open(
                    os.path.join(
                        PluginRegistry.getInstance().getPluginPath(
                            "LegacyProfileReader"), "DictionaryOfDoom.json"),
                    "r", -1, "utf-8") as f:
                dict_of_doom = json.load(f)  # Parse the Dictionary of Doom.
        except IOError as e:
            Logger.log("e",
                       "Could not open DictionaryOfDoom.json for reading: %s",
                       str(e))
            return None
        except Exception as e:
            Logger.log("e", "Could not parse DictionaryOfDoom.json: %s",
                       str(e))
            return None

        defaults = self.prepareDefaults(dict_of_doom)
        legacy_settings = self.prepareLocals(
            parser, section,
            defaults)  #Gets the settings from the legacy profile.

        #Check the target version in the Dictionary of Doom with this application version.
        if "target_version" not in dict_of_doom:
            Logger.log(
                "e",
                "Dictionary of Doom has no target version. Is it the correct JSON file?"
            )
            return None
        if InstanceContainer.Version != dict_of_doom["target_version"]:
            Logger.log(
                "e",
                "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!",
                dict_of_doom["target_version"], str(InstanceContainer.Version))
            return None

        if "translation" not in dict_of_doom:
            Logger.log(
                "e",
                "Dictionary of Doom has no translation. Is it the correct JSON file?"
            )
            return None
        current_printer_definition = global_container_stack.definition
        profile.setDefinition(current_printer_definition.getId())
        for new_setting in dict_of_doom[
                "translation"]:  # Evaluate all new settings that would get a value from the translations.
            old_setting_expression = dict_of_doom["translation"][new_setting]
            compiled = compile(old_setting_expression, new_setting, "eval")
            try:
                new_value = eval(
                    compiled, {"math": math}, legacy_settings
                )  # Pass the legacy settings as local variables to allow access to in the evaluation.
                value_using_defaults = eval(
                    compiled, {"math": math}, defaults
                )  #Evaluate again using only the default values to try to see if they are default.
            except Exception:  # Probably some setting name that was missing or something else that went wrong in the ini file.
                Logger.log(
                    "w", "Setting " + new_setting +
                    " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile."
                )
                continue
            definitions = current_printer_definition.findDefinitions(
                key=new_setting)
            if definitions:
                if new_value != value_using_defaults and definitions[
                        0].default_value != new_value:  # Not equal to the default in the new Cura OR the default in the legacy Cura.
                    profile.setProperty(
                        new_setting, "value",
                        new_value)  # Store the setting in the profile!

        if len(profile.getAllKeys()) == 0:
            Logger.log(
                "i",
                "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile."
            )

        profile.addMetaDataEntry("type", "profile")
        # don't know what quality_type it is based on, so use "normal" by default
        profile.addMetaDataEntry("quality_type", "normal")
        profile.setName(profile_id)
        profile.setDirty(True)

        #Serialise and deserialise in order to perform the version upgrade.
        parser = configparser.ConfigParser(interpolation=None)
        data = profile.serialize()
        parser.read_string(data)
        parser["general"]["version"] = "1"
        if parser.has_section("values"):
            parser["settings"] = parser["values"]
            del parser["values"]
        stream = io.StringIO()
        parser.write(stream)
        data = stream.getvalue()
        profile.deserialize(data)

        #We need to return one extruder stack and one global stack.
        global_container_id = container_registry.uniqueName(
            "Global Imported Legacy Profile")
        global_profile = profile.duplicate(
            new_id=global_container_id, new_name=profile_id
        )  #Needs to have the same name as the extruder profile.
        global_profile.setDirty(True)

        #Only the extruder stack has an extruder metadata entry.
        profile.addMetaDataEntry(
            "extruder",
            ExtruderManager.getInstance().getActiveExtruderStack().definition.
            getId())

        #Split all settings into per-extruder and global settings.
        for setting_key in profile.getAllKeys():
            settable_per_extruder = global_container_stack.getProperty(
                setting_key, "settable_per_extruder")
            if settable_per_extruder:
                global_profile.removeInstance(setting_key)
            else:
                profile.removeInstance(setting_key)

        return [global_profile, profile]
Ejemplo n.º 47
0
    def event(self, event) -> bool:
        modifiers = QApplication.keyboardModifiers()
        ctrl_is_active = modifiers & Qt.ControlModifier
        shift_is_active = modifiers & Qt.ShiftModifier
        if event.type == Event.KeyPressEvent and ctrl_is_active:
            amount = 10 if shift_is_active else 1
            if event.key == KeyEvent.UpKey:
                self.setLayer(self._current_layer_num + amount)
                return True
            if event.key == KeyEvent.DownKey:
                self.setLayer(self._current_layer_num - amount)
                return True

        if event.type == Event.ViewActivateEvent:
            # Start listening to changes.
            Application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
            self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)

            self.calculateColorSchemeLimits()
            self.calculateMaxLayers()
            self.calculateMaxPathsOnLayer(self._current_layer_num)

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

            # Make sure the SimulationPass is created
            layer_pass = self.getSimulationPass()
            renderer = self.getRenderer()
            if renderer is None:
                return False

            renderer.addRenderPass(layer_pass)

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

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

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

            if not self._composite_pass:
                self._composite_pass = cast(CompositePass, renderer.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)
            self._updateSliceWarningVisibility()

        elif event.type == Event.ViewDeactivateEvent:
            self._controller.getScene().getRoot().childrenChanged.disconnect(self._onSceneChanged)
            Application.getInstance().getPreferences().preferenceChanged.disconnect(self._onPreferencesChanged)
            self._wireprint_warning_message.hide()
            self._slice_first_warning_message.hide()
            Application.getInstance().globalContainerStackChanged.disconnect(self._onGlobalStackChanged)
            if self._global_container_stack:
                self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged)
            if self._nozzle_node:
                self._nozzle_node.setParent(None)

            renderer = self.getRenderer()
            if renderer is None:
                return False

            if self._layer_pass is not None:
                renderer.removeRenderPass(self._layer_pass)
            if self._composite_pass:
                self._composite_pass.setLayerBindings(cast(List[str], self._old_layer_bindings))
                self._composite_pass.setCompositeShader(cast(ShaderProgram, self._old_composite_shader))

        return False
Ejemplo n.º 48
0
    __container_types = {
        "definition": DefinitionContainer,
        "instance": InstanceContainer,
        "stack": ContainerStack,
    }

    __mime_type_map = {
        "application/x-uranium-definitioncontainer": DefinitionContainer,
        "application/x-uranium-instancecontainer": InstanceContainer,
        "application/x-uranium-containerstack": ContainerStack,
        "application/x-uranium-extruderstack": ContainerStack
    }


PluginRegistry.addType("settings_container",
                       ContainerRegistry.addContainerType)


class _EmptyInstanceContainer(InstanceContainer):
    def isDirty(self) -> bool:
        return False

    def isReadOnly(self) -> bool:
        return True

    def getProperty(self, key, property_name):
        return None

    def setProperty(self,
                    key,
                    property_name,
Ejemplo n.º 49
0
    def render(self):
        if not self._layer_shader:
            self._layer_shader = OpenGL.getInstance().createShaderProgram(
                os.path.join(
                    PluginRegistry.getInstance().getPluginPath("LayerView"),
                    "layers.shader"))
        # Use extruder 0 if the extruder manager reports extruder index -1 (for single extrusion printers)
        self._layer_shader.setUniformValue(
            "u_active_extruder",
            float(max(0, self._extruder_manager.activeExtruderIndex)))
        if not self._tool_handle_shader:
            self._tool_handle_shader = OpenGL.getInstance(
            ).createShaderProgram(
                Resources.getPath(Resources.Shaders, "toolhandle.shader"))

        self.bind()

        tool_handle_batch = RenderBatch(self._tool_handle_shader,
                                        type=RenderBatch.RenderType.Overlay)

        for node in DepthFirstIterator(self._scene.getRoot()):

            if isinstance(node, ToolHandle):
                tool_handle_batch.addItem(node.getWorldTransformation(),
                                          mesh=node.getSolidMesh())

            elif isinstance(node, SceneNode) and (node.getMeshData(
            ) or node.callDecoration("isBlockSlicing")) and node.isVisible():
                layer_data = node.callDecoration("getLayerData")
                if not layer_data:
                    continue

                # Render all layers below a certain number as line mesh instead of vertices.
                if self._layerview._current_layer_num - self._layerview._solid_layers > -1 and not self._layerview._only_show_top_layers:
                    start = 0
                    end = 0
                    element_counts = layer_data.getElementCounts()
                    for layer, counts in element_counts.items():
                        if layer + self._layerview._solid_layers > self._layerview._current_layer_num:
                            break
                        end += counts

                    # This uses glDrawRangeElements internally to only draw a certain range of lines.
                    batch = RenderBatch(self._layer_shader,
                                        type=RenderBatch.RenderType.Solid,
                                        mode=RenderBatch.RenderMode.Lines,
                                        range=(start, end))
                    batch.addItem(node.getWorldTransformation(), layer_data)
                    batch.render(self._scene.getActiveCamera())

                # Create a new batch that is not range-limited
                batch = RenderBatch(self._layer_shader,
                                    type=RenderBatch.RenderType.Solid)

                if self._layerview._current_layer_mesh:
                    batch.addItem(node.getWorldTransformation(),
                                  self._layerview._current_layer_mesh)

                if self._layerview._current_layer_jumps:
                    batch.addItem(node.getWorldTransformation(),
                                  self._layerview._current_layer_jumps)

                if len(batch.items) > 0:
                    batch.render(self._scene.getActiveCamera())

        # Render toolhandles on top of the layerview
        if len(tool_handle_batch.items) > 0:
            tool_handle_batch.render(self._scene.getActiveCamera())

        self.release()
Ejemplo n.º 50
0
    def initialize(self) -> None:
        # For Ubuntu Unity this makes Qt use its own menu bar rather than pass it on to Unity.
        os.putenv("UBUNTU_MENUPROXY", "0")

        # Custom signal handling
        Signal._app = self
        Signal._signalQueue = self

        # Initialize Resources. Set the application name and version here because we can only know the actual info
        # after the __init__() has been called.
        Resources.ApplicationIdentifier = self._app_name
        Resources.ApplicationVersion = self._version

        Resources.addSearchPath(os.path.join(os.path.dirname(sys.executable), "resources"))
        Resources.addSearchPath(os.path.join(self._app_install_dir, "share", "uranium", "resources"))
        Resources.addSearchPath(os.path.join(self._app_install_dir, "Resources", "uranium", "resources"))
        Resources.addSearchPath(os.path.join(self._app_install_dir, "Resources", self._app_name, "resources"))

        if not hasattr(sys, "frozen"):
            Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources"))

        i18nCatalog.setApplication(self)

        PluginRegistry.addType("backend", self.setBackend)
        PluginRegistry.addType("logger", Logger.addLogger)
        PluginRegistry.addType("extension", self.addExtension)

        self._preferences = Preferences()
        self._preferences.addPreference("general/language", self._default_language)
        self._preferences.addPreference("general/visible_settings", "")
        self._preferences.addPreference("general/plugins_to_remove", "")
        self._preferences.addPreference("general/disabled_plugins", "")

        self._controller = Controller(self)
        self._output_device_manager = OutputDeviceManager()

        self._operation_stack = OperationStack(self._controller) # type: OperationStack

        self._plugin_registry = PluginRegistry(self)  #type: PluginRegistry

        self._plugin_registry.addPluginLocation(os.path.join(self._app_install_dir, UraniumLibraryDir, "uranium"))
        self._plugin_registry.addPluginLocation(os.path.join(os.path.dirname(sys.executable), "plugins"))
        self._plugin_registry.addPluginLocation(os.path.join(self._app_install_dir, "Resources", "uranium", "plugins"))
        self._plugin_registry.addPluginLocation(os.path.join(self._app_install_dir, "Resources", self._app_name, "plugins"))
        # Locally installed plugins
        local_path = os.path.join(Resources.getStoragePath(Resources.Resources), "plugins")
        # Ensure the local plugins directory exists
        try:
            os.makedirs(local_path)
        except OSError:
            pass
        self._plugin_registry.addPluginLocation(local_path)

        if not hasattr(sys, "frozen"):
            self._plugin_registry.addPluginLocation(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins"))

        self._container_registry = self._container_registry_class(self)

        UM.Settings.InstanceContainer.setContainerRegistry(self._container_registry)
        UM.Settings.ContainerStack.setContainerRegistry(self._container_registry)

        # Initialize the package manager to remove and install scheduled packages.
        self._package_manager = self._package_manager_class(self)

        self.showMessageSignal.connect(self.showMessage)
        self.hideMessageSignal.connect(self.hideMessage)

        self.change_log_url = "https://github.com/Ultimaker/Uranium" #Where to find a more detailed description of the recent updates.
Ejemplo n.º 51
0
 def registerObjects(self, engine):
     engine.rootContext().setContextProperty("PluginRegistry",
                                             PluginRegistry.getInstance())
Ejemplo n.º 52
0
class Application:
    ##  Init method
    #
    #   \param name \type{string} The name of the application.
    #   \param version \type{string} Version, formatted as major.minor.rev
    #   \param build_type Additional version info on the type of build this is, such as "master".
    #   \param is_debug_mode Whether to run in debug mode.
    def __init__(self, name: str, version: str, build_type: str = "", is_debug_mode: bool = False, **kwargs) -> None:
        if Application.__instance is not None:
            raise RuntimeError("Try to create singleton '%s' more than once" % self.__class__.__name__)
        Application.__instance = self

        super().__init__()  # Call super to make multiple inheritance work.

        self._app_name = name #type: str
        self._version = version #type: str
        self._build_type = build_type #type: str
        self._is_debug_mode = is_debug_mode #type: bool
        self._is_headless = False #type: bool
        self._use_external_backend = False #type: bool

        self._cli_args = None #type: argparse.Namespace
        self._cli_parser = argparse.ArgumentParser(prog = self._app_name, add_help = False) #type: argparse.ArgumentParser

        self._main_thread = threading.current_thread() #type: threading.Thread

        self.default_theme = self._app_name  #type: str # Default theme is the application name
        self._default_language = "en_US" #type: str

        self._preferences_filename = None #type: str
        self._preferences = None #type: Preferences

        self._extensions = [] #type: List[Extension]
        self._required_plugins = [] #type: List[str]

        self._package_manager_class = PackageManager  # type: type
        self._package_manager = None  # type: PackageManager

        self._plugin_registry = None #type: PluginRegistry
        self._container_registry_class = ContainerRegistry #type: type
        self._container_registry = None #type: ContainerRegistry
        self._global_container_stack = None #type: ContainerStack

        self._controller = None #type: Controller
        self._backend = None #type: Backend
        self._output_device_manager = None #type: OutputDeviceManager
        self._operation_stack = None #type: OperationStack

        self._visible_messages = [] #type: List[Message]
        self._message_lock = threading.Lock() #type: threading.Lock

        self._app_install_dir = self.getInstallPrefix() #type: str

    # Adds the command line options that can be parsed by the command line parser.
    # Can be overridden to add additional command line options to the parser.
    def addCommandLineOptions(self) -> None:
        self._cli_parser.add_argument("--version",
                                      action = "version",
                                      version = "%(prog)s version: {0}".format(self._version))
        self._cli_parser.add_argument("--external-backend",
                                      action = "store_true",
                                      default = False,
                                      help = "Use an externally started backend instead of starting it automatically. This is a debug feature to make it possible to run the engine with debug options enabled.")
        self._cli_parser.add_argument('--headless',
                                      action = 'store_true',
                                      default = False,
                                      help = "Hides all GUI elements.")
        self._cli_parser.add_argument("--debug",
                                      action = "store_true",
                                      default = False,
                                      help = "Turn on the debug mode by setting this option.")

    def parseCliOptions(self) -> None:
        self._cli_args = self._cli_parser.parse_args()

        self._is_headless = self._cli_args.headless
        self._is_debug_mode = self._cli_args.debug or self._is_debug_mode
        self._use_external_backend = self._cli_args.external_backend

    # Performs initialization that must be done before start.
    def initialize(self) -> None:
        # For Ubuntu Unity this makes Qt use its own menu bar rather than pass it on to Unity.
        os.putenv("UBUNTU_MENUPROXY", "0")

        # Custom signal handling
        Signal._app = self
        Signal._signalQueue = self

        # Initialize Resources. Set the application name and version here because we can only know the actual info
        # after the __init__() has been called.
        Resources.ApplicationIdentifier = self._app_name
        Resources.ApplicationVersion = self._version

        Resources.addSearchPath(os.path.join(os.path.dirname(sys.executable), "resources"))
        Resources.addSearchPath(os.path.join(self._app_install_dir, "share", "uranium", "resources"))
        Resources.addSearchPath(os.path.join(self._app_install_dir, "Resources", "uranium", "resources"))
        Resources.addSearchPath(os.path.join(self._app_install_dir, "Resources", self._app_name, "resources"))

        if not hasattr(sys, "frozen"):
            Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources"))

        i18nCatalog.setApplication(self)

        PluginRegistry.addType("backend", self.setBackend)
        PluginRegistry.addType("logger", Logger.addLogger)
        PluginRegistry.addType("extension", self.addExtension)

        self._preferences = Preferences()
        self._preferences.addPreference("general/language", self._default_language)
        self._preferences.addPreference("general/visible_settings", "")
        self._preferences.addPreference("general/plugins_to_remove", "")
        self._preferences.addPreference("general/disabled_plugins", "")

        self._controller = Controller(self)
        self._output_device_manager = OutputDeviceManager()

        self._operation_stack = OperationStack(self._controller) # type: OperationStack

        self._plugin_registry = PluginRegistry(self)  #type: PluginRegistry

        self._plugin_registry.addPluginLocation(os.path.join(self._app_install_dir, UraniumLibraryDir, "uranium"))
        self._plugin_registry.addPluginLocation(os.path.join(os.path.dirname(sys.executable), "plugins"))
        self._plugin_registry.addPluginLocation(os.path.join(self._app_install_dir, "Resources", "uranium", "plugins"))
        self._plugin_registry.addPluginLocation(os.path.join(self._app_install_dir, "Resources", self._app_name, "plugins"))
        # Locally installed plugins
        local_path = os.path.join(Resources.getStoragePath(Resources.Resources), "plugins")
        # Ensure the local plugins directory exists
        try:
            os.makedirs(local_path)
        except OSError:
            pass
        self._plugin_registry.addPluginLocation(local_path)

        if not hasattr(sys, "frozen"):
            self._plugin_registry.addPluginLocation(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins"))

        self._container_registry = self._container_registry_class(self)

        UM.Settings.InstanceContainer.setContainerRegistry(self._container_registry)
        UM.Settings.ContainerStack.setContainerRegistry(self._container_registry)

        # Initialize the package manager to remove and install scheduled packages.
        self._package_manager = self._package_manager_class(self)

        self.showMessageSignal.connect(self.showMessage)
        self.hideMessageSignal.connect(self.hideMessage)

        self.change_log_url = "https://github.com/Ultimaker/Uranium" #Where to find a more detailed description of the recent updates.

    def startSplashWindowPhase(self) -> None:
        pass

    def startPostSplashWindowPhase(self) -> None:
        pass

    ##  Run the main event loop.
    #   This method should be re-implemented by subclasses to start the main event loop.
    #   \exception NotImplementedError
    def run(self):
        self.addCommandLineOptions()
        self.parseCliOptions()
        self.initialize()

        self.startSplashWindowPhase()
        self.startPostSplashWindowPhase()

    def getContainerRegistry(self):
        return self._container_registry

    ##  Emitted when the application window was closed and we need to shut down the application
    applicationShuttingDown = Signal()

    showMessageSignal = Signal()

    hideMessageSignal = Signal()

    globalContainerStackChanged = Signal()

    workspaceLoaded = Signal()

    def setGlobalContainerStack(self, stack: "ContainerStack") -> None:
        self._global_container_stack = stack
        self.globalContainerStackChanged.emit()

    def getGlobalContainerStack(self) -> Optional["ContainerStack"]:
        return self._global_container_stack

    def hideMessage(self, message: Message) -> None:
        raise NotImplementedError

    def showMessage(self, message: Message) -> None:
        raise NotImplementedError

    def showToastMessage(self, title: str, message: str) -> None:
        raise NotImplementedError

    ##  Get the version of the application
    def getVersion(self) -> str:
        return self._version

    ##  Get the build type of the application
    def getBuildType(self) -> str:
        return self._build_type

    def getIsDebugMode(self) -> bool:
        return self._is_debug_mode

    def getIsHeadLess(self) -> bool:
        return self._is_headless

    def getUseExternalBackend(self) -> bool:
        return self._use_external_backend

    visibleMessageAdded = Signal()

    ##  Hide message by ID (as provided by built-in id function)
    def hideMessageById(self, message_id: int) -> None:
        # If a user and the application tries to close same message dialog simultaneously, message_id could become an empty
        # string, and then the application will raise an error when trying to do "int(message_id)".
        # So we check the message_id here.
        if not message_id:
            return

        found_message = None
        with self._message_lock:
            for message in self._visible_messages:
                if id(message) == int(message_id):
                    found_message = message
        if found_message is not None:
            self.hideMessageSignal.emit(found_message)

    visibleMessageRemoved = Signal()

    ##  Get list of all visible messages
    def getVisibleMessages(self) -> List[Message]:
        with self._message_lock:
            return self._visible_messages

    ##  Function that needs to be overridden by child classes with a list of plugins it needs.
    def _loadPlugins(self) -> None:
        pass

    ##  Get name of the application.
    #   \returns app_name \type{string}
    def getApplicationName(self) -> str:
        return self._app_name

    ##  Get the preferences.
    #   \return preferences \type{Preferences}
    def getPreferences(self) -> Preferences:
        return self._preferences

    def savePreferences(self) -> None:
        if self._preferences_filename:
            self._preferences.writeToFile(self._preferences_filename)
        else:
            Logger.log("i", "Preferences filename not set. Unable to save file.")

    ##  Get the currently used IETF language tag.
    #   The returned tag is during runtime used to translate strings.
    #   \returns Language tag.
    def getApplicationLanguage(self) -> str:
        language = os.getenv("URANIUM_LANGUAGE")
        if not language:
            language = self._preferences.getValue("general/language")
        if not language:
            language = os.getenv("LANGUAGE")
        if not language:
            language = self._default_language

        return language

    ##  Application has a list of plugins that it *must* have. If it does not have these, it cannot function.
    #   These plugins can not be disabled in any way.
    def getRequiredPlugins(self) -> List[str]:
        return self._required_plugins

    ##  Set the plugins that the application *must* have in order to function.
    #   \param plugin_names \type{list} List of strings with the names of the required plugins
    def setRequiredPlugins(self, plugin_names: List[str]) -> None:
        self._required_plugins = plugin_names

    ##  Set the backend of the application (the program that does the heavy lifting).
    def setBackend(self, backend: "Backend") -> None:
        self._backend = backend

    ##  Get the backend of the application (the program that does the heavy lifting).
    #   \returns Backend \type{Backend}
    def getBackend(self) -> "Backend":
        return self._backend

    ##  Get the PluginRegistry of this application.
    #   \returns PluginRegistry \type{PluginRegistry}
    def getPluginRegistry(self) -> PluginRegistry:
        return self._plugin_registry

    ##  Get the Controller of this application.
    #   \returns Controller \type{Controller}
    def getController(self) -> Controller:
        return self._controller

    def getOperationStack(self) -> OperationStack:
        return self._operation_stack

    def getOutputDeviceManager(self) -> OutputDeviceManager:
        return self._output_device_manager

    ##  Return an application-specific Renderer object.
    #   \exception NotImplementedError
    def getRenderer(self) -> Renderer:
        raise NotImplementedError("getRenderer must be implemented by subclasses.")

    ##  Post a function event onto the event loop.
    #
    #   This takes a CallFunctionEvent object and puts it into the actual event loop.
    #   \exception NotImplementedError
    def functionEvent(self, event: CallFunctionEvent) -> None:
        raise NotImplementedError("functionEvent must be implemented by subclasses.")

    ##  Call a function the next time the event loop runs.
    #
    #   You can't get the result of this function directly. It won't block.
    #   \param function The function to call.
    #   \param args The positional arguments to pass to the function.
    #   \param kwargs The keyword arguments to pass to the function.
    def callLater(self, func: Callable[..., Any], *args, **kwargs) -> None:
        event = CallFunctionEvent(func, args, kwargs)
        self.functionEvent(event)

    ##  Get the application's main thread.
    def getMainThread(self) -> threading.Thread:
        return self._main_thread

    def addExtension(self, extension: "Extension") -> None:
        self._extensions.append(extension)

    def getExtensions(self) -> List["Extension"]:
        return self._extensions

    @staticmethod
    def getInstallPrefix() -> str:
        if "python" in os.path.basename(sys.executable):
            return os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), ".."))
        else:
            return os.path.abspath(os.path.join(os.path.dirname(sys.executable), ".."))

    __instance = None   # type: Application

    @classmethod
    def getInstance(cls, *args, **kwargs) -> "Application":
        return cls.__instance
Ejemplo n.º 53
0
 def _createViewFromQML(self):
     path = os.path.join(
         PluginRegistry.getInstance().getPluginPath(self.getPluginId()),
         self._qml_url)
     self._view = Application.getInstance().createQmlComponent(
         path, {"manager": self})
Ejemplo n.º 54
0
 def initialize(self):
     # Add machine_action as plugin type
     PluginRegistry.addType("machine_action", self.addMachineAction)
Ejemplo n.º 55
0
    def showFirmwareInterface(self):
        if self._firmware_view is None:
            path = os.path.join(PluginRegistry.getInstance().getPluginPath("USBPrinting"), "FirmwareUpdateWindow.qml")
            self._firmware_view = Application.getInstance().createQmlComponent(path, {"manager": self})

        self._firmware_view.show()
Ejemplo n.º 56
0
    def __init__(self, name, version, **kwargs):
        if (Application._instance != None):
            raise ValueError("Duplicate singleton creation")

        # If the constructor is called and there is no instance, set the instance to self.
        # This is done because we can't make constructor private
        Application._instance = self

        self._application_name = name
        self._version = version

        Signal._app = self
        Resources.ApplicationIdentifier = name
        i18nCatalog.setApplication(self)

        Resources.addSearchPath(os.path.dirname(sys.executable))
        Resources.addSearchPath(
            os.path.join(Application.getInstallPrefix(), "share", "uranium"))
        Resources.addSearchPath(
            os.path.join(Application.getInstallPrefix(), "Resources",
                         "uranium"))
        Resources.addSearchPath(
            os.path.join(Application.getInstallPrefix(), "Resources",
                         self.getApplicationName()))
        if not hasattr(sys, "frozen"):
            Resources.addSearchPath(
                os.path.join(os.path.abspath(os.path.dirname(__file__)), ".."))

        self._main_thread = threading.current_thread()

        super().__init__(
            **kwargs)  # Call super to make multiple inheritence work.

        self._renderer = None

        PluginRegistry.addType("backend", self.setBackend)
        PluginRegistry.addType("logger", Logger.addLogger)
        PluginRegistry.addType("extension", self.addExtension)

        preferences = Preferences.getInstance()
        preferences.addPreference("general/language", "en")
        try:
            preferences.readFromFile(
                Resources.getPath(Resources.Preferences,
                                  self._application_name + ".cfg"))
        except FileNotFoundError:
            pass

        self._controller = Controller(self)
        self._mesh_file_handler = MeshFileHandler()
        self._extensions = []
        self._backend = None
        self._output_device_manager = OutputDeviceManager()
        self._machine_manager = MachineManager(self._application_name)

        self._required_plugins = []

        self._operation_stack = OperationStack()

        self._plugin_registry = PluginRegistry.getInstance()

        self._plugin_registry.addPluginLocation(
            os.path.join(Application.getInstallPrefix(), "lib", "uranium"))
        self._plugin_registry.addPluginLocation(
            os.path.join(os.path.dirname(sys.executable), "plugins"))
        self._plugin_registry.addPluginLocation(
            os.path.join(Application.getInstallPrefix(), "Resources",
                         "uranium", "plugins"))
        self._plugin_registry.addPluginLocation(
            os.path.join(Application.getInstallPrefix(), "Resources",
                         self.getApplicationName(), "plugins"))
        # Locally installed plugins
        self._plugin_registry.addPluginLocation(
            os.path.join(Resources.getStoragePath(Resources.Resources),
                         "plugins"))
        if not hasattr(sys, "frozen"):
            self._plugin_registry.addPluginLocation(
                os.path.join(os.path.abspath(os.path.dirname(__file__)), "..",
                             "plugins"))

        self._plugin_registry.setApplication(self)

        self._parsed_command_line = None
        self.parseCommandLine()

        self._visible_messages = []
        self._message_lock = threading.Lock()
        self.showMessageSignal.connect(self.showMessage)
        self.hideMessageSignal.connect(self.hideMessage)
    def requestWrite(self,
                     nodes: List["SceneNode"],
                     file_name: Optional[str] = None,
                     limit_mimetypes: bool = False,
                     file_handler: Optional["FileHandler"] = None,
                     **kwargs: str) -> None:
        global_container_stack = CuraApplication.getInstance(
        ).getGlobalContainerStack()
        if not global_container_stack:
            return

        # Make sure post-processing plugin are run on the gcode
        self.writeStarted.emit(self)

        # Get the g-code through the GCodeWriter plugin
        # This produces the same output as "Save to File", adding the print settings to the bottom of the file
        gcode_writer = cast(
            MeshWriter,
            PluginRegistry.getInstance().getPluginObject("GCodeWriter"))
        self._gcode_stream = StringIO()
        if not gcode_writer.write(self._gcode_stream, None):
            Logger.log("e",
                       "GCodeWrite failed: %s" % gcode_writer.getInformation())
            return

        if self._error_message:
            self._error_message.hide()
            self._error_message = None

        if self._progress_message:
            self._progress_message.hide()
            self._progress_message = None

        self._auto_print = parseBool(
            global_container_stack.getMetaDataEntry("octoprint_auto_print",
                                                    True))
        self._forced_queue = False

        if self.activePrinter.state not in ["idle", ""]:
            Logger.log(
                "d", "Tried starting a print, but current state is %s" %
                self.activePrinter.state)
            if not self._auto_print:
                # Allow queueing the job even if OctoPrint is currently busy if autoprinting is disabled
                self._error_message = None
            elif self.activePrinter.state == "offline":
                self._error_message = Message(
                    i18n_catalog.i18nc(
                        "@info:status",
                        "The printer is offline. Unable to start a new job."))
            else:
                self._error_message = Message(
                    i18n_catalog.i18nc(
                        "@info:status",
                        "OctoPrint is busy. Unable to start a new job."))

            if self._error_message:
                self._error_message.addAction(
                    "Queue", i18n_catalog.i18nc("@action:button", "Queue job"),
                    "",
                    i18n_catalog.i18nc(
                        "@action:tooltip",
                        "Queue this print job so it can be printed later"))
                self._error_message.actionTriggered.connect(self._queuePrint)
                self._error_message.show()
                return

        self._startPrint()
Ejemplo n.º 58
0
    def event(self, event: Event) -> bool:

        if not self._selection_pass:
            self._selection_pass = cast(
                SelectionPass,
                Application.getInstance().getRenderer().getRenderPass(
                    "selection"))
            if not self._selection_pass:
                return False

        # Tool activated - make sure we render the faces
        if event.type == Event.ToolActivateEvent:
            self._changeRenderMode(faces=True)
            self._enabled = True
            if self._bc_list and self._bc_list.getActiveNode():
                self._controller.getScene().sceneChanged.emit(
                    self._bc_list.getActiveNode())
            return False

        # Tool deactivated - make sure we render the faces
        if event.type == Event.ToolDeactivateEvent:
            self._changeRenderMode(faces=False)
            self._enabled = False
            if self._bc_list and self._bc_list.getActiveNode():
                self._controller.getScene().sceneChanged.emit(
                    self._bc_list.getActiveNode())
                self._bc_list = None
            return False

        if not self.getEnabled():
            return False

        # Not a load face - make sure we render faces
        if not self._bc_list or not self._bc_list.getActiveNode(
        ) or isinstance(self._bc_list.getActiveNode(),
                        SmartSliceScene.AnchorFace):
            self._changeRenderMode(faces=True)
            return False

        active_node = self._bc_list.getActiveNode()  # Load face
        rotator = active_node.getRotator()  # Rotator on the load face
        arrow = active_node.activeArrow  # Active arrow on the load face

        if event.type == Event.MousePressEvent:

            # Must be a left mouse event to select or rotate
            if MouseEvent.LeftButton not in event.buttons:
                return False

            pixel_color = self._selection_pass.getIdAtPosition(
                event.x, event.y)

            # We did not click the tool - we need to select the surface under it if it exists
            # TODO - This is a little hacky.... we should implement a SelectionPass just for this Tool
            if not pixel_color or not arrow.isAxis(pixel_color):
                if Selection.hasSelection(
                ) and not Selection.getFaceSelectMode():
                    self._changeRenderMode(faces=True)
                    select_tool = PluginRegistry.getInstance().getPluginObject(
                        "SelectionTool")
                    return select_tool.event(event)

            # Rotator isn't enabled - we don't need to do anything
            if not rotator.isEnabled():
                return False

            # If we made it here, we have clicked the tool. Set the locked color to our tool color, and set the plane
            # the user will be constrained to drag in
            self.setLockedAxis(pixel_color)
            self.setDragPlane(Plane(rotator.rotation_axis))

            self.setDragStart(event.x, event.y)
            self._rotating = True
            self._angle = 0
            return True

        if event.type == Event.MouseMoveEvent:

            # Rotator isn't enabled - we don't need to do anything
            if not rotator.isEnabled():
                return False

            event = cast(MouseEvent, event)

            # Turn the shader on for the rotator and arrow if the mouse is hovered on them
            # in the above, pixel_color is the color of the solid mesh of the pixekl the mouse is on
            # For some reason, "ActiveAxis" means the color of the tool we are interested in
            if not self._rotating:
                self._changeRenderMode(faces=False)
                pixel_color = self._selection_pass.getIdAtPosition(
                    event.x, event.y)

                if rotator.isAxis(pixel_color):
                    rotator.setActiveAxis(pixel_color)
                    arrow.setActiveAxis(pixel_color)
                else:
                    rotator.setActiveAxis(None)
                    arrow.setActiveAxis(None)

                return False

            # We are rotating. Check to ensure we have a starting position for the mouse
            if not self.getDragStart():
                self.setDragStart(event.x, event.y)
                if not self.getDragStart():  #May have set it to None.
                    return False

            self.operationStarted.emit(self)

            drag_start = self.getDragStart() - rotator.center
            drag_position = self.getDragPosition(event.x, event.y)
            if not drag_position:
                return False
            drag_end = drag_position - rotator.center

            # Project the vectors back to the plane of the rotator
            drag_start = drag_start - drag_start.dot(
                rotator.rotation_axis) * rotator.rotation_axis
            drag_end = drag_end - drag_end.dot(
                rotator.rotation_axis) * rotator.rotation_axis

            angle = angleBetweenVectors(drag_start, drag_end)

            axes_length = (rotator.rotation_axis.normalized() -
                           drag_end.cross(drag_start).normalized()).length()
            angle = -angle if axes_length < 1.e-2 else angle

            rotation = Quaternion.fromAngleAxis(angle, rotator.rotation_axis)

            self._angle += angle
            active_node.rotateArrow(angle)
            self.setDragStart(event.x, event.y)

            return True

        # Finished the rotation - reset everything and update the arrow direction
        if event.type == Event.MouseReleaseEvent:
            if self._rotating:
                self.setDragPlane(None)
                self.setLockedAxis(ToolHandle.NoAxis)
                self._angle = None
                self._rotating = False
                self.propertyChanged.emit()
                active_node.facePropertyChanged.emit(active_node)
                # self._changeRenderMode(faces=True)
                self.operationStopped.emit(self)

                return True

        return False
Ejemplo n.º 59
0
    def initialize(self) -> None:
        Logger.log("d", "Initializing %s", self._app_display_name)
        Logger.log("d", "App Version %s", self._version)
        Logger.log("d", "Api Version %s", self._api_version)
        Logger.log("d", "Build type %s", self._build_type or "None")
        # For Ubuntu Unity this makes Qt use its own menu bar rather than pass it on to Unity.
        os.putenv("UBUNTU_MENUPROXY", "0")

        # Custom signal handling
        Signal._app = self
        Signal._signalQueue = self

        # Initialize Resources. Set the application name and version here because we can only know the actual info
        # after the __init__() has been called.
        Resources.ApplicationIdentifier = self._app_name
        Resources.ApplicationVersion = self._version

        Resources.addSearchPath(os.path.join(os.path.dirname(sys.executable), "resources"))
        Resources.addSearchPath(os.path.join(self._app_install_dir, "share", "uranium", "resources"))
        Resources.addSearchPath(os.path.join(self._app_install_dir, "Resources", "uranium", "resources"))
        Resources.addSearchPath(os.path.join(self._app_install_dir, "Resources", self._app_name, "resources"))

        if not hasattr(sys, "frozen"):
            Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources"))

        i18nCatalog.setApplication(self)

        PluginRegistry.addType("backend", self.setBackend)
        PluginRegistry.addType("logger", Logger.addLogger)
        PluginRegistry.addType("extension", self.addExtension)

        self._preferences = Preferences()
        self._preferences.addPreference("general/language", self._default_language)
        self._preferences.addPreference("general/visible_settings", "")
        self._preferences.addPreference("general/plugins_to_remove", "")
        self._preferences.addPreference("general/disabled_plugins", "")

        self._controller = Controller(self)
        self._output_device_manager = OutputDeviceManager()

        self._operation_stack = OperationStack(self._controller)

        self._plugin_registry = PluginRegistry(self)

        self._plugin_registry.addPluginLocation(os.path.join(self._app_install_dir, "lib", "uranium"))
        self._plugin_registry.addPluginLocation(os.path.join(self._app_install_dir, "lib64", "uranium"))
        self._plugin_registry.addPluginLocation(os.path.join(self._app_install_dir, "lib32", "uranium"))
        self._plugin_registry.addPluginLocation(os.path.join(os.path.dirname(sys.executable), "plugins"))
        self._plugin_registry.addPluginLocation(os.path.join(self._app_install_dir, "Resources", "uranium", "plugins"))
        self._plugin_registry.addPluginLocation(os.path.join(self._app_install_dir, "Resources", self._app_name, "plugins"))
        # Locally installed plugins
        local_path = os.path.join(Resources.getStoragePath(Resources.Resources), "plugins")
        # Ensure the local plugins directory exists
        try:
            os.makedirs(local_path)
        except OSError:
            pass
        self._plugin_registry.addPluginLocation(local_path)

        if not hasattr(sys, "frozen"):
            self._plugin_registry.addPluginLocation(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "plugins"))

        self._container_registry = self._container_registry_class(self)

        UM.Settings.InstanceContainer.setContainerRegistry(self._container_registry)
        UM.Settings.ContainerStack.setContainerRegistry(self._container_registry)

        self.showMessageSignal.connect(self.showMessage)
        self.hideMessageSignal.connect(self.hideMessage)
Ejemplo n.º 60
0
    def importProfile(self, file_name):
        if not file_name:
            return {
                "status":
                "error",
                "message":
                catalog.i18nc(
                    "@info:status",
                    "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                    file_name, "Invalid path")
            }

        plugin_registry = PluginRegistry.getInstance()
        for plugin_id, meta_data in self._getIOPlugins("profile_reader"):
            profile_reader = plugin_registry.getPluginObject(plugin_id)
            try:
                profile_or_list = profile_reader.read(
                    file_name)  # Try to open the file with the profile reader.
            except Exception as e:
                #Note that this will fail quickly. That is, if any profile reader throws an exception, it will stop reading. It will only continue reading if the reader returned None.
                Logger.log("e", "Failed to import profile from %s: %s",
                           file_name, str(e))
                return {
                    "status":
                    "error",
                    "message":
                    catalog.i18nc(
                        "@info:status",
                        "Failed to import profile from <filename>{0}</filename>: <message>{1}</message>",
                        file_name, str(e))
                }
            if profile_or_list:  # Success!
                name_seed = os.path.splitext(os.path.basename(file_name))[0]
                if type(profile_or_list) is not list:
                    profile = profile_or_list
                    self._configureProfile(profile, name_seed)
                    return {
                        "status":
                        "ok",
                        "message":
                        catalog.i18nc("@info:status",
                                      "Successfully imported profile {0}",
                                      profile.getName())
                    }
                else:
                    for profile in profile_or_list:
                        self._configureProfile(profile, name_seed)

                    if len(profile_or_list) == 1:
                        return {
                            "status":
                            "ok",
                            "message":
                            catalog.i18nc("@info:status",
                                          "Successfully imported profile {0}",
                                          profile_or_list[0].getName())
                        }
                    else:
                        profile_names = ", ".join(
                            [profile.getName() for profile in profile_or_list])
                        return {
                            "status":
                            "ok",
                            "message":
                            catalog.i18nc(
                                "@info:status",
                                "Successfully imported profiles {0}",
                                profile_names)
                        }

        #If it hasn't returned by now, none of the plugins loaded the profile successfully.
        return {
            "status":
            "error",
            "message":
            catalog.i18nc("@info:status",
                          "Profile {0} has an unknown file type.", file_name)
        }