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 init_country_names_from_code(locale): '''Init the country description as found in GCompris geography/resource/board/board*.qml''' '''in the global descriptions hash''' po = None try: po = polib.pofile( gcompris_qt + '/po/gcompris_' + locale + '.po') except: print "**ERROR: Failed to load po file %s**" %(gcompris_qt + '/po/gcompris_' + locale + '.po') print '' app = QCoreApplication(sys.argv) engine = QQmlEngine() component = QQmlComponent(engine) for qml in glob.glob(gcompris_qt + '/src/activities/geography/resource/board/*.qml'): component.loadUrl(QUrl(qml)) board = component.create() levels = board.property('levels') for level in levels.toVariant(): if level.has_key('soundFile') and level.has_key('toolTipText'): sound = level['soundFile'].split('/')[-1].replace('$CA', 'ogg') tooltip = level['toolTipText'] if po: tooltip = po.find(tooltip).msgstr if po.find(tooltip) else tooltip descriptions[sound] = tooltip
def spawnFirmwareInterface(self, serial_port): if self._firmware_view is None: path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("Doodle3D"), "SettingsWindow.qml")) component = QQmlComponent(Application.getInstance()._engine, path) self._firmware_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._firmware_context.setContextProperty("manager", self) self._firmware_view = component.create(self._firmware_context) self._firmware_view.show()
def createControlInterface(self): if self._control_view is None: Logger.log("d", "Creating control interface for printer connection") path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("USBPrinting"), "ControlWindow.qml")) component = QQmlComponent(Application.getInstance()._engine, path) self._control_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._control_context.setContextProperty("manager", self) self._control_view = component.create(self._control_context)
def _createConfigUI(self): if self._ui_view is None: Logger.log("d", "Creating ImageReader config UI") path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("ImageReader"), "ConfigUI.qml")) component = QQmlComponent(Application.getInstance()._engine, path) self._ui_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._ui_context.setContextProperty("manager", self) self._ui_view = component.create(self._ui_context) self._ui_view.setFlags(self._ui_view.flags() & ~Qt.WindowCloseButtonHint & ~Qt.WindowMinimizeButtonHint & ~Qt.WindowMaximizeButtonHint); self._disable_size_callbacks = False
def get_geography_on_letter_from_code(): '''Return all the countries in geography/resource/board/board-x.json''' words = set() app = QCoreApplication(sys.argv) engine = QQmlEngine() component = QQmlComponent(engine) for qml in glob.glob(gcompris_qt + '/src/activities/geography/resource/board/*.qml'): component.loadUrl(QUrl(qml)) board = component.create() levels = board.property('levels') for level in levels.toVariant(): if level.has_key('soundFile') and (not level.has_key('type') or level['type'] != "SHAPE_BACKGROUND"): sound = level['soundFile'].split('/')[-1].replace('$CA', 'ogg') words.add(sound) return words
def main(): # Set up correct Ctrl-C behavior. signal.signal(signal.SIGINT, signal.SIG_DFL) app = QGuiApplication( sys.argv ) engine = QQmlEngine() engine.addImportPath( path.join( path.dirname( __file__ ), 'ui', 'qml' ) ) core.register_types() component = QQmlComponent( engine ) component.loadUrl( QUrl( '/home/feoh3/.config/nube/hud.qml' ) ) if component.isError(): print( "\n".join( error.toString() for error in component.errors() ) ) else: window = component.create() app.exec_()
def createQmlComponent(self, qml_file_path: str, context_properties: Dict[str, "QObject"] = None) -> Optional["QObject"]: if self._qml_engine is None: # Protect in case the engine was not initialized yet return None path = QUrl.fromLocalFile(qml_file_path) component = QQmlComponent(self._qml_engine, path) result_context = QQmlContext(self._qml_engine.rootContext()) #type: ignore #MyPy doens't realise that self._qml_engine can't be None here. if context_properties is not None: for name, value in context_properties.items(): result_context.setContextProperty(name, value) result = component.create(result_context) for err in component.errors(): Logger.log("e", str(err.toString())) if result is None: return None # We need to store the context with the qml object, else the context gets garbage collected and the qml objects # no longer function correctly/application crashes. result.attached_context = result_context return result
class PauseBackend(QObject, Extension): def __init__(self, parent = None): super().__init__(parent = parent) self._additional_component = None self._additional_components_view = None Application.getInstance().engineCreatedSignal.connect(self._createAdditionalComponentsView) def _createAdditionalComponentsView(self): Logger.log("d", "Creating additional ui components for Pause Backend plugin.") path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("PauseBackendPlugin"), "PauseBackend.qml")) self._additional_component = QQmlComponent(Application.getInstance()._engine, path) # We need access to engine (although technically we can't) self._additional_components_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._additional_components_context.setContextProperty("manager", self) self._additional_components_view = self._additional_component.create(self._additional_components_context) if not self._additional_components_view: Logger.log("w", "Could not create additional components for Pause Backend plugin.") return Application.getInstance().addAdditionalComponent("saveButton", self._additional_components_view.findChild(QObject, "pauseResumeButton")) @pyqtSlot() def pauseBackend(self): backend = Application.getInstance().getBackend() backend._change_timer.timeout.disconnect(backend.slice) backend._terminate() backend.backendStateChange.emit(BackendState.Error) @pyqtSlot() def resumeBackend(self): backend = Application.getInstance().getBackend() backend._change_timer.timeout.connect(backend.slice) backend.forceSlice()
class PostProcessingPlugin(QObject, Extension): def __init__(self, parent = None): super().__init__(parent) self.addMenuItem(i18n_catalog.i18n("Modify G-Code"), self.showPopup) self._view = None # Loaded scripts are all scripts that can be used self._loaded_scripts = {} self._script_labels = {} # Script list contains instances of scripts in loaded_scripts. There can be duplicates and they will be executed in sequence. self._script_list = [] self._selected_script_index = 0 @pyqtSlot(int, result = "QVariant") def getSettingModel(self, index): return self._script_list[index].getSettingsModel() @pyqtSlot(str, "QVariant") ## Called when the setting is changed. def setSettingValue(self, key, value): setting = self._script_list[self._selected_script_index].getSettings().getSettingByKey(key) if setting: setting.setValue(value) #self._script_list[self._selected_script_index].getSettings().setSettingValue selectedIndexChanged = pyqtSignal() @pyqtProperty("QVariant", notify = selectedIndexChanged) def selectedScriptSettingsModel(self): try: return self._script_list[self._selected_script_index].getSettingsModel() except: return None @pyqtSlot() def execute(self): scene = Application.getInstance().getController().getScene() if hasattr(scene, "gcode_list"): gcode_list = getattr(scene, "gcode_list") if gcode_list: for script in self._script_list: try: gcode_list = script.execute(gcode_list) except Exception as e: print(e) pass setattr(scene, "gcode_list", gcode_list) @pyqtSlot(int) def setSelectedScriptIndex(self, index): self._selected_script_index = index self.selectedIndexChanged.emit() @pyqtProperty(int, notify = selectedIndexChanged) def selectedScriptIndex(self): return self._selected_script_index @pyqtSlot(int, int) def moveScript(self, index, new_index): if new_index < 0 or new_index > len(self._script_list)-1: return #nothing needs to be done else: # Magical switch code. self._script_list[new_index], self._script_list[index] = self._script_list[index], self._script_list[new_index] self.scriptListChanged.emit() self.selectedIndexChanged.emit() #Ensure that settings are updated ## Remove a script from the active script list by index. @pyqtSlot(int) def removeScriptByIndex(self, index): self._script_list.pop(index) if len(self._script_list) - 1 < self._selected_script_index: self._selected_script_index = len(self._script_list) - 1 self.scriptListChanged.emit() self.selectedIndexChanged.emit() #Ensure that settings are updated ## Load all scripts from provided path. This should probably only be done on init. def loadAllScripts(self, path): scripts = pkgutil.iter_modules(path = [path]) for loader, script_name, ispkg in scripts: if script_name not in sys.modules: # Import module loaded_script = __import__("PostProcessingPlugin.scripts."+ script_name, fromlist = [script_name]) loaded_class = getattr(loaded_script, script_name) temp_object = loaded_class() try: setting_data = temp_object.getSettingData() if "label" in setting_data and "key" in setting_data: self._script_labels[setting_data["key"]] = setting_data["label"] self._loaded_scripts[setting_data["key"]] = loaded_class else: Logger.log("w", "Script %s.py has no label or key", script_name) self._script_labels[script_name] = script_name self._loaded_scripts[script_name] = loaded_class #self._script_list.append(loaded_class()) self.loadedScriptListChanged.emit() except AttributeError: Logger.log("e", "Script %s.py is not a recognised script type. Ensure it inherits Script", script_name) except NotImplementedError: Logger.log("e", "Script %s.py has no implemented settings",script_name) loadedScriptListChanged = pyqtSignal() @pyqtProperty("QVariantList", notify = loadedScriptListChanged) def loadedScriptList(self): return list(self._loaded_scripts.keys()) @pyqtSlot(str, result = str) def getScriptLabelByKey(self, key): return self._script_labels[key] scriptListChanged = pyqtSignal() @pyqtProperty("QVariantList", notify = scriptListChanged) def scriptList(self): script_list = [script.getSettingData()["key"] for script in self._script_list] return script_list @pyqtSlot(str) def addScriptToList(self, key): self._script_list.append(self._loaded_scripts[key]()) self.setSelectedScriptIndex(len(self._script_list) - 1) self.scriptListChanged.emit() ## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection. 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) ## Show the (GUI) popup of the post processing plugin. def showPopup(self): if self._view is None: self._createView() self._view.show()
# Initialize PhotoBoothEngine. pbengine = PhotoBoothEngine() pbengine.on_status.connect(appLabel.rootObject().status) pbengine.on_update_filter_preview.connect( appLabel.rootObject().updateImageFilterPreview) appLabel.rootContext().setContextProperty('pbengine', pbengine) # Create a component factory and load the QML script. print("Hello") component = QQmlComponent(appLabel.engine()) component.loadUrl(QUrl('TextStatusFly.qml')) print("Hello2") asdf = component.create(appLabel.rootContext()) print("Hello3") asdf.setParentItem(appLabel.rootObject()) asdf.setParent(appLabel.rootObject()) print("Hello4") #asdf.setProperty("targetX", 100) asdf.setProperty("objectName", "textStatusBar") print("Hello5") appLabel.rootContext().setContextProperty('textStatusBar', asdf) asdf.setProperty("parentSet", True) #asdf.setProperty("y", 100)
class DiscoverUM3Action(MachineAction): def __init__(self): super().__init__("DiscoverUM3Action", catalog.i18nc("@action", "Connect via Network")) self._qml_url = "DiscoverUM3Action.qml" self._network_plugin = None self.__additional_components_context = None self.__additional_component = None self.__additional_components_view = None Application.getInstance().engineCreatedSignal.connect( self._createAdditionalComponentsView) self._last_zeroconf_event_time = time.time() self._zeroconf_change_grace_period = 0.25 # Time to wait after a zeroconf service change before allowing a zeroconf reset printersChanged = pyqtSignal() @pyqtSlot() def startDiscovery(self): if not self._network_plugin: self._network_plugin = Application.getInstance( ).getOutputDeviceManager().getOutputDevicePlugin( "UM3NetworkPrinting") self._network_plugin.printerListChanged.connect( self._onPrinterDiscoveryChanged) self.printersChanged.emit() ## Re-filters the list of printers. @pyqtSlot() def reset(self): self.printersChanged.emit() @pyqtSlot() def restartDiscovery(self): # Ensure that there is a bit of time after a printer has been discovered. # This is a work around for an issue with Qt 5.5.1 up to Qt 5.7 which can segfault if we do this too often. # It's most likely that the QML engine is still creating delegates, where the python side already deleted or # garbage collected the data. # Whatever the case, waiting a bit ensures that it doesn't crash. if time.time( ) - self._last_zeroconf_event_time > self._zeroconf_change_grace_period: if not self._network_plugin: self.startDiscovery() else: self._network_plugin.startDiscovery() @pyqtSlot(str, str) def removeManualPrinter(self, key, address): if not self._network_plugin: return self._network_plugin.removeManualPrinter(key, address) @pyqtSlot(str, str) def setManualPrinter(self, key, address): if key != "": # This manual printer replaces a current manual printer self._network_plugin.removeManualPrinter(key) if address != "": self._network_plugin.addManualPrinter(address) def _onPrinterDiscoveryChanged(self, *args): self._last_zeroconf_event_time = time.time() self.printersChanged.emit() @pyqtProperty("QVariantList", notify=printersChanged) def foundDevices(self): if self._network_plugin: if Application.getInstance().getGlobalContainerStack(): global_printer_type = Application.getInstance( ).getGlobalContainerStack().getBottom().getId() else: global_printer_type = "unknown" printers = list(self._network_plugin.getPrinters().values()) # TODO; There are still some testing printers that don't have a correct printer type, so don't filter out unkown ones just yet. printers = [ printer for printer in printers if printer.printerType == global_printer_type or printer.printerType == "unknown" ] printers.sort(key=lambda k: k.name) return printers else: return [] @pyqtSlot(str) def setKey(self, key): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: meta_data = global_container_stack.getMetaData() if "um_network_key" in meta_data: global_container_stack.setMetaDataEntry("um_network_key", key) # Delete old authentication data. global_container_stack.removeMetaDataEntry( "network_authentication_id") global_container_stack.removeMetaDataEntry( "network_authentication_key") else: global_container_stack.addMetaDataEntry("um_network_key", key) if self._network_plugin: # Ensure that the connection states are refreshed. self._network_plugin.reCheckConnections() @pyqtSlot(result=str) def getStoredKey(self): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: meta_data = global_container_stack.getMetaData() if "um_network_key" in meta_data: return global_container_stack.getMetaDataEntry( "um_network_key") return "" @pyqtSlot() def loadConfigurationFromPrinter(self): machine_manager = Application.getInstance().getMachineManager() hotend_ids = machine_manager.printerOutputDevices[0].hotendIds for index in range(len(hotend_ids)): machine_manager.printerOutputDevices[0].hotendIdChanged.emit( index, hotend_ids[index]) material_ids = machine_manager.printerOutputDevices[0].materialIds for index in range(len(material_ids)): machine_manager.printerOutputDevices[0].materialIdChanged.emit( index, material_ids[index]) def _createAdditionalComponentsView(self): Logger.log("d", "Creating additional ui components for UM3.") path = QUrl.fromLocalFile( os.path.join( PluginRegistry.getInstance().getPluginPath( "UM3NetworkPrinting"), "UM3InfoComponents.qml")) self.__additional_component = QQmlComponent( Application.getInstance()._engine, path) # We need access to engine (although technically we can't) self.__additional_components_context = QQmlContext( Application.getInstance()._engine.rootContext()) self.__additional_components_context.setContextProperty( "manager", self) self.__additional_components_view = self.__additional_component.create( self.__additional_components_context) if not self.__additional_components_view: Logger.log("w", "Could not create ui components for UM3.") return Application.getInstance().addAdditionalComponent( "monitorButtons", self.__additional_components_view.findChild( QObject, "networkPrinterConnectButton")) Application.getInstance().addAdditionalComponent( "machinesDetailPane", self.__additional_components_view.findChild( QObject, "networkPrinterConnectionInfo"))
class PluginBrowser(QObject, Extension): def __init__(self, parent=None): super().__init__(parent) self.addMenuItem(i18n_catalog.i18n("Browse plugins"), self.browsePlugins) self._api_version = 1 self._api_url = "http://software.ultimaker.com/cura/v%s/" % self._api_version self._plugin_list_request = None self._download_plugin_request = None self._download_plugin_reply = None self._network_manager = None self._plugins_metadata = [] self._plugins_model = None self._qml_component = None self._qml_context = None self._dialog = None pluginsMetadataChanged = pyqtSignal() def browsePlugins(self): self._createNetworkManager() self.requestPluginList() if not self._dialog: self._createDialog() self._dialog.show() def requestPluginList(self): url = QUrl(self._api_url + "plugins") self._plugin_list_request = QNetworkRequest(url) self._network_manager.get(self._plugin_list_request) def _createDialog(self): Logger.log("d", "PluginBrowser") path = QUrl.fromLocalFile( os.path.join( PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "PluginBrowser.qml")) self._qml_component = QQmlComponent(Application.getInstance()._engine, path) # We need access to engine (although technically we can't) self._qml_context = QQmlContext( Application.getInstance()._engine.rootContext()) self._qml_context.setContextProperty("manager", self) self._dialog = self._qml_component.create(self._qml_context) if self._dialog is None: Logger.log("e", "QQmlComponent status %s", self._qml_component.status()) Logger.log("e", "QQmlComponent errorString %s", self._qml_component.errorString()) def _onDownloadPluginProgress(self, bytes_sent, bytes_total): if bytes_total > 0: new_progress = bytes_sent / bytes_total * 100 if new_progress == 100.0: self._download_plugin_reply.downloadProgress.disconnect( self._onDownloadPluginProgress) self._temp_plugin_file = tempfile.NamedTemporaryFile( suffix=".curaplugin") self._temp_plugin_file.write( self._download_plugin_reply.readAll()) result = PluginRegistry.getInstance().installPlugin( "file://" + self._temp_plugin_file.name) self._temp_plugin_file.close( ) # Plugin was installed, delete temp file @pyqtSlot(str) def downloadAndInstallPlugin(self, url): Logger.log("i", "Attempting to download & install plugin from %s", url) url = QUrl(url) self._download_plugin_request = QNetworkRequest(url) self._download_plugin_reply = self._network_manager.get( self._download_plugin_request) self._download_plugin_reply.downloadProgress.connect( self._onDownloadPluginProgress) @pyqtProperty(QObject, notify=pluginsMetadataChanged) def pluginsModel(self): if self._plugins_model is None: self._plugins_model = ListModel() self._plugins_model.addRoleName(Qt.UserRole + 1, "name") self._plugins_model.addRoleName(Qt.UserRole + 2, "version") self._plugins_model.addRoleName(Qt.UserRole + 3, "short_description") self._plugins_model.addRoleName(Qt.UserRole + 4, "author") self._plugins_model.addRoleName(Qt.UserRole + 5, "already_installed") self._plugins_model.addRoleName(Qt.UserRole + 6, "file_location") else: self._plugins_model.clear() items = [] for metadata in self._plugins_metadata: items.append({ "name": metadata["label"], "version": metadata["version"], "short_description": metadata["short_description"], "author": metadata["author"], "already_installed": self._checkAlreadyInstalled(metadata["id"], metadata["version"]), "file_location": metadata["file_location"] }) self._plugins_model.setItems(items) return self._plugins_model def _checkAlreadyInstalled(self, id, version): plugin_registry = PluginRegistry.getInstance() metadata = plugin_registry.getMetaData(id) if metadata != {}: current_version = Version(metadata["plugin"]["version"]) new_version = Version(version) if new_version > current_version: return False return True def _onRequestFinished(self, reply): reply_url = reply.url().toString() if reply.operation() == QNetworkAccessManager.GetOperation: if reply_url == self._api_url + "plugins": try: json_data = json.loads( bytes(reply.readAll()).decode("utf-8")) self._plugins_metadata = json_data self.pluginsMetadataChanged.emit() except json.decoder.JSONDecodeError: Logger.log( "w", "Received an invalid print job state message: Not valid JSON." ) return else: # Ignore any operation that is not a get operation pass def _createNetworkManager(self): if self._network_manager: self._network_manager.finished.disconnect(self._onRequestFinished) self._network_manager = QNetworkAccessManager() self._network_manager.finished.connect(self._onRequestFinished)
class ApplicationHandler(QObject): end_find_handling = pyqtSignal() messages_recieved = pyqtSignal() end_chat = pyqtSignal() MAX_MESSAGE_WIDTH = 160 FIND_CHAT_SLEEP_TIME = 2 GET_MESSAGES_SLEEP_TIME = 0.1 def __init__(self): QObject.__init__(self) self.connectengine = QQmlApplicationEngine("connectform.qml") self.root = self.connectengine.rootObjects()[0] button = self.get_obj('connect_button') button.clicked.connect(self.connect) self.root.show() self.drawn = [] self.end_find_handling.connect(self.create_chat) self.messages_recieved.connect(self.message_drawer) self.end_chat.connect(self.end_chat_locally) def address(self): return 'http://' + self.host + ':' + self.port def connect(self): self.host = self.get_obj('host_input_text').property('text') self.port = self.get_obj('port_input_text').property('text') answer = requests.get(self.address() + '/') if answer.status_code == 200: args = answer.json() self.id = args['id'] self.key = args['key'] self.root.close() self.engine = QQmlApplicationEngine('form.qml') self.root = self.engine.rootObjects()[0] self.root.show() self.get_obj('return_button').clicked.connect(self.stop_find_chat) self.get_obj('find_chat_button').clicked.connect(self.find_chat) self.get_obj('disconnect_button').clicked.connect(self.disconnect) self.get_obj('text_input').message_ready.connect(self.send_message) self.get_obj('leave_chat_button').clicked.connect(self.leave_chat) self.message0_component = QQmlComponent(self.engine) self.message0_component.loadUrl(QUrl('message0.qml')) self.message1_component = QQmlComponent(self.engine) self.message1_component.loadUrl(QUrl('message1.qml')) self.draw_main() def find_chat(self): self.draw_waiting() handler = threading.Thread(target=self.find_chat_handler) self.looking_for_chat = True handler.start() def find_chat_handler(self): while self.looking_for_chat: answer = requests.post(self.address() + '/user', params=dict(cmd='find_chat', id=self.id, key=self.key)) args = answer.json() if args['status'] == 'ok': self.end_find_handling.emit() return time.sleep(self.FIND_CHAT_SLEEP_TIME) def create_chat(self): self.draw_chat() handler = threading.Thread(target=self.get_messages_handler) self.chatting = True handler.start() self.items = [] self.item_id = 0 self.column_height = 0 self.get_obj('text_input').setProperty('text', '') def get_messages_handler(self): while self.chatting: answer = requests.post(self.address() + '/chat', params=dict(cmd='get_messages', id=self.id, key=self.key)) args = answer.json() if args['status'] == 'chat ended': self.end_chat.emit() return if args['msg']: self.message_buffer = args['msg'] self.messages_recieved.emit() time.sleep(self.GET_MESSAGES_SLEEP_TIME) def message_drawer(self): current = self.message_buffer self.message_buffer = [] for msg_response in current: id = msg_response['id'] text = msg_response['msg'] if id == int(self.id): item = self.message0_component.create() else: item = self.message1_component.create() item.setProperty('text', text) if int(item.property('width')) > self.MAX_MESSAGE_WIDTH: item.setProperty('width', str(self.MAX_MESSAGE_WIDTH)) height = item.property('height') self.column_height += height + 8 self.get_obj('message_column_layout').setProperty( 'height', self.column_height) item.setParentItem(self.get_obj('message_column_layout')) self.items.append(item) self.get_obj('message_frame').update() def send_message(self): text = self.get_obj('text_input').property('text') self.get_obj('text_input').setProperty('text', '') requests.post(self.address() + '/chat', params=dict(cmd='send_message', msg=text, id=self.id, key=self.key)) def leave_chat(self): self.chatting = False requests.post(self.address() + '/chat', params=dict(cmd='stop', id=self.id, key=self.key)) self.draw_main() def end_chat_locally(self): self.chatting = False self.draw_main() def stop_find_chat(self): requests.post(self.address() + '/user', params=dict(cmd='stop_find_chat', id=self.id, key=self.key)) self.looking_for_chat = False self.draw_main() def disconnect(self): self.looking_for_chat = False requests.post(self.address() + '/user', params=dict(cmd='disconnect', id=self.id, key=self.key)) self.root.close() self.root = self.connectengine.rootObjects()[0] button = self.get_obj('connect_button') button.clicked.connect(self.connect) self.root.show() def get_obj(self, name): return self.root.findChild(QObject, name) def show_obj(self, name): obj = self.get_obj(name) obj.setVisible(True) def hide_obj(self, name): obj = self.get_obj(name) obj.setVisible(False) def draw_main(self): self.draw_clear() self.draw_obj('find_chat_button') self.draw_obj('disconnect_button') def draw_waiting(self): self.draw_clear() self.draw_obj('looking_for_chat_text') self.draw_obj('return_button') def draw_chat(self): self.draw_clear() self.draw_obj('message_frame') self.draw_obj('text_input_rectangle') self.draw_obj('leave_chat_button') def draw_obj(self, name): self.show_obj(name) self.drawn.append(name) def draw_clear(self): for item in self.drawn: self.hide_obj(item) self.drawn = []
return self._name @name.setter def name(self, name): self._name = name @pyqtProperty(int) def shoeSize(self): return self._shoeSize @shoeSize.setter def shoeSize(self, shoeSize): self._shoeSize = shoeSize app = QCoreApplication(sys.argv) qmlRegisterType(Person, 'People', 1, 0, 'Person') engine = QQmlEngine() component = QQmlComponent(engine) component.loadUrl(QUrl('example.qml')) person = component.create() if person is not None: print "The person's name is %s." % person.name print "They wear a size %d shoe." % person.shoeSize else: for error in component.errors(): print error.toString()
class PluginBrowser(QObject, Extension): def __init__(self, parent=None): super().__init__(parent) self._api_version = 2 self._api_url = "http://software.ultimaker.com/cura/v%s/" % self._api_version self._plugin_list_request = None self._download_plugin_request = None self._download_plugin_reply = None self._network_manager = None self._plugins_metadata = [] self._plugins_model = None self._qml_component = None self._qml_context = None self._dialog = None self._download_progress = 0 self._is_downloading = False self._request_header = [ b"User-Agent", str.encode("%s/%s (%s %s)" % ( Application.getInstance().getApplicationName(), Application.getInstance().getVersion(), platform.system(), platform.machine(), )) ] # Installed plugins are really installed after reboot. In order to prevent the user from downloading the # same file over and over again, we keep track of the upgraded plugins. self._newly_installed_plugin_ids = [] # variables for the license agreement dialog self._license_dialog_plugin_name = "" self._license_dialog_license_content = "" self._license_dialog_plugin_file_location = "" showLicenseDialog = pyqtSignal() @pyqtSlot(result=str) def getLicenseDialogPluginName(self): return self._license_dialog_plugin_name @pyqtSlot(result=str) def getLicenseDialogPluginFileLocation(self): return self._license_dialog_plugin_file_location @pyqtSlot(result=str) def getLicenseDialogLicenseContent(self): return self._license_dialog_license_content def openLicenseDialog(self, plugin_name, license_content, plugin_file_location): self._license_dialog_plugin_name = plugin_name self._license_dialog_license_content = license_content self._license_dialog_plugin_file_location = plugin_file_location self.showLicenseDialog.emit() pluginsMetadataChanged = pyqtSignal() onDownloadProgressChanged = pyqtSignal() onIsDownloadingChanged = pyqtSignal() @pyqtProperty(bool, notify=onIsDownloadingChanged) def isDownloading(self): return self._is_downloading @pyqtSlot() def browsePlugins(self): self._createNetworkManager() self.requestPluginList() if not self._dialog: self._dialog = self._createDialog("PluginBrowser.qml") self._dialog.show() @pyqtSlot() def requestPluginList(self): Logger.log("i", "Requesting plugin list") url = QUrl(self._api_url + "plugins") self._plugin_list_request = QNetworkRequest(url) self._plugin_list_request.setRawHeader(*self._request_header) self._network_manager.get(self._plugin_list_request) def _createDialog(self, qml_name): Logger.log("d", "Creating dialog [%s]", qml_name) path = QUrl.fromLocalFile( os.path.join( PluginRegistry.getInstance().getPluginPath(self.getPluginId()), qml_name)) self._qml_component = QQmlComponent(Application.getInstance()._engine, path) # We need access to engine (although technically we can't) self._qml_context = QQmlContext( Application.getInstance()._engine.rootContext()) self._qml_context.setContextProperty("manager", self) dialog = self._qml_component.create(self._qml_context) if dialog is None: Logger.log("e", "QQmlComponent status %s", self._qml_component.status()) Logger.log("e", "QQmlComponent errorString %s", self._qml_component.errorString()) return dialog def setIsDownloading(self, is_downloading): if self._is_downloading != is_downloading: self._is_downloading = is_downloading self.onIsDownloadingChanged.emit() def _onDownloadPluginProgress(self, bytes_sent, bytes_total): if bytes_total > 0: new_progress = bytes_sent / bytes_total * 100 self.setDownloadProgress(new_progress) if new_progress == 100.0: self.setIsDownloading(False) self._download_plugin_reply.downloadProgress.disconnect( self._onDownloadPluginProgress) # must not delete the temporary file on Windows self._temp_plugin_file = tempfile.NamedTemporaryFile( mode="w+b", suffix=".curaplugin", delete=False) location = self._temp_plugin_file.name # write first and close, otherwise on Windows, it cannot read the file self._temp_plugin_file.write( self._download_plugin_reply.readAll()) self._temp_plugin_file.close() self._checkPluginLicenseOrInstall(location) return ## Checks if the downloaded plugin ZIP file contains a license file or not. # If it does, it will show a popup dialog displaying the license to the user. The plugin will be installed if the # user accepts the license. # If there is no license file, the plugin will be directory installed. def _checkPluginLicenseOrInstall(self, file_path): with zipfile.ZipFile(file_path, "r") as zip_ref: plugin_id = None for file in zip_ref.infolist(): if file.filename.endswith("/"): plugin_id = file.filename.strip("/") break if plugin_id is None: msg = i18n_catalog.i18nc( "@info:status", "Failed to get plugin ID from <filename>{0}</filename>", file_path) msg_title = i18n_catalog.i18nc("@info:tile", "Warning") self._progress_message = Message(msg, lifetime=0, dismissable=False, title=msg_title) return # find a potential license file plugin_root_dir = plugin_id + "/" license_file = None for f in zip_ref.infolist(): # skip directories (with file_size = 0) and files not in the plugin directory if f.file_size == 0 or not f.filename.startswith( plugin_root_dir): continue file_name = os.path.basename(f.filename).lower() file_base_name, file_ext = os.path.splitext(file_name) if file_base_name in ["license", "licence"]: license_file = f.filename break # show a dialog for user to read and accept/decline the license if license_file is not None: Logger.log( "i", "Found license file for plugin [%s], showing the license dialog to the user", plugin_id) license_content = zip_ref.read(license_file).decode('utf-8') self.openLicenseDialog(plugin_id, license_content, file_path) return # there is no license file, directly install the plugin self.installPlugin(file_path) @pyqtSlot(str) def installPlugin(self, file_path): if not file_path.startswith("/"): location = "/" + file_path # Ensure that it starts with a /, as otherwise it doesn't work on windows. else: location = file_path result = PluginRegistry.getInstance().installPlugin("file://" + location) self._newly_installed_plugin_ids.append(result["id"]) self.pluginsMetadataChanged.emit() Application.getInstance().messageBox( i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"]) @pyqtProperty(int, notify=onDownloadProgressChanged) def downloadProgress(self): return self._download_progress def setDownloadProgress(self, progress): if progress != self._download_progress: self._download_progress = progress self.onDownloadProgressChanged.emit() @pyqtSlot(str) def downloadAndInstallPlugin(self, url): Logger.log("i", "Attempting to download & install plugin from %s", url) url = QUrl(url) self._download_plugin_request = QNetworkRequest(url) self._download_plugin_request.setRawHeader(*self._request_header) self._download_plugin_reply = self._network_manager.get( self._download_plugin_request) self.setDownloadProgress(0) self.setIsDownloading(True) self._download_plugin_reply.downloadProgress.connect( self._onDownloadPluginProgress) @pyqtSlot() def cancelDownload(self): Logger.log("i", "user cancelled the download of a plugin") self._download_plugin_reply.abort() self._download_plugin_reply.downloadProgress.disconnect( self._onDownloadPluginProgress) self._download_plugin_reply = None self._download_plugin_request = None self.setDownloadProgress(0) self.setIsDownloading(False) @pyqtProperty(QObject, notify=pluginsMetadataChanged) def pluginsModel(self): if self._plugins_model is None: self._plugins_model = ListModel() self._plugins_model.addRoleName(Qt.UserRole + 1, "name") self._plugins_model.addRoleName(Qt.UserRole + 2, "version") self._plugins_model.addRoleName(Qt.UserRole + 3, "short_description") self._plugins_model.addRoleName(Qt.UserRole + 4, "author") self._plugins_model.addRoleName(Qt.UserRole + 5, "already_installed") self._plugins_model.addRoleName(Qt.UserRole + 6, "file_location") self._plugins_model.addRoleName(Qt.UserRole + 7, "can_upgrade") else: self._plugins_model.clear() items = [] for metadata in self._plugins_metadata: items.append({ "name": metadata["label"], "version": metadata["version"], "short_description": metadata["short_description"], "author": metadata["author"], "already_installed": self._checkAlreadyInstalled(metadata["id"]), "file_location": metadata["file_location"], "can_upgrade": self._checkCanUpgrade(metadata["id"], metadata["version"]) }) self._plugins_model.setItems(items) return self._plugins_model def _checkCanUpgrade(self, id, version): plugin_registry = PluginRegistry.getInstance() metadata = plugin_registry.getMetaData(id) if metadata != {}: if id in self._newly_installed_plugin_ids: return False # We already updated this plugin. current_version = Version(metadata["plugin"]["version"]) new_version = Version(version) if new_version > current_version: return True return False def _checkAlreadyInstalled(self, id): plugin_registry = PluginRegistry.getInstance() metadata = plugin_registry.getMetaData(id) if metadata != {}: return True else: if id in self._newly_installed_plugin_ids: return True # We already installed this plugin, but the registry just doesn't know it yet. return False def _onRequestFinished(self, reply): reply_url = reply.url().toString() if reply.error() == QNetworkReply.TimeoutError: Logger.log("w", "Got a timeout.") # Reset everything. self.setDownloadProgress(0) self.setIsDownloading(False) if self._download_plugin_reply: self._download_plugin_reply.downloadProgress.disconnect( self._onDownloadPluginProgress) self._download_plugin_reply.abort() self._download_plugin_reply = None return elif reply.error() == QNetworkReply.HostNotFoundError: Logger.log("w", "Unable to reach server.") return if reply.operation() == QNetworkAccessManager.GetOperation: if reply_url == self._api_url + "plugins": try: json_data = json.loads( bytes(reply.readAll()).decode("utf-8")) self._plugins_metadata = json_data self.pluginsMetadataChanged.emit() except json.decoder.JSONDecodeError: Logger.log( "w", "Received an invalid print job state message: Not valid JSON." ) return else: # Ignore any operation that is not a get operation pass def _onNetworkAccesibleChanged(self, accessible): if accessible == 0: self.setDownloadProgress(0) self.setIsDownloading(False) if self._download_plugin_reply: self._download_plugin_reply.downloadProgress.disconnect( self._onDownloadPluginProgress) self._download_plugin_reply.abort() self._download_plugin_reply = None def _createNetworkManager(self): if self._network_manager: self._network_manager.finished.disconnect(self._onRequestFinished) self._network_manager.networkAccessibleChanged.disconnect( self._onNetworkAccesibleChanged) self._network_manager = QNetworkAccessManager() self._network_manager.finished.connect(self._onRequestFinished) self._network_manager.networkAccessibleChanged.connect( self._onNetworkAccesibleChanged)
def getID(self, val): global PORT global producerID producerID = val print("Received: " + val) if val == "Producer1": PORT = 33000 elif val == "Producer2": PORT = 33001 else: PORT = 33003 startConnections() @pyqtSlot(str) def send_extracted_message(self, msg): send_msg(msg) # Initialize the software window. app = QApplication(sys.argv) # Create the QtQuick engine and load the software's root component. engine = QQmlApplicationEngine() irc = IRC() engine.rootContext().setContextProperty("irc", irc) component = QQmlComponent(engine) component.loadUrl(QUrl('main.qml')) window = component.create() app.exec_()
class WorkspaceDialog(QObject): showDialogSignal = pyqtSignal() def __init__(self, parent = None): super().__init__(parent) self._component = None self._context = None self._view = None self._qml_url = "WorkspaceDialog.qml" self._lock = threading.Lock() self._default_strategy = None self._result = {"machine": self._default_strategy, "quality_changes": self._default_strategy, "definition_changes": self._default_strategy, "material": self._default_strategy} self._visible = False self.showDialogSignal.connect(self.__show) self._has_quality_changes_conflict = False self._has_definition_changes_conflict = False self._has_machine_conflict = False self._has_material_conflict = False self._has_visible_settings_field = False self._num_visible_settings = 0 self._num_user_settings = 0 self._active_mode = "" self._quality_name = "" self._num_settings_overriden_by_quality_changes = 0 self._quality_type = "" self._machine_name = "" self._machine_type = "" self._variant_type = "" self._material_labels = [] self._extruders = [] self._objects_on_plate = False machineConflictChanged = pyqtSignal() qualityChangesConflictChanged = pyqtSignal() definitionChangesConflictChanged = pyqtSignal() materialConflictChanged = pyqtSignal() numVisibleSettingsChanged = pyqtSignal() activeModeChanged = pyqtSignal() qualityNameChanged = pyqtSignal() hasVisibleSettingsFieldChanged = pyqtSignal() numSettingsOverridenByQualityChangesChanged = pyqtSignal() qualityTypeChanged = pyqtSignal() machineNameChanged = pyqtSignal() materialLabelsChanged = pyqtSignal() objectsOnPlateChanged = pyqtSignal() numUserSettingsChanged = pyqtSignal() machineTypeChanged = pyqtSignal() variantTypeChanged = pyqtSignal() extrudersChanged = pyqtSignal() @pyqtProperty(str, notify=variantTypeChanged) def variantType(self): return self._variant_type def setVariantType(self, variant_type): if self._variant_type != variant_type: self._variant_type = variant_type self.variantTypeChanged.emit() @pyqtProperty(str, notify=machineTypeChanged) def machineType(self): return self._machine_type def setMachineType(self, machine_type): self._machine_type = machine_type self.machineTypeChanged.emit() def setNumUserSettings(self, num_user_settings): if self._num_user_settings != num_user_settings: self._num_user_settings = num_user_settings self.numVisibleSettingsChanged.emit() @pyqtProperty(int, notify=numUserSettingsChanged) def numUserSettings(self): return self._num_user_settings @pyqtProperty(bool, notify=objectsOnPlateChanged) def hasObjectsOnPlate(self): return self._objects_on_plate def setHasObjectsOnPlate(self, objects_on_plate): if self._objects_on_plate != objects_on_plate: self._objects_on_plate = objects_on_plate self.objectsOnPlateChanged.emit() @pyqtProperty("QVariantList", notify = materialLabelsChanged) def materialLabels(self): return self._material_labels def setMaterialLabels(self, material_labels): if self._material_labels != material_labels: self._material_labels = material_labels self.materialLabelsChanged.emit() @pyqtProperty("QVariantList", notify=extrudersChanged) def extruders(self): return self._extruders def setExtruders(self, extruders): if self._extruders != extruders: self._extruders = extruders self.extrudersChanged.emit() @pyqtProperty(str, notify = machineNameChanged) def machineName(self): return self._machine_name def setMachineName(self, machine_name): if self._machine_name != machine_name: self._machine_name = machine_name self.machineNameChanged.emit() @pyqtProperty(str, notify=qualityTypeChanged) def qualityType(self): return self._quality_type def setQualityType(self, quality_type): if self._quality_type != quality_type: self._quality_type = quality_type self.qualityTypeChanged.emit() @pyqtProperty(int, notify=numSettingsOverridenByQualityChangesChanged) def numSettingsOverridenByQualityChanges(self): return self._num_settings_overriden_by_quality_changes def setNumSettingsOverridenByQualityChanges(self, num_settings_overriden_by_quality_changes): self._num_settings_overriden_by_quality_changes = num_settings_overriden_by_quality_changes self.numSettingsOverridenByQualityChangesChanged.emit() @pyqtProperty(str, notify=qualityNameChanged) def qualityName(self): return self._quality_name def setQualityName(self, quality_name): if self._quality_name != quality_name: self._quality_name = quality_name self.qualityNameChanged.emit() @pyqtProperty(str, notify=activeModeChanged) def activeMode(self): return self._active_mode def setActiveMode(self, active_mode): if active_mode == 0: self._active_mode = i18n_catalog.i18nc("@title:tab", "Recommended") else: self._active_mode = i18n_catalog.i18nc("@title:tab", "Custom") self.activeModeChanged.emit() @pyqtProperty(int, notify = hasVisibleSettingsFieldChanged) def hasVisibleSettingsField(self): return self._has_visible_settings_field def setHasVisibleSettingsField(self, has_visible_settings_field): self._has_visible_settings_field = has_visible_settings_field self.hasVisibleSettingsFieldChanged.emit() @pyqtProperty(int, constant = True) def totalNumberOfSettings(self): return len(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0].getAllKeys()) @pyqtProperty(int, notify = numVisibleSettingsChanged) def numVisibleSettings(self): return self._num_visible_settings def setNumVisibleSettings(self, num_visible_settings): if self._num_visible_settings != num_visible_settings: self._num_visible_settings = num_visible_settings self.numVisibleSettingsChanged.emit() @pyqtProperty(bool, notify = machineConflictChanged) def machineConflict(self): return self._has_machine_conflict @pyqtProperty(bool, notify=qualityChangesConflictChanged) def qualityChangesConflict(self): return self._has_quality_changes_conflict @pyqtProperty(bool, notify=definitionChangesConflictChanged) def definitionChangesConflict(self): return self._has_definition_changes_conflict @pyqtProperty(bool, notify=materialConflictChanged) def materialConflict(self): return self._has_material_conflict @pyqtSlot(str, str) def setResolveStrategy(self, key, strategy): if key in self._result: self._result[key] = strategy ## Close the backend: otherwise one could end up with "Slicing..." @pyqtSlot() def closeBackend(self): Application.getInstance().getBackend().close() def setMaterialConflict(self, material_conflict): if self._has_material_conflict != material_conflict: self._has_material_conflict = material_conflict self.materialConflictChanged.emit() def setMachineConflict(self, machine_conflict): if self._has_machine_conflict != machine_conflict: self._has_machine_conflict = machine_conflict self.machineConflictChanged.emit() def setQualityChangesConflict(self, quality_changes_conflict): if self._has_quality_changes_conflict != quality_changes_conflict: self._has_quality_changes_conflict = quality_changes_conflict self.qualityChangesConflictChanged.emit() def setDefinitionChangesConflict(self, definition_changes_conflict): if self._has_definition_changes_conflict != definition_changes_conflict: self._has_definition_changes_conflict = definition_changes_conflict self.definitionChangesConflictChanged.emit() def getResult(self): if "machine" in self._result and not self._has_machine_conflict: self._result["machine"] = None if "quality_changes" in self._result and not self._has_quality_changes_conflict: self._result["quality_changes"] = None if "definition_changes" in self._result and not self._has_definition_changes_conflict: self._result["definition_changes"] = None if "material" in self._result and not self._has_material_conflict: self._result["material"] = None # If the machine needs to be re-created, the definition_changes should also be re-created. # If the machine strategy is None, it means that there is no name conflict with existing ones. In this case # new definitions changes are created if "machine" in self._result: if self._result["machine"] == "new" or self._result["machine"] is None and self._result["definition_changes"] is None: self._result["definition_changes"] = "new" return self._result def _createViewFromQML(self): path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("3MFReader"), 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) if self._view is None: Logger.log("c", "QQmlComponent status %s", self._component.status()) Logger.log("c", "QQmlComponent error string %s", self._component.errorString()) def show(self): # Emit signal so the right thread actually shows the view. if threading.current_thread() != threading.main_thread(): self._lock.acquire() # Reset the result self._result = {"machine": self._default_strategy, "quality_changes": self._default_strategy, "definition_changes": self._default_strategy, "material": self._default_strategy} self._visible = True self.showDialogSignal.emit() @pyqtSlot() ## Used to notify the dialog so the lock can be released. def notifyClosed(self): self._result = {} # The result should be cleared before hide, because after it is released the main thread lock self._visible = False try: self._lock.release() except: pass def hide(self): self._visible = False self._view.hide() try: self._lock.release() except: pass @pyqtSlot(bool) def _onVisibilityChanged(self, visible): if not visible: try: self._lock.release() except: pass @pyqtSlot() def onOkButtonClicked(self): self._view.hide() self.hide() @pyqtSlot() def onCancelButtonClicked(self): self._result = {} self._view.hide() self.hide() ## Block thread until the dialog is closed. def waitForClose(self): if self._visible: if threading.current_thread() != threading.main_thread(): self._lock.acquire() self._lock.release() else: # If this is not run from a separate thread, we need to ensure that the events are still processed. while self._visible: time.sleep(1 / 50) QCoreApplication.processEvents() # Ensure that the GUI does not freeze. def __show(self): if self._view is None: self._createViewFromQML() if self._view: self._view.show()
# load the components component = QQmlComponent(engine) component.loadUrl(QUrl("components/Class.qml")) line_component = QQmlComponent(engine) line_component.loadUrl(QUrl("components/Edge.qml")) # check for component creation errors for error in component.errors(): print(error.toString()) # check for component creation errors for error in line_component.errors(): print(error.toString()) classes = [] for index, class_name in enumerate(["Person", "Home"]): # create a new instance of the component cclass = component.create() # set the class name property of the component cclass.setProperty("className", class_name) cclass.setX((cclass.width() + 50) * index) cclass.setParentItem(engine.rootObjects()[0].findChild(QQuickItem)) classes.append(cclass) line = line_component.beginCreate(engine.rootContext()) line.setProperty("anchor1", classes[0]) line.setProperty("anchor2", classes[1]) line_component.completeCreate() # check for object creation errors for error in line_component.errors(): print(error.toString()) for error in component.errors():
class DiscoverOctoPrintAction(MachineAction): def __init__(self, parent=None): super().__init__("DiscoverOctoPrintAction", catalog.i18nc("@action", "Connect OctoPrint")) self._qml_url = "DiscoverOctoPrintAction.qml" self._window = None self._context = None self._network_plugin = None # QNetwork manager needs to be created in advance. If we don't it can happen that it doesn't correctly # hook itself into the event loop, which results in events never being fired / done. self._manager = QNetworkAccessManager() self._manager.finished.connect(self._onRequestFinished) self._settings_reply = None # Try to get version information from plugin.json plugin_file_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "plugin.json") try: with open(plugin_file_path) as plugin_file: plugin_info = json.load(plugin_file) plugin_version = plugin_info["version"] except: # The actual version info is not critical to have so we can continue plugin_version = "Unknown" Logger.logException( "w", "Could not get version information for the plugin") self._user_agent = ( "%s/%s %s/%s" % (Application.getInstance().getApplicationName(), Application.getInstance().getVersion(), "OctoPrintPlugin", Application.getInstance().getVersion())).encode() self._instance_responded = False self._instance_api_key_accepted = False self._instance_supports_sd = False self._instance_supports_camera = False ContainerRegistry.getInstance().containerAdded.connect( self._onContainerAdded) Application.getInstance().engineCreatedSignal.connect( self._createAdditionalComponentsView) @pyqtSlot() def startDiscovery(self): if not self._network_plugin: self._network_plugin = Application.getInstance( ).getOutputDeviceManager().getOutputDevicePlugin("OctoPrintPlugin") self._network_plugin.addInstanceSignal.connect( self._onInstanceDiscovery) self._network_plugin.removeInstanceSignal.connect( self._onInstanceDiscovery) self._network_plugin.instanceListChanged.connect( self._onInstanceDiscovery) self.instancesChanged.emit() else: # Restart bonjour discovery self._network_plugin.startDiscovery() def _onInstanceDiscovery(self, *args): self.instancesChanged.emit() @pyqtSlot(str) def removeManualInstance(self, name): if not self._network_plugin: return self._network_plugin.removeManualInstance(name) @pyqtSlot(str, str, int, str, bool, str, str) def setManualInstance(self, name, address, port, path, useHttps, userName, password): # This manual printer could replace a current manual printer self._network_plugin.removeManualInstance(name) self._network_plugin.addManualInstance(name, address, port, path, useHttps, userName, password) def _onContainerAdded(self, container): # Add this action as a supported action to all machine definitions if isinstance(container, DefinitionContainer) and container.getMetaDataEntry( "type") == "machine" and container.getMetaDataEntry( "supports_usb_connection"): Application.getInstance().getMachineActionManager( ).addSupportedAction(container.getId(), self.getKey()) instancesChanged = pyqtSignal() @pyqtProperty("QVariantList", notify=instancesChanged) def discoveredInstances(self): if self._network_plugin: instances = list(self._network_plugin.getInstances().values()) instances.sort(key=lambda k: k.name) return instances else: return [] @pyqtSlot(str) def setKey(self, key): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: if "octoprint_id" in global_container_stack.getMetaData(): global_container_stack.setMetaDataEntry("octoprint_id", key) else: global_container_stack.addMetaDataEntry("octoprint_id", key) if self._network_plugin: # Ensure that the connection states are refreshed. self._network_plugin.reCheckConnections() @pyqtSlot(result=str) def getStoredKey(self): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: meta_data = global_container_stack.getMetaData() if "octoprint_id" in meta_data: return global_container_stack.getMetaDataEntry("octoprint_id") return "" @pyqtSlot(str, str, str, str) def testApiKey(self, base_url, api_key, basic_auth_username="", basic_auth_password=""): self._instance_responded = False self._instance_api_key_accepted = False self._instance_supports_sd = False self._instance_supports_camera = False self.selectedInstanceSettingsChanged.emit() if api_key != "": Logger.log( "d", "Trying to access OctoPrint instance at %s with the provided API key." % base_url) ## Request 'settings' dump url = QUrl(base_url + "api/settings") settings_request = QNetworkRequest(url) settings_request.setRawHeader("X-Api-Key".encode(), api_key.encode()) settings_request.setRawHeader("User-Agent".encode(), self._user_agent) if basic_auth_username and basic_auth_password: data = base64.b64encode( ("%s:%s" % (basic_auth_username, basic_auth_password)).encode()).decode("utf-8") settings_request.setRawHeader("Authorization".encode(), ("Basic %s" % data).encode()) self._settings_reply = self._manager.get(settings_request) else: if self._settings_reply: self._settings_reply.abort() self._settings_reply = None @pyqtSlot(str) def setApiKey(self, api_key): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: if "octoprint_api_key" in global_container_stack.getMetaData(): global_container_stack.setMetaDataEntry( "octoprint_api_key", api_key) else: global_container_stack.addMetaDataEntry( "octoprint_api_key", api_key) if self._network_plugin: # Ensure that the connection states are refreshed. self._network_plugin.reCheckConnections() apiKeyChanged = pyqtSignal() ## Get the stored API key of this machine # \return key String containing the key of the machine. @pyqtProperty(str, notify=apiKeyChanged) def apiKey(self): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: return global_container_stack.getMetaDataEntry("octoprint_api_key") else: return "" selectedInstanceSettingsChanged = pyqtSignal() @pyqtProperty(bool, notify=selectedInstanceSettingsChanged) def instanceResponded(self): return self._instance_responded @pyqtProperty(bool, notify=selectedInstanceSettingsChanged) def instanceApiKeyAccepted(self): return self._instance_api_key_accepted @pyqtProperty(bool, notify=selectedInstanceSettingsChanged) def instanceSupportsSd(self): return self._instance_supports_sd @pyqtProperty(bool, notify=selectedInstanceSettingsChanged) def instanceSupportsCamera(self): return self._instance_supports_camera @pyqtSlot(str, str, str) def setContainerMetaDataEntry(self, container_id, key, value): containers = ContainerRegistry.getInstance().findContainers( None, id=container_id) if not containers: UM.Logger.log( "w", "Could not set metadata of container %s because it was not found.", container_id) return False container = containers[0] if key in container.getMetaData(): container.setMetaDataEntry(key, value) else: container.addMetaDataEntry(key, value) @pyqtSlot(bool) def applyGcodeFlavorFix(self, apply_fix): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if not global_container_stack: return gcode_flavor = "RepRap (Marlin/Sprinter)" if apply_fix else "UltiGCode" if global_container_stack.getProperty("machine_gcode_flavor", "value") == gcode_flavor: # No need to add a definition_changes container if the setting is not going to be changed return # Make sure there is a definition_changes container to store the machine settings definition_changes_container = global_container_stack.definitionChanges if definition_changes_container == ContainerRegistry.getInstance( ).getEmptyInstanceContainer(): definition_changes_container = CuraStackBuilder.createDefinitionChangesContainer( global_container_stack, global_container_stack.getId() + "_settings") definition_changes_container.setProperty("machine_gcode_flavor", "value", gcode_flavor) # Update the has_materials metadata flag after switching gcode flavor definition = global_container_stack.getBottom() if definition.getProperty( "machine_gcode_flavor", "value") != "UltiGCode" or definition.getMetaDataEntry( "has_materials", False): # In other words: only continue for the UM2 (extended), but not for the UM2+ return has_materials = global_container_stack.getProperty( "machine_gcode_flavor", "value") != "UltiGCode" material_container = global_container_stack.material if has_materials: if "has_materials" in global_container_stack.getMetaData(): global_container_stack.setMetaDataEntry("has_materials", True) else: global_container_stack.addMetaDataEntry("has_materials", True) # Set the material container to a sane default if material_container == ContainerRegistry.getInstance( ).getEmptyInstanceContainer(): search_criteria = { "type": "material", "definition": "fdmprinter", "id": global_container_stack.getMetaDataEntry( "preferred_material") } materials = ContainerRegistry.getInstance( ).findInstanceContainers(**search_criteria) if materials: global_container_stack.material = materials[0] else: # The metadata entry is stored in an ini, and ini files are parsed as strings only. # Because any non-empty string evaluates to a boolean True, we have to remove the entry to make it False. if "has_materials" in global_container_stack.getMetaData(): global_container_stack.removeMetaDataEntry("has_materials") global_container_stack.material = ContainerRegistry.getInstance( ).getEmptyInstanceContainer() Application.getInstance().globalContainerStackChanged.emit() @pyqtSlot(str) def openWebPage(self, url): QDesktopServices.openUrl(QUrl(url)) def _createAdditionalComponentsView(self): Logger.log( "d", "Creating additional ui components for OctoPrint-connected printers." ) path = QUrl.fromLocalFile( os.path.join(os.path.dirname(os.path.abspath(__file__)), "OctoPrintComponents.qml")) self._additional_component = QQmlComponent( Application.getInstance()._engine, path) # We need access to engine (although technically we can't) self._additional_components_context = QQmlContext( Application.getInstance()._engine.rootContext()) self._additional_components_context.setContextProperty("manager", self) self._additional_components_view = self._additional_component.create( self._additional_components_context) if not self._additional_components_view: Logger.log( "w", "Could not create additional components for OctoPrint-connected printers." ) return Application.getInstance().addAdditionalComponent( "monitorButtons", self._additional_components_view.findChild(QObject, "openOctoPrintButton")) ## Handler for all requests that have finished. def _onRequestFinished(self, reply): http_status_code = reply.attribute( QNetworkRequest.HttpStatusCodeAttribute) if not http_status_code: # Received no or empty reply return if reply.operation() == QNetworkAccessManager.GetOperation: if "api/settings" in reply.url().toString( ): # OctoPrint settings dump from /settings: if http_status_code == 200: Logger.log("d", "API key accepted by OctoPrint.") self._instance_api_key_accepted = True 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._instance_supports_sd = json_data["feature"][ "sdSupport"] if "webcam" in json_data and "streamUrl" in json_data[ "webcam"]: stream_url = json_data["webcam"]["streamUrl"] if stream_url: #not empty string or None self._instance_supports_camera = True elif http_status_code == 401: Logger.log("d", "Invalid API key for OctoPrint.") self._instance_api_key_accepted = False self._instance_responded = True self.selectedInstanceSettingsChanged.emit()
class DiscoverOctoPrintAction(MachineAction): def __init__(self, parent=None): super().__init__("DiscoverOctoPrintAction", catalog.i18nc("@action", "Connect OctoPrint")) self._qml_url = "DiscoverOctoPrintAction.qml" self._window = None self._context = None self._network_plugin = None cura.Settings.CuraContainerRegistry.getInstance( ).containerAdded.connect(self._onContainerAdded) Application.getInstance().engineCreatedSignal.connect( self._createAdditionalComponentsView) instancesChanged = pyqtSignal() @pyqtSlot() def startDiscovery(self): if not self._network_plugin: self._network_plugin = Application.getInstance( ).getOutputDeviceManager().getOutputDevicePlugin( "SculptoPrintPlugin") self._network_plugin.addInstanceSignal.connect( self._onInstanceDiscovery) self._network_plugin.removeInstanceSignal.connect( self._onInstanceDiscovery) self._network_plugin.instanceListChanged.connect( self._onInstanceDiscovery) self.instancesChanged.emit() else: # Restart bonjour discovery self._network_plugin.startDiscovery() def _onInstanceDiscovery(self, *args): self.instancesChanged.emit() @pyqtSlot(str) def removeManualInstance(self, name): if not self._network_plugin: return self._network_plugin.removeManualInstance(name) @pyqtSlot(str, str, int, str) def setManualInstance(self, name, address, port, path): # This manual printer could replace a current manual printer self._network_plugin.removeManualInstance(name) self._network_plugin.addManualInstance(name, address, port, path) def _onContainerAdded(self, container): # Add this action as a supported action to all machine definitions if isinstance(container, DefinitionContainer) and container.getMetaDataEntry( "type") == "machine" and container.getMetaDataEntry( "supports_usb_connection"): Application.getInstance().getMachineActionManager( ).addSupportedAction(container.getId(), self.getKey()) @pyqtProperty("QVariantList", notify=instancesChanged) def discoveredInstances(self): if self._network_plugin: instances = list(self._network_plugin.getInstances().values()) instances.sort(key=lambda k: k.name) return instances else: return [] @pyqtSlot(str) def setKey(self, key): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: if "sculptoprint_id" in global_container_stack.getMetaData(): global_container_stack.setMetaDataEntry("sculptoprint_id", key) else: global_container_stack.addMetaDataEntry("sculptoprint_id", key) if self._network_plugin: # Ensure that the connection states are refreshed. self._network_plugin.reCheckConnections() @pyqtSlot(result=str) def getStoredKey(self): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: meta_data = global_container_stack.getMetaData() if "sculptoprint_id" in meta_data: return global_container_stack.getMetaDataEntry( "sculptoprint_id") return "" @pyqtSlot(str) def setApiKey(self, api_key): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: if "Sculptoprint_api_key" in global_container_stack.getMetaData(): global_container_stack.setMetaDataEntry( "sculptoprint_api_key", api_key) else: global_container_stack.addMetaDataEntry( "sculptoprint_api_key", api_key) if self._network_plugin: # Ensure that the connection states are refreshed. self._network_plugin.reCheckConnections() apiKeyChanged = pyqtSignal() ## Get the stored API key of this machine # \return key String containing the key of the machine. @pyqtProperty(str, notify=apiKeyChanged) def apiKey(self): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: return global_container_stack.getMetaDataEntry( "sculptoprint_api_key") else: return "" @pyqtSlot(str, str, str) def setContainerMetaDataEntry(self, container_id, key, value): containers = UM.Settings.ContainerRegistry.getInstance( ).findContainers(None, id=container_id) if not containers: UM.Logger.log( "w", "Could not set metadata of container %s because it was not found.", container_id) return False container = containers[0] if key in container.getMetaData(): container.setMetaDataEntry(key, value) else: container.addMetaDataEntry(key, value) @pyqtSlot(str) def openWebPage(self, url): QDesktopServices.openUrl(QUrl(url)) def _createAdditionalComponentsView(self): Logger.log( "d", "Creating additional ui components for OctoPrint-connected printers." ) path = QUrl.fromLocalFile( os.path.join( PluginRegistry.getInstance().getPluginPath( "SculptoPrintPlugin"), "SculptoPrintPlugin.qml")) self._additional_component = QQmlComponent( Application.getInstance()._engine, path) # We need access to engine (although technically we can't) self._additional_components_context = QQmlContext( Application.getInstance()._engine.rootContext()) self._additional_components_context.setContextProperty("manager", self) self._additional_components_view = self._additional_component.create( self._additional_components_context) if not self._additional_components_view: Logger.log( "w", "Could not create additional components for OctoPrint-connected printers." ) return
class PluginBrowser(QObject, Extension): def __init__(self, parent=None): super().__init__(parent) self.addMenuItem(i18n_catalog.i18n("Browse plugins"), self.browsePlugins) self._api_version = 1 self._api_url = "http://software.ultimaker.com/cura/v%s/" % self._api_version self._plugin_list_request = None self._download_plugin_request = None self._download_plugin_reply = None self._network_manager = None self._plugins_metadata = [] self._plugins_model = None self._qml_component = None self._qml_context = None self._dialog = None self._download_progress = 0 self._is_downloading = False self._request_header = [ b"User-Agent", str.encode("%s - %s" % (Application.getInstance().getApplicationName(), Application.getInstance().getVersion())) ] # Installed plugins are really installed after reboot. In order to prevent the user from downloading the # same file over and over again, we keep track of the upgraded plugins. self._newly_installed_plugin_ids = [] pluginsMetadataChanged = pyqtSignal() onDownloadProgressChanged = pyqtSignal() onIsDownloadingChanged = pyqtSignal() @pyqtProperty(bool, notify=onIsDownloadingChanged) def isDownloading(self): return self._is_downloading def browsePlugins(self): self._createNetworkManager() self.requestPluginList() if not self._dialog: self._createDialog() self._dialog.show() @pyqtSlot() def requestPluginList(self): Logger.log("i", "Requesting plugin list") url = QUrl(self._api_url + "plugins") self._plugin_list_request = QNetworkRequest(url) self._plugin_list_request.setRawHeader(*self._request_header) self._network_manager.get(self._plugin_list_request) def _createDialog(self): Logger.log("d", "PluginBrowser") path = QUrl.fromLocalFile( os.path.join( PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "PluginBrowser.qml")) self._qml_component = QQmlComponent(Application.getInstance()._engine, path) # We need access to engine (although technically we can't) self._qml_context = QQmlContext( Application.getInstance()._engine.rootContext()) self._qml_context.setContextProperty("manager", self) self._dialog = self._qml_component.create(self._qml_context) if self._dialog is None: Logger.log("e", "QQmlComponent status %s", self._qml_component.status()) Logger.log("e", "QQmlComponent errorString %s", self._qml_component.errorString()) def setIsDownloading(self, is_downloading): if self._is_downloading != is_downloading: self._is_downloading = is_downloading self.onIsDownloadingChanged.emit() def _onDownloadPluginProgress(self, bytes_sent, bytes_total): if bytes_total > 0: new_progress = bytes_sent / bytes_total * 100 self.setDownloadProgress(new_progress) if new_progress == 100.0: self.setIsDownloading(False) self._download_plugin_reply.downloadProgress.disconnect( self._onDownloadPluginProgress) self._temp_plugin_file = tempfile.NamedTemporaryFile( suffix=".curaplugin") self._temp_plugin_file.write( self._download_plugin_reply.readAll()) result = PluginRegistry.getInstance().installPlugin( "file://" + self._temp_plugin_file.name) self._newly_installed_plugin_ids.append(result["id"]) self.pluginsMetadataChanged.emit() Application.getInstance().messageBox( i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"]) self._temp_plugin_file.close( ) # Plugin was installed, delete temp file @pyqtProperty(int, notify=onDownloadProgressChanged) def downloadProgress(self): return self._download_progress def setDownloadProgress(self, progress): if progress != self._download_progress: self._download_progress = progress self.onDownloadProgressChanged.emit() @pyqtSlot(str) def downloadAndInstallPlugin(self, url): Logger.log("i", "Attempting to download & install plugin from %s", url) url = QUrl(url) self._download_plugin_request = QNetworkRequest(url) self._download_plugin_request.setRawHeader(*self._request_header) self._download_plugin_reply = self._network_manager.get( self._download_plugin_request) self.setDownloadProgress(0) self.setIsDownloading(True) self._download_plugin_reply.downloadProgress.connect( self._onDownloadPluginProgress) @pyqtProperty(QObject, notify=pluginsMetadataChanged) def pluginsModel(self): if self._plugins_model is None: self._plugins_model = ListModel() self._plugins_model.addRoleName(Qt.UserRole + 1, "name") self._plugins_model.addRoleName(Qt.UserRole + 2, "version") self._plugins_model.addRoleName(Qt.UserRole + 3, "short_description") self._plugins_model.addRoleName(Qt.UserRole + 4, "author") self._plugins_model.addRoleName(Qt.UserRole + 5, "already_installed") self._plugins_model.addRoleName(Qt.UserRole + 6, "file_location") self._plugins_model.addRoleName(Qt.UserRole + 7, "can_upgrade") else: self._plugins_model.clear() items = [] for metadata in self._plugins_metadata: items.append({ "name": metadata["label"], "version": metadata["version"], "short_description": metadata["short_description"], "author": metadata["author"], "already_installed": self._checkAlreadyInstalled(metadata["id"]), "file_location": metadata["file_location"], "can_upgrade": self._checkCanUpgrade(metadata["id"], metadata["version"]) }) self._plugins_model.setItems(items) return self._plugins_model def _checkCanUpgrade(self, id, version): plugin_registry = PluginRegistry.getInstance() metadata = plugin_registry.getMetaData(id) if metadata != {}: if id in self._newly_installed_plugin_ids: return False # We already updated this plugin. current_version = Version(metadata["plugin"]["version"]) new_version = Version(version) if new_version > current_version: return True return False def _checkAlreadyInstalled(self, id): plugin_registry = PluginRegistry.getInstance() metadata = plugin_registry.getMetaData(id) if metadata != {}: return True else: if id in self._newly_installed_plugin_ids: return True # We already installed this plugin, but the registry just doesn't know it yet. return False def _onRequestFinished(self, reply): reply_url = reply.url().toString() if reply.error() == QNetworkReply.TimeoutError: Logger.log("w", "Got a timeout.") # Reset everything. self.setDownloadProgress(0) self.setIsDownloading(False) if self._download_plugin_reply: self._download_plugin_reply.downloadProgress.disconnect( self._onDownloadPluginProgress) self._download_plugin_reply.abort() self._download_plugin_reply = None return elif reply.error() == QNetworkReply.HostNotFoundError: Logger.log("w", "Unable to reach server.") return if reply.operation() == QNetworkAccessManager.GetOperation: if reply_url == self._api_url + "plugins": try: json_data = json.loads( bytes(reply.readAll()).decode("utf-8")) self._plugins_metadata = json_data self.pluginsMetadataChanged.emit() except json.decoder.JSONDecodeError: Logger.log( "w", "Received an invalid print job state message: Not valid JSON." ) return else: # Ignore any operation that is not a get operation pass def _onNetworkAccesibleChanged(self, accessible): if accessible == 0: self.setDownloadProgress(0) self.setIsDownloading(False) if self._download_plugin_reply: self._download_plugin_reply.downloadProgress.disconnect( self._onDownloadPluginProgress) self._download_plugin_reply.abort() self._download_plugin_reply = None def _createNetworkManager(self): if self._network_manager: self._network_manager.finished.disconnect(self._onRequestFinished) self._network_manager.networkAccessibleChanged.disconnect( self._onNetworkAccesibleChanged) self._network_manager = QNetworkAccessManager() self._network_manager.finished.connect(self._onRequestFinished) self._network_manager.networkAccessibleChanged.connect( self._onNetworkAccesibleChanged)
class WorkspaceDialog(QObject): showDialogSignal = pyqtSignal() def __init__(self, parent = None): super().__init__(parent) self._component = None self._context = None self._view = None self._qml_url = "WorkspaceDialog.qml" self._lock = threading.Lock() self._default_strategy = "override" self._result = {"machine": self._default_strategy, "quality_changes": self._default_strategy, "material": self._default_strategy} self._visible = False self.showDialogSignal.connect(self.__show) self._has_quality_changes_conflict = False self._has_machine_conflict = False self._has_material_conflict = False machineConflictChanged = pyqtSignal() qualityChangesConflictChanged = pyqtSignal() materialConflictChanged = pyqtSignal() @pyqtProperty(bool, notify = machineConflictChanged) def machineConflict(self): return self._has_machine_conflict @pyqtProperty(bool, notify=qualityChangesConflictChanged) def qualityChangesConflict(self): return self._has_quality_changes_conflict @pyqtProperty(bool, notify=materialConflictChanged) def materialConflict(self): return self._has_material_conflict @pyqtSlot(str, str) def setResolveStrategy(self, key, strategy): if key in self._result: self._result[key] = strategy def setMaterialConflict(self, material_conflict): self._has_material_conflict = material_conflict self.materialConflictChanged.emit() def setMachineConflict(self, machine_conflict): self._has_machine_conflict = machine_conflict self.machineConflictChanged.emit() def setQualityChangesConflict(self, quality_changes_conflict): self._has_quality_changes_conflict = quality_changes_conflict self.qualityChangesConflictChanged.emit() def getResult(self): if "machine" in self._result and not self._has_machine_conflict: self._result["machine"] = None if "quality_changes" in self._result and not self._has_quality_changes_conflict: self._result["quality_changes"] = None if "material" in self._result and not self._has_material_conflict: self._result["material"] = None return self._result def _createViewFromQML(self): path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("3MFReader"), 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) if self._view is None: Logger.log("c", "QQmlComponent status %s", self._component.status()) Logger.log("c", "QQmlComponent error string %s", self._component.errorString()) def show(self): # Emit signal so the right thread actually shows the view. if threading.current_thread() != threading.main_thread(): self._lock.acquire() # Reset the result self._result = {"machine": self._default_strategy, "quality_changes": self._default_strategy, "material": self._default_strategy} self._visible = True self.showDialogSignal.emit() @pyqtSlot() ## Used to notify the dialog so the lock can be released. def notifyClosed(self): if self._result is None: self._result = {} self._lock.release() def hide(self): self._visible = False self._lock.release() self._view.hide() @pyqtSlot() def onOkButtonClicked(self): self._view.hide() self.hide() @pyqtSlot() def onCancelButtonClicked(self): self._view.hide() self.hide() self._result = {} ## Block thread until the dialog is closed. def waitForClose(self): if self._visible: if threading.current_thread() != threading.main_thread(): self._lock.acquire() self._lock.release() else: # If this is not run from a separate thread, we need to ensure that the events are still processed. while self._visible: time.sleep(1 / 50) QCoreApplication.processEvents() # Ensure that the GUI does not freeze. def __show(self): if self._view is None: self._createViewFromQML() if self._view: self._view.show()
class DuetRRFPlugin(QObject, Extension, OutputDevicePlugin): def __init__(self, parent=None): QObject.__init__(self, parent) Extension.__init__(self) OutputDevicePlugin.__init__(self) self.addMenuItem(catalog.i18n("DuetRRF Connections"), self.showSettingsDialog) self._dialogs = {} self._dialogView = None Preferences.getInstance().addPreference("duetrrf/instances", json.dumps({})) self._instances = json.loads( Preferences.getInstance().getValue("duetrrf/instances")) def start(self): manager = self.getOutputDeviceManager() for name, instance in self._instances.items(): manager.addOutputDevice( DuetRRFOutputDevice.DuetRRFOutputDevice( name, instance["url"], instance["duet_password"], instance["http_user"], instance["http_password"], device_type=DuetRRFOutputDevice.DeviceType.print)) manager.addOutputDevice( DuetRRFOutputDevice.DuetRRFOutputDevice( name, instance["url"], instance["duet_password"], instance["http_user"], instance["http_password"], device_type=DuetRRFOutputDevice.DeviceType.simulate)) manager.addOutputDevice( DuetRRFOutputDevice.DuetRRFOutputDevice( name, instance["url"], instance["duet_password"], instance["http_user"], instance["http_password"], device_type=DuetRRFOutputDevice.DeviceType.upload)) def stop(self): manager = self.getOutputDeviceManager() for name in self._instances.keys(): manager.removeOutputDevice(name + "-print") manager.removeOutputDevice(name + "-simulate") manager.removeOutputDevice(name + "-upload") def _createDialog(self, qml): path = QUrl.fromLocalFile( os.path.join(os.path.dirname(os.path.abspath(__file__)), qml)) self._component = QQmlComponent(Application.getInstance()._engine, path) self._context = QQmlContext( Application.getInstance()._engine.rootContext()) self._context.setContextProperty("manager", self) dialog = self._component.create(self._context) if dialog is None: Logger.log("e", "QQmlComponent status %s", self._component.status()) Logger.log("e", "QQmlComponent errorString %s", self._component.errorString()) raise RuntimeError(self._component.errorString()) return dialog def _showDialog(self, qml): if not qml in self._dialogs: self._dialogs[qml] = self._createDialog(qml) self._dialogs[qml].show() def showSettingsDialog(self): self._showDialog("DuetRRFPlugin.qml") serverListChanged = pyqtSignal() @pyqtProperty("QVariantList", notify=serverListChanged) def serverList(self): return list(self._instances.keys()) @pyqtSlot(str, result=str) def instanceUrl(self, name): if name in self._instances.keys(): return self._instances[name]["url"] return None @pyqtSlot(str, result=str) def instanceDuetPassword(self, name): if name in self._instances.keys(): return self._instances[name]["duet_password"] return None @pyqtSlot(str, result=str) def instanceHTTPUser(self, name): if name in self._instances.keys(): return self._instances[name]["http_user"] return None @pyqtSlot(str, result=str) def instanceHTTPPassword(self, name): if name in self._instances.keys(): return self._instances[name]["http_password"] return None @pyqtSlot(str, str, str, str, str, str) def saveInstance(self, oldName, name, url, duet_password, http_user, http_password): manager = self.getOutputDeviceManager() if oldName and oldName != name: manager.removeOutputDevice(oldName) if oldName in self._instances: del self._instances[oldName] self._instances[name] = { "url": url, "duet_password": duet_password, "http_user": http_user, "http_password": http_password } manager.addOutputDevice( DuetRRFOutputDevice.DuetRRFOutputDevice( name, url, duet_password, http_user, http_password, device_type=DuetRRFOutputDevice.DeviceType.print)) manager.addOutputDevice( DuetRRFOutputDevice.DuetRRFOutputDevice( name, url, duet_password, http_user, http_password, device_type=DuetRRFOutputDevice.DeviceType.simulate)) manager.addOutputDevice( DuetRRFOutputDevice.DuetRRFOutputDevice( name, url, duet_password, http_user, http_password, device_type=DuetRRFOutputDevice.DeviceType.upload)) Preferences.getInstance().setValue("duetrrf/instances", json.dumps(self._instances)) self.serverListChanged.emit() @pyqtSlot(str) def removeInstance(self, name): self.getOutputDeviceManager().removeOutputDevice(name + "-print") self.getOutputDeviceManager().removeOutputDevice(name + "-simulate") self.getOutputDeviceManager().removeOutputDevice(name + "-upload") del self._instances[name] Preferences.getInstance().setValue("duetrrf/instances", json.dumps(self._instances)) self.serverListChanged.emit() @pyqtSlot(str, str, result=bool) def validName(self, oldName, newName): # empty string isn't allowed if not newName: return False # if name hasn't changed, not a duplicate, just no rename if oldName == newName: return True # duplicates not allowed return (not newName in self._instances.keys())
def ros_qml_main(): try: rospy.init_node('ros_qml', sys.argv) my_argv = rospy.myargv(sys.argv) signal.signal(signal.SIGINT, sigint_handler) app = QApplication(my_argv) engine = QQmlEngine() engine.quit.connect(app.quit) plugins_dir = QLibraryInfo.location(QLibraryInfo.PluginsPath) # ## Add QML extension modules and plugins to the path, including ourselves plugins_paths = rospack.get_manifest(THIS_PACKAGE).get_export(THIS_PACKAGE, 'plugins') for idx, p in enumerate(plugins_paths): # If a relative path provided, treat it as relative to Qt plugins dir if not os.path.isabs(p): plugins_paths[idx] = plugins_dir + '/' + p qml_paths = rospack.get_manifest(THIS_PACKAGE).get_export(THIS_PACKAGE, 'imports') deps = rospack.get_depends_on(THIS_PACKAGE) for d in deps: pp = rospack.get_manifest(d).get_export(THIS_PACKAGE, 'plugins') for idx, p in enumerate(pp): # If a relative path provided, treat it as relative to Qt plugins dir if not os.path.isabs(p): pp[idx] = plugins_dir + '/' + p plugins_paths += pp qp = rospack.get_manifest(d).get_export(THIS_PACKAGE, 'imports') qml_paths += qp for p in plugins_paths: engine.addPluginPath(p) for p in qml_paths: engine.addImportPath(p) qml_sys_path = QLibraryInfo.location(QLibraryInfo.ImportsPath) engine.addImportPath(qml_sys_path) # Somehow we need to set the path both with QQmlEngine and with environment, # commenting any of the two will lead to 'module not installed' error os.environ['QML2_IMPORT_PATH'] = ':'.join(qml_paths) + ':' + qml_sys_path comp = QQmlComponent(engine) if len(my_argv) > 1 and my_argv[1]: qml_url = my_argv[1] comp.loadUrl(QUrl(qml_url)) elif rospy.has_param('qml_url'): qml_url = rospy.get_param('qml_url') comp.loadUrl(QUrl(qml_url)) elif rospy.has_param('qml_description'): # experimental qml_description = rospy.get_param('qml_description') # FIXME that hangs for unknown reason comp.setData(QByteArray(qml_description), QUrl()) else: rospy.logfatal('Neither /qml_url nor /qml_description (experimental) parameter is present') sys.exit(1) if not comp.isReady(): sys.stderr.write(comp.errorString()) sys.exit(1) win = comp.create() if not win: rospy.logfatal('Your root item has to be a Window') sys.exit(1) engine.setIncubationController(win.incubationController()) if win: win.show() # Poll Python interpreter every 500ms timer = QTimer() timer.start(500) timer.timeout.connect(lambda: None) sys.exit(app.exec_()) except KeyboardInterrupt: pass except rospy.ROSInterruptException: pass
class DiscoverUM3Action(MachineAction): def __init__(self): super().__init__("DiscoverUM3Action", catalog.i18nc("@action", "Connect via Network")) self._qml_url = "DiscoverUM3Action.qml" self._network_plugin = None self.__additional_components_context = None self.__additional_component = None self.__additional_components_view = None Application.getInstance().engineCreatedSignal.connect(self._createAdditionalComponentsView) self._last_zeroconf_event_time = time.time() self._zeroconf_change_grace_period = ( 0.25 ) # Time to wait after a zeroconf service change before allowing a zeroconf reset printersChanged = pyqtSignal() @pyqtSlot() def startDiscovery(self): if not self._network_plugin: self._network_plugin = ( Application.getInstance().getOutputDeviceManager().getOutputDevicePlugin("UM3NetworkPrinting") ) self._network_plugin.printerListChanged.connect(self._onPrinterDiscoveryChanged) self.printersChanged.emit() ## Re-filters the list of printers. @pyqtSlot() def reset(self): self.printersChanged.emit() @pyqtSlot() def restartDiscovery(self): # Ensure that there is a bit of time after a printer has been discovered. # This is a work around for an issue with Qt 5.5.1 up to Qt 5.7 which can segfault if we do this too often. # It's most likely that the QML engine is still creating delegates, where the python side already deleted or # garbage collected the data. # Whatever the case, waiting a bit ensures that it doesn't crash. if time.time() - self._last_zeroconf_event_time > self._zeroconf_change_grace_period: if not self._network_plugin: self.startDiscovery() else: self._network_plugin.startDiscovery() @pyqtSlot(str, str) def removeManualPrinter(self, key, address): if not self._network_plugin: return self._network_plugin.removeManualPrinter(key, address) @pyqtSlot(str, str) def setManualPrinter(self, key, address): if key != "": # This manual printer replaces a current manual printer self._network_plugin.removeManualPrinter(key) if address != "": self._network_plugin.addManualPrinter(address) def _onPrinterDiscoveryChanged(self, *args): self._last_zeroconf_event_time = time.time() self.printersChanged.emit() @pyqtProperty("QVariantList", notify=printersChanged) def foundDevices(self): if self._network_plugin: if Application.getInstance().getGlobalContainerStack(): global_printer_type = Application.getInstance().getGlobalContainerStack().getBottom().getId() else: global_printer_type = "unknown" printers = list(self._network_plugin.getPrinters().values()) # TODO; There are still some testing printers that don't have a correct printer type, so don't filter out unkown ones just yet. printers = [ printer for printer in printers if printer.printerType == global_printer_type or printer.printerType == "unknown" ] printers.sort(key=lambda k: k.name) return printers else: return [] @pyqtSlot(str) def setKey(self, key): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack: meta_data = global_container_stack.getMetaData() if "um_network_key" in meta_data: global_container_stack.setMetaDataEntry("um_network_key", key) # Delete old authentication data. global_container_stack.removeMetaDataEntry("network_authentication_id") global_container_stack.removeMetaDataEntry("network_authentication_key") else: global_container_stack.addMetaDataEntry("um_network_key", key) if self._network_plugin: # Ensure that the connection states are refreshed. self._network_plugin.reCheckConnections() @pyqtSlot(result=str) def getStoredKey(self): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack: meta_data = global_container_stack.getMetaData() if "um_network_key" in meta_data: return global_container_stack.getMetaDataEntry("um_network_key") return "" @pyqtSlot() def loadConfigurationFromPrinter(self): machine_manager = Application.getInstance().getMachineManager() hotend_ids = machine_manager.printerOutputDevices[0].hotendIds for index in range(len(hotend_ids)): machine_manager.printerOutputDevices[0].hotendIdChanged.emit(index, hotend_ids[index]) material_ids = machine_manager.printerOutputDevices[0].materialIds for index in range(len(material_ids)): machine_manager.printerOutputDevices[0].materialIdChanged.emit(index, material_ids[index]) def _createAdditionalComponentsView(self): Logger.log("d", "Creating additional ui components for UM3.") path = QUrl.fromLocalFile( os.path.join(PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"), "UM3InfoComponents.qml") ) self.__additional_component = QQmlComponent(Application.getInstance()._engine, path) # We need access to engine (although technically we can't) self.__additional_components_context = QQmlContext(Application.getInstance()._engine.rootContext()) self.__additional_components_context.setContextProperty("manager", self) self.__additional_components_view = self.__additional_component.create(self.__additional_components_context) if not self.__additional_components_view: Logger.log("w", "Could not create ui components for UM3.") return Application.getInstance().addAdditionalComponent( "monitorButtons", self.__additional_components_view.findChild(QObject, "networkPrinterConnectButton") ) Application.getInstance().addAdditionalComponent( "machinesDetailPane", self.__additional_components_view.findChild(QObject, "networkPrinterConnectionInfo") )
class MachineAction(QObject, PluginObject): def __init__(self, key, label = ""): super().__init__() self._key = key self._label = label self._qml_url = "" self._component = None self._context = None self._view = None self._finished = False labelChanged = pyqtSignal() onFinished = pyqtSignal() def getKey(self): return self._key @pyqtProperty(str, notify = labelChanged) def label(self): return self._label def setLabel(self, label): if self._label != label: self._label = label self.labelChanged.emit() ## Reset the action to it's default state. # This should not be re-implemented by child classes, instead re-implement _reset. # /sa _reset @pyqtSlot() def reset(self): self._finished = False self._reset() ## Protected implementation of reset. # /sa reset() def _reset(self): pass @pyqtSlot() def setFinished(self): self._finished = True self._reset() self.onFinished.emit() @pyqtProperty(bool, notify = onFinished) def finished(self): return self._finished 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) @pyqtProperty(QObject, constant = True) def displayItem(self): if not self._component: self._createViewFromQML() return self._view
from PyQt5.QtQml import QQmlComponent, QQmlApplicationEngine from PyQt5.QtQuick import QQuickWindow from view_model import ViewModel if __name__ == '__main__': myApp = QApplication(sys.argv) engine = QQmlApplicationEngine() context = engine.rootContext() engine.addImportPath("/home/bob/Qt/5.11.2/Automotive/sources/qtapplicationmanager/dummyimports/") # create a view model view_model = ViewModel() # bind the view model to the context context.setContextProperty('view_model', view_model) component = QQmlComponent(engine) component.loadUrl(QUrl('mainwindow.qml')) # some boilerplate to make sure the component is ready before showing if component.status() != QQmlComponent.Ready: if component.status() == QQmlComponent.Error: sys.exit(component.errorString()) root_window: QQuickWindow = component.create() myApp.exec_() sys.exit()
class MachineAction(QObject, PluginObject): ## Create a new Machine action. # \param key unique key of the machine action # \param label Human readable label used to identify the machine action. def __init__(self, key, label = ""): super().__init__() self._key = key self._label = label self._qml_url = "" self._component = None self._context = None self._view = None self._finished = False labelChanged = pyqtSignal() onFinished = pyqtSignal() def getKey(self): return self._key @pyqtProperty(str, notify = labelChanged) def label(self): return self._label def setLabel(self, label): if self._label != label: self._label = label self.labelChanged.emit() ## Reset the action to it's default state. # This should not be re-implemented by child classes, instead re-implement _reset. # /sa _reset @pyqtSlot() def reset(self): self._component = None self._finished = False self._reset() ## Protected implementation of reset. # /sa reset() def _reset(self): pass @pyqtSlot() def setFinished(self): self._finished = True self._reset() self.onFinished.emit() @pyqtProperty(bool, notify = onFinished) def finished(self): return self._finished ## Protected helper to create a view object based on provided QML. 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) if self._view is None: Logger.log("c", "QQmlComponent status %s", self._component.status()) Logger.log("c", "QQmlComponent error string %s", self._component.errorString()) @pyqtProperty(QObject, constant = True) def displayItem(self): if not self._component: self._createViewFromQML() return self._view
engine = QQmlEngine() # Initialize PhotoBoothEngine. pbengine = PhotoBoothEngine() pbengine.on_status.connect(appLabel.rootObject().status) pbengine.on_update_filter_preview.connect(appLabel.rootObject().updateImageFilterPreview) appLabel.rootContext().setContextProperty('pbengine', pbengine) # Create a component factory and load the QML script. print("Hello") component = QQmlComponent(appLabel.engine()) component.loadUrl(QUrl('TextStatusFly.qml')) print("Hello2") asdf = component.create(appLabel.rootContext()) print("Hello3") asdf.setParentItem(appLabel.rootObject()) asdf.setParent(appLabel.rootObject()) print("Hello4") #asdf.setProperty("targetX", 100) asdf.setProperty("objectName", "textStatusBar") print("Hello5") appLabel.rootContext().setContextProperty('textStatusBar', asdf) asdf.setProperty("parentSet", True) #asdf.setProperty("y", 100)
@pyqtProperty('QString') def name(self): return self._name @name.setter def name(self, name): self._name = name @pyqtProperty(int) def shoeSize(self): return self._shoeSize if __name__ == '__main__': app = QCoreApplication(sys.argv) qmlRegisterType(Person, 'People', 1, 0, 'Person') engine = QQmlEngine() component = QQmlComponent(engine) component.loadUrl(QUrl('example.qml')) person = component.create() if person is not None: print("The person's name is {0}.".format(person.name)) print("They wear a size {0}.".format(person.shoeSize)) else: for error in component.errors(): print(error.toString())
#!/usr/bin/env python3 import sys from PyQt5.QtCore import pyqtProperty, QObject, QUrl from PyQt5.QtWidgets import QApplication from PyQt5.QtQml import qmlRegisterType, QQmlComponent, QQmlEngine # Create the application instance. app = QApplication(sys.argv) # Create a QML engine. engine = QQmlEngine() # Create a component factory and load the QML script. component = QQmlComponent(engine) component.loadUrl(QUrl('main.qml')) # get window window = component.create() window.show() app.exec_()
# -*- coding : utf-8 -*- from PyQt5.QtCore import QUrl,QCoreApplication from PyQt5.QtGui import QGuiApplication from PyQt5.QtQml import QQmlEngine,QQmlComponent import os import sys app = QGuiApplication(sys.argv) engine = QQmlEngine() component = QQmlComponent(engine) component.loadUrl(QUrl("main.qml")) top = component.create() if top : top.show() sys.exit(app.exec_())
class DiscoverRepetierAction(MachineAction): def __init__(self, parent=None): super().__init__("DiscoverRepetierAction", catalog.i18nc("@action", "Connect Repetier")) self._qml_url = "DiscoverRepetierAction.qml" self._window = None self._context = None self._network_plugin = None # QNetwork manager needs to be created in advance. If we don't it can happen that it doesn't correctly # hook itself into the event loop, which results in events never being fired / done. self._manager = QNetworkAccessManager() self._manager.finished.connect(self._onRequestFinished) self._settings_reply = None # Try to get version information from plugin.json plugin_file_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "plugin.json") try: with open(plugin_file_path) as plugin_file: plugin_info = json.load(plugin_file) plugin_version = plugin_info["version"] except: # The actual version info is not critical to have so we can continue plugin_version = "Unknown" Logger.logException( "w", "Could not get version information for the plugin") self._user_agent = ( "%s/%s %s/%s" % (Application.getInstance().getApplicationName(), Application.getInstance().getVersion(), "RepetierPlugin", Application.getInstance().getVersion())).encode() self._instance_responded = False self._instance_api_key_accepted = False self._instance_supports_sd = False self._instance_supports_camera = False ContainerRegistry.getInstance().containerAdded.connect( self._onContainerAdded) Application.getInstance().engineCreatedSignal.connect( self._createAdditionalComponentsView) @pyqtSlot() def startDiscovery(self): if not self._network_plugin: self._network_plugin = Application.getInstance( ).getOutputDeviceManager().getOutputDevicePlugin("RepetierPlugin") self._network_plugin.addInstanceSignal.connect( self._onInstanceDiscovery) self._network_plugin.removeInstanceSignal.connect( self._onInstanceDiscovery) self._network_plugin.instanceListChanged.connect( self._onInstanceDiscovery) self.instancesChanged.emit() else: # Restart bonjour discovery self._network_plugin.startDiscovery() def _onInstanceDiscovery(self, *args): self.instancesChanged.emit() @pyqtSlot(str) def removeManualInstance(self, name): if not self._network_plugin: return self._network_plugin.removeManualInstance(name) @pyqtSlot(str, str, int, str, bool, str, str) def setManualInstance(self, name, address, port, path, useHttps, userName, password): # This manual printer could replace a current manual printer self._network_plugin.removeManualInstance(name) self._network_plugin.addManualInstance(name, address, port, path, useHttps, userName, password) def _onContainerAdded(self, container): # Add this action as a supported action to all machine definitions if isinstance(container, DefinitionContainer) and container.getMetaDataEntry( "type") == "machine" and container.getMetaDataEntry( "supports_usb_connection"): Application.getInstance().getMachineActionManager( ).addSupportedAction(container.getId(), self.getKey()) instancesChanged = pyqtSignal() @pyqtProperty("QVariantList", notify=instancesChanged) def discoveredInstances(self): if self._network_plugin: instances = list(self._network_plugin.getInstances().values()) instances.sort(key=lambda k: k.name) return instances else: return [] @pyqtSlot(str) def setKey(self, key): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: if "repetier_id" in global_container_stack.getMetaData(): global_container_stack.setMetaDataEntry("repetier_id", key) else: global_container_stack.addMetaDataEntry("repetier_id", key) if self._network_plugin: # Ensure that the connection states are refreshed. self._network_plugin.reCheckConnections() @pyqtSlot(result=str) def getStoredKey(self): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: meta_data = global_container_stack.getMetaData() if "repetier_id" in meta_data: return global_container_stack.getMetaDataEntry("repetier_id") return "" @pyqtSlot(str) def setApiKey(self, api_key): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: if "repetier_api_key" in global_container_stack.getMetaData(): global_container_stack.setMetaDataEntry( "repetier_api_key", api_key) else: global_container_stack.addMetaDataEntry( "repetier_api_key", "e27f74f0-b759-4cfc-9316-c5758f3a387e") if self._network_plugin: # Ensure that the connection states are refreshed. self._network_plugin.reCheckConnections() apiKeyChanged = pyqtSignal() ## Get the stored API key of this machine # \return key String containing the key of the machine. @pyqtProperty(str, notify=apiKeyChanged) def apiKey(self): global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: Logger.log( "d", "APIKEY read %s" % global_container_stack.getMetaDataEntry("repetier_api_key")) return global_container_stack.getMetaDataEntry("repetier_api_key") else: return "" selectedInstanceSettingsChanged = pyqtSignal() @pyqtProperty(bool, notify=selectedInstanceSettingsChanged) def instanceResponded(self): return self._instance_responded @pyqtProperty(bool, notify=selectedInstanceSettingsChanged) def instanceApiKeyAccepted(self): return self._instance_api_key_accepted @pyqtProperty(bool, notify=selectedInstanceSettingsChanged) def instanceSupportsSd(self): return self._instance_supports_sd @pyqtProperty(bool, notify=selectedInstanceSettingsChanged) def instanceSupportsCamera(self): return self._instance_supports_camera @pyqtSlot(str, str, str) def setContainerMetaDataEntry(self, container_id, key, value): containers = ContainerRegistry.getInstance().findContainers( None, id=container_id) if not containers: UM.Logger.log( "w", "Could not set metadata of container %s because it was not found.", container_id) return False container = containers[0] if key in container.getMetaData(): container.setMetaDataEntry(key, value) else: container.addMetaDataEntry(key, value) @pyqtSlot(str) def openWebPage(self, url): QDesktopServices.openUrl(QUrl(url)) def _createAdditionalComponentsView(self): Logger.log( "d", "Creating additional ui components for Repetier-connected printers." ) path = QUrl.fromLocalFile( os.path.join(os.path.dirname(os.path.abspath(__file__)), "RepetierComponents.qml")) self._additional_component = QQmlComponent( Application.getInstance()._engine, path) # We need access to engine (although technically we can't) self._additional_components_context = QQmlContext( Application.getInstance()._engine.rootContext()) self._additional_components_context.setContextProperty("manager", self) self._additional_components_view = self._additional_component.create( self._additional_components_context) if not self._additional_components_view: Logger.log( "w", "Could not create additional components for Repetier-connected printers." ) return Application.getInstance().addAdditionalComponent( "monitorButtons", self._additional_components_view.findChild(QObject, "openRepetierButton")) ## Handler for all requests that have finished. def _onRequestFinished(self, reply): http_status_code = reply.attribute( QNetworkRequest.HttpStatusCodeAttribute) if not http_status_code: # Received no or empty reply return if reply.operation() == QNetworkAccessManager.GetOperation: if "getPrinterConfig" in reply.url().toString( ): # Repetier settings dump from getPrinterConfig: if http_status_code == 200: Logger.log("d", "API key accepted by Repetier.") self._instance_api_key_accepted = True try: json_data = json.loads( bytes(reply.readAll()).decode("utf-8")) Logger.log("d", reply.url().toString()) Logger.log("d", json_data) except json.decoder.JSONDecodeError: Logger.log( "w", "Received invalid JSON from Repetier instance.") json_data = {} if "general" in json_data and "sdcard" in json_data[ "general"]: self._instance_supports_sd = json_data["general"][ "sdcard"] if "webcam" in json_data and "dynamicUrl" in json_data[ "webcam"]: Logger.log( "d", "DiscoverRepetierAction: Checking streamurl") stream_url = json_data["webcam"]["dynamicUrl"] Logger.log("d", "DiscoverRepetierAction: stream_url: %s", stream_url) Logger.log("d", "DiscoverRepetierAction: reply_url: %s", reply.url()) if stream_url: #not empty string or None self._instance_supports_camera = True elif http_status_code == 401: Logger.log("d", "Invalid API key for Repetier.") self._instance_api_key_accepted = False self._instance_responded = True self.selectedInstanceSettingsChanged.emit() @pyqtSlot(str, str, str, str) def testApiKey(self, base_url, api_key, basic_auth_username="", basic_auth_password=""): self._instance_responded = False self._instance_api_key_accepted = False self._instance_supports_sd = False self._instance_supports_camera = False self.selectedInstanceSettingsChanged.emit() global_container_stack = Application.getInstance( ).getGlobalContainerStack() if global_container_stack: work_id = global_container_stack.getMetaDataEntry("repetier_id") else: work_id = "k200" if api_key != "": Logger.log( "d", "Trying to access Repetier instance at %s with the provided API key." % base_url) Logger.log("d", "Using %s as API key" % api_key) Logger.log("d", "Using %s as work_id" % work_id) ## Request 'settings' dump url = QUrl( base_url + "printer/api/k200?a=getPrinterConfig&apikey=e27f74f0-b759-4cfc-9316-c5758f3a387e" ) settings_request = QNetworkRequest(url) settings_request.setRawHeader("x-api-key".encode(), api_key.encode()) settings_request.setRawHeader("User-Agent".encode(), self._user_agent) if basic_auth_username and basic_auth_password: data = base64.b64encode( ("%s:%s" % (basic_auth_username, basic_auth_password)).encode()).decode("utf-8") settings_request.setRawHeader("Authorization".encode(), ("Basic %s" % data).encode()) self._settings_reply = self._manager.get(settings_request) else: if self._settings_reply: self._settings_reply.abort() self._settings_reply = None
class PrinterOutputDevice(QObject, OutputDevice): def __init__(self, device_id, parent = None): super().__init__(device_id = device_id, parent = parent) self._container_registry = ContainerRegistry.getInstance() self._target_bed_temperature = 0 self._bed_temperature = 0 self._num_extruders = 1 self._hotend_temperatures = [0] * self._num_extruders self._target_hotend_temperatures = [0] * self._num_extruders self._material_ids = [""] * self._num_extruders self._hotend_ids = [""] * self._num_extruders self._progress = 0 self._head_x = 0 self._head_y = 0 self._head_z = 0 self._connection_state = ConnectionState.closed self._connection_text = "" self._time_elapsed = 0 self._time_total = 0 self._job_state = "" self._job_name = "" self._error_text = "" self._accepts_commands = True self._preheat_bed_timeout = 900 # Default time-out for pre-heating the bed, in seconds. self._preheat_bed_timer = QTimer() # Timer that tracks how long to preheat still. self._preheat_bed_timer.setSingleShot(True) self._preheat_bed_timer.timeout.connect(self.cancelPreheatBed) self._printer_state = "" self._printer_type = "unknown" self._camera_active = False self._monitor_view_qml_path = "" self._monitor_component = None self._monitor_item = None self._qml_context = None def requestWrite(self, nodes, file_name = None, filter_by_machine = False, file_handler = None): raise NotImplementedError("requestWrite needs to be implemented") ## Signals # Signal to be emitted when bed temp is changed bedTemperatureChanged = pyqtSignal() # Signal to be emitted when target bed temp is changed targetBedTemperatureChanged = pyqtSignal() # Signal when the progress is changed (usually when this output device is printing / sending lots of data) progressChanged = pyqtSignal() # Signal to be emitted when hotend temp is changed hotendTemperaturesChanged = pyqtSignal() # Signal to be emitted when target hotend temp is changed targetHotendTemperaturesChanged = pyqtSignal() # Signal to be emitted when head position is changed (x,y,z) headPositionChanged = pyqtSignal() # Signal to be emitted when either of the material ids is changed materialIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) # Signal to be emitted when either of the hotend ids is changed hotendIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) # Signal that is emitted every time connection state is changed. # it also sends it's own device_id (for convenience sake) connectionStateChanged = pyqtSignal(str) connectionTextChanged = pyqtSignal() timeElapsedChanged = pyqtSignal() timeTotalChanged = pyqtSignal() jobStateChanged = pyqtSignal() jobNameChanged = pyqtSignal() errorTextChanged = pyqtSignal() acceptsCommandsChanged = pyqtSignal() printerStateChanged = pyqtSignal() printerTypeChanged = pyqtSignal() # Signal to be emitted when some drastic change occurs in the remaining time (not when the time just passes on normally). preheatBedRemainingTimeChanged = pyqtSignal() @pyqtProperty(QObject, constant=True) def monitorItem(self): # Note that we specifically only check if the monitor component is created. # It could be that it failed to actually create the qml item! If we check if the item was created, it will try to # create the item (and fail) every time. if not self._monitor_component: self._createMonitorViewFromQML() return self._monitor_item def _createMonitorViewFromQML(self): path = QUrl.fromLocalFile(self._monitor_view_qml_path) # Because of garbage collection we need to keep this referenced by python. self._monitor_component = QQmlComponent(Application.getInstance()._engine, path) # Check if the context was already requested before (Printer output device might have multiple items in the future) if self._qml_context is None: self._qml_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._qml_context.setContextProperty("OutputDevice", self) self._monitor_item = self._monitor_component.create(self._qml_context) if self._monitor_item is None: Logger.log("e", "QQmlComponent status %s", self._monitor_component.status()) Logger.log("e", "QQmlComponent error string %s", self._monitor_component.errorString()) @pyqtProperty(str, notify=printerTypeChanged) def printerType(self): return self._printer_type @pyqtProperty(str, notify=printerStateChanged) def printerState(self): return self._printer_state @pyqtProperty(str, notify = jobStateChanged) def jobState(self): return self._job_state def _updatePrinterType(self, printer_type): if self._printer_type != printer_type: self._printer_type = printer_type self.printerTypeChanged.emit() def _updatePrinterState(self, printer_state): if self._printer_state != printer_state: self._printer_state = printer_state self.printerStateChanged.emit() def _updateJobState(self, job_state): if self._job_state != job_state: self._job_state = job_state self.jobStateChanged.emit() @pyqtSlot(str) def setJobState(self, job_state): self._setJobState(job_state) def _setJobState(self, job_state): Logger.log("w", "_setJobState is not implemented by this output device") @pyqtSlot() def startCamera(self): self._camera_active = True self._startCamera() def _startCamera(self): Logger.log("w", "_startCamera is not implemented by this output device") @pyqtSlot() def stopCamera(self): self._camera_active = False self._stopCamera() def _stopCamera(self): Logger.log("w", "_stopCamera is not implemented by this output device") @pyqtProperty(str, notify = jobNameChanged) def jobName(self): return self._job_name def setJobName(self, name): if self._job_name != name: self._job_name = name self.jobNameChanged.emit() ## Gives a human-readable address where the device can be found. @pyqtProperty(str, constant = True) def address(self): Logger.log("w", "address is not implemented by this output device.") ## A human-readable name for the device. @pyqtProperty(str, constant = True) def name(self): Logger.log("w", "name is not implemented by this output device.") return "" @pyqtProperty(str, notify = errorTextChanged) def errorText(self): return self._error_text ## Set the error-text that is shown in the print monitor in case of an error def setErrorText(self, error_text): if self._error_text != error_text: self._error_text = error_text self.errorTextChanged.emit() @pyqtProperty(bool, notify = acceptsCommandsChanged) def acceptsCommands(self): return self._accepts_commands ## Set a flag to signal the UI that the printer is not (yet) ready to receive commands def setAcceptsCommands(self, accepts_commands): if self._accepts_commands != accepts_commands: self._accepts_commands = accepts_commands self.acceptsCommandsChanged.emit() ## Get the bed temperature of the bed (if any) # This function is "final" (do not re-implement) # /sa _getBedTemperature implementation function @pyqtProperty(float, notify = bedTemperatureChanged) def bedTemperature(self): return self._bed_temperature ## Set the (target) bed temperature # This function is "final" (do not re-implement) # /param temperature new target temperature of the bed (in deg C) # /sa _setTargetBedTemperature implementation function @pyqtSlot(int) def setTargetBedTemperature(self, temperature): self._setTargetBedTemperature(temperature) if self._target_bed_temperature != temperature: self._target_bed_temperature = temperature self.targetBedTemperatureChanged.emit() ## The total duration of the time-out to pre-heat the bed, in seconds. # # \return The duration of the time-out to pre-heat the bed, in seconds. @pyqtProperty(int, constant = True) def preheatBedTimeout(self): return self._preheat_bed_timeout ## The remaining duration of the pre-heating of the bed. # # This is formatted in M:SS format. # \return The duration of the time-out to pre-heat the bed, formatted. @pyqtProperty(str, notify = preheatBedRemainingTimeChanged) def preheatBedRemainingTime(self): if not self._preheat_bed_timer.isActive(): return "" period = self._preheat_bed_timer.remainingTime() if period <= 0: return "" minutes, period = divmod(period, 60000) #60000 milliseconds in a minute. seconds, _ = divmod(period, 1000) #1000 milliseconds in a second. if minutes <= 0 and seconds <= 0: return "" return "%d:%02d" % (minutes, seconds) ## Time the print has been printing. # Note that timeTotal - timeElapsed should give time remaining. @pyqtProperty(float, notify = timeElapsedChanged) def timeElapsed(self): return self._time_elapsed ## Total time of the print # Note that timeTotal - timeElapsed should give time remaining. @pyqtProperty(float, notify=timeTotalChanged) def timeTotal(self): return self._time_total @pyqtSlot(float) def setTimeTotal(self, new_total): if self._time_total != new_total: self._time_total = new_total self.timeTotalChanged.emit() @pyqtSlot(float) def setTimeElapsed(self, time_elapsed): if self._time_elapsed != time_elapsed: self._time_elapsed = time_elapsed self.timeElapsedChanged.emit() ## Home the head of the connected printer # This function is "final" (do not re-implement) # /sa _homeHead implementation function @pyqtSlot() def homeHead(self): self._homeHead() ## Home the head of the connected printer # This is an implementation function and should be overriden by children. def _homeHead(self): Logger.log("w", "_homeHead is not implemented by this output device") ## Home the bed of the connected printer # This function is "final" (do not re-implement) # /sa _homeBed implementation function @pyqtSlot() def homeBed(self): self._homeBed() ## Home the bed of the connected printer # This is an implementation function and should be overriden by children. # /sa homeBed def _homeBed(self): Logger.log("w", "_homeBed is not implemented by this output device") ## Protected setter for the bed temperature of the connected printer (if any). # /parameter temperature Temperature bed needs to go to (in deg celsius) # /sa setTargetBedTemperature def _setTargetBedTemperature(self, temperature): Logger.log("w", "_setTargetBedTemperature is not implemented by this output device") ## Pre-heats the heated bed of the printer. # # \param temperature The temperature to heat the bed to, in degrees # Celsius. # \param duration How long the bed should stay warm, in seconds. @pyqtSlot(float, float) def preheatBed(self, temperature, duration): Logger.log("w", "preheatBed is not implemented by this output device.") ## Cancels pre-heating the heated bed of the printer. # # If the bed is not pre-heated, nothing happens. @pyqtSlot() def cancelPreheatBed(self): Logger.log("w", "cancelPreheatBed is not implemented by this output device.") ## Protected setter for the current bed temperature. # This simply sets the bed temperature, but ensures that a signal is emitted. # /param temperature temperature of the bed. def _setBedTemperature(self, temperature): if self._bed_temperature != temperature: self._bed_temperature = temperature self.bedTemperatureChanged.emit() ## Get the target bed temperature if connected printer (if any) @pyqtProperty(int, notify = targetBedTemperatureChanged) def targetBedTemperature(self): return self._target_bed_temperature ## Set the (target) hotend temperature # This function is "final" (do not re-implement) # /param index the index of the hotend that needs to change temperature # /param temperature The temperature it needs to change to (in deg celsius). # /sa _setTargetHotendTemperature implementation function @pyqtSlot(int, int) def setTargetHotendTemperature(self, index, temperature): self._setTargetHotendTemperature(index, temperature) if self._target_hotend_temperatures[index] != temperature: self._target_hotend_temperatures[index] = temperature self.targetHotendTemperaturesChanged.emit() ## Implementation function of setTargetHotendTemperature. # /param index Index of the hotend to set the temperature of # /param temperature Temperature to set the hotend to (in deg C) # /sa setTargetHotendTemperature def _setTargetHotendTemperature(self, index, temperature): Logger.log("w", "_setTargetHotendTemperature is not implemented by this output device") @pyqtProperty("QVariantList", notify = targetHotendTemperaturesChanged) def targetHotendTemperatures(self): return self._target_hotend_temperatures @pyqtProperty("QVariantList", notify = hotendTemperaturesChanged) def hotendTemperatures(self): return self._hotend_temperatures ## Protected setter for the current hotend temperature. # This simply sets the hotend temperature, but ensures that a signal is emitted. # /param index Index of the hotend # /param temperature temperature of the hotend (in deg C) def _setHotendTemperature(self, index, temperature): if self._hotend_temperatures[index] != temperature: self._hotend_temperatures[index] = temperature self.hotendTemperaturesChanged.emit() @pyqtProperty("QVariantList", notify = materialIdChanged) def materialIds(self): return self._material_ids @pyqtProperty("QVariantList", notify = materialIdChanged) def materialNames(self): result = [] for material_id in self._material_ids: if material_id is None: result.append(i18n_catalog.i18nc("@item:material", "No material loaded")) continue containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_id) if containers: result.append(containers[0].getName()) else: result.append(i18n_catalog.i18nc("@item:material", "Unknown material")) return result ## List of the colours of the currently loaded materials. # # The list is in order of extruders. If there is no material in an # extruder, the colour is shown as transparent. # # The colours are returned in hex-format AARRGGBB or RRGGBB # (e.g. #800000ff for transparent blue or #00ff00 for pure green). @pyqtProperty("QVariantList", notify = materialIdChanged) def materialColors(self): result = [] for material_id in self._material_ids: if material_id is None: result.append("#00000000") #No material. continue containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_id) if containers: result.append(containers[0].getMetaDataEntry("color_code")) else: result.append("#00000000") #Unknown material. return result ## Protected setter for the current material id. # /param index Index of the extruder # /param material_id id of the material def _setMaterialId(self, index, material_id): if material_id and material_id != "" and material_id != self._material_ids[index]: Logger.log("d", "Setting material id of hotend %d to %s" % (index, material_id)) self._material_ids[index] = material_id self.materialIdChanged.emit(index, material_id) @pyqtProperty("QVariantList", notify = hotendIdChanged) def hotendIds(self): return self._hotend_ids ## Protected setter for the current hotend id. # /param index Index of the extruder # /param hotend_id id of the hotend def _setHotendId(self, index, hotend_id): if hotend_id and hotend_id != self._hotend_ids[index]: Logger.log("d", "Setting hotend id of hotend %d to %s" % (index, hotend_id)) self._hotend_ids[index] = hotend_id self.hotendIdChanged.emit(index, hotend_id) elif not hotend_id: Logger.log("d", "Removing hotend id of hotend %d.", index) self._hotend_ids[index] = None self.hotendIdChanged.emit(index, None) ## Let the user decide if the hotends and/or material should be synced with the printer # NB: the UX needs to be implemented by the plugin def materialHotendChangedMessage(self, callback): Logger.log("w", "materialHotendChangedMessage needs to be implemented, returning 'Yes'") callback(QMessageBox.Yes) ## Attempt to establish connection def connect(self): raise NotImplementedError("connect needs to be implemented") ## Attempt to close the connection def close(self): raise NotImplementedError("close needs to be implemented") @pyqtProperty(bool, notify = connectionStateChanged) def connectionState(self): return self._connection_state ## Set the connection state of this output device. # /param connection_state ConnectionState enum. def setConnectionState(self, connection_state): if self._connection_state != connection_state: self._connection_state = connection_state self.connectionStateChanged.emit(self._id) @pyqtProperty(str, notify = connectionTextChanged) def connectionText(self): return self._connection_text ## Set a text that is shown on top of the print monitor tab def setConnectionText(self, connection_text): if self._connection_text != connection_text: self._connection_text = connection_text self.connectionTextChanged.emit() ## Ensure that close gets called when object is destroyed def __del__(self): self.close() ## Get the x position of the head. # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headX(self): return self._head_x ## Get the y position of the head. # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headY(self): return self._head_y ## Get the z position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headZ(self): return self._head_z ## Update the saved position of the head # This function should be called when a new position for the head is received. def _updateHeadPosition(self, x, y ,z): position_changed = False if self._head_x != x: self._head_x = x position_changed = True if self._head_y != y: self._head_y = y position_changed = True if self._head_z != z: self._head_z = z position_changed = True if position_changed: self.headPositionChanged.emit() ## Set the position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement) # /param x new x location of the head. # /param y new y location of the head. # /param z new z location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadPosition implementation function @pyqtSlot("long", "long", "long") @pyqtSlot("long", "long", "long", "long") def setHeadPosition(self, x, y, z, speed = 3000): self._setHeadPosition(x, y , z, speed) ## Set the X position of the head. # This function is "final" (do not re-implement) # /param x x position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadx implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadX(self, x, speed = 3000): self._setHeadX(x, speed) ## Set the Y position of the head. # This function is "final" (do not re-implement) # /param y y position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadY implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadY(self, y, speed = 3000): self._setHeadY(y, speed) ## Set the Z position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement) # /param z z position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadZ implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadZ(self, z, speed = 3000): self._setHeadY(z, speed) ## Move the head of the printer. # Note that this is a relative move. If you want to move the head to a specific position you can use # setHeadPosition # This function is "final" (do not re-implement) # /param x distance in x to move # /param y distance in y to move # /param z distance in z to move # /param speed Speed by which it needs to move (in mm/minute) # /sa _moveHead implementation function @pyqtSlot("long", "long", "long") @pyqtSlot("long", "long", "long", "long") def moveHead(self, x = 0, y = 0, z = 0, speed = 3000): self._moveHead(x, y, z, speed) ## Implementation function of moveHead. # /param x distance in x to move # /param y distance in y to move # /param z distance in z to move # /param speed Speed by which it needs to move (in mm/minute) # /sa moveHead def _moveHead(self, x, y, z, speed): Logger.log("w", "_moveHead is not implemented by this output device") ## Implementation function of setHeadPosition. # /param x new x location of the head. # /param y new y location of the head. # /param z new z location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa setHeadPosition def _setHeadPosition(self, x, y, z, speed): Logger.log("w", "_setHeadPosition is not implemented by this output device") ## Implementation function of setHeadX. # /param x new x location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa setHeadX def _setHeadX(self, x, speed): Logger.log("w", "_setHeadX is not implemented by this output device") ## Implementation function of setHeadY. # /param y new y location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadY def _setHeadY(self, y, speed): Logger.log("w", "_setHeadY is not implemented by this output device") ## Implementation function of setHeadZ. # /param z new z location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadZ def _setHeadZ(self, z, speed): Logger.log("w", "_setHeadZ is not implemented by this output device") ## Get the progress of any currently active process. # This function is "final" (do not re-implement) # /sa _getProgress # /returns float progress of the process. -1 indicates that there is no process. @pyqtProperty(float, notify = progressChanged) def progress(self): return self._progress ## Set the progress of any currently active process # /param progress Progress of the process. def setProgress(self, progress): if self._progress != progress: self._progress = progress self.progressChanged.emit()
class PrinterOutputDevice(QObject, OutputDevice): def __init__(self, device_id, parent = None): super().__init__(device_id = device_id, parent = parent) self._container_registry = ContainerRegistry.getInstance() self._target_bed_temperature = 0 self._bed_temperature = 0 self._num_extruders = 1 self._hotend_temperatures = [0] * self._num_extruders self._target_hotend_temperatures = [0] * self._num_extruders self._material_ids = [""] * self._num_extruders self._hotend_ids = [""] * self._num_extruders self._progress = 0 self._head_x = 0 self._head_y = 0 self._head_z = 0 self._connection_state = ConnectionState.closed self._connection_text = "" self._time_elapsed = 0 self._time_total = 0 self._job_state = "" self._job_name = "" self._error_text = "" self._accepts_commands = True self._preheat_bed_timeout = 900 # Default time-out for pre-heating the bed, in seconds. self._preheat_bed_timer = QTimer() # Timer that tracks how long to preheat still. self._preheat_bed_timer.setSingleShot(True) self._preheat_bed_timer.timeout.connect(self.cancelPreheatBed) self._printer_state = "" self._printer_type = "unknown" self._camera_active = False self._monitor_view_qml_path = "" self._monitor_component = None self._monitor_item = None self._control_view_qml_path = "" self._control_component = None self._control_item = None self._qml_context = None def requestWrite(self, nodes, file_name = None, filter_by_machine = False, file_handler = None): raise NotImplementedError("requestWrite needs to be implemented") ## Signals # Signal to be emitted when bed temp is changed bedTemperatureChanged = pyqtSignal() # Signal to be emitted when target bed temp is changed targetBedTemperatureChanged = pyqtSignal() # Signal when the progress is changed (usually when this output device is printing / sending lots of data) progressChanged = pyqtSignal() # Signal to be emitted when hotend temp is changed hotendTemperaturesChanged = pyqtSignal() # Signal to be emitted when target hotend temp is changed targetHotendTemperaturesChanged = pyqtSignal() # Signal to be emitted when head position is changed (x,y,z) headPositionChanged = pyqtSignal() # Signal to be emitted when either of the material ids is changed materialIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) # Signal to be emitted when either of the hotend ids is changed hotendIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) # Signal that is emitted every time connection state is changed. # it also sends it's own device_id (for convenience sake) connectionStateChanged = pyqtSignal(str) connectionTextChanged = pyqtSignal() timeElapsedChanged = pyqtSignal() timeTotalChanged = pyqtSignal() jobStateChanged = pyqtSignal() jobNameChanged = pyqtSignal() errorTextChanged = pyqtSignal() acceptsCommandsChanged = pyqtSignal() printerStateChanged = pyqtSignal() printerTypeChanged = pyqtSignal() # Signal to be emitted when some drastic change occurs in the remaining time (not when the time just passes on normally). preheatBedRemainingTimeChanged = pyqtSignal() @pyqtProperty(QObject, constant=True) def monitorItem(self): # Note that we specifically only check if the monitor component is created. # It could be that it failed to actually create the qml item! If we check if the item was created, it will try to # create the item (and fail) every time. if not self._monitor_component: self._createMonitorViewFromQML() return self._monitor_item @pyqtProperty(QObject, constant=True) def controlItem(self): if not self._control_component: self._createControlViewFromQML() return self._control_item def _createControlViewFromQML(self): if not self._control_view_qml_path: return path = QUrl.fromLocalFile(self._control_view_qml_path) # Because of garbage collection we need to keep this referenced by python. self._control_component = QQmlComponent(Application.getInstance()._engine, path) # Check if the context was already requested before (Printer output device might have multiple items in the future) if self._qml_context is None: self._qml_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._qml_context.setContextProperty("OutputDevice", self) self._control_item = self._control_component.create(self._qml_context) if self._control_item is None: Logger.log("e", "QQmlComponent status %s", self._control_component.status()) Logger.log("e", "QQmlComponent error string %s", self._control_component.errorString()) def _createMonitorViewFromQML(self): if not self._monitor_view_qml_path: return path = QUrl.fromLocalFile(self._monitor_view_qml_path) # Because of garbage collection we need to keep this referenced by python. self._monitor_component = QQmlComponent(Application.getInstance()._engine, path) # Check if the context was already requested before (Printer output device might have multiple items in the future) if self._qml_context is None: self._qml_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._qml_context.setContextProperty("OutputDevice", self) self._monitor_item = self._monitor_component.create(self._qml_context) if self._monitor_item is None: Logger.log("e", "QQmlComponent status %s", self._monitor_component.status()) Logger.log("e", "QQmlComponent error string %s", self._monitor_component.errorString()) @pyqtProperty(str, notify=printerTypeChanged) def printerType(self): return self._printer_type @pyqtProperty(str, notify=printerStateChanged) def printerState(self): return self._printer_state @pyqtProperty(str, notify = jobStateChanged) def jobState(self): return self._job_state def _updatePrinterType(self, printer_type): if self._printer_type != printer_type: self._printer_type = printer_type self.printerTypeChanged.emit() def _updatePrinterState(self, printer_state): if self._printer_state != printer_state: self._printer_state = printer_state self.printerStateChanged.emit() def _updateJobState(self, job_state): if self._job_state != job_state: self._job_state = job_state self.jobStateChanged.emit() @pyqtSlot(str) def setJobState(self, job_state): self._setJobState(job_state) def _setJobState(self, job_state): Logger.log("w", "_setJobState is not implemented by this output device") @pyqtSlot() def startCamera(self): self._camera_active = True self._startCamera() def _startCamera(self): Logger.log("w", "_startCamera is not implemented by this output device") @pyqtSlot() def stopCamera(self): self._camera_active = False self._stopCamera() def _stopCamera(self): Logger.log("w", "_stopCamera is not implemented by this output device") @pyqtProperty(str, notify = jobNameChanged) def jobName(self): return self._job_name def setJobName(self, name): if self._job_name != name: self._job_name = name self.jobNameChanged.emit() ## Gives a human-readable address where the device can be found. @pyqtProperty(str, constant = True) def address(self): Logger.log("w", "address is not implemented by this output device.") ## A human-readable name for the device. @pyqtProperty(str, constant = True) def name(self): Logger.log("w", "name is not implemented by this output device.") return "" @pyqtProperty(str, notify = errorTextChanged) def errorText(self): return self._error_text ## Set the error-text that is shown in the print monitor in case of an error def setErrorText(self, error_text): if self._error_text != error_text: self._error_text = error_text self.errorTextChanged.emit() @pyqtProperty(bool, notify = acceptsCommandsChanged) def acceptsCommands(self): return self._accepts_commands ## Set a flag to signal the UI that the printer is not (yet) ready to receive commands def setAcceptsCommands(self, accepts_commands): if self._accepts_commands != accepts_commands: self._accepts_commands = accepts_commands self.acceptsCommandsChanged.emit() ## Get the bed temperature of the bed (if any) # This function is "final" (do not re-implement) # /sa _getBedTemperature implementation function @pyqtProperty(float, notify = bedTemperatureChanged) def bedTemperature(self): return self._bed_temperature ## Set the (target) bed temperature # This function is "final" (do not re-implement) # /param temperature new target temperature of the bed (in deg C) # /sa _setTargetBedTemperature implementation function @pyqtSlot(int) def setTargetBedTemperature(self, temperature): self._setTargetBedTemperature(temperature) if self._target_bed_temperature != temperature: self._target_bed_temperature = temperature self.targetBedTemperatureChanged.emit() ## The total duration of the time-out to pre-heat the bed, in seconds. # # \return The duration of the time-out to pre-heat the bed, in seconds. @pyqtProperty(int, constant = True) def preheatBedTimeout(self): return self._preheat_bed_timeout ## The remaining duration of the pre-heating of the bed. # # This is formatted in M:SS format. # \return The duration of the time-out to pre-heat the bed, formatted. @pyqtProperty(str, notify = preheatBedRemainingTimeChanged) def preheatBedRemainingTime(self): if not self._preheat_bed_timer.isActive(): return "" period = self._preheat_bed_timer.remainingTime() if period <= 0: return "" minutes, period = divmod(period, 60000) #60000 milliseconds in a minute. seconds, _ = divmod(period, 1000) #1000 milliseconds in a second. if minutes <= 0 and seconds <= 0: return "" return "%d:%02d" % (minutes, seconds) ## Time the print has been printing. # Note that timeTotal - timeElapsed should give time remaining. @pyqtProperty(float, notify = timeElapsedChanged) def timeElapsed(self): return self._time_elapsed ## Total time of the print # Note that timeTotal - timeElapsed should give time remaining. @pyqtProperty(float, notify=timeTotalChanged) def timeTotal(self): return self._time_total @pyqtSlot(float) def setTimeTotal(self, new_total): if self._time_total != new_total: self._time_total = new_total self.timeTotalChanged.emit() @pyqtSlot(float) def setTimeElapsed(self, time_elapsed): if self._time_elapsed != time_elapsed: self._time_elapsed = time_elapsed self.timeElapsedChanged.emit() ## Home the head of the connected printer # This function is "final" (do not re-implement) # /sa _homeHead implementation function @pyqtSlot() def homeHead(self): self._homeHead() ## Home the head of the connected printer # This is an implementation function and should be overriden by children. def _homeHead(self): Logger.log("w", "_homeHead is not implemented by this output device") ## Home the bed of the connected printer # This function is "final" (do not re-implement) # /sa _homeBed implementation function @pyqtSlot() def homeBed(self): self._homeBed() ## Home the bed of the connected printer # This is an implementation function and should be overriden by children. # /sa homeBed def _homeBed(self): Logger.log("w", "_homeBed is not implemented by this output device") ## Protected setter for the bed temperature of the connected printer (if any). # /parameter temperature Temperature bed needs to go to (in deg celsius) # /sa setTargetBedTemperature def _setTargetBedTemperature(self, temperature): Logger.log("w", "_setTargetBedTemperature is not implemented by this output device") ## Pre-heats the heated bed of the printer. # # \param temperature The temperature to heat the bed to, in degrees # Celsius. # \param duration How long the bed should stay warm, in seconds. @pyqtSlot(float, float) def preheatBed(self, temperature, duration): Logger.log("w", "preheatBed is not implemented by this output device.") ## Cancels pre-heating the heated bed of the printer. # # If the bed is not pre-heated, nothing happens. @pyqtSlot() def cancelPreheatBed(self): Logger.log("w", "cancelPreheatBed is not implemented by this output device.") ## Protected setter for the current bed temperature. # This simply sets the bed temperature, but ensures that a signal is emitted. # /param temperature temperature of the bed. def _setBedTemperature(self, temperature): if self._bed_temperature != temperature: self._bed_temperature = temperature self.bedTemperatureChanged.emit() ## Get the target bed temperature if connected printer (if any) @pyqtProperty(int, notify = targetBedTemperatureChanged) def targetBedTemperature(self): return self._target_bed_temperature ## Set the (target) hotend temperature # This function is "final" (do not re-implement) # /param index the index of the hotend that needs to change temperature # /param temperature The temperature it needs to change to (in deg celsius). # /sa _setTargetHotendTemperature implementation function @pyqtSlot(int, int) def setTargetHotendTemperature(self, index, temperature): self._setTargetHotendTemperature(index, temperature) if self._target_hotend_temperatures[index] != temperature: self._target_hotend_temperatures[index] = temperature self.targetHotendTemperaturesChanged.emit() ## Implementation function of setTargetHotendTemperature. # /param index Index of the hotend to set the temperature of # /param temperature Temperature to set the hotend to (in deg C) # /sa setTargetHotendTemperature def _setTargetHotendTemperature(self, index, temperature): Logger.log("w", "_setTargetHotendTemperature is not implemented by this output device") @pyqtProperty("QVariantList", notify = targetHotendTemperaturesChanged) def targetHotendTemperatures(self): return self._target_hotend_temperatures @pyqtProperty("QVariantList", notify = hotendTemperaturesChanged) def hotendTemperatures(self): return self._hotend_temperatures ## Protected setter for the current hotend temperature. # This simply sets the hotend temperature, but ensures that a signal is emitted. # /param index Index of the hotend # /param temperature temperature of the hotend (in deg C) def _setHotendTemperature(self, index, temperature): if self._hotend_temperatures[index] != temperature: self._hotend_temperatures[index] = temperature self.hotendTemperaturesChanged.emit() @pyqtProperty("QVariantList", notify = materialIdChanged) def materialIds(self): return self._material_ids @pyqtProperty("QVariantList", notify = materialIdChanged) def materialNames(self): result = [] for material_id in self._material_ids: if material_id is None: result.append(i18n_catalog.i18nc("@item:material", "No material loaded")) continue containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_id) if containers: result.append(containers[0].getName()) else: result.append(i18n_catalog.i18nc("@item:material", "Unknown material")) return result ## List of the colours of the currently loaded materials. # # The list is in order of extruders. If there is no material in an # extruder, the colour is shown as transparent. # # The colours are returned in hex-format AARRGGBB or RRGGBB # (e.g. #800000ff for transparent blue or #00ff00 for pure green). @pyqtProperty("QVariantList", notify = materialIdChanged) def materialColors(self): result = [] for material_id in self._material_ids: if material_id is None: result.append("#00000000") #No material. continue containers = self._container_registry.findInstanceContainers(type = "material", GUID = material_id) if containers: result.append(containers[0].getMetaDataEntry("color_code")) else: result.append("#00000000") #Unknown material. return result ## Protected setter for the current material id. # /param index Index of the extruder # /param material_id id of the material def _setMaterialId(self, index, material_id): if material_id and material_id != "" and material_id != self._material_ids[index]: Logger.log("d", "Setting material id of hotend %d to %s" % (index, material_id)) self._material_ids[index] = material_id self.materialIdChanged.emit(index, material_id) @pyqtProperty("QVariantList", notify = hotendIdChanged) def hotendIds(self): return self._hotend_ids ## Protected setter for the current hotend id. # /param index Index of the extruder # /param hotend_id id of the hotend def _setHotendId(self, index, hotend_id): if hotend_id and hotend_id != self._hotend_ids[index]: Logger.log("d", "Setting hotend id of hotend %d to %s" % (index, hotend_id)) self._hotend_ids[index] = hotend_id self.hotendIdChanged.emit(index, hotend_id) elif not hotend_id: Logger.log("d", "Removing hotend id of hotend %d.", index) self._hotend_ids[index] = None self.hotendIdChanged.emit(index, None) ## Let the user decide if the hotends and/or material should be synced with the printer # NB: the UX needs to be implemented by the plugin def materialHotendChangedMessage(self, callback): Logger.log("w", "materialHotendChangedMessage needs to be implemented, returning 'Yes'") callback(QMessageBox.Yes) ## Attempt to establish connection def connect(self): raise NotImplementedError("connect needs to be implemented") ## Attempt to close the connection def close(self): raise NotImplementedError("close needs to be implemented") @pyqtProperty(bool, notify = connectionStateChanged) def connectionState(self): return self._connection_state ## Set the connection state of this output device. # /param connection_state ConnectionState enum. def setConnectionState(self, connection_state): if self._connection_state != connection_state: self._connection_state = connection_state self.connectionStateChanged.emit(self._id) @pyqtProperty(str, notify = connectionTextChanged) def connectionText(self): return self._connection_text ## Set a text that is shown on top of the print monitor tab def setConnectionText(self, connection_text): if self._connection_text != connection_text: self._connection_text = connection_text self.connectionTextChanged.emit() ## Ensure that close gets called when object is destroyed def __del__(self): self.close() ## Get the x position of the head. # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headX(self): return self._head_x ## Get the y position of the head. # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headY(self): return self._head_y ## Get the z position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement) @pyqtProperty(float, notify = headPositionChanged) def headZ(self): return self._head_z ## Update the saved position of the head # This function should be called when a new position for the head is received. def _updateHeadPosition(self, x, y ,z): position_changed = False if self._head_x != x: self._head_x = x position_changed = True if self._head_y != y: self._head_y = y position_changed = True if self._head_z != z: self._head_z = z position_changed = True if position_changed: self.headPositionChanged.emit() ## Set the position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement) # /param x new x location of the head. # /param y new y location of the head. # /param z new z location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadPosition implementation function @pyqtSlot("long", "long", "long") @pyqtSlot("long", "long", "long", "long") def setHeadPosition(self, x, y, z, speed = 3000): self._setHeadPosition(x, y , z, speed) ## Set the X position of the head. # This function is "final" (do not re-implement) # /param x x position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadx implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadX(self, x, speed = 3000): self._setHeadX(x, speed) ## Set the Y position of the head. # This function is "final" (do not re-implement) # /param y y position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadY implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadY(self, y, speed = 3000): self._setHeadY(y, speed) ## Set the Z position of the head. # In some machines it's actually the bed that moves. For convenience sake we simply see it all as head movements. # This function is "final" (do not re-implement) # /param z z position head needs to move to. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadZ implementation function @pyqtSlot("long") @pyqtSlot("long", "long") def setHeadZ(self, z, speed = 3000): self._setHeadZ(z, speed) ## Move the head of the printer. # Note that this is a relative move. If you want to move the head to a specific position you can use # setHeadPosition # This function is "final" (do not re-implement) # /param x distance in x to move # /param y distance in y to move # /param z distance in z to move # /param speed Speed by which it needs to move (in mm/minute) # /sa _moveHead implementation function @pyqtSlot("long", "long", "long") @pyqtSlot("long", "long", "long", "long") def moveHead(self, x = 0, y = 0, z = 0, speed = 3000): self._moveHead(x, y, z, speed) ## Implementation function of moveHead. # /param x distance in x to move # /param y distance in y to move # /param z distance in z to move # /param speed Speed by which it needs to move (in mm/minute) # /sa moveHead def _moveHead(self, x, y, z, speed): Logger.log("w", "_moveHead is not implemented by this output device") ## Implementation function of setHeadPosition. # /param x new x location of the head. # /param y new y location of the head. # /param z new z location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa setHeadPosition def _setHeadPosition(self, x, y, z, speed): Logger.log("w", "_setHeadPosition is not implemented by this output device") ## Implementation function of setHeadX. # /param x new x location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa setHeadX def _setHeadX(self, x, speed): Logger.log("w", "_setHeadX is not implemented by this output device") ## Implementation function of setHeadY. # /param y new y location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadY def _setHeadY(self, y, speed): Logger.log("w", "_setHeadY is not implemented by this output device") ## Implementation function of setHeadZ. # /param z new z location of the head. # /param speed Speed by which it needs to move (in mm/minute) # /sa _setHeadZ def _setHeadZ(self, z, speed): Logger.log("w", "_setHeadZ is not implemented by this output device") ## Get the progress of any currently active process. # This function is "final" (do not re-implement) # /sa _getProgress # /returns float progress of the process. -1 indicates that there is no process. @pyqtProperty(float, notify = progressChanged) def progress(self): return self._progress ## Set the progress of any currently active process # /param progress Progress of the process. def setProgress(self, progress): if self._progress != progress: self._progress = progress self.progressChanged.emit()
def ros_qml_main(): try: rospy.init_node('ros_qml', sys.argv) my_argv = rospy.myargv(sys.argv) signal.signal(signal.SIGINT, sigint_handler) app = QApplication(my_argv) engine = QQmlEngine() engine.quit.connect(app.quit) plugins_dir = QLibraryInfo.location(QLibraryInfo.PluginsPath) # ## Add QML extension modules and plugins to the path, including ourselves plugins_paths = rospack.get_manifest(THIS_PACKAGE).get_export( THIS_PACKAGE, 'plugins') for idx, p in enumerate(plugins_paths): # If a relative path provided, treat it as relative to Qt plugins dir if not os.path.isabs(p): plugins_paths[idx] = plugins_dir + '/' + p qml_paths = rospack.get_manifest(THIS_PACKAGE).get_export( THIS_PACKAGE, 'imports') deps = rospack.get_depends_on(THIS_PACKAGE) for d in deps: pp = rospack.get_manifest(d).get_export(THIS_PACKAGE, 'plugins') for idx, p in enumerate(pp): # If a relative path provided, treat it as relative to Qt plugins dir if not os.path.isabs(p): pp[idx] = plugins_dir + '/' + p plugins_paths += pp qp = rospack.get_manifest(d).get_export(THIS_PACKAGE, 'imports') qml_paths += qp for p in plugins_paths: engine.addPluginPath(p) for p in qml_paths: engine.addImportPath(p) qml_sys_path = QLibraryInfo.location(QLibraryInfo.ImportsPath) engine.addImportPath(qml_sys_path) # Somehow we need to set the path both with QQmlEngine and with environment, # commenting any of the two will lead to 'module not installed' error os.environ['QML2_IMPORT_PATH'] = ':'.join( qml_paths) + ':' + qml_sys_path comp = QQmlComponent(engine) if len(my_argv) > 1 and my_argv[1]: qml_url = my_argv[1] comp.loadUrl(QUrl(qml_url)) elif rospy.has_param('qml_url'): qml_url = rospy.get_param('qml_url') comp.loadUrl(QUrl(qml_url)) elif rospy.has_param('qml_description'): # experimental qml_description = rospy.get_param('qml_description') # FIXME that hangs for unknown reason comp.setData(QByteArray(qml_description), QUrl()) else: rospy.logfatal( 'Neither /qml_url nor /qml_description (experimental) parameter is present' ) sys.exit(1) if not comp.isReady(): sys.stderr.write(comp.errorString()) sys.exit(1) win = comp.create() if not win: rospy.logfatal('Your root item has to be a Window') sys.exit(1) engine.setIncubationController(win.incubationController()) if win: win.show() # Poll Python interpreter every 500ms timer = QTimer() timer.start(500) timer.timeout.connect(lambda: None) sys.exit(app.exec_()) except KeyboardInterrupt: pass except rospy.ROSInterruptException: pass
class PostProcessingPlugin(QObject, Extension): def __init__(self, parent = None): super().__init__(parent) self.addMenuItem(i18n_catalog.i18n("Modify G-Code"), self.showPopup) self._view = None # Loaded scripts are all scripts that can be used self._loaded_scripts = {} self._script_labels = {} # Script list contains instances of scripts in loaded_scripts. # There can be duplicates, which will be executed in sequence. self._script_list = [] self._selected_script_index = -1 Application.getInstance().getOutputDeviceManager().writeStarted.connect(self.execute) selectedIndexChanged = pyqtSignal() @pyqtProperty("QVariant", notify = selectedIndexChanged) def selectedScriptDefinitionId(self): try: return self._script_list[self._selected_script_index].getDefinitionId() except: return "" @pyqtProperty("QVariant", notify=selectedIndexChanged) def selectedScriptStackId(self): try: return self._script_list[self._selected_script_index].getStackId() except: return "" ## Execute all post-processing scripts on the gcode. def execute(self, output_device): scene = Application.getInstance().getController().getScene() if hasattr(scene, "gcode_list"): gcode_list = getattr(scene, "gcode_list") if gcode_list: if ";POSTPROCESSED" not in gcode_list[0]: for script in self._script_list: try: gcode_list = script.execute(gcode_list) except Exception: Logger.logException("e", "Exception in post-processing script.") if len(self._script_list): # Add comment to g-code if any changes were made. gcode_list[0] += ";POSTPROCESSED\n" setattr(scene, "gcode_list", gcode_list) else: Logger.log("e", "Already post processed") @pyqtSlot(int) def setSelectedScriptIndex(self, index): self._selected_script_index = index self.selectedIndexChanged.emit() @pyqtProperty(int, notify = selectedIndexChanged) def selectedScriptIndex(self): return self._selected_script_index @pyqtSlot(int, int) def moveScript(self, index, new_index): if new_index < 0 or new_index > len(self._script_list) - 1: return # nothing needs to be done else: # Magical switch code. self._script_list[new_index], self._script_list[index] = self._script_list[index], self._script_list[new_index] self.scriptListChanged.emit() self.selectedIndexChanged.emit() #Ensure that settings are updated ## Remove a script from the active script list by index. @pyqtSlot(int) def removeScriptByIndex(self, index): self._script_list.pop(index) if len(self._script_list) - 1 < self._selected_script_index: self._selected_script_index = len(self._script_list) - 1 self.scriptListChanged.emit() self.selectedIndexChanged.emit() # Ensure that settings are updated ## Load all scripts from provided path. # This should probably only be done on init. # \param path Path to check for scripts. def loadAllScripts(self, path): scripts = pkgutil.iter_modules(path = [path]) for loader, script_name, ispkg in scripts: # Iterate over all scripts. if script_name not in sys.modules: # Import module loaded_script = __import__("PostProcessingPlugin.scripts."+ script_name, fromlist = [script_name]) loaded_class = getattr(loaded_script, script_name) temp_object = loaded_class() Logger.log("d", "Begin loading of script: %s", script_name) try: setting_data = temp_object.getSettingData() if "name" in setting_data and "key" in setting_data: self._script_labels[setting_data["key"]] = setting_data["name"] self._loaded_scripts[setting_data["key"]] = loaded_class else: Logger.log("w", "Script %s.py has no name or key", script_name) self._script_labels[script_name] = script_name self._loaded_scripts[script_name] = loaded_class except AttributeError: Logger.log("e", "Script %s.py is not a recognised script type. Ensure it inherits Script", script_name) except NotImplementedError: Logger.log("e", "Script %s.py has no implemented settings", script_name) self.loadedScriptListChanged.emit() loadedScriptListChanged = pyqtSignal() @pyqtProperty("QVariantList", notify = loadedScriptListChanged) def loadedScriptList(self): return sorted(list(self._loaded_scripts.keys())) @pyqtSlot(str, result = str) def getScriptLabelByKey(self, key): return self._script_labels[key] scriptListChanged = pyqtSignal() @pyqtProperty("QVariantList", notify = scriptListChanged) def scriptList(self): script_list = [script.getSettingData()["key"] for script in self._script_list] return script_list @pyqtSlot(str) def addScriptToList(self, key): Logger.log("d", "Adding script %s to list.", key) new_script = self._loaded_scripts[key]() self._script_list.append(new_script) self.setSelectedScriptIndex(len(self._script_list) - 1) self.scriptListChanged.emit() ## Creates the view used by show popup. The view is saved because of the fairly aggressive garbage collection. def _createView(self): Logger.log("d", "Creating post processing plugin view.") ## Load all scripts in the scripts folder try: self.loadAllScripts(os.path.join(PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), "scripts")) except Exception as e: print("Exception occured", e) # TODO: Debug code (far to general catch. Remove this once done testing) path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("PostProcessingPlugin"), "PostProcessingPlugin.qml")) self._component = QQmlComponent(Application.getInstance()._engine, path) # We need access to engine (although technically we can't) self._context = QQmlContext(Application.getInstance()._engine.rootContext()) self._context.setContextProperty("manager", self) self._view = self._component.create(self._context) Logger.log("d", "Post processing view created.") Application.getInstance().addAdditionalComponent("saveButton", self._view.findChild(QObject, "postProcessingSaveAreaButton")) ## Show the (GUI) popup of the post processing plugin. def showPopup(self): if self._view is None: self._createView() self._view.show()
qmlRegisterType(BirthdayPartyAttached) qmlRegisterType(BirthdayParty, "People", 1, 0, "BirthdayParty", attachedProperties=BirthdayPartyAttached) qmlRegisterType(HappyBirthdaySong, "People", 1, 0, "HappyBirthdaySong") qmlRegisterType(ShoeDescription) qmlRegisterType(Person) qmlRegisterType(Boy, "People", 1, 0, "Boy") qmlRegisterType(Girl, "People", 1, 0, "Girl") engine = QQmlEngine() component = QQmlComponent(engine) component.setData(QML, QUrl()) party = component.create() if party is not None and party.host is not None: print("\"%s\" is having a birthday!" % party.host.name) if isinstance(party.host, Boy): print("He is inviting:") else: print("She is inviting:") for guest in party.guests: attached = qmlAttachedPropertiesObject(BirthdayParty, guest, False) if attached is not None: rsvpDate = attached.property('rsvp') else:
app = QCoreApplication(sys.argv) qmlRegisterType(BirthdayPartyAttached) qmlRegisterType(BirthdayParty, "People", 1, 0, "BirthdayParty", attachedProperties=BirthdayPartyAttached) qmlRegisterType(ShoeDescription) qmlRegisterType(Person) qmlRegisterType(Boy, "People", 1, 0, "Boy") qmlRegisterType(Girl, "People", 1, 0, "Girl") engine = QQmlEngine() component = QQmlComponent(engine) component.setData(QML, QUrl()) party = component.create() if party is not None and party.host is not None: print("\"%s\" is having a birthday!" % party.host.name) if isinstance(party.host, Boy): print("He is inviting:") else: print("She is inviting:") for ii in range(party.guestCount()): guest = party.guest(ii) attached = qmlAttachedPropertiesObject(BirthdayParty, guest, False) if attached is not None:
class WorkspaceDialog(QObject): showDialogSignal = pyqtSignal() def __init__(self, parent = None): super().__init__(parent) self._component = None self._context = None self._view = None self._qml_url = "WorkspaceDialog.qml" self._lock = threading.Lock() self._default_strategy = "override" self._result = {"machine": self._default_strategy, "quality_changes": self._default_strategy, "definition_changes": self._default_strategy, "material": self._default_strategy} self._visible = False self.showDialogSignal.connect(self.__show) self._has_quality_changes_conflict = False self._has_definition_changes_conflict = False self._has_machine_conflict = False self._has_material_conflict = False self._num_visible_settings = 0 self._num_user_settings = 0 self._active_mode = "" self._quality_name = "" self._num_settings_overriden_by_quality_changes = 0 self._quality_type = "" self._machine_name = "" self._machine_type = "" self._variant_type = "" self._material_labels = [] self._extruders = [] self._objects_on_plate = False machineConflictChanged = pyqtSignal() qualityChangesConflictChanged = pyqtSignal() definitionChangesConflictChanged = pyqtSignal() materialConflictChanged = pyqtSignal() numVisibleSettingsChanged = pyqtSignal() activeModeChanged = pyqtSignal() qualityNameChanged = pyqtSignal() numSettingsOverridenByQualityChangesChanged = pyqtSignal() qualityTypeChanged = pyqtSignal() machineNameChanged = pyqtSignal() materialLabelsChanged = pyqtSignal() objectsOnPlateChanged = pyqtSignal() numUserSettingsChanged = pyqtSignal() machineTypeChanged = pyqtSignal() variantTypeChanged = pyqtSignal() extrudersChanged = pyqtSignal() @pyqtProperty(str, notify=variantTypeChanged) def variantType(self): return self._variant_type def setVariantType(self, variant_type): if self._variant_type != variant_type: self._variant_type = variant_type self.variantTypeChanged.emit() @pyqtProperty(str, notify=machineTypeChanged) def machineType(self): return self._machine_type def setMachineType(self, machine_type): self._machine_type = machine_type self.machineTypeChanged.emit() def setNumUserSettings(self, num_user_settings): if self._num_user_settings != num_user_settings: self._num_user_settings = num_user_settings self.numVisibleSettingsChanged.emit() @pyqtProperty(int, notify=numUserSettingsChanged) def numUserSettings(self): return self._num_user_settings @pyqtProperty(bool, notify=objectsOnPlateChanged) def hasObjectsOnPlate(self): return self._objects_on_plate def setHasObjectsOnPlate(self, objects_on_plate): if self._objects_on_plate != objects_on_plate: self._objects_on_plate = objects_on_plate self.objectsOnPlateChanged.emit() @pyqtProperty("QVariantList", notify = materialLabelsChanged) def materialLabels(self): return self._material_labels def setMaterialLabels(self, material_labels): if self._material_labels != material_labels: self._material_labels = material_labels self.materialLabelsChanged.emit() @pyqtProperty("QVariantList", notify=extrudersChanged) def extruders(self): return self._extruders def setExtruders(self, extruders): if self._extruders != extruders: self._extruders = extruders self.extrudersChanged.emit() @pyqtProperty(str, notify = machineNameChanged) def machineName(self): return self._machine_name def setMachineName(self, machine_name): if self._machine_name != machine_name: self._machine_name = machine_name self.machineNameChanged.emit() @pyqtProperty(str, notify=qualityTypeChanged) def qualityType(self): return self._quality_type def setQualityType(self, quality_type): if self._quality_type != quality_type: self._quality_type = quality_type self.qualityTypeChanged.emit() @pyqtProperty(int, notify=numSettingsOverridenByQualityChangesChanged) def numSettingsOverridenByQualityChanges(self): return self._num_settings_overriden_by_quality_changes def setNumSettingsOverridenByQualityChanges(self, num_settings_overriden_by_quality_changes): self._num_settings_overriden_by_quality_changes = num_settings_overriden_by_quality_changes self.numSettingsOverridenByQualityChangesChanged.emit() @pyqtProperty(str, notify=qualityNameChanged) def qualityName(self): return self._quality_name def setQualityName(self, quality_name): if self._quality_name != quality_name: self._quality_name = quality_name self.qualityNameChanged.emit() @pyqtProperty(str, notify=activeModeChanged) def activeMode(self): return self._active_mode def setActiveMode(self, active_mode): if active_mode == 0: self._active_mode = i18n_catalog.i18nc("@title:tab", "Recommended") else: self._active_mode = i18n_catalog.i18nc("@title:tab", "Custom") self.activeModeChanged.emit() @pyqtProperty(int, constant = True) def totalNumberOfSettings(self): return len(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0].getAllKeys()) @pyqtProperty(int, notify = numVisibleSettingsChanged) def numVisibleSettings(self): return self._num_visible_settings def setNumVisibleSettings(self, num_visible_settings): if self._num_visible_settings != num_visible_settings: self._num_visible_settings = num_visible_settings self.numVisibleSettingsChanged.emit() @pyqtProperty(bool, notify = machineConflictChanged) def machineConflict(self): return self._has_machine_conflict @pyqtProperty(bool, notify=qualityChangesConflictChanged) def qualityChangesConflict(self): return self._has_quality_changes_conflict @pyqtProperty(bool, notify=definitionChangesConflictChanged) def definitionChangesConflict(self): return self._has_definition_changes_conflict @pyqtProperty(bool, notify=materialConflictChanged) def materialConflict(self): return self._has_material_conflict @pyqtSlot(str, str) def setResolveStrategy(self, key, strategy): if key in self._result: self._result[key] = strategy ## Close the backend: otherwise one could end up with "Slicing..." @pyqtSlot() def closeBackend(self): Application.getInstance().getBackend().close() def setMaterialConflict(self, material_conflict): if self._has_material_conflict != material_conflict: self._has_material_conflict = material_conflict self.materialConflictChanged.emit() def setMachineConflict(self, machine_conflict): if self._has_machine_conflict != machine_conflict: self._has_machine_conflict = machine_conflict self.machineConflictChanged.emit() def setQualityChangesConflict(self, quality_changes_conflict): if self._has_quality_changes_conflict != quality_changes_conflict: self._has_quality_changes_conflict = quality_changes_conflict self.qualityChangesConflictChanged.emit() def setDefinitionChangesConflict(self, definition_changes_conflict): if self._has_definition_changes_conflict != definition_changes_conflict: self._has_definition_changes_conflict = definition_changes_conflict self.definitionChangesConflictChanged.emit() def getResult(self): if "machine" in self._result and not self._has_machine_conflict: self._result["machine"] = None if "quality_changes" in self._result and not self._has_quality_changes_conflict: self._result["quality_changes"] = None if "definition_changes" in self._result and not self._has_definition_changes_conflict: self._result["definition_changes"] = None if "material" in self._result and not self._has_material_conflict: self._result["material"] = None return self._result def _createViewFromQML(self): path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("3MFReader"), 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) if self._view is None: Logger.log("c", "QQmlComponent status %s", self._component.status()) Logger.log("c", "QQmlComponent error string %s", self._component.errorString()) def show(self): # Emit signal so the right thread actually shows the view. if threading.current_thread() != threading.main_thread(): self._lock.acquire() # Reset the result self._result = {"machine": self._default_strategy, "quality_changes": self._default_strategy, "definition_changes": self._default_strategy, "material": self._default_strategy} self._visible = True self.showDialogSignal.emit() @pyqtSlot() ## Used to notify the dialog so the lock can be released. def notifyClosed(self): self._result = {} self._visible = False self._lock.release() def hide(self): self._visible = False self._lock.release() self._view.hide() @pyqtSlot() def onOkButtonClicked(self): self._view.hide() self.hide() @pyqtSlot() def onCancelButtonClicked(self): self._view.hide() self.hide() self._result = {} ## Block thread until the dialog is closed. def waitForClose(self): if self._visible: if threading.current_thread() != threading.main_thread(): self._lock.acquire() self._lock.release() else: # If this is not run from a separate thread, we need to ensure that the events are still processed. while self._visible: time.sleep(1 / 50) QCoreApplication.processEvents() # Ensure that the GUI does not freeze. def __show(self): if self._view is None: self._createViewFromQML() if self._view: self._view.show()
class PluginBrowser(QObject, Extension): def __init__(self, parent = None): super().__init__(parent) self.addMenuItem(i18n_catalog.i18n("Browse plugins"), self.browsePlugins) self._api_version = 1 self._api_url = "http://software.ultimaker.com/cura/v%s/" % self._api_version self._plugin_list_request = None self._download_plugin_request = None self._download_plugin_reply = None self._network_manager = None self._plugins_metadata = [] self._plugins_model = None self._qml_component = None self._qml_context = None self._dialog = None self._download_progress = 0 self._is_downloading = False self._request_header = [b"User-Agent", str.encode("%s - %s" % (Application.getInstance().getApplicationName(), Application.getInstance().getVersion()))] # Installed plugins are really installed after reboot. In order to prevent the user from downloading the # same file over and over again, we keep track of the upgraded plugins. self._newly_installed_plugin_ids = [] pluginsMetadataChanged = pyqtSignal() onDownloadProgressChanged = pyqtSignal() onIsDownloadingChanged = pyqtSignal() @pyqtProperty(bool, notify = onIsDownloadingChanged) def isDownloading(self): return self._is_downloading def browsePlugins(self): self._createNetworkManager() self.requestPluginList() if not self._dialog: self._createDialog() self._dialog.show() def requestPluginList(self): url = QUrl(self._api_url + "plugins") self._plugin_list_request = QNetworkRequest(url) self._plugin_list_request.setRawHeader(*self._request_header) self._network_manager.get(self._plugin_list_request) def _createDialog(self): Logger.log("d", "PluginBrowser") path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), "PluginBrowser.qml")) self._qml_component = QQmlComponent(Application.getInstance()._engine, path) # We need access to engine (although technically we can't) self._qml_context = QQmlContext(Application.getInstance()._engine.rootContext()) self._qml_context.setContextProperty("manager", self) self._dialog = self._qml_component.create(self._qml_context) if self._dialog is None: Logger.log("e", "QQmlComponent status %s", self._qml_component.status()) Logger.log("e", "QQmlComponent errorString %s", self._qml_component.errorString()) def setIsDownloading(self, is_downloading): if self._is_downloading != is_downloading: self._is_downloading = is_downloading self.onIsDownloadingChanged.emit() def _onDownloadPluginProgress(self, bytes_sent, bytes_total): if bytes_total > 0: new_progress = bytes_sent / bytes_total * 100 if new_progress > self._download_progress: self._download_progress = new_progress self.onDownloadProgressChanged.emit() self._download_progress = new_progress if new_progress == 100.0: self.setIsDownloading(False) self._download_plugin_reply.downloadProgress.disconnect(self._onDownloadPluginProgress) self._temp_plugin_file = tempfile.NamedTemporaryFile(suffix = ".curaplugin") self._temp_plugin_file.write(self._download_plugin_reply.readAll()) result = PluginRegistry.getInstance().installPlugin("file://" + self._temp_plugin_file.name) self._newly_installed_plugin_ids.append(result["id"]) self.pluginsMetadataChanged.emit() Application.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Plugin browser"), result["message"]) self._temp_plugin_file.close() # Plugin was installed, delete temp file @pyqtProperty(int, notify = onDownloadProgressChanged) def downloadProgress(self): return self._download_progress @pyqtSlot(str) def downloadAndInstallPlugin(self, url): Logger.log("i", "Attempting to download & install plugin from %s", url) url = QUrl(url) self._download_plugin_request = QNetworkRequest(url) self._download_plugin_request.setRawHeader(*self._request_header) self._download_plugin_reply = self._network_manager.get(self._download_plugin_request) self._download_progress = 0 self.setIsDownloading(True) self.onDownloadProgressChanged.emit() self._download_plugin_reply.downloadProgress.connect(self._onDownloadPluginProgress) @pyqtProperty(QObject, notify=pluginsMetadataChanged) def pluginsModel(self): if self._plugins_model is None: self._plugins_model = ListModel() self._plugins_model.addRoleName(Qt.UserRole + 1, "name") self._plugins_model.addRoleName(Qt.UserRole + 2, "version") self._plugins_model.addRoleName(Qt.UserRole + 3, "short_description") self._plugins_model.addRoleName(Qt.UserRole + 4, "author") self._plugins_model.addRoleName(Qt.UserRole + 5, "already_installed") self._plugins_model.addRoleName(Qt.UserRole + 6, "file_location") self._plugins_model.addRoleName(Qt.UserRole + 7, "can_upgrade") else: self._plugins_model.clear() items = [] for metadata in self._plugins_metadata: items.append({ "name": metadata["label"], "version": metadata["version"], "short_description": metadata["short_description"], "author": metadata["author"], "already_installed": self._checkAlreadyInstalled(metadata["id"]), "file_location": metadata["file_location"], "can_upgrade": self._checkCanUpgrade(metadata["id"], metadata["version"]) }) self._plugins_model.setItems(items) return self._plugins_model def _checkCanUpgrade(self, id, version): plugin_registry = PluginRegistry.getInstance() metadata = plugin_registry.getMetaData(id) if metadata != {}: if id in self._newly_installed_plugin_ids: return False # We already updated this plugin. current_version = Version(metadata["plugin"]["version"]) new_version = Version(version) if new_version > current_version: return True return False def _checkAlreadyInstalled(self, id): plugin_registry = PluginRegistry.getInstance() metadata = plugin_registry.getMetaData(id) if metadata != {}: return True else: if id in self._newly_installed_plugin_ids: return True # We already installed this plugin, but the registry just doesn't know it yet. return False def _onRequestFinished(self, reply): reply_url = reply.url().toString() if reply.operation() == QNetworkAccessManager.GetOperation: if reply_url == self._api_url + "plugins": try: json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) self._plugins_metadata = json_data self.pluginsMetadataChanged.emit() except json.decoder.JSONDecodeError: Logger.log("w", "Received an invalid print job state message: Not valid JSON.") return else: # Ignore any operation that is not a get operation pass def _createNetworkManager(self): if self._network_manager: self._network_manager.finished.disconnect(self._onRequestFinished) self._network_manager = QNetworkAccessManager() self._network_manager.finished.connect(self._onRequestFinished)