"""Main file of qdarktheme."""
from __future__ import annotations
import json
import shutil
import warnings
from functools import partial
from qdarktheme import __version__, filter, resources
from qdarktheme.color import Color
from qdarktheme.template_engine import Template
from qdarktheme.util import get_cash_root_path, get_logger
_logger = get_logger(__name__)
def _color_schema(theme: str) -> dict[str, str | dict]:
try:
return json.loads(resources.COLOR_SCHEMAS[theme])
except KeyError:
raise ValueError(f'invalid argument, not a dark or light: "{theme}"') from None
def _marge_colors(color_schema: dict[str, str | dict], custom_colors: dict[str, str]):
for color_id, color_format in custom_colors.items():
if not Color.check_hex_format(color_format):
raise ValueError(
f'invalid value for argument custom_colors: "{color_format}". '
"Only support following hexadecimal notations: #RGB, #RGBA, #RRGGBB and #RRGGBBAA. "
"R (red), G (green), B (blue), and A (alpha) are hexadecimal characters "
"(0-9, a-f or A-F)."
) from None
parent_key, *child_key = color_id.split(">")
try:
color_info = color_schema[parent_key]
if len(child_key) == 0:
if isinstance(color_info, str):
color_schema[parent_key] = color_format
else:
color_info["base"] = color_format
elif len(child_key) == 1:
color_info = color_schema[parent_key]
if isinstance(color_info, dict):
# Check if child_key is valid.
color_info[child_key[0]]
color_info[child_key[0]] = color_format
else:
raise KeyError
except KeyError:
raise KeyError(f'invalid color id for argument custom_colors: "{color_id}".') from None
[docs]def load_stylesheet(
theme: str = "dark",
corner_shape: str = "rounded",
custom_colors: dict[str, str] | None = None,
*,
border: str | None = None,
) -> str:
"""Load the style sheet which looks like flat design. There are `dark` and `light` theme.
Args:
theme: The name of the theme. There are `dark` and `light` theme.
corner_shape: The corner shape. There are `rounded` and `sharp` shape.
custom_colors: The custom color map. Overrides the default color for color id you set.
border: The corner shape. There are `rounded` and `sharp` shape.
This argument is deprecated since v1.2.0. Please use `corner_shape` instead.
This argument override value of argument `corner_shape`.
Raises:
ValueError: If the arguments of this method is wrong.
KeyError: If the color id of custom_colors is wrong.
Returns:
The stylesheet string for the given arguments.
Examples:
Set stylesheet to your Qt application.
1. Dark Theme ::
app = QApplication([])
app.setStyleSheet(qdarktheme.load_stylesheet())
# or
app.setStyleSheet(qdarktheme.load_stylesheet("dark"))
2. Light Theme ::
app = QApplication([])
app.setStyleSheet(qdarktheme.load_stylesheet("light"))
3. Sharp corner ::
# Change corner shape to sharp.
app = QApplication([])
app.setStyleSheet(qdarktheme.load_stylesheet(corner_shape="sharp"))
4. Customize color ::
app = QApplication([])
app.setStyleSheet(qdarktheme.load_stylesheet(custom_colors={"primary": "#D0BCFF"}))
"""
color_schema = _color_schema(theme)
if corner_shape not in ("rounded", "sharp"):
raise ValueError(f'invalid argument, not a rounded or sharp: "{corner_shape}"')
if border not in (None, "rounded", "sharp"):
raise ValueError(f'invalid argument, not a rounded or sharp: "{border}"')
if border is not None:
warnings.warn(
'deprecated argument, "border" is deprecated since v2.0.0. '
'Please use "corner_shape" instead.',
FutureWarning,
)
corner_shape = border
get_cash_root_path(__version__).mkdir(parents=True, exist_ok=True)
if custom_colors is not None:
_marge_colors(color_schema, custom_colors)
# Build stylesheet
template = Template(
resources.TEMPLATE_STYLESHEET,
{"color": filter.color, "corner": filter.corner, "env": filter.env, "url": filter.url},
)
replacements = dict(color_schema, **{"corner-shape": corner_shape})
return template.render(replacements)
[docs]def clear_cache() -> None:
"""Clear the caches in system home path.
PyQtDarkTheme build the caches of resources in the system home path.
You can clear the caches by running this method.
"""
try:
shutil.rmtree(get_cash_root_path(__version__))
_logger.info(f"The caches({get_cash_root_path(__version__)}) has been deleted")
except FileNotFoundError:
_logger.info("There is no caches")
[docs]def load_palette(theme: str = "dark", custom_colors: dict[str, str] | None = None):
"""Load the QPalette for the dark or light theme.
Args:
theme: The name of the theme. Available theme are `dark` and `light`.
custom_colors: The custom color map. Overrides the default color for color id you set.
Raises:
TypeError: If the arg name of theme is wrong.
KeyError: If the color id of custom_colors is wrong.
Returns:
QPalette: The QPalette for the given theme.
Examples:
Set QPalette to your Qt application.
1. Dark Theme ::
app = QApplication([])
app.setPalette(qdarktheme.load_palette())
# or
app.setPalette(qdarktheme.load_palette("dark"))
2. Light Theme ::
app = QApplication([])
app.setPalette(qdarktheme.load_palette("light"))
3. Customize color ::
app = QApplication([])
app.setPalette(custom_colors={"primary": "#D0BCFF"})
"""
color_schema = _color_schema(theme)
if custom_colors is not None:
_marge_colors(color_schema, custom_colors)
mk_template = partial(Template, filters={"color": filter.color, "palette": filter.palette_format})
return resources.mk_q_palette(mk_template, color_schema)
[docs]def get_themes() -> tuple[str, ...]:
"""Return available theme names.
Returns:
Tuple of available theme names.
"""
return resources.THEMES