Flat dark theme for PySide and PyQt applications with OS theme synchronization
npx @tessl/cli install tessl/pypi-pyqtdarktheme@2.1.0A comprehensive theming library for Qt-based Python applications (PySide and PyQt) that applies flat dark and light themes with automatic OS synchronization capabilities. Provides seamless theme switching, automatic detection and synchronization with operating system appearance settings, customizable styling options, and built-in support for HiDPI displays.
pip install pyqtdarkthemeimport qdarkthemeimport sys
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton
import qdarktheme
# Enable HiDPI support (call before QApplication)
qdarktheme.enable_hi_dpi()
app = QApplication(sys.argv)
# Apply complete dark theme to your Qt application
qdarktheme.setup_theme()
main_win = QMainWindow()
push_button = QPushButton("Dark Theme Example")
main_win.setCentralWidget(push_button)
main_win.show()
app.exec()PyQtDarkTheme uses a modular architecture:
Apply comprehensive theming to Qt applications with customization options and OS synchronization.
def setup_theme(
theme: str = "dark",
corner_shape: str = "rounded",
custom_colors: dict[str, str | dict[str, str]] | None = None,
additional_qss: str | None = None,
*,
default_theme: str = "dark",
) -> None:
"""
Apply the theme which looks like flat design to the Qt App completely.
Args:
theme: Theme name - "dark", "light", or "auto"
If "auto", sync with OS theme and accent (Mac accent support)
Falls back to default_theme if OS detection fails
corner_shape: Corner style - "rounded" or "sharp"
custom_colors: Color customization map
Format: {"color_id": "#hex"} or {"[theme]": {"color_id": "#hex"}}
Example: {"primary": "#D0BCFF"} or {"[dark]": {"primary": "#D0BCFF"}}
additional_qss: Additional stylesheet text to append
default_theme: Fallback theme when auto detection fails
Raises:
ValueError: Invalid theme/corner_shape values
KeyError: Invalid color IDs in custom_colors
Exception: QApplication not instantiated
Examples:
# Dark theme
qdarktheme.setup_theme("dark")
# Auto sync with OS
qdarktheme.setup_theme("auto")
# Custom colors
qdarktheme.setup_theme(custom_colors={"primary": "#D0BCFF"})
# Theme-specific colors
qdarktheme.setup_theme(
theme="auto",
custom_colors={"[dark]": {"primary": "#D0BCFF"}}
)
"""Load theme stylesheets as strings for manual application.
def load_stylesheet(
theme: str = "dark",
corner_shape: str = "rounded",
custom_colors: dict[str, str | dict[str, str]] | None = None,
*,
default_theme: str = "dark",
) -> str:
"""
Load the style sheet which looks like flat design.
Args:
theme: Theme name - "dark", "light", or "auto"
corner_shape: Corner style - "rounded" or "sharp"
custom_colors: Color customization map
default_theme: Fallback theme for auto detection failures
Returns:
Stylesheet string for Qt application
Raises:
ValueError: Invalid theme/corner_shape arguments
KeyError: Invalid color IDs in custom_colors
Examples:
# Basic usage
app.setStyleSheet(qdarktheme.load_stylesheet())
# Light theme
app.setStyleSheet(qdarktheme.load_stylesheet("light"))
# Auto with OS sync
app.setStyleSheet(qdarktheme.load_stylesheet("auto"))
# Custom colors
app.setStyleSheet(qdarktheme.load_stylesheet(
custom_colors={"primary": "#D0BCFF"}
))
"""Load theme QPalette objects for color role customization.
def load_palette(
theme: str = "dark",
custom_colors: dict[str, str | dict[str, str]] | None = None,
*,
default_theme: str = "dark",
for_stylesheet: bool = False,
):
"""
Load the QPalette for the dark or light theme.
Args:
theme: Theme name - "dark", "light", or "auto"
custom_colors: Color customization map
default_theme: Fallback theme for auto detection failures
for_stylesheet: If True, only colors not settable via stylesheets
Returns:
QPalette object configured for the theme
Raises:
TypeError: Invalid theme argument type
KeyError: Invalid color IDs in custom_colors
Examples:
# Basic usage
app.setPalette(qdarktheme.load_palette())
# Light theme
app.setPalette(qdarktheme.load_palette("light"))
# Auto detection
app.setPalette(qdarktheme.load_palette("auto"))
# Custom colors
app.setPalette(qdarktheme.load_palette(
custom_colors={"primary": "#D0BCFF"}
))
"""Enable high-resolution display support for Qt applications.
def enable_hi_dpi() -> None:
"""
Allow to HiDPI.
This function must be set before instantiation of QApplication.
For Qt6 bindings, HiDPI "just works" without using this function.
Examples:
# Enable before QApplication
qdarktheme.enable_hi_dpi()
app = QApplication(sys.argv)
qdarktheme.setup_theme()
"""Control automatic OS theme synchronization.
def stop_sync() -> None:
"""
Stop sync with system theme.
Stops the background listener that monitors OS theme changes
when using theme="auto" mode.
Examples:
# Stop OS synchronization
qdarktheme.stop_sync()
"""Manage theme resource caches stored in system home directory.
def clear_cache() -> None:
"""
Clear the caches in system home path.
PyQtDarkTheme builds caches of resources in the system home path.
You can clear the caches by running this method.
Cache location: ~/.cache/qdarktheme/v{version}/
Examples:
# Clear all cached resources
qdarktheme.clear_cache()
"""Query available theme names for validation and UI purposes.
def get_themes() -> tuple[str, ...]:
"""
Return available theme names.
Returns:
Tuple of available theme names: ("dark", "light", "auto")
Examples:
# Get all available themes
themes = qdarktheme.get_themes()
print(themes) # ("dark", "light", "auto")
"""# Package version
__version__: str = "2.1.0"
# Color customization type
ColorMap = dict[str, str | dict[str, str]]
# Theme names
THEMES = ("dark", "light", "auto")
# Corner shapes
CORNER_SHAPES = ("rounded", "sharp")Interactive demonstration of all Qt widgets with theme applied.
# Run widget gallery demo
python -m qdarktheme.widget_galleryThis launches a comprehensive showcase of Qt widgets styled with the current theme, useful for testing theme appearance and compatibility.
Customize colors for specific themes only:
# Different colors for dark and light themes
qdarktheme.setup_theme(
theme="auto",
custom_colors={
"[dark]": {
"primary": "#D0BCFF",
"primary>selection.background": "#6750A4"
},
"[light]": {
"primary": "#6750A4",
"primary>selection.background": "#D0BCFF"
}
}
)For advanced control, load components separately:
# Load components individually
palette = qdarktheme.load_palette(theme="dark")
stylesheet = qdarktheme.load_stylesheet(theme="dark")
app.setPalette(palette)
app.setStyleSheet(stylesheet)Common exceptions and handling patterns:
try:
qdarktheme.setup_theme("auto", custom_colors={"invalid_color": "#FF0000"})
except KeyError as e:
print(f"Invalid color ID: {e}")
except ValueError as e:
print(f"Invalid theme argument: {e}")
except Exception as e:
print(f"QApplication not instantiated: {e}")