fxstyle
fxstyle
¶
Styling, theming, and color management for fxgui.
This module provides comprehensive styling functionality including
- Multiple theme support with dynamic theme switching
- Theme persistence across application restarts (via fxconfig)
- QSS stylesheet loading with dynamic color replacement
- Custom QProxyStyle for standard icon overrides
- Theme toggling with icon cache invalidation
- Color loading from YAML configuration files with inheritance support
Theme Color Reference¶
Each theme in style.yaml defines these semantic color roles:
Accent Colors (interactive highlights):
- accent_primary: Hover borders, selections, progress gradients (end)
- accent_secondary: Gradient starts, item hover backgrounds
Surface Colors (backgrounds):
- surface: Main widget/window backgrounds, buttons, selected tabs
- surface_alt: Alternate row backgrounds in lists/tables
- surface_sunken: Recessed areas - inputs, lists, menus, status bar
- tooltip: Tooltip backgrounds
Border Colors:
- border: Standard borders on inputs, containers, menus
- border_light: Subtle borders - tooltips, buttons, tabs
- border_strong: Emphasized borders - frames, separators
Text Colors:
- text: Primary text for all widgets
- text_muted: De-emphasized text - inactive tabs, placeholders
- text_disabled: Disabled widget text
- text_on_accent_primary: Text on accent_primary backgrounds (optional, auto-computed)
- text_on_accent_secondary: Text on accent_secondary backgrounds (optional, auto-computed)
Interactive States:
- state_hover: Hover state backgrounds
- state_pressed: Pressed/checked/active backgrounds
Scrollbar:
- scrollbar_track: Track/gutter background
- scrollbar_thumb: Draggable thumb
- scrollbar_thumb_hover: Thumb hover state
Layout:
- grid: Table gridlines, header borders
- separator: Separator/splitter hover backgrounds
Slider:
- slider_thumb: Slider handle color
- slider_thumb_hover: Slider handle hover/pressed
Icon:
- icon: Monochrome icon tint color
Theme Font Reference¶
The color file may also name typefaces by role, in a fonts: mapping
that a theme can override role by role:
- ``title``: Reached from QSS through the ``fxTitle`` property; see
:func:`mark_as_title`
- ``body``: Every widget, through the ``*`` selector
- ``mono``: Code, logs, anything whose columns must line up
Each role is a family stack. A missing section, a missing role or an empty value all mean the platform default UI font, so a color file written before roles existed renders unchanged. Families the running Qt does not have are dropped and the platform default appended, so a role always resolves to something real.
Because :func:set_color_file replaces this file wholesale, a consumer
declaring fonts: in its own copy has full typographic control with no
further API. Font files those names refer to are registered with
:func:register_fonts.
Classes:
| Name | Description |
|---|---|
FXProxyStyle |
Custom style providing Material Design icons for Qt standard icons. |
FXThemeManager |
Singleton that emits signals when theme changes. |
FXThemeAware |
Mixin for widgets that auto-update on theme changes. |
Functions:
| Name | Description |
|---|---|
load_stylesheet |
Load and customize QSS stylesheets. |
get_colors |
Get the cached color configuration. |
set_color_file |
Set a custom color configuration file. |
apply_theme |
Apply a theme to all registered roots (stylesheet + icons). |
get_available_themes |
Get list of available theme names. |
get_theme |
Get the current theme name. |
get_theme_colors |
Get the color palette for the current theme. |
get_accent_colors |
Get primary/secondary accent colors. |
get_icon_color |
Get the icon tint color for current theme. |
register_fonts |
Register font files so a color file may name them. |
get_fonts |
Get the resolved font stack for every role. |
get_font_family |
Get the resolved font stack for one role. |
mark_as_title |
Draw a widget's text in the title font role. |
is_light_theme |
Check if the current theme is light or dark. |
save_theme |
Save the current theme to persistent storage. |
load_saved_theme |
Load the previously saved theme. |
set_default_theme |
Set the theme to fall back to when none is saved. |
get_default_theme |
Get the theme to fall back to when none is saved. |
Constants
STYLE_FILE: Path to the default QSS stylesheet. DEFAULT_COLOR_FILE: Path to the default color configuration. TITLE_PROPERTY: Dynamic property name selecting the title font role.
Examples:
Loading a stylesheet with a theme:
>>> from fxgui import fxstyle
>>> stylesheet = fxstyle.load_stylesheet(theme="dracula")
>>> widget.setStyleSheet(stylesheet)
New code should prefer apply_theme / register_themed_root instead.
Applying a theme to a window:
For DCC-embedded windows, call fxstyle.register_themed_root(window)
once at construction; FXMainWindow does this automatically.
Getting colors for custom widgets:
>>> colors = fxstyle.get_theme_colors()
>>> surface = colors["surface"] # Main background
>>> sunken = colors["surface_sunken"] # Input/list backgrounds
>>> text = colors["text"] # Primary text color
Theme persistence (automatic):
Themes are automatically saved when using apply_theme().
On next application startup, the saved theme is automatically loaded.
Classes¶
FXProxyStyle
¶
Bases: QProxyStyle
flowchart TD
fxgui.fxstyle.FXProxyStyle[FXProxyStyle]
click fxgui.fxstyle.FXProxyStyle href "" "fxgui.fxstyle.FXProxyStyle"
A custom style class that extends QProxyStyle to provide custom icons.
This style provides theme-aware standard icons (file dialogs, message boxes, etc.) using Material Design icons from the fxicons library.
Note
Qt stylesheets bypass QProxyStyle's drawControl() method, which means
icon colorization for item views (lists, trees) and menus cannot be
handled here when stylesheets are applied. Use FXIconColorDelegate
from fxwidgets for icon colorization in item views instead.
Examples:
Methods:
| Name | Description |
|---|---|
set_icon_color |
Set the color of the icons. |
standardIcon |
Return an icon for the given standardIcon. |
Methods:¶
set_icon_color
¶
standardIcon
¶
standardIcon(
standardIcon: StandardPixmap,
option: Optional[QStyleOption] = None,
widget: Optional[QWidget] = None,
) -> QIcon
Return an icon for the given standardIcon.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
standardIcon
¶ |
StandardPixmap
|
The standard pixmap for which an icon should be returned. |
required |
option
¶ |
Optional[QStyleOption]
|
An option that can be used to fine-tune the look of the icon. Defaults to None. |
None
|
widget
¶ |
Optional[QWidget]
|
The widget for which the icon is being requested. Defaults to None. |
None
|
Returns:
| Type | Description |
|---|---|
QIcon
|
The icon for the standardIcon. If no custom icon is found, |
QIcon
|
the default icon is returned. |
FXThemeAware
¶
Mixin that makes widgets automatically respond to theme changes.
This mixin provides automatic theme updates for custom widgets. When the theme changes, connected widgets are notified and can update their appearance.
Usage
- Inherit from FXThemeAware FIRST:
class MyWidget(FXThemeAware, QWidget) - Override
_on_theme_changed()to apply custom colors (optional) - Use
self.themeproperty to access current theme colors - Optionally declare a
theme_styleclass attribute for automatic QSS
Examples:
New API (recommended):
>>> from fxgui import fxstyle
>>> class FXMyWidget(FXThemeAware, QWidget):
... # Option 1: Declarative QSS with color tokens
... theme_style = '''
... FXMyWidget {
... background: @surface;
... border: 1px solid @border;
... }
... '''
...
... # Option 2: Programmatic colors in paintEvent
... def paintEvent(self, event):
... painter = QPainter(self)
... painter.fillRect(self.rect(), QColor(self.theme.surface))
Legacy API (deprecated, still works):
>>> class FXMyWidget(FXThemeAware, QWidget):
... def _apply_theme_styles(self):
... colors = fxstyle.get_theme_colors()
... self.setStyleSheet(f"background: {colors['surface']};")
Attributes:
| Name | Type | Description |
|---|---|---|
theme |
FXThemeColors
|
Property returning current theme colors as a FXThemeColors object. |
theme_style |
str
|
Optional class attribute with QSS containing @color tokens. |
Attributes¶
theme
property
¶
theme: FXThemeColors
Get current theme colors as a namespace object.
Returns:
| Type | Description |
|---|---|
FXThemeColors
|
FXThemeColors object with color attributes (e.g., theme.surface, |
FXThemeColors
|
theme.accent_primary, theme.text). |
Examples:
>>> def paintEvent(self, event):
... painter = QPainter(self)
... painter.fillRect(self.rect(), QColor(self.theme.surface))
... painter.setPen(QColor(self.theme.text))
Note
The returned object is a shared, cached snapshot of the current theme; treat it as read-only. It is rebuilt whenever the theme changes.
Methods:¶
__apply_theme_style_attribute
¶
Process the theme_style class attribute and apply it.
FXThemeColors
¶
FXThemeColors(colors_dict: dict)
Namespace for accessing theme colors with dot notation.
This class provides a convenient way to access theme colors using attribute access instead of dictionary lookup.
Examples:
>>> colors = FXThemeColors(fxstyle.get_theme_colors())
>>> colors.surface # "#302f2f"
>>> colors.accent_primary # "#2196F3"
Initialize with a colors dictionary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
dict
|
Dictionary of color name to hex value mappings. |
required |
Methods:¶
FXThemeManager
¶
Bases: QObject
flowchart TD
fxgui.fxstyle.FXThemeManager[FXThemeManager]
click fxgui.fxstyle.FXThemeManager href "" "fxgui.fxstyle.FXThemeManager"
Singleton that emits theme_changed(str) when the theme changes.
Methods:
| Name | Description |
|---|---|
notify_theme_changed |
Called by apply_theme() when theme changes. |
Attributes:
| Name | Type | Description |
|---|---|---|
current_theme |
str
|
Return the current theme name. |
Functions:¶
apply_theme
¶
Apply a theme everywhere.
Canonical form::
fxstyle.apply_theme("dracula")
Updates the persistent theme state, rebuilds the theme stylesheet,
re-applies it to every registered root (see
:func:register_themed_root), refreshes icon colors, and emits
theme_changed.
.. deprecated::
The old form apply_theme(widget, theme) still works: it
registers widget as a themed root and proceeds. Prefer
apply_theme(theme).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Optional[str]
|
The theme name to apply (e.g., "dark", "light"). |
None
|
|
Optional[QWidget]
|
Deprecated. A widget to register as a themed root. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The theme that was applied. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the theme does not exist. |
TypeError
|
If no theme name was provided. |
build_stylesheet
¶
Build the complete theme stylesheet.
Concatenates the platform font block, the base style.qss, and all
fragments registered via :func:register_widget_style, then resolves
every @token in a single pass. Pure: no global state is modified.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Optional[str]
|
Theme name. Defaults to the current theme. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The ready-to-apply stylesheet string. |
colors
¶
colors() -> FXThemeColors
Get the current theme colors as a namespace (canonical read API).
Cheap enough for paintEvent hot paths: the namespace is cached
per theme and rebuilt only on theme switches. Treat it as read-only.
Returns:
| Type | Description |
|---|---|
FXThemeColors
|
FXThemeColors with one attribute per color role. |
Examples:
get_accent_colors
¶
get_accent_colors() -> dict
Get the accent colors for the current theme.
Accent colors are used for interactive elements:
-
primary: Hover borders on input widgets (QLineEdit, QComboBox, etc.), selection backgrounds, progress bar/slider gradients (end color), menu bar selections, pressed/selected items in item views.
-
secondary: Progress bar/slider gradients (start color), widget item hover backgrounds, menu pressed backgrounds, list/tree item hover highlights.
Returns:
| Type | Description |
|---|---|
dict
|
Dictionary containing 'primary' and 'secondary' accent colors |
dict
|
from the current theme. |
Examples:
get_available_themes
¶
get_available_themes() -> list
Get a list of all available theme names from the color configuration.
Returns:
| Type | Description |
|---|---|
list
|
List of theme names (e.g., ["dark", "light", "dracula", "one_dark_pro"]). |
Examples:
get_colors
¶
get_colors() -> dict
Get the cached color configuration dictionary.
This is the preferred way to access colors throughout the application. Colors are loaded once from the YAML file and cached for subsequent calls.
Returns:
| Type | Description |
|---|---|
dict
|
The complete color configuration containing 'feedback', 'dcc', and |
dict
|
'themes' sections. |
Examples:
get_contrast_text_color
¶
get_contrast_text_color(background_hex: str) -> str
Determine whether to use white or black text on a given background.
Uses WCAG luminance calculation to ensure readable contrast.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
The background color as a hex string. |
required |
Returns:
| Type | Description |
|---|---|
str
|
"#FFFFFF" for dark backgrounds, "#000000" for light backgrounds. |
get_default_theme
¶
get_default_theme() -> str
Get the theme an application falls back to when none is saved.
Returns:
| Type | Description |
|---|---|
str
|
The name set by |
get_feedback_colors
¶
get_feedback_colors() -> dict
Get the feedback/status colors for notifications and logging.
These colors are used by FXNotificationBanner, FXLogWidget,
and other status/feedback widgets.
Each level provides both a foreground (text/icon) and background
color designed to work together with appropriate contrast.
The function first checks for theme-specific feedback colors (defined within the current theme), then falls back to the global feedback colors for backward compatibility.
Returns:
| Type | Description |
|---|---|
dict
|
Dictionary with keys: 'debug', 'info', 'success', 'warning', 'error'. |
dict
|
Each value is a dict with 'foreground' and 'background' keys. |
Examples:
get_font_family
¶
Get the resolved font stack for a single role.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
One of "title", "body", or "mono". Unknown roles fall back to "body". Defaults to "body". |
'body'
|
|
Optional[str]
|
Theme name. Defaults to the current theme. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
A QSS |
Examples:
get_fonts
¶
Get the resolved font stack for every role in a theme.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Optional[str]
|
Theme name. Defaults to the current theme. |
None
|
Returns:
| Type | Description |
|---|---|
Dict[str, str]
|
Mapping of role name ("title", "body", "mono") to a QSS |
Dict[str, str]
|
|
Dict[str, str]
|
removed, so this reports what will actually be drawn. |
Examples:
get_icon_color
¶
get_icon_color() -> str
Get the icon color for the current theme.
This color is used to tint monochrome SVG icons so they match the theme.
It's applied by fxicons.get_icon() and FXProxyStyle for standard
Qt icons.
Returns:
| Type | Description |
|---|---|
str
|
The icon color as a hex string from the current theme's configuration. |
Examples:
get_icon_on_accent_primary
¶
get_icon_on_accent_primary() -> str
Get the icon color for accent_primary backgrounds.
This color should be used for icons displayed on selected items or other elements that use the accent_primary color as their background.
If not explicitly defined in the theme, falls back to text_on_accent_primary, which is auto-computed based on the accent_primary color's luminance.
Returns:
| Type | Description |
|---|---|
str
|
The icon color as a hex string for use on accent_primary backgrounds. |
Examples:
get_icon_on_accent_secondary
¶
get_icon_on_accent_secondary() -> str
Get the icon color for accent_secondary backgrounds.
This color should be used for icons displayed on hovered items or other elements that use the accent_secondary color as their background.
If not explicitly defined in the theme, falls back to text_on_accent_secondary, which is auto-computed based on the accent_secondary color's luminance.
Returns:
| Type | Description |
|---|---|
str
|
The icon color as a hex string for use on accent_secondary backgrounds. |
Examples:
get_luminance
¶
Calculate the relative luminance of a color.
Uses the WCAG 2.0 formula for relative luminance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
A hex color string (e.g., "#007ACC" or "007ACC"). |
required |
Returns:
| Type | Description |
|---|---|
float
|
The relative luminance value between 0 (black) and 1 (white). |
get_theme
¶
get_theme() -> str
Get the current theme name.
On first access, the theme is loaded from persistent storage. If no theme was previously saved, defaults to "dark".
Returns:
| Type | Description |
|---|---|
str
|
The current theme name (e.g., "dark", "light"). |
get_theme_colors
¶
get_theme_colors() -> dict
Get the color palette for the current theme.
Returns a dictionary with all semantic color roles:
Surface Colors (Backgrounds):
surface: Main widget/window backgrounds, buttons, selected tabssurface_alt: Alternate row backgrounds in lists/tablessurface_sunken: Recessed areas - input fields, lists, menustooltip: Tooltip backgrounds
Border Colors:
border: Standard borders on inputs, containers, menusborder_light: Subtle borders - tooltips, buttons, tabsborder_strong: Emphasized borders - frames, separators
Text Colors:
text: Primary text for all widgetstext_muted: De-emphasized text - inactive tabs, placeholderstext_disabled: Disabled widget texttext_on_accent_primary: Text on accent_primary backgrounds (optional)text_on_accent_secondary: Text on accent_secondary backgrounds (optional)
Interactive States:
state_hover: Hover state backgroundsstate_pressed: Pressed/checked/active backgrounds
Scrollbar Colors:
scrollbar_track: Track/gutter backgroundscrollbar_thumb: Draggable thumbscrollbar_thumb_hover: Thumb hover state
Layout Colors:
grid: Table gridlines, header bordersseparator: Separator/splitter hover backgrounds
Slider Colors:
slider_thumb: Slider handle colorslider_thumb_hover: Slider handle hover/pressed
Icon Colors:
icon: Tint color for monochrome iconsicon_on_accent_primary: Icon color on accent_primary backgrounds (optional)icon_on_accent_secondary: Icon color on accent_secondary backgrounds (optional)
Returns:
| Type | Description |
|---|---|
dict
|
Dictionary containing theme-specific colors. |
Examples:
invalidate_standard_icon_map
¶
Invalidate the cached standard icon map.
This should be called when changing themes so icons are regenerated with the new color scheme on next access.
is_light_theme
¶
is_light_theme() -> bool
Check if the current theme is a light theme.
Determines theme brightness by analyzing the surface color's lightness. This is more reliable than checking the theme name since it works with any custom theme.
Returns:
| Type | Description |
|---|---|
bool
|
True if the current theme is light, False if dark. |
Examples:
load_saved_theme
¶
load_saved_theme() -> str
Load the saved theme from persistent storage.
If no theme has been saved, returns the default theme -- "dark", or
whatever set_default_theme was given.
Returns:
| Type | Description |
|---|---|
str
|
The saved theme name, or the default if none is saved or the |
str
|
saved one is not offered by the current color file. |
Examples:
load_stylesheet
¶
load_stylesheet(
style_file: str = STYLE_FILE, extra: Optional[str] = None, theme: str = None
) -> str
Load the stylesheet and replace placeholders with actual values.
Note
Kept for backward compatibility and manual DCC styling. New code
should rely on :func:register_themed_root /
:func:apply_theme, which use :func:build_stylesheet
(including registered widget fragments; this function does not).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
The path to the QSS file. Defaults to |
STYLE_FILE
|
|
Optional[str]
|
Extra stylesheet content to append. Defaults to None. |
None
|
|
str
|
The theme to use (e.g., "dark", "light", "dracula"). If None, uses the saved theme from persistent storage. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The stylesheet with all placeholders replaced. |
mark_as_title
¶
Draw a widget's text in the theme's title font role.
Sets the dynamic property the theme stylesheet keys the title role
on, then repolishes so the change lands on an already-shown widget.
Only the family changes: size and weight keep coming from whatever
rule or setFont call already governed the widget.
With a color file that leaves title empty, or names the same
family for both roles, this is a no-op visually.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
QWidget
|
The widget whose text is a title. |
required |
|
bool
|
False removes the mark and returns the widget to the body role. Defaults to True. |
True
|
Examples:
register_fonts
¶
Register font files with Qt so a color file may name them.
Hands each file to QFontDatabase.addApplicationFont and reports
the outcome per file instead of swallowing it: a face that fails to
load is not an error Qt raises, it is a family that silently is not
there, and the stylesheet naming it then renders as an arbitrary
substitution. Themed roots are restyled afterwards, so registering
late is safe and call order does not matter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
A font file path, or an iterable of them. Anything
|
required |
Returns:
| Type | Description |
|---|---|
Dict[str, list]
|
Mapping of each path, as given, to the family names Qt |
Dict[str, list]
|
registered from it. **An empty list means that file did not |
Dict[str, list]
|
load.** Those family names are the ones to put in the color |
Dict[str, list]
|
file's |
Dict[str, list]
|
predictable from its filename. |
Examples:
>>> loaded = fxstyle.register_fonts(brand_dir.glob("*.ttf"))
>>> missing = [path for path, families in loaded.items()
... if not families]
Note
Qt needs a live QApplication before it will accept an application font. Called earlier than that, every file reports as failed.
register_themed_root
¶
register_themed_root(root: QObject) -> None
Register a widget (or QApplication) as a themed root.
The current theme stylesheet is applied to it immediately and
re-applied on every subsequent :func:apply_theme call. Qt cascades
the sheet to all descendants, so children need no registration.
Standalone apps: FXApplication registers itself; nothing to do.
DCC-embedded windows: FXMainWindow registers itself when the
running QApplication is foreign, so the host app is never restyled.
Roots are held weakly; destroyed widgets drop out automatically.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
QObject
|
Any object with |
required |
register_widget_style
¶
Register a widget's QSS fragment with the theme stylesheet.
Call once at module import time. The fragment may use @tokens
(e.g. @surface, @border); use your widget's class name as
selector to scope the rules. Identical fragments are registered once.
If themed roots already exist, the rebuilt sheet is re-applied to them immediately, so late registration is safe.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Stylesheet fragment with optional |
required |
Examples:
replace_colors
¶
replace_colors(
stylesheet: str, colors_dict: dict = None, prefix: str = ""
) -> str
Replace color placeholders in a stylesheet with actual color values.
This function searches for placeholders in the format @{prefix}{key}
and replaces them with the corresponding color values from the dictionary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
The stylesheet string containing color placeholders. |
required |
|
dict
|
Dictionary containing color definitions. Only top-level non-dict values are used. Defaults to colors from get_colors(). |
None
|
|
str
|
Prefix for placeholder names. Defaults to empty string. |
''
|
Returns:
| Type | Description |
|---|---|
str
|
The stylesheet with all matching placeholders replaced. |
Examples:
save_theme
¶
set_color_file
¶
set_color_file(color_file: str) -> None
Set a custom color configuration file.
This clears the color cache and sets the new file as the active
color source. The next call to get_colors() will load from this file.
Supports both YAML (.yaml, .yml) files with inheritance via anchors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Path to the custom YAML color configuration file. |
required |
Examples:
set_default_theme
¶
Set the theme an application falls back to when none is saved.
"dark" unless this is called. For an application that ships a theme of its own in a custom color file: without this, its own first run is indistinguishable from a person having chosen "dark", so it cannot both honour a saved choice and default to its own brand.
Not validated here, because the color file that has to offer the
theme may be set afterwards; load_saved_theme falls back to "dark"
if the file turns out not to offer it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
The theme name to fall back to. |
required |
Examples:
set_style
¶
set_style(widget: QWidget, style: str = None) -> FXProxyStyle
Set the style.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
QWidget
|
The QWidget subclass to set the style to. |
required |
|
str
|
The style to set. Defaults to None. |
None
|
Returns:
| Type | Description |
|---|---|
FXProxyStyle
|
The custom style. |
Note
You can retrieve the styles available on your system with
QStyleFactory.keys(). Only those string values are accepted
in the style argument.