From af88749c389267fa9a0c3de344960dbd703409ac Mon Sep 17 00:00:00 2001 From: probonopd Date: Tue, 18 Feb 2025 20:47:19 +0100 Subject: [PATCH] FIx styling issues --- main_window.py | 20 +- menus.py | 4 +- siracusa.py | 23 +- styling.py | 204 ++++++++++-------- window_start_menu.py => windows_start_menu.py | 5 +- 5 files changed, 128 insertions(+), 128 deletions(-) rename window_start_menu.py => windows_start_menu.py (98%) diff --git a/main_window.py b/main_window.py index 4d44e83..5aee3b2 100644 --- a/main_window.py +++ b/main_window.py @@ -7,13 +7,7 @@ including file navigation, status bar updates, etc. """ -# FIXME: For whatever strange reason we need to do this here or else Windows will say -# QWidget: Must construct a QApplication before a QWidget -import sys -from PyQt6 import QtWidgets -app = QtWidgets.QApplication(sys.argv) - -import os +import os, sys # FIXME: Import Qt like this: from PyQt6 import QtWidgets, QtGui, QtCore from PyQt6.QtWidgets import QApplication, QMainWindow, QHBoxLayout, QListView, QWidget, QAbstractItemView, QMessageBox, QLabel, QTextEdit, QStackedWidget, QInputDialog, QMenu, QStyle @@ -26,6 +20,8 @@ import menus, toolbar, status_bar, getinfo, appimage +from styling import Styling + class CustomFileSystemModel(QFileSystemModel): """ Custom file system model that allows us to customize e.g., the icons being used. @@ -595,7 +591,8 @@ def empty_trash(self): QtWidgets.QMessageBox.critical(self, "Error", f"Failed to empty trash: {e}") if __name__ == "__main__": - # app = QApplication(sys.argv) # See the top of this file + app = QApplication(sys.argv) + s = Styling(app) # Output not only to the console but also to the GUI try: @@ -607,13 +604,6 @@ def empty_trash(self): sys.stdout = log_console.Tee(sys.stdout, app.log_console) sys.stderr = log_console.Tee(sys.stderr, app.log_console) - try: - import styling - except ImportError: - pass - if "styling" in sys.modules: - styling.apply_styling(app) - app.setWindowIcon(app.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) window = MillerColumns() window.show() diff --git a/menus.py b/menus.py index c2624e3..201be08 100644 --- a/menus.py +++ b/menus.py @@ -16,7 +16,7 @@ if sys.platform == "win32": import win32gui, win32con, win32ui # For managing windows - import window_start_menu + import windows_start_menu class ColorMenu(QtWidgets.QMenu): def __init__(self, title, window): @@ -89,7 +89,7 @@ def create_menus(window): # Start Menu if sys.platform == "win32" and window.is_desktop_window: - start_menu = window_start_menu.StartMenu(window) + start_menu = windows_start_menu.StartMenu(window) left_menubar.addMenu(start_menu) # File Menu diff --git a/siracusa.py b/siracusa.py index 0c86efe..38210bc 100644 --- a/siracusa.py +++ b/siracusa.py @@ -3,12 +3,6 @@ # A file manager in Python? # "These days I prefer programming in Python... it's beautiful, exppressive, and simple" - Andy Hertzfeld, https://youtu.be/kqm7ahl2ZYg?feature=shared&t=3705 -# FIXME: For whatever strange reason we need to do this here or else Windows will say -# QWidget: Must construct a QApplication before a QWidget -import sys -from PyQt6 import QtWidgets -app = QtWidgets.QApplication(sys.argv) - """ A spatial file manager (“Siracusa style spatial Filer) implemented in PyQt6. Features: @@ -61,15 +55,13 @@ import getinfo, menus, fileops, appimage +from styling import Styling + LAYOUT_FILENAME = "._layout.json" item_width = grid_width = 100 item_height = grid_height = 60 -from styling import setup_icon_theme -setup_icon_theme() -icon_provider = QtWidgets.QFileIconProvider() - # DriveWatcher: Detects newly inserted drives and updates the UI class DriveWatcher(QtCore.QThread): newDriveDetected = QtCore.pyqtSignal(str) @@ -375,6 +367,7 @@ def paint(self, painter: QtGui.QPainter, option: QtWidgets.QStyleOptionGraphicsI if self.icon is None: self.icon = QtGui.QIcon.fromTheme("application-x-executable") else: + icon_provider = QtWidgets.QFileIconProvider() self.icon = icon_provider.icon(file_info) icon_size = QtCore.QSize(32, 32) self.pixmap = self.icon.pixmap(icon_size) @@ -1710,6 +1703,9 @@ def apply_desktop_picture_with_gradient(view, desktop_picture_path, target_width # Ctrl-C quits signal.signal(signal.SIGINT, signal.SIG_DFL) + app = QtWidgets.QApplication(sys.argv) + s = Styling(app) + # app = QtWidgets.QApplication(sys.argv) # See top of this file app.setApplicationName("Spatial") app.preferences = QtCore.QSettings(app.applicationName()) @@ -1718,13 +1714,6 @@ def apply_desktop_picture_with_gradient(view, desktop_picture_path, target_width # Global registry of open windows by folder path. open_windows = {} - try: - import styling - except ImportError: - pass - if "styling" in sys.modules: - styling.apply_styling(app) - # Reserving space for the menu bar on Windows if sys.platform == "win32": appbar = windows_struts.Strut() diff --git a/styling.py b/styling.py index 5c93e2d..26556ca 100644 --- a/styling.py +++ b/styling.py @@ -1,104 +1,126 @@ import os, sys from PyQt6 import QtGui, QtCore, QtWidgets -def apply_styling(app): - app.setStyle("Fusion") - - if not sys.platform == "win32": - # Load the custom fonts - font_paths = { - "regular": os.path.join(os.path.dirname(__file__), "fonts", "Inter-Regular.ttf"), - "bold": os.path.join(os.path.dirname(__file__), "fonts", "Inter-Bold.ttf"), - "italic": os.path.join(os.path.dirname(__file__), "fonts", "Inter-Italic.ttf"), - "bold_italic": os.path.join(os.path.dirname(__file__), "fonts", "Inter-BoldItalic.ttf"), - } - - # Load each font and set it in the application - fonts = {} - missing_fonts = [] - for style, path in font_paths.items(): - font_id = QtGui.QFontDatabase.addApplicationFont(path) - if font_id == -1: - missing_fonts.append(style) - else: - font_families = QtGui.QFontDatabase.applicationFontFamilies(font_id) - if font_families: - fonts[style] = font_families[0] # Store the family name - - # Show a dialog if any fonts are missing - if missing_fonts: - missing_fonts_str = ", ".join(missing_fonts) +class Styling: + _instance = None + + def __new__(cls, app): + if cls._instance is None: + cls._instance = super(Styling, cls).__new__(cls) + else: + if cls._instance.app != app: + raise Exception("Styling instance already exists with a different QApplication") + cls._instance.init(app) + return cls._instance + + def init(self, app): + self.app = app + self.apply_styling() + self.setup_icon_theme() + + def resource_path(self, relative_path): + if hasattr(sys, '_MEIPASS'): + return os.path.join(sys._MEIPASS, relative_path) + return os.path.join(os.path.abspath("."), relative_path) + + def apply_styling(self): + app = self.app + app.setStyle("Fusion") + + if not sys.platform == "win32": + # Load the custom fonts + font_paths = { + "regular": self.resource_path("fonts/Inter-Regular.ttf"), + "bold": self.resource_path("fonts/Inter-Bold.ttf"), + "italic": self.resource_path("fonts/Inter-Italic.ttf"), + "bold_italic": self.resource_path("fonts/Inter-BoldItalic.ttf"), + } + + # Load each font and set it in the application + fonts = {} + missing_fonts = [] + for style, path in font_paths.items(): + font_id = QtGui.QFontDatabase.addApplicationFont(path) + if font_id == -1: + missing_fonts.append(style) + else: + font_families = QtGui.QFontDatabase.applicationFontFamilies(font_id) + if font_families: + fonts[style] = font_families[0] # Store the family name + + # Show a dialog if any fonts are missing + if missing_fonts: + missing_fonts_str = ", ".join(missing_fonts) + msg_box = QtWidgets.QMessageBox() + msg_box.setIcon(QtWidgets.QMessageBox.Icon.Warning) + msg_box.setText("Missing Fonts") + msg_box.setInformativeText( + f"Oops! It looks like the following fonts are missing: {missing_fonts_str}.\n\n" + "Please download the 'Inter' font family from the following link:\n" + "https://fonts.google.com/specimen/Inter\n\n" + "After downloading, place the font files in the 'fonts' directory located in the same folder as your script." + ) + msg_box.setWindowTitle("Font Error") + msg_box.exec() + + # Set the default font + if "regular" in fonts: + default_font = QtGui.QFont(fonts["regular"], 9) # Use the regular font + app.setFont(default_font) + + # Create font instances for bold, italic, and bold-italic + if "bold" in fonts: + bold_font = QtGui.QFont(fonts["bold"], 9, QtGui.QFont.Weight.Bold) + if "italic" in fonts: + italic_font = QtGui.QFont(fonts["italic"], 9, QtGui.QFont.Weight.Normal, True) + if "bold_italic" in fonts: + bold_italic_font = QtGui.QFont(fonts["bold_italic"], 9, QtGui.QFont.Weight.Bold, True) + + # Set highlight color for selected items to blue + palette = app.palette() + palette.setColor(QtGui.QPalette.ColorRole.Highlight, QtGui.QColor(64, 64, 255)) + app.setPalette(palette) + + # 0px window border in red + app.setStyleSheet("QMainWindow { border: 0px; }") + + def setup_icon_theme(self): + # Set icon theme + icon_theme_path = self.resource_path("icons/") + # Check if the icon theme path exists + if not os.path.exists(icon_theme_path): + print(f"Icon theme path does not exist: {icon_theme_path}") + + # Check that it contains a folder named elementary-xfce that contains index.theme; if not, show a dialog + if not os.path.exists(os.path.join(icon_theme_path, "elementary-xfce", "index.theme")): msg_box = QtWidgets.QMessageBox() msg_box.setIcon(QtWidgets.QMessageBox.Icon.Warning) - msg_box.setText("Missing Fonts") + msg_box.setText("Missing Icon Theme") msg_box.setInformativeText( - f"Oops! It looks like the following fonts are missing: {missing_fonts_str}.\n\n" - "Please download the 'Inter' font family from the following link:\n" - "https://fonts.google.com/specimen/Inter\n\n" - "After downloading, place the font files in the 'fonts' directory located in the same folder as your script." + "Oops! It looks like the icon theme is missing.\n\n" + "Please download the 'elementary-xfce' icon theme from the following link:\n" + "http://archive.ubuntu.com/ubuntu/pool/universe/x/xubuntu-artwork/xubuntu-artwork_16.04.2.tar.xz\n" + "and extract 'elementary-xfce' to the 'icons' directory located in the same folder as your script." ) - msg_box.setWindowTitle("Font Error") + msg_box.setWindowTitle("Icon Theme Error") msg_box.exec() - # Set the default font - if "regular" in fonts: - default_font = QtGui.QFont(fonts["regular"], 9) # Use the regular font - app.setFont(default_font) - - # Create font instances for bold, italic, and bold-italic - if "bold" in fonts: - bold_font = QtGui.QFont(fonts["bold"], 9, QtGui.QFont.Weight.Bold) - if "italic" in fonts: - italic_font = QtGui.QFont(fonts["italic"], 9, QtGui.QFont.Weight.Normal, True) - if "bold_italic" in fonts: - bold_italic_font = QtGui.QFont(fonts["bold_italic"], 9, QtGui.QFont.Weight.Bold, True) - - # Set highlight color for selected items to blue - palette = app.palette() - palette.setColor(QtGui.QPalette.ColorRole.Highlight, QtGui.QColor(64, 64, 255)) - app.setPalette(palette) - - # 0px window border in red - app.setStyleSheet("QMainWindow { border: 0px; }") - -def setup_icon_theme(): - # Set icon theme - icon_theme_path = os.path.join(os.path.dirname(__file__), "icons/") - # Check if the icon theme path exists - if not os.path.exists(icon_theme_path): - print(f"Icon theme path does not exist: {icon_theme_path}") - - # Check that it contains a folder named elementary-xfce that contains index.theme; if not, show a dialog - if not os.path.exists(os.path.join(icon_theme_path, "elementary-xfce", "index.theme")): - msg_box = QtWidgets.QMessageBox() - msg_box.setIcon(QtWidgets.QMessageBox.Icon.Warning) - msg_box.setText("Missing Icon Theme") - msg_box.setInformativeText( - "Oops! It looks like the icon theme is missing.\n\n" - "Please download the 'elementary-xfce' icon theme from the following link:\n" - "http://archive.ubuntu.com/ubuntu/pool/universe/x/xubuntu-artwork/xubuntu-artwork_16.04.2.tar.xz\n" - "and extract 'elementary-xfce' to the 'icons' directory located in the same folder as your script." - ) - msg_box.setWindowTitle("Icon Theme Error") - msg_box.exec() - - # qicon_instance = QtGui.QIcon() - QtGui.QIcon.setThemeSearchPaths([icon_theme_path]) - QtGui.QIcon.setThemeName("elementary-xfce") - - available_fallback_themes = [] - if os.path.exists("/usr/share/icons"): - available_fallback_themes += [d for d in os.listdir("/usr/share/icons") if os.path.isdir(os.path.join("/usr/share/icons", d))] - if os.path.exists("/usr/local/share/icons"): - available_fallback_themes += [d for d in os.listdir("/usr/local/share/icons") if os.path.isdir(os.path.join("/usr/local/share/icons", d))] - print(f"Available fallback themes: {available_fallback_themes}") - QtGui.QIcon.setThemeSearchPaths(QtGui.QIcon.themeSearchPaths() + ["/usr/share/icons", "/usr/local/share/icons"] + [os.path.join("/usr/share/icons", d) for d in available_fallback_themes]) - QtGui.QIcon.setFallbackThemeName("hicolor") + # qicon_instance = QtGui.QIcon() + QtGui.QIcon.setThemeSearchPaths([icon_theme_path]) + QtGui.QIcon.setThemeName("elementary-xfce") + + available_fallback_themes = [] + if os.path.exists("/usr/share/icons"): + available_fallback_themes += [d for d in os.listdir("/usr/share/icons") if os.path.isdir(os.path.join("/usr/share/icons", d))] + if os.path.exists("/usr/local/share/icons"): + available_fallback_themes += [d for d in os.listdir("/usr/local/share/icons") if os.path.isdir(os.path.join("/usr/local/share/icons", d))] + print(f"Available fallback themes: {available_fallback_themes}") + QtGui.QIcon.setThemeSearchPaths(QtGui.QIcon.themeSearchPaths() + ["/usr/share/icons", "/usr/local/share/icons"] + [os.path.join("/usr/share/icons", d) for d in available_fallback_themes]) + QtGui.QIcon.setFallbackThemeName("hicolor") if __name__ == "__main__": - setup_icon_theme() - app = QtWidgets.QApplication([]) - apply_styling(app) + app = QtWidgets.QApplication(sys.argv) + s = Styling(app) # Make a window with an icon to test the icon theme window = QtWidgets.QMainWindow() window.setWindowIcon(QtGui.QIcon.fromTheme("folder")) @@ -113,4 +135,4 @@ def setup_icon_theme(): window.show() app.lastWindowClosed.connect(app.quit) - app.exec() \ No newline at end of file + app.exec() diff --git a/window_start_menu.py b/windows_start_menu.py similarity index 98% rename from window_start_menu.py rename to windows_start_menu.py index 03eeeb1..7940aad 100644 --- a/window_start_menu.py +++ b/windows_start_menu.py @@ -5,9 +5,7 @@ from PyQt6 import QtWidgets, QtGui, QtCore from pylnk3 import Lnk -from styling import setup_icon_theme -setup_icon_theme() -icon_provider = QtWidgets.QFileIconProvider() +from styling import Styling def resolve_shortcut(lnk_path): try: @@ -178,6 +176,7 @@ def __init__(self): if __name__ == "__main__": app = QtWidgets.QApplication(sys.argv) + s = Styling(app) window = StartMenuWindow() window.show() sys.exit(app.exec())