def __init__(self): super().__init__() self._workspace_readers = [] self._workspace_writers = [] PluginRegistry.addType("workspace_reader", self.addReader) PluginRegistry.addType("workspace_writer", self.addWriter)
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
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)
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()
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()
def __init__(self): super().__init__() self._mesh_readers = {} self._mesh_writers = {} PluginRegistry.addType("mesh_writer", self.addWriter) PluginRegistry.addType("mesh_reader", self.addReader)
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
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)
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)
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)
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()
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)
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
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
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)
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]
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)
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)
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())
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)
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)
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)
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)}
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)
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)
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)
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)}
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
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")
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)
def _createSocket(self): super()._createSocket(os.path.abspath(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "Cura.proto"))) self._engine_is_fresh = True
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) }
def plugin_registry(application): PluginRegistry._PluginRegistry__instance = None plugin_registry = PluginRegistry(application) plugin_registry._plugin_locations = [] # Clear pre-defined plugin locations return plugin_registry
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
def createUserAgreementWindow(self): path = os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "UserAgreement.qml") self._user_agreement_window = Application.getInstance().createQmlComponent(path, {"manager": self})
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())
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
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()
def _setMainOverlay(self): main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("MonitorStage"), "MonitorMainView.qml") self.addDisplayComponent("main", main_component_path)
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)}
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
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"])
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]
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]
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
__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,
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()
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 registerObjects(self, engine): engine.rootContext().setContextProperty("PluginRegistry", PluginRegistry.getInstance())
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
def _createViewFromQML(self): path = os.path.join( PluginRegistry.getInstance().getPluginPath(self.getPluginId()), self._qml_url) self._view = Application.getInstance().createQmlComponent( path, {"manager": self})
def initialize(self): # Add machine_action as plugin type PluginRegistry.addType("machine_action", self.addMachineAction)
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()
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()
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
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)
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) }