def test_addGetDisplayComponent(): stage = Stage() stage.addDisplayComponent("BLORP", "location") assert stage.getDisplayComponent("BLORP") == QUrl.fromLocalFile("location") stage.addDisplayComponent("MEEP!", QUrl.fromLocalFile("MEEP")) assert stage.getDisplayComponent("MEEP!") == QUrl.fromLocalFile("MEEP")
def createQmlComponent(self, qml_file_path: str, context_properties: Dict[str, "QObject"] = None) -> Optional["QObject"]: """Create a QML component from a qml file. :param qml_file_path:: The absolute file path to the root qml file. :param context_properties:: Optional dictionary containing the properties that will be set on the context of the qml instance before creation. :return: None in case the creation failed (qml error), else it returns the qml instance. :note If the creation fails, this function will ensure any errors are logged to the logging service. """ 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
def test_activeToolPanel(self): # There is no active tool, so it should be empty assert self.proxy.activeToolPanel == QUrl() with patch.object(self.tool, "getMetaData", MagicMock(return_value={"tool_panel": "derp"})): with patch("UM.PluginRegistry.PluginRegistry.getPluginPath", MagicMock(return_value="OMG")): Application.getInstance().getController().setActiveTool( self.tool) assert self.proxy.activeToolPanel == QUrl.fromLocalFile( "OMG/derp") # Try again with empty metadata with patch("UM.PluginRegistry.PluginRegistry.getMetaData", MagicMock(return_value={"tool": {}})): Application.getInstance().getController().setActiveTool("") Application.getInstance().getController().setActiveTool(self.tool) assert self.proxy.activeToolPanel == QUrl.fromLocalFile("")
def activeToolPanel(self): if not self._active_tool: return QUrl() try: panel_file = self._active_tool.getMetaData()["tool_panel"] except KeyError: return QUrl() return QUrl.fromLocalFile( os.path.join( PluginRegistry.getInstance().getPluginPath( self._active_tool.getPluginId()), panel_file))
def addFileToRecentFiles(self, file_name: str) -> None: file_path = QUrl.fromLocalFile(file_name) if file_path in self._recent_files: self._recent_files.remove(file_path) self._recent_files.insert(0, file_path) if len(self._recent_files) > 10: del self._recent_files[10] pref = "" for path in self._recent_files: pref += path.toLocalFile() + ";" self.getPreferences().setValue("%s/recent_files" % self.getApplicationName(), pref) self.recentFilesChanged.emit()
def _onEngineCreated(self) -> None: qmlRegisterType( MaterialSettingsPluginVisibilityHandler. MaterialSettingsPluginVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler") # Adding/removing pages from the preferences dialog is handles in QML # There is no way to access the preferences dialog directly, so we have to search for it preferencesDialog = None main_window = CuraApplication.getInstance().getMainWindow() if not main_window: Logger.log( "e", "Could not replace Materials preferencepane with patched version because there is no main window" ) return for child in main_window.contentItem().children(): try: test = child.setPage # only PreferencesDialog has a setPage function preferencesDialog = child break except: pass if preferencesDialog: Logger.log( "d", "Replacing Materials preferencepane with patched version") qml_folder = "qml" if not USE_QT5 else "qml_qt5" materialPreferencesPage = QUrl.fromLocalFile( os.path.join(os.path.dirname(os.path.abspath(__file__)), qml_folder, "MaterialsPage.qml")) if USE_QT5: materialPreferencesPage = materialPreferencesPage.toString() preferencesDialog.removePage(3) preferencesDialog.insertPage( 3, catalog.i18nc("@title:tab", "Materials"), materialPreferencesPage) else: Logger.log( "e", "Could not replace Materials preferencepane with patched version" )
def openVideoFile(self): # self.mediaPlayer.setMedia(QMediaContent()) if self.sender() == self.openVideoAction: self.videoFile, _ = QFileDialog.getOpenFileName( self, "Open video", QDir.homePath()) # if self.videoFile != '': # self.setWindowTitle('{} - {}'.format(os.path.basename(self.videoFile), # os.path.basename(self.projectFile))) if self.videoFile != '': self.setWindowTitle('{} - {}'.format( os.path.basename(self.videoFile), os.path.basename(self.projectFile))) self.saveProjectAction.setEnabled(True) self.maskGenAction.setEnabled(True) # self.loadGraphAction.setEnabled(True) # self.saveGraphAction.setEnabled(True) # self.drawPointAction.setEnabled(True) # self.drawLineAction.setEnabled(True) # self.drawZoneAction.setEnabled(True) creation_datetime, width, height = getVideoMetadata(self.videoFile) self.videoStartDatetime = self.videoCurrentDatetime = creation_datetime self.dateLabel.setText(creation_datetime.strftime('%a, %b %d, %Y')) self.gView.setSceneRect(0, 0, width, height) self.videoItem = QGraphicsVideoItem() self.videoItem.setAspectRatioMode( Qt.AspectRatioMode.KeepAspectRatio) self.gScene.addItem(self.videoItem) self.videoItem.mouseMoveEvent = self.gView.mouseMoveEvent self.videoItem.setSize(QSizeF(width, height)) self.mediaPlayer.setVideoOutput(self.videoItem) self.mediaPlayer.setSource(QUrl.fromLocalFile(self.videoFile)) self.gView.labelSize = width / 50 self.playButton.setEnabled(True) # self.gView.setViewport(QOpenGLWidget()) self.mediaPlayer.pause()
loaded = pyqtSignal() error = pyqtSignal(str, arguments=["errorText"]) metaDataChanged = pyqtSignal() @pyqtProperty("QVariantMap", notify=metaDataChanged) def metaData(self): return self._metadata @pyqtProperty(str, notify=loaded) def definitionId(self): return self._definition_id signal.signal(signal.SIGINT, signal.SIG_DFL) file_name = None if len(sys.argv) > 1: file_name = sys.argv[1] del sys.argv[1] app = QApplication(sys.argv) engine = QQmlApplicationEngine() qmlRegisterType(DefinitionLoader, "Example", 1, 0, "DefinitionLoader") qmlRegisterType(DefinitionTreeModel.DefinitionTreeModel, "Example", 1, 0, "DefinitionTreeModel") if file_name: engine.rootContext().setContextProperty("open_file", QUrl.fromLocalFile(file_name)) engine.load(os.path.join(os.path.dirname(os.path.abspath(__file__)), "main.qml")) app.exec()
def open_local_file(path): QDesktopServices.openUrl(QUrl.fromLocalFile(path))
def show_html(self, html, in_current_tab=True): if isinstance(html, bytes): html = html.decode('utf-8') tab = self.get_tab_for_load(in_current_tab=in_current_tab) tab.setHtml(html, QUrl.fromLocalFile(os.path.expanduser('~')))
def load(self, path: str, is_first_call: bool = True) -> None: if path == self._path: return theme_full_path = os.path.join(path, "theme.json") Logger.log( "d", "Loading theme file: {theme_full_path}".format( theme_full_path=theme_full_path)) try: with open(theme_full_path, encoding="utf-8") as f: data = json.load(f) except EnvironmentError as e: Logger.error( "Unable to load theme file at {theme_full_path}: {err}".format( theme_full_path=theme_full_path, err=e)) return except UnicodeDecodeError: Logger.error( "Theme file at {theme_full_path} is corrupt (invalid UTF-8 bytes)." .format(theme_full_path=theme_full_path)) return except json.JSONDecodeError: Logger.error( "Theme file at {theme_full_path} is corrupt (invalid JSON syntax)." .format(theme_full_path=theme_full_path)) return # Iteratively load inherited themes try: theme_id = data["metadata"]["inherits"] self.load(Resources.getPath(Resources.Themes, theme_id), is_first_call=False) except FileNotFoundError: Logger.log("e", "Could not find inherited theme %s", theme_id) except KeyError: pass # No metadata or no inherits keyword in the theme.json file if "colors" in data: for name, value in data["colors"].items(): if not is_first_call and isinstance(value, str): # Keep parent theme string colors as strings and parse later self._colors[name] = value continue if isinstance(value, str) and is_first_call: # value is reference to base_colors color name try: color = data["base_colors"][value] except IndexError: Logger.log( "w", "Colour {value} could not be found in base_colors". format(value=value)) continue else: color = value try: c = QColor(color[0], color[1], color[2], color[3]) except IndexError: # Color doesn't have enough components. Logger.log( "w", "Colour {name} doesn't have enough components. Need to have 4, but had {num_components}." .format(name=name, num_components=len(color))) continue # Skip this one then. self._colors[name] = c if "base_colors" in data: for name, color in data["base_colors"].items(): try: c = QColor(color[0], color[1], color[2], color[3]) except IndexError: # Color doesn't have enough components. Logger.log( "w", "Colour {name} doesn't have enough components. Need to have 4, but had {num_components}." .format(name=name, num_components=len(color))) continue # Skip this one then. self._colors[name] = c if is_first_call and self._colors: #Convert all string value colors to their referenced color for name, color in self._colors.items(): if isinstance(color, str): try: c = self._colors[color] self._colors[name] = c except: Logger.log( "w", "Colour {name} {color} does".format(name=name, color=color)) fonts_dir = os.path.join(path, "fonts") if os.path.isdir(fonts_dir): for root, dirnames, filenames in os.walk(fonts_dir): for filename in filenames: if filename.lower().endswith(".ttf"): QFontDatabase.addApplicationFont( os.path.join(root, filename)) if "fonts" in data: system_font_size = QCoreApplication.instance().font().pointSize() for name, font in data["fonts"].items(): q_font = QFont() q_font.setFamily( font.get("family", QCoreApplication.instance().font().family())) if font.get("bold"): q_font.setBold(font.get("bold", False)) else: q_font.setWeight(font.get("weight", 500)) q_font.setLetterSpacing(QFont.SpacingType.AbsoluteSpacing, font.get("letterSpacing", 0)) q_font.setItalic(font.get("italic", False)) q_font.setPointSize(int( font.get("size", 1) * system_font_size)) q_font.setCapitalization(QFont.Capitalization.AllUppercase if font.get("capitalize", False) else QFont.Capitalization.MixedCase) self._fonts[name] = q_font if "sizes" in data: for name, size in data["sizes"].items(): s = QSizeF() s.setWidth(round(size[0] * self._em_width)) s.setHeight(round(size[1] * self._em_height)) self._sizes[name] = s iconsdir = os.path.join(path, "icons") if os.path.isdir(iconsdir): try: for base_path, _, icons in os.walk(iconsdir): detail_level = base_path.split(os.sep)[-1] if detail_level not in self._icons: self._icons[detail_level] = {} for icon in icons: name = os.path.splitext(icon)[0] self._icons[detail_level][name] = QUrl.fromLocalFile( os.path.join(base_path, icon)) except EnvironmentError as err: # Exception when calling os.walk, e.g. no access rights. Logger.error( f"Can't access icons of theme ({iconsdir}): {err}") # Won't get any icons then. Images will show as black squares. deprecated_icons_file = os.path.join(iconsdir, "deprecated_icons.json") if os.path.isfile(deprecated_icons_file): try: with open(deprecated_icons_file, encoding="utf-8") as f: data = json.load(f) for icon in data: self._deprecated_icons[icon] = data[icon] except (UnicodeDecodeError, json.decoder.JSONDecodeError, EnvironmentError): Logger.logException( "w", "Could not parse deprecated icons list %s", deprecated_icons_file) imagesdir = os.path.join(path, "images") if os.path.isdir(imagesdir): try: for image in os.listdir(imagesdir): name = os.path.splitext(image)[0] self._images[name] = QUrl.fromLocalFile( os.path.join(imagesdir, image)) except EnvironmentError as err: # Exception when calling os.listdir, e.g. no access rights. Logger.error( f"Can't access image of theme ({imagesdir}): {err}") # Won't get any images then. They will show as black squares. Logger.log("d", "Loaded theme %s", path) Logger.info(f"System's em size is {self._em_height}px.") self._path = path # only emit the theme loaded signal once after all the themes in the inheritance chain have been loaded if is_first_call: self.themeLoaded.emit()
def test_knownImage(theme): image_location = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_theme", "images", "kitten.jpg") assert theme.getImage("kitten") == QUrl.fromLocalFile(image_location)
def test_getKnownIcon(theme): icon_location = os.path.join(os.path.dirname(os.path.abspath(__file__)), "test_theme", "icons", "test.svg") assert theme.getIcon("test") == QUrl.fromLocalFile(icon_location)
def getDefaultPath(self) -> QUrl: return QUrl.fromLocalFile(os.path.expanduser("~/"))
def _getArticle(self, article_id, language="en_US") -> List[List[str]]: """ Gets the rich text of a specified article. This function lazily loads an article from a file. If it's never been loaded before the article gets parsed and stored. Otherwise it'll get taken from the cache. :param article_id: The ID of the article to get. :param language: The language to get the article in. :return: A list of article "parts". Each article part is a list, where the first element indicates the type of part and the rest contains the content. Possible types of parts are "rich_text", "images" or "checkbox". """ if article_id in self.articles and language in self.articles[article_id]: return self.articles[article_id][language] images_path = os.path.join(os.path.dirname(__file__), "resources", "articles", "images") try: if language not in self.article_locations[article_id]: language = "en_US" # Fall back to English if the preferred language is not available. markdown_file = self.article_locations[article_id][language] with open(markdown_file, encoding="utf-8") as f: markdown_str = f.read() images_path = os.path.dirname(markdown_file) except (OSError, KeyError): # File doesn't exist or is otherwise not readable. if self.definition_container and article_id in self.definition_container.getAllKeys(): markdown_str = self.definition_container.getProperty(article_id, "label") + "\n====\n" markdown_str += "*" + self.definition_container.getProperty(article_id, "description") + "*" # Use the setting description as fallback. else: markdown_str = "There is no article on this topic." # Store this unparsed for later. if language not in self.articles_source: self.articles_source[language] = {} self.articles_source[language][article_id] = markdown_str if images_path not in self._markdown_per_folder: renderer = QtMarkdownRenderer.QtMarkdownRenderer(images_path) self._markdown_per_folder[images_path] = mistune.Markdown(renderer=renderer) # Renders the Markdown articles into the subset of HTML supported by Qt. # Pre-process so that comments and conditionals don't influence the business. markdown_str = QtMarkdownRenderer.QtMarkdownRenderer.preprocess_conditionals(markdown_str) markdown_str = QtMarkdownRenderer.QtMarkdownRenderer.preprocess_comments(markdown_str) find_images = re.compile(r"!\[(.*)\]\(([^\)]+)\)") find_checkboxes = re.compile(r"\[ \]\s*([^\n]+)") image_description = None parts = [] # type: List[List[str]] # List of items in the article. Each item starts with a type ID, and then a variable number of data items. for index, part_between_images in enumerate(find_images.split(markdown_str)): # The parts of the regex split alternate between text, image description and image URL. if index % 3 == 0: part_between_images = part_between_images.strip() if part_between_images or index == 0: parts_between_checkboxes = find_checkboxes.split(part_between_images) for index2, part_between_checkboxes in enumerate(parts_between_checkboxes): part_between_checkboxes = part_between_checkboxes.strip() # The parts of the regex split alternate between text and checkbox description. if index2 % 2 == 0: if part_between_checkboxes: rich_text = self._markdown_per_folder[images_path](part_between_checkboxes) parts.append(["rich_text", rich_text]) else: # if index2 == 1: preference_key = "settings_guide/" + urllib.parse.quote_plus(part_between_checkboxes).lower() parts.append(["checkbox", preference_key, part_between_checkboxes]) elif index % 3 == 1: image_description = mistune.markdown(part_between_images) else: # if index % 3 == 2: if image_description is not None: if parts[-1][0] != "images": # List of images. parts.append(["images"]) image_url = os.path.join(images_path, part_between_images) parts[-1].append(QUrl.fromLocalFile(image_url).toString() + "|" + image_description) image_description = None if article_id not in self.articles: self.articles[article_id] = {} self.articles[article_id][language] = parts if article_id not in self.articles_rich_text: self.articles_rich_text[article_id] = {} self.articles_rich_text[article_id][language] = self._markdown_per_folder[images_path](markdown_str) return self.articles[article_id][language]
def addDisplayComponent(self, name: str, source: Union[str, QUrl]) -> None: """Add a QML component to the stage""" if type(source) == str: source = QUrl.fromLocalFile(source) self._components[name] = source
def getImageSourceAsQUrl(image_source: Union[QUrl, str]) -> QUrl: if type(image_source) is str: return QUrl.fromLocalFile(image_source) elif type(image_source) is QUrl: return image_source return QUrl.fromLocalFile("")
def addDisplayComponent(self, name: str, source: Union[str, QUrl]) -> None: """Add a QML component that is provided by this View.""" if type(source) == str: source = QUrl.fromLocalFile(source) self._components[name] = source
def openHtmlPage(page_name, html_contents): target = os.path.join(tempfile.gettempdir(), page_name) with open(target, 'w', encoding='utf-8') as fhandle: fhandle.write(html_contents) QDesktopServices.openUrl(QUrl.fromLocalFile(target))
def _onMessageActionTriggered(self, message, action): if action == "open_folder" and hasattr(message, "_folder"): QDesktopServices.openUrl(QUrl.fromLocalFile(message._folder))
def startSplashWindowPhase(self) -> None: super().startSplashWindowPhase() i18n_catalog = i18nCatalog("uranium") self.showSplashMessage(i18n_catalog.i18nc("@info:progress", "Initializing package manager...")) self._package_manager.initialize() signal.signal(signal.SIGINT, signal.SIG_DFL) # This is done here as a lot of plugins require a correct gl context. If you want to change the framework, # these checks need to be done in your <framework>Application.py class __init__(). self._configuration_error_message = ConfigurationErrorMessage(self, i18n_catalog.i18nc("@info:status", "Your configuration seems to be corrupt."), lifetime = 0, title = i18n_catalog.i18nc("@info:title", "Configuration errors") ) # Remove, install, and then loading plugins self.showSplashMessage(i18n_catalog.i18nc("@info:progress", "Loading plugins...")) # Remove and install the plugins that have been scheduled self._plugin_registry.initializeBeforePluginsAreLoaded() self._plugin_registry.pluginLoadStarted.connect(self._displayLoadingPluginSplashMessage) self._loadPlugins() self._plugin_registry.pluginLoadStarted.disconnect(self._displayLoadingPluginSplashMessage) self._plugin_registry.checkRequiredPlugins(self.getRequiredPlugins()) self.pluginsLoaded.emit() self.showSplashMessage(i18n_catalog.i18nc("@info:progress", "Updating configuration...")) with self._container_registry.lockFile(): VersionUpgradeManager.getInstance().upgrade() # Load preferences again because before we have loaded the plugins, we don't have the upgrade routine for # the preferences file. Now that we have, load the preferences file again so it can be upgraded and loaded. self.showSplashMessage(i18n_catalog.i18nc("@info:progress", "Loading preferences...")) try: preferences_filename = Resources.getPath(Resources.Preferences, self._app_name + ".cfg") with open(preferences_filename, "r", encoding = "utf-8") as f: serialized = f.read() # This performs the upgrade for Preferences self._preferences.deserialize(serialized) self._preferences.setValue("general/plugins_to_remove", "") self._preferences.writeToFile(preferences_filename) except (EnvironmentError, UnicodeDecodeError): Logger.log("i", "The preferences file cannot be opened or it is corrupted, so we will use default values") self.processEvents() # Force the configuration file to be written again since the list of plugins to remove maybe changed try: self.readPreferencesFromConfiguration() except FileNotFoundError: Logger.log("i", "The preferences file '%s' cannot be found, will use default values", self._preferences_filename) self._preferences_filename = Resources.getStoragePath(Resources.Preferences, self._app_name + ".cfg") Logger.info("Completed loading preferences.") # FIXME: This is done here because we now use "plugins.json" to manage plugins instead of the Preferences file, # but the PluginRegistry will still import data from the Preferences files if present, such as disabled plugins, # so we need to reset those values AFTER the Preferences file is loaded. self._plugin_registry.initializeAfterPluginsAreLoaded() # Check if we have just updated from an older version self._preferences.addPreference("general/last_run_version", "") last_run_version_str = self._preferences.getValue("general/last_run_version") if not last_run_version_str: last_run_version_str = self._version last_run_version = Version(last_run_version_str) current_version = Version(self._version) if last_run_version < current_version: self._just_updated_from_old_version = True self._preferences.setValue("general/last_run_version", str(current_version)) self._preferences.writeToFile(self._preferences_filename) # Preferences: recent files self._preferences.addPreference("%s/recent_files" % self._app_name, "") file_names = self._preferences.getValue("%s/recent_files" % self._app_name).split(";") for file_name in file_names: if not os.path.isfile(file_name): continue self._recent_files.append(QUrl.fromLocalFile(file_name)) if not self.getIsHeadLess(): # Initialize System tray icon and make it invisible because it is used only to show pop up messages self._tray_icon = None if self._tray_icon_name: try: self._tray_icon = QIcon(Resources.getPath(Resources.Images, self._tray_icon_name)) self._tray_icon_widget = QSystemTrayIcon(self._tray_icon) self._tray_icon_widget.setVisible(False) Logger.info("Created system tray icon.") except FileNotFoundError: Logger.log("w", "Could not find the icon %s", self._tray_icon_name)