- added qdarktheme package into the code
6
libs/qdarktheme/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
"""PyQtDarkTheme - A flat dark theme for PySide and PyQt.
|
||||
|
||||
- Repository: https://github.com/5yutan5/PyQtDarkTheme
|
||||
- Documentation: https://pyqtdarktheme.readthedocs.io
|
||||
"""
|
||||
from libs.qdarktheme.main import __version__, clear_cache, get_themes, load_palette, load_stylesheet
|
||||
251
libs/qdarktheme/main.py
Normal file
@@ -0,0 +1,251 @@
|
||||
"""Main file of qdarktheme."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from libs.qdarktheme.qtpy import __version__ as qt_version
|
||||
from libs.qdarktheme.qtpy.qt_compat import QT_API
|
||||
from libs.qdarktheme.util import OPERATORS, compare_v, get_logger, multi_replace
|
||||
|
||||
# Version of PyQtDarkTheme
|
||||
__version__ = "1.1.1"
|
||||
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
if qt_version is None:
|
||||
_logger.warning("Failed to detect Qt version. Load Qt version as the latest version.")
|
||||
_qt_version = "10.0.0" # Fairly future version for always setting latest version.
|
||||
else:
|
||||
_qt_version = qt_version
|
||||
|
||||
if QT_API is None:
|
||||
_qt_api = "PySide6"
|
||||
_logger.warning(f"Failed to detect Qt binding. Load Qt API as '{_qt_api}'.")
|
||||
else:
|
||||
_qt_api = QT_API
|
||||
|
||||
if None in [qt_version, QT_API]:
|
||||
_logger.warning(
|
||||
"Maybe you need to install qt-binding. Available Qt-binding packages: PySide6, PyQt6, PyQt5, PySide2."
|
||||
)
|
||||
|
||||
_RESOURCE_HOME_DIR = Path.home() / ".qdarktheme"
|
||||
_RESOURCES_BASE_DIR = _RESOURCE_HOME_DIR / f"v{__version__}"
|
||||
|
||||
# Pattern
|
||||
_PATTERN_RADIUS = re.compile(r"\$radius\{[\s\S]*?\}")
|
||||
_PATTERN_ENV_PATCH = re.compile(r"\$env_patch\{[\s\S]*?\}")
|
||||
|
||||
|
||||
def _build_svg_files(theme: str, theme_resources_dir: Path) -> None:
|
||||
svg_resources_dir = theme_resources_dir / "svg"
|
||||
if not svg_resources_dir.exists():
|
||||
svg_resources_dir.mkdir()
|
||||
else:
|
||||
return
|
||||
|
||||
if theme == "dark":
|
||||
from libs.qdarktheme.themes.dark.svg import SVG_RESOURCES
|
||||
else:
|
||||
from libs.qdarktheme.themes.light.svg import SVG_RESOURCES
|
||||
|
||||
for file_name, code in json.loads(SVG_RESOURCES).items():
|
||||
(svg_resources_dir / f"{file_name}.svg").write_text(code)
|
||||
|
||||
|
||||
def get_themes() -> tuple[str, ...]:
|
||||
"""Return available theme list.
|
||||
|
||||
Returns:
|
||||
Available themes.
|
||||
"""
|
||||
from libs.qdarktheme.themes import THEMES
|
||||
|
||||
return THEMES
|
||||
|
||||
|
||||
def _replace_rounded(match: re.Match) -> str:
|
||||
return match.group().replace("$radius{", "").replace("}", "")
|
||||
|
||||
|
||||
def _replace_sharp(match: re.Match) -> str:
|
||||
return _PATTERN_RADIUS.sub("0", match.group())
|
||||
|
||||
|
||||
def _parse_radius(stylesheet: str, border: str = "rounded") -> dict[str, str]:
|
||||
"""Parse `$radius{...}` placeholder in template stylesheet."""
|
||||
matches = _PATTERN_RADIUS.finditer(stylesheet)
|
||||
replace = _replace_rounded if border == "rounded" else _replace_sharp
|
||||
return {match.group(): replace(match) for match in matches}
|
||||
|
||||
|
||||
def _parse_env_patch(stylesheet: str) -> dict[str, str]:
|
||||
"""Parse `$env_patch{...}` placeholder in template stylesheet.
|
||||
|
||||
Template stylesheet has `$env_patch{...}` symbol.
|
||||
This symbol has json string and resolve the differences of the style between qt versions.
|
||||
The json keys:
|
||||
* version - the qt version and qualifier. Available qualifiers: [==, !=, >=, <=, >, <].
|
||||
* qt - the name of qt binding.
|
||||
* value - the qt stylesheet string
|
||||
|
||||
Args:
|
||||
stylesheet: The qt stylesheet string.
|
||||
|
||||
Raises:
|
||||
SyntaxError: If the version operator in version key of `$env_patch{...}` is wrong.
|
||||
|
||||
Returns:
|
||||
The dictionary. Key is the text of $env_patch{...} symbol.
|
||||
Value is the value of the `value` key in $env_patch.
|
||||
"""
|
||||
replacements: dict[str, str] = {}
|
||||
for match in re.finditer(_PATTERN_ENV_PATCH, stylesheet):
|
||||
match_text = match.group()
|
||||
json_text = match_text.replace("$env_patch", "")
|
||||
env_property: dict[str, str] = json.loads(json_text)
|
||||
|
||||
patch_version = env_property.get("version")
|
||||
patch_qt = env_property.get("qt")
|
||||
patch_os = env_property.get("os")
|
||||
patch_value = env_property["value"]
|
||||
|
||||
results: list[bool] = []
|
||||
# Parse version
|
||||
if patch_version is not None:
|
||||
for operator in OPERATORS:
|
||||
if operator not in patch_version:
|
||||
continue
|
||||
version = patch_version.replace(operator, "")
|
||||
results.append(compare_v(_qt_version, operator, version))
|
||||
break
|
||||
else:
|
||||
raise SyntaxError(
|
||||
f"invalid character in qualifier. Available qualifiers {list(OPERATORS.keys())}"
|
||||
) from None
|
||||
# Parse qt binding
|
||||
if patch_qt is not None:
|
||||
if QT_API is None:
|
||||
results.append(False)
|
||||
results.append(patch_qt.lower() == _qt_api.lower())
|
||||
# Parse os
|
||||
if patch_os is not None:
|
||||
results.append(platform.system().lower() in patch_os.lower())
|
||||
|
||||
replacements[match_text] = patch_value if all(results) else ""
|
||||
return replacements
|
||||
|
||||
|
||||
def load_stylesheet(theme: str = "dark", border: str = "rounded") -> str:
|
||||
"""Load the style sheet which looks like flat design. There are two themes, dark theme and light theme.
|
||||
|
||||
Args:
|
||||
theme: The name of the theme. Available themes are "dark" and "light".
|
||||
border: The border style. Available styles are "rounded" and "sharp".
|
||||
|
||||
Raises:
|
||||
TypeError: If the arg of theme name is wrong.
|
||||
|
||||
Returns:
|
||||
The stylesheet string for the given theme.
|
||||
|
||||
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"))
|
||||
|
||||
Change sharp frame.
|
||||
|
||||
Sharp Frame::
|
||||
|
||||
app = QApplication([])
|
||||
app.setStyleSheet(qdarktheme.load_stylesheet(border="sharp"))
|
||||
"""
|
||||
if theme not in get_themes():
|
||||
raise TypeError("The argument [theme] can only be specified as 'dark' or 'light'.") from None
|
||||
|
||||
if border not in ("rounded", "sharp"):
|
||||
raise TypeError("The argument [border] can only be specified as 'rounded' or 'sharp'.")
|
||||
|
||||
theme_resources_dir = _RESOURCES_BASE_DIR / theme
|
||||
theme_resources_dir.mkdir(parents=True, exist_ok=True)
|
||||
_build_svg_files(theme, theme_resources_dir)
|
||||
|
||||
if theme == "dark":
|
||||
from libs.qdarktheme.themes.dark.stylesheet import STYLE_SHEET
|
||||
else:
|
||||
from libs.qdarktheme.themes.light.stylesheet import STYLE_SHEET
|
||||
|
||||
# Build stylesheet
|
||||
# Radius
|
||||
replacements_radius = _parse_radius(STYLE_SHEET, border)
|
||||
stylesheet = multi_replace(STYLE_SHEET, replacements_radius)
|
||||
# Env
|
||||
replacements_env = _parse_env_patch(stylesheet)
|
||||
# Path
|
||||
replacements_env["${path}"] = _RESOURCES_BASE_DIR.as_posix()
|
||||
return multi_replace(stylesheet, replacements_env)
|
||||
|
||||
|
||||
def clear_cache():
|
||||
"""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(_RESOURCE_HOME_DIR)
|
||||
_logger.info(f"The caches({_RESOURCE_HOME_DIR}) has been deleted")
|
||||
except FileNotFoundError:
|
||||
_logger.info("There is no caches")
|
||||
|
||||
|
||||
def load_palette(theme: str = "dark"):
|
||||
"""Load the QPalette for the dark or light theme.
|
||||
|
||||
Args:
|
||||
theme: The name of the theme. Available theme are "dark" and "light".
|
||||
|
||||
Raises:
|
||||
TypeError: If the arg name of theme 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"))
|
||||
"""
|
||||
if theme not in get_themes():
|
||||
raise TypeError("The argument [theme] can only be specified as 'dark' or 'light'.") from None
|
||||
|
||||
if theme == "dark":
|
||||
from libs.qdarktheme.themes.dark.palette import PALETTE
|
||||
else:
|
||||
from libs.qdarktheme.themes.light.palette import PALETTE
|
||||
return PALETTE
|
||||
19
libs/qdarktheme/qtpy/QtCore/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
||||
"""Module for QtCore."""
|
||||
from libs.qdarktheme.qtpy.qt_compat import QT_API, qt_import_error
|
||||
|
||||
if QT_API is None:
|
||||
raise qt_import_error
|
||||
if QT_API == "PySide6":
|
||||
from PySide6.QtCore import * # type: ignore # noqa: F403
|
||||
elif QT_API == "PyQt6":
|
||||
from PyQt6.QtCore import * # type: ignore # noqa: F403
|
||||
|
||||
Slot = pyqtSlot # noqa: F405
|
||||
Signal = pyqtSignal # noqa: F405
|
||||
elif QT_API == "PyQt5":
|
||||
from PyQt5.QtCore import * # type: ignore # noqa: F403
|
||||
|
||||
Slot = pyqtSlot # noqa: F405
|
||||
Signal = pyqtSignal # noqa: F405
|
||||
elif QT_API == "PySide2":
|
||||
from PySide2.QtCore import * # type: ignore # noqa: F403
|
||||
20
libs/qdarktheme/qtpy/QtGui/__init__.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""Module for QtGui."""
|
||||
from libs.qdarktheme.qtpy.qt_compat import QT_API, qt_import_error
|
||||
|
||||
if QT_API is None:
|
||||
raise qt_import_error
|
||||
if QT_API == "PySide6":
|
||||
from PySide6.QtGui import * # type: ignore # noqa: F403
|
||||
elif QT_API == "PyQt6":
|
||||
from PyQt6.QtGui import * # type: ignore # noqa: F403
|
||||
elif QT_API == "PyQt5":
|
||||
from PyQt5.QtGui import * # type: ignore # noqa: F403
|
||||
from PyQt5.QtWidgets import QAction, QActionGroup, QShortcut # type: ignore
|
||||
elif QT_API == "PySide2":
|
||||
from PySide2.QtGui import * # type: ignore # noqa: F403
|
||||
from PySide2.QtWidgets import QAction, QActionGroup, QShortcut # type: ignore
|
||||
|
||||
if QT_API in ["PyQt5", "PySide2"]:
|
||||
QAction = QAction # type: ignore # noqa: SIM909
|
||||
QActionGroup = QActionGroup # type: ignore # noqa: SIM909
|
||||
QShortcut = QShortcut # type: ignore # noqa: SIM909
|
||||
13
libs/qdarktheme/qtpy/QtSvg/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""Module for QtSvg."""
|
||||
from libs.qdarktheme.qtpy.qt_compat import QT_API, qt_import_error
|
||||
|
||||
if QT_API is None:
|
||||
raise qt_import_error
|
||||
if QT_API == "PySide6":
|
||||
from PySide6.QtSvg import * # type: ignore # noqa: F403
|
||||
elif QT_API == "PyQt6":
|
||||
from PyQt6.QtSvg import * # type: ignore # noqa: F403
|
||||
elif QT_API == "PyQt5":
|
||||
from PyQt5.QtSvg import * # type: ignore # noqa: F403
|
||||
elif QT_API == "PySide2":
|
||||
from PySide2.QtSvg import * # type: ignore # noqa: F403
|
||||
50
libs/qdarktheme/qtpy/QtWidgets/__init__.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""Module for QtWidgets."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from libs.qdarktheme.qtpy.qt_compat import QT_API
|
||||
from libs.qdarktheme.qtpy.QtCore import Qt
|
||||
from libs.qdarktheme.qtpy.QtGui import QPalette
|
||||
|
||||
if QT_API == "PySide6":
|
||||
from PySide6.QtWidgets import * # type: ignore # noqa: F403
|
||||
elif QT_API == "PyQt6":
|
||||
from PyQt6.QtWidgets import * # type: ignore # noqa: F403
|
||||
elif QT_API == "PyQt5":
|
||||
from PyQt5.QtWidgets import * # type: ignore # noqa: F403
|
||||
elif QT_API == "PySide2":
|
||||
from PySide2.QtWidgets import * # type: ignore # noqa: F403
|
||||
|
||||
|
||||
class Application(QApplication): # type: ignore # noqa: F405
|
||||
"""Override QApplication."""
|
||||
|
||||
def __init__(self, args: Sequence[str] | None = None) -> None:
|
||||
"""Override QApplication method."""
|
||||
super().__init__(args)
|
||||
|
||||
def exec(self) -> int:
|
||||
"""Override QApplication method."""
|
||||
if hasattr(super(), "exec"):
|
||||
return super().exec()
|
||||
return super().exec_()
|
||||
|
||||
def exit(self, returnCode: int = 0) -> None: # noqa: N803
|
||||
"""Override QApplication method."""
|
||||
return super().exit(returnCode)
|
||||
|
||||
def setStyleSheet(self, sheet: str) -> None: # noqa: N802
|
||||
"""Override QApplication method."""
|
||||
return super().setStyleSheet(sheet)
|
||||
|
||||
def setAttribute(self, attribute: Qt.ApplicationAttribute, on: bool = True) -> None: # noqa: N802
|
||||
"""Override QApplication method."""
|
||||
super().setAttribute(attribute, on)
|
||||
|
||||
def setPalette(self, palette: QPalette, className: str | None = None) -> None: # noqa: N802, N803
|
||||
"""Override QApplication method."""
|
||||
super().setPalette(palette, className)
|
||||
|
||||
|
||||
QApplication = Application
|
||||
11
libs/qdarktheme/qtpy/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
"""Package applying Qt compat of PyQt6, PySide6, PyQt5 and PySide2."""
|
||||
from libs.qdarktheme.qtpy.qt_compat import QtImportError
|
||||
from libs.qdarktheme.qtpy.qt_version import __version__
|
||||
|
||||
try:
|
||||
from libs.qdarktheme.qtpy import QtCore, QtGui, QtSvg, QtWidgets
|
||||
except ImportError:
|
||||
from libs.qdarktheme.util import get_logger as __get_logger
|
||||
|
||||
__logger = __get_logger(__name__)
|
||||
__logger.warning("Failed to import QtCore, QtGui, QtSvg and QtWidgets.")
|
||||
82
libs/qdarktheme/qtpy/qt_compat.py
Normal file
@@ -0,0 +1,82 @@
|
||||
"""Module for Qt compat."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
class QtImportError(ImportError):
|
||||
"""Error raise if no bindings could be selected."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
qt_import_error = QtImportError(
|
||||
"Failed to import qt-binding. Check packages(pip list)."
|
||||
"\n\tAvailable Qt-binding packages: PySide6, PyQt6, PyQt5, PySide2."
|
||||
)
|
||||
|
||||
|
||||
# Qt6
|
||||
_QT_API_PYSIDE6 = "PySide6"
|
||||
_QT_API_PYQT6 = "PyQt6"
|
||||
# Qt5
|
||||
_QT_API_PYQT5 = "PyQt5"
|
||||
_QT_API_PYSIDE2 = "PySide2"
|
||||
|
||||
|
||||
_API_LIST = [_QT_API_PYSIDE6, _QT_API_PYQT6, _QT_API_PYQT5, _QT_API_PYSIDE2]
|
||||
|
||||
|
||||
def _get_loaded_api() -> str | None:
|
||||
"""Return which API is loaded.
|
||||
|
||||
If this returns anything besides None,
|
||||
importing any other Qt-binding is unsafe.
|
||||
"""
|
||||
for api in _API_LIST:
|
||||
if sys.modules.get(f"{api}.QtCore"):
|
||||
return api
|
||||
return None
|
||||
|
||||
|
||||
def _get_environ_api() -> str | None:
|
||||
"""Return which API is specified in environ."""
|
||||
_qt_api_env = os.environ.get("QT_API")
|
||||
if _qt_api_env is not None:
|
||||
_qt_api_env = _qt_api_env.lower()
|
||||
|
||||
_env_to_module = {
|
||||
"pyside6": _QT_API_PYSIDE6,
|
||||
"pyqt6": _QT_API_PYQT6,
|
||||
"pyqt5": _QT_API_PYQT5,
|
||||
"pyside2": _QT_API_PYSIDE2,
|
||||
None: None,
|
||||
}
|
||||
try:
|
||||
return _env_to_module[_qt_api_env]
|
||||
except KeyError:
|
||||
raise KeyError(
|
||||
"The environment variable QT_API has the unrecognized value "
|
||||
f"{_qt_api_env!r}. "
|
||||
f"Valid values are {[k for k in _env_to_module if k is not None]}"
|
||||
) from None
|
||||
|
||||
|
||||
def _get_installed_api() -> str | None:
|
||||
"""Return which API is installed."""
|
||||
# Fix [AttributeError: module 'importlib' has no attribute 'util']
|
||||
# See https://stackoverflow.com/a/39661116/13452582
|
||||
from importlib import util
|
||||
|
||||
for api in _API_LIST:
|
||||
if util.find_spec(api) is not None:
|
||||
return api
|
||||
return None
|
||||
|
||||
|
||||
QT_API = _get_loaded_api()
|
||||
if QT_API is None:
|
||||
QT_API = _get_environ_api()
|
||||
if QT_API is None:
|
||||
QT_API = _get_installed_api()
|
||||
18
libs/qdarktheme/qtpy/qt_version.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""Module for detecting Qt version."""
|
||||
from __future__ import annotations
|
||||
|
||||
from libs.qdarktheme.qtpy.qt_compat import QT_API
|
||||
|
||||
__version__: str | None = None
|
||||
if QT_API == "PySide6":
|
||||
from PySide6 import __version__ # type: ignore
|
||||
elif QT_API == "PyQt6":
|
||||
from PyQt6.QtCore import PYQT_VERSION_STR # type: ignore
|
||||
|
||||
__version__ = PYQT_VERSION_STR
|
||||
elif QT_API == "PyQt5":
|
||||
from PyQt5.QtCore import PYQT_VERSION_STR # type: ignore
|
||||
|
||||
__version__ = PYQT_VERSION_STR
|
||||
elif QT_API == "PySide2":
|
||||
from PySide2 import __version__ # type: ignore # noqa: F401
|
||||
45
libs/qdarktheme/themes/__init__.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""Package including resources.
|
||||
|
||||
**Warning**
|
||||
|
||||
This package created programmatically. All changes made in this file will be lost!
|
||||
Created by the `qdarktheme/tools/build_resources`.
|
||||
|
||||
|
||||
License Information
|
||||
===================
|
||||
|
||||
Material design icons
|
||||
---------------------
|
||||
|
||||
All svg files in PyQtDarkTheme is from Material design icons(which uses an Apache 2.0 license).
|
||||
|
||||
- Author: Google
|
||||
- Site: https://fonts.google.com/icons
|
||||
- Source: https://github.com/google/material-design-icons
|
||||
- License: Apache License Version 2.0 | https://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
|
||||
Modifications made to each files to change the icon color and angle and remove svg namespace.
|
||||
|
||||
The current Material design icons license summary can be viewed at:
|
||||
https://github.com/google/material-design-icons/blob/master/LICENSE
|
||||
|
||||
|
||||
QDarkStyleSheet(Source code)
|
||||
----------------------------
|
||||
|
||||
Qt stylesheets are originally fork of QDarkStyleSheet(MIT License).
|
||||
|
||||
- Author: Colin Duquesnoy
|
||||
- Site: https://github.com/ColinDuquesnoy/QDarkStyleSheet
|
||||
- Source: https://github.com/ColinDuquesnoy/QDarkStyleSheet
|
||||
- License: MIT License | https://opensource.org/licenses/MIT
|
||||
|
||||
Modifications made to a file to change the style.
|
||||
|
||||
The current QDarkStyleSheet license summary can be viewed at:
|
||||
https://github.com/ColinDuquesnoy/QDarkStyleSheet/blob/master/LICENSE.rst
|
||||
|
||||
"""
|
||||
|
||||
THEMES = ("dark", "light")
|
||||
1
libs/qdarktheme/themes/dark/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Package containing the resources for dark theme."""
|
||||
45
libs/qdarktheme/themes/dark/palette.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""Module loading QPalette."""
|
||||
from libs.qdarktheme.qtpy.QtGui import QColor, QPalette
|
||||
|
||||
_palette = QPalette()
|
||||
|
||||
# base
|
||||
_palette.setColor(QPalette.ColorRole.WindowText, QColor("#e4e7eb"))
|
||||
_palette.setColor(QPalette.ColorRole.Button, QColor("#202124"))
|
||||
_palette.setColor(QPalette.ColorRole.Text, QColor("#eff1f1"))
|
||||
_palette.setColor(QPalette.ColorRole.ButtonText, QColor("#8ab4f7"))
|
||||
_palette.setColor(QPalette.ColorRole.Base, QColor("#202124"))
|
||||
_palette.setColor(QPalette.ColorRole.Window, QColor("#202124"))
|
||||
_palette.setColor(QPalette.ColorRole.Highlight, QColor("#8ab4f7"))
|
||||
_palette.setColor(QPalette.ColorRole.HighlightedText, QColor("#202124"))
|
||||
_palette.setColor(QPalette.ColorRole.Link, QColor("#202124"))
|
||||
_palette.setColor(QPalette.ColorRole.AlternateBase, QColor("#292b2e"))
|
||||
_palette.setColor(QPalette.ColorRole.ToolTipBase, QColor("#292a2d"))
|
||||
_palette.setColor(QPalette.ColorRole.ToolTipText, QColor("#e4e7eb"))
|
||||
_palette.setColor(QPalette.ColorRole.LinkVisited, QColor("#c58af8"))
|
||||
_palette.setColor(QPalette.ColorRole.ToolTipText, QColor("#292a2d"))
|
||||
_palette.setColor(QPalette.ColorRole.ToolTipBase, QColor("#e4e7eb"))
|
||||
if hasattr(QPalette.ColorRole, "Foreground"):
|
||||
_palette.setColor(QPalette.ColorRole.Foreground, QColor("#e4e7eb")) # type: ignore
|
||||
if hasattr(QPalette.ColorRole, "PlaceholderText"):
|
||||
_palette.setColor(QPalette.ColorRole.PlaceholderText, QColor("#8a8b8d"))
|
||||
|
||||
_palette.setColor(QPalette.ColorRole.Light, QColor("#3f4042"))
|
||||
_palette.setColor(QPalette.ColorRole.Midlight, QColor("#3f4042"))
|
||||
_palette.setColor(QPalette.ColorRole.Dark, QColor("#e4e7eb"))
|
||||
_palette.setColor(QPalette.ColorRole.Mid, QColor("#3f4042"))
|
||||
_palette.setColor(QPalette.ColorRole.Shadow, QColor("#3f4042"))
|
||||
|
||||
# disabled
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, QColor("#697177"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, QColor("#697177"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor("#3f4042"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Highlight, QColor("#53575b"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, QColor("#697177"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Link, QColor("#697177"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.LinkVisited, QColor("#697177"))
|
||||
|
||||
# inactive
|
||||
_palette.setColor(QPalette.ColorGroup.Inactive, QPalette.ColorRole.Highlight, QColor("#393d41"))
|
||||
|
||||
PALETTE = _palette
|
||||
1097
libs/qdarktheme/themes/dark/stylesheet.py
Normal file
5
libs/qdarktheme/themes/dark/svg.py
Normal file
1
libs/qdarktheme/themes/light/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Package containing the resources for light theme."""
|
||||
45
libs/qdarktheme/themes/light/palette.py
Normal file
@@ -0,0 +1,45 @@
|
||||
"""Module loading QPalette."""
|
||||
from libs.qdarktheme.qtpy.QtGui import QColor, QPalette
|
||||
|
||||
_palette = QPalette()
|
||||
|
||||
# base
|
||||
_palette.setColor(QPalette.ColorRole.WindowText, QColor("#4d5157"))
|
||||
_palette.setColor(QPalette.ColorRole.Button, QColor("#f8f9fa"))
|
||||
_palette.setColor(QPalette.ColorRole.Text, QColor("#4d5157"))
|
||||
_palette.setColor(QPalette.ColorRole.ButtonText, QColor("#0081db"))
|
||||
_palette.setColor(QPalette.ColorRole.Base, QColor("#f8f9fa"))
|
||||
_palette.setColor(QPalette.ColorRole.Window, QColor("#f8f9fa"))
|
||||
_palette.setColor(QPalette.ColorRole.Highlight, QColor("#0081db"))
|
||||
_palette.setColor(QPalette.ColorRole.HighlightedText, QColor("#f8f9fa"))
|
||||
_palette.setColor(QPalette.ColorRole.Link, QColor("#f8f9fa"))
|
||||
_palette.setColor(QPalette.ColorRole.AlternateBase, QColor("#e9ecef"))
|
||||
_palette.setColor(QPalette.ColorRole.ToolTipBase, QColor("#ffffff"))
|
||||
_palette.setColor(QPalette.ColorRole.ToolTipText, QColor("#4d5157"))
|
||||
_palette.setColor(QPalette.ColorRole.LinkVisited, QColor("#660098"))
|
||||
_palette.setColor(QPalette.ColorRole.ToolTipText, QColor("#ffffff"))
|
||||
_palette.setColor(QPalette.ColorRole.ToolTipBase, QColor("#4d5157"))
|
||||
if hasattr(QPalette.ColorRole, "Foreground"):
|
||||
_palette.setColor(QPalette.ColorRole.Foreground, QColor("#4d5157")) # type: ignore
|
||||
if hasattr(QPalette.ColorRole, "PlaceholderText"):
|
||||
_palette.setColor(QPalette.ColorRole.PlaceholderText, QColor("#696a6c"))
|
||||
|
||||
_palette.setColor(QPalette.ColorRole.Light, QColor("#dadce0"))
|
||||
_palette.setColor(QPalette.ColorRole.Midlight, QColor("#dadce0"))
|
||||
_palette.setColor(QPalette.ColorRole.Dark, QColor("#4d5157"))
|
||||
_palette.setColor(QPalette.ColorRole.Mid, QColor("#dadce0"))
|
||||
_palette.setColor(QPalette.ColorRole.Shadow, QColor("#dadce0"))
|
||||
|
||||
# disabled
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.WindowText, QColor("#babdc2"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Text, QColor("#babdc2"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.ButtonText, QColor("#dadce0"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Highlight, QColor("#dadce0"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.HighlightedText, QColor("#babdc2"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.Link, QColor("#babdc2"))
|
||||
_palette.setColor(QPalette.ColorGroup.Disabled, QPalette.ColorRole.LinkVisited, QColor("#babdc2"))
|
||||
|
||||
# inactive
|
||||
_palette.setColor(QPalette.ColorGroup.Inactive, QPalette.ColorRole.Highlight, QColor("#e4e6f2"))
|
||||
|
||||
PALETTE = _palette
|
||||
1097
libs/qdarktheme/themes/light/stylesheet.py
Normal file
5
libs/qdarktheme/themes/light/svg.py
Normal file
67
libs/qdarktheme/util.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""Utility methods for qdarktheme."""
|
||||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
import logging
|
||||
import operator as ope
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import libs.qdarktheme
|
||||
|
||||
# greater_equal and less_equal must be evaluated before greater and less.
|
||||
OPERATORS = {"==": ope.eq, "!=": ope.ne, ">=": ope.ge, "<=": ope.le, ">": ope.gt, "<": ope.lt}
|
||||
|
||||
|
||||
def multi_replace(target: str, replacements: dict[str, str]) -> str:
|
||||
"""Given a string and a replacement map, it returns the replaced string.
|
||||
|
||||
See https://gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729.
|
||||
|
||||
Args:
|
||||
target: String to execute replacements on.
|
||||
replacements: Replacement dictionary {value to find: value to replace}.
|
||||
|
||||
Returns:
|
||||
str: Target string that replaced with `replacements`.
|
||||
"""
|
||||
if len(replacements) == 0:
|
||||
return target
|
||||
|
||||
replacements_sorted = sorted(replacements, key=len, reverse=True)
|
||||
replacements_escaped = [re.escape(i) for i in replacements_sorted]
|
||||
pattern = re.compile("|".join(replacements_escaped))
|
||||
return pattern.sub(lambda match: replacements[match.group()], target)
|
||||
|
||||
|
||||
def get_logger(logger_name: str) -> logging.Logger:
|
||||
"""Return the logger with the name specified by logger_name arg.
|
||||
|
||||
Args:
|
||||
logger_name: The name of logger.
|
||||
|
||||
Returns:
|
||||
Logger reformatted for this package.
|
||||
"""
|
||||
logger = logging.getLogger(logger_name)
|
||||
logger.propagate = False
|
||||
logger.setLevel(logging.INFO)
|
||||
ch = logging.StreamHandler()
|
||||
ch.setFormatter(logging.Formatter("[%(name)s] [%(levelname)s] %(message)s"))
|
||||
logger.addHandler(ch)
|
||||
return logger
|
||||
|
||||
|
||||
def get_qdarktheme_root_path() -> Path:
|
||||
"""Return the qdarktheme package root path.
|
||||
|
||||
Returns:
|
||||
qdarktheme package root path.
|
||||
"""
|
||||
return Path(inspect.getfile(libs.qdarktheme)).parent
|
||||
|
||||
|
||||
def compare_v(v1: str, operator: str, v2: str) -> bool:
|
||||
"""Comparing two versions."""
|
||||
v1_list, v2_list = (tuple(map(int, (v.split(".")))) for v in (v1, v2))
|
||||
return OPERATORS[operator](v1_list, v2_list)
|
||||
8
libs/qdarktheme/widget_gallery/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""Example of PyQtDarkTheme use for Qt applications.
|
||||
|
||||
To check example app, run:
|
||||
|
||||
```shell
|
||||
python -m qdarktheme.widget_gallery
|
||||
```
|
||||
"""
|
||||
17
libs/qdarktheme/widget_gallery/__main__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""Module allowing for `python -m qdarktheme.widget_gallery`."""
|
||||
import sys
|
||||
|
||||
import libs.qdarktheme
|
||||
from libs.qdarktheme.qtpy.QtCore import Qt
|
||||
from libs.qdarktheme.qtpy.QtWidgets import QApplication
|
||||
from libs.qdarktheme.widget_gallery.mainwindow import WidgetGallery
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
if hasattr(Qt.ApplicationAttribute, "AA_UseHighDpiPixmaps"): # Enable High DPI display with Qt5
|
||||
app.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps) # type: ignore
|
||||
win = WidgetGallery()
|
||||
win.menuBar().setNativeMenuBar(False)
|
||||
app.setStyleSheet(libs.qdarktheme.load_stylesheet())
|
||||
win.show()
|
||||
app.exec()
|
||||
204
libs/qdarktheme/widget_gallery/mainwindow.py
Normal file
@@ -0,0 +1,204 @@
|
||||
"""Main module of widget gallery."""
|
||||
import libs.qdarktheme
|
||||
from libs.qdarktheme.qtpy.QtCore import QDir, Qt, Slot
|
||||
from libs.qdarktheme.qtpy.QtGui import QAction, QActionGroup, QFont, QIcon
|
||||
from libs.qdarktheme.qtpy.QtWidgets import (
|
||||
QApplication,
|
||||
QColorDialog,
|
||||
QFileDialog,
|
||||
QFontDialog,
|
||||
QLabel,
|
||||
QMainWindow,
|
||||
QMenuBar,
|
||||
QMessageBox,
|
||||
QSizePolicy,
|
||||
QStackedWidget,
|
||||
QStatusBar,
|
||||
QToolBar,
|
||||
QToolButton,
|
||||
QWidget,
|
||||
)
|
||||
from libs.qdarktheme.util import get_qdarktheme_root_path
|
||||
from libs.qdarktheme.widget_gallery.ui.dock_ui import DockUI
|
||||
from libs.qdarktheme.widget_gallery.ui.frame_ui import FrameUI
|
||||
from libs.qdarktheme.widget_gallery.ui.widgets_ui import WidgetsUI
|
||||
|
||||
|
||||
class _WidgetGalleryUI:
|
||||
def setup_ui(self, main_win: QMainWindow) -> None:
|
||||
# Actions
|
||||
self.action_open_folder = QAction(QIcon("icons:folder_open_24dp.svg"), "Open folder dialog")
|
||||
self.action_open_color_dialog = QAction(QIcon("icons:palette_24dp.svg"), "Open color dialog")
|
||||
self.action_open_font_dialog = QAction(QIcon("icons:font_download_24dp.svg"), "Open font dialog")
|
||||
self.action_enable = QAction(QIcon("icons:circle_24dp.svg"), "Enable")
|
||||
self.action_disable = QAction(QIcon("icons:clear_24dp.svg"), "Disable")
|
||||
self.actions_theme = [QAction(theme, main_win) for theme in ["dark", "light"]]
|
||||
self.actions_page = (
|
||||
QAction(QIcon("icons:widgets_24dp.svg"), "Move to widgets"),
|
||||
QAction(QIcon("icons:flip_to_front_24dp.svg"), "Move to dock"),
|
||||
QAction(QIcon("icons:crop_din_24dp.svg"), "Move to frame"),
|
||||
)
|
||||
self.actions_message_box = (
|
||||
QAction(text="Open question dialog"),
|
||||
QAction(text="Open information dialog"),
|
||||
QAction(text="Open warning dialog"),
|
||||
QAction(text="Open critical dialog"),
|
||||
)
|
||||
self.actions_corner_radius = (QAction(text="rounded"), QAction(text="sharp"))
|
||||
|
||||
action_group_toolbar = QActionGroup(main_win)
|
||||
|
||||
# Widgets
|
||||
self.central_window = QMainWindow()
|
||||
self.stack_widget = QStackedWidget()
|
||||
self.toolbar = QToolBar("Toolbar")
|
||||
|
||||
activitybar = QToolBar("activitybar")
|
||||
statusbar = QStatusBar()
|
||||
menubar = QMenuBar()
|
||||
tool_btn_settings, tool_btn_theme, tool_btn_enable, tool_btn_disable, tool_btn_message_box = (
|
||||
QToolButton() for _ in range(5)
|
||||
)
|
||||
|
||||
spacer = QToolButton()
|
||||
|
||||
# Setup Actions
|
||||
for action in self.actions_page:
|
||||
action.setCheckable(True)
|
||||
action_group_toolbar.addAction(action)
|
||||
self.actions_page[0].setChecked(True)
|
||||
|
||||
# Setup Widgets
|
||||
spacer.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding)
|
||||
spacer.setEnabled(False)
|
||||
|
||||
activitybar.setMovable(False)
|
||||
activitybar.addActions(self.actions_page)
|
||||
activitybar.addWidget(spacer)
|
||||
activitybar.addWidget(tool_btn_settings)
|
||||
|
||||
tool_btn_settings.setIcon(QIcon("icons:settings_24dp.svg"))
|
||||
tool_btn_settings.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
|
||||
tool_btn_enable.setDefaultAction(self.action_enable)
|
||||
tool_btn_disable.setDefaultAction(self.action_disable)
|
||||
tool_btn_message_box.setIcon(QIcon("icons:announcement_24dp.svg"))
|
||||
tool_btn_message_box.setPopupMode(QToolButton.ToolButtonPopupMode.MenuButtonPopup)
|
||||
tool_btn_theme.setIcon(QIcon("icons:contrast_24dp.svg"))
|
||||
tool_btn_theme.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
|
||||
|
||||
self.toolbar.addActions((self.action_open_folder, self.action_open_color_dialog, self.action_open_font_dialog))
|
||||
self.toolbar.addSeparator()
|
||||
self.toolbar.addWidget(QLabel("Popup"))
|
||||
self.toolbar.addWidget(tool_btn_message_box)
|
||||
self.toolbar.addWidget(tool_btn_theme)
|
||||
|
||||
statusbar.addPermanentWidget(tool_btn_enable)
|
||||
statusbar.addPermanentWidget(tool_btn_disable)
|
||||
statusbar.showMessage("Enable")
|
||||
|
||||
menu_toggle = menubar.addMenu("&Toggle")
|
||||
menu_toggle.addActions((self.action_enable, self.action_disable))
|
||||
menu_theme = menubar.addMenu("&Theme")
|
||||
menu_theme.addActions(self.actions_theme)
|
||||
menu_dialog = menubar.addMenu("&Dialog")
|
||||
menu_option = menubar.addMenu("&Option")
|
||||
menu_option.addActions(self.actions_corner_radius)
|
||||
menu_dialog.addActions((self.action_open_folder, self.action_open_color_dialog, self.action_open_font_dialog))
|
||||
menu_message_box = menu_dialog.addMenu("&Messages")
|
||||
menu_message_box.addActions(self.actions_message_box)
|
||||
|
||||
tool_btn_settings.setMenu(menu_toggle)
|
||||
tool_btn_theme.setMenu(menu_theme)
|
||||
tool_btn_message_box.setMenu(menu_message_box)
|
||||
|
||||
self.action_enable.setEnabled(False)
|
||||
|
||||
# Layout
|
||||
for ui in (WidgetsUI, DockUI, FrameUI):
|
||||
container = QWidget()
|
||||
ui().setup_ui(container)
|
||||
self.stack_widget.addWidget(container)
|
||||
|
||||
self.central_window.setCentralWidget(self.stack_widget)
|
||||
self.central_window.addToolBar(self.toolbar)
|
||||
|
||||
main_win.setCentralWidget(self.central_window)
|
||||
main_win.addToolBar(Qt.ToolBarArea.LeftToolBarArea, activitybar)
|
||||
main_win.setMenuBar(menubar)
|
||||
main_win.setStatusBar(statusbar)
|
||||
|
||||
|
||||
class WidgetGallery(QMainWindow):
|
||||
"""The main window class of example app."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the WidgetGallery class."""
|
||||
super().__init__()
|
||||
QDir.addSearchPath("icons", f"{get_qdarktheme_root_path().as_posix()}/widget_gallery/svg")
|
||||
self._ui = _WidgetGalleryUI()
|
||||
self._ui.setup_ui(self)
|
||||
self._theme = "dark"
|
||||
self._border_radius = "rounded"
|
||||
|
||||
# Signal
|
||||
self._ui.action_open_folder.triggered.connect(
|
||||
lambda: QFileDialog.getOpenFileName(self, "Open File", options=QFileDialog.Option.DontUseNativeDialog)
|
||||
)
|
||||
self._ui.action_open_color_dialog.triggered.connect(
|
||||
lambda: QColorDialog.getColor(parent=self, options=QColorDialog.ColorDialogOption.DontUseNativeDialog)
|
||||
)
|
||||
self._ui.action_open_font_dialog.triggered.connect(
|
||||
lambda: QFontDialog.getFont(QFont(), parent=self, options=QFontDialog.FontDialogOption.DontUseNativeDialog)
|
||||
)
|
||||
self._ui.action_enable.triggered.connect(self._toggle_state)
|
||||
self._ui.action_disable.triggered.connect(self._toggle_state)
|
||||
for action in self._ui.actions_theme:
|
||||
action.triggered.connect(self._change_theme)
|
||||
for action in self._ui.actions_page:
|
||||
action.triggered.connect(self._change_page)
|
||||
for action in self._ui.actions_message_box:
|
||||
action.triggered.connect(self._popup_message_box)
|
||||
for action in self._ui.actions_corner_radius:
|
||||
action.triggered.connect(self._change_corner_radius)
|
||||
|
||||
@Slot()
|
||||
def _change_page(self) -> None:
|
||||
action_name: str = self.sender().text() # type: ignore
|
||||
if "widgets" in action_name:
|
||||
index = 0
|
||||
elif "dock" in action_name:
|
||||
index = 1
|
||||
else:
|
||||
index = 2
|
||||
self._ui.stack_widget.setCurrentIndex(index)
|
||||
|
||||
@Slot()
|
||||
def _toggle_state(self) -> None:
|
||||
state: str = self.sender().text() # type: ignore
|
||||
self._ui.central_window.centralWidget().setEnabled(state == "Enable")
|
||||
self._ui.toolbar.setEnabled(state == "Enable")
|
||||
self._ui.action_enable.setEnabled(state == "Disable")
|
||||
self._ui.action_disable.setEnabled(state == "Enable")
|
||||
self.statusBar().showMessage(state)
|
||||
|
||||
@Slot()
|
||||
def _change_theme(self) -> None:
|
||||
self._theme = self.sender().text() # type: ignore
|
||||
QApplication.instance().setStyleSheet(libs.qdarktheme.load_stylesheet(self._theme, self._border_radius))
|
||||
|
||||
@Slot()
|
||||
def _change_corner_radius(self) -> None:
|
||||
self._border_radius: str = self.sender().text() # type: ignore
|
||||
QApplication.instance().setStyleSheet(libs.qdarktheme.load_stylesheet(self._theme, self._border_radius))
|
||||
|
||||
@Slot()
|
||||
def _popup_message_box(self) -> None:
|
||||
action_name: str = self.sender().text() # type: ignore
|
||||
if "question" in action_name:
|
||||
QMessageBox.question(self, "Question", "Question")
|
||||
elif "information" in action_name:
|
||||
QMessageBox.information(self, "Information", "Information")
|
||||
elif "warning" in action_name:
|
||||
QMessageBox.warning(self, "Warning", "Warning")
|
||||
elif "critical" in action_name:
|
||||
QMessageBox.critical(self, "Critical", "Critical")
|
||||
1
libs/qdarktheme/widget_gallery/svg/announcement_24dp.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#d89e76"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8 9c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1s1 .45 1 1v4c0 .55-.45 1-1 1zm1 4h-2v-2h2v2z"/></svg>
|
||||
|
After Width: | Height: | Size: 308 B |
1
libs/qdarktheme/widget_gallery/svg/circle_24dp.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#008000"><g><rect fill="none" height="24" width="24"/></g><g><path d="M12,2C6.47,2,2,6.47,2,12c0,5.53,4.47,10,10,10s10-4.47,10-10C22,6.47,17.53,2,12,2z M12,20c-4.42,0-8-3.58-8-8 c0-4.42,3.58-8,8-8s8,3.58,8,8C20,16.42,16.42,20,12,20z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 372 B |
1
libs/qdarktheme/widget_gallery/svg/clear_24dp.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#FF0000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z"/></svg>
|
||||
|
After Width: | Height: | Size: 430 B |
1
libs/qdarktheme/widget_gallery/svg/contrast_24dp.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#d89e76"><g><rect fill="none" height="24" width="24"/></g><g><path d="M12,22c5.52,0,10-4.48,10-10S17.52,2,12,2S2,6.48,2,12S6.48,22,12,22z M13,4.07c3.94,0.49,7,3.85,7,7.93s-3.05,7.44-7,7.93 V4.07z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 335 B |
1
libs/qdarktheme/widget_gallery/svg/crop_din_24dp.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#d89e76"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14z"/></svg>
|
||||
|
After Width: | Height: | Size: 249 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#ff1493"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19.66 3.99c-2.64-1.8-5.9-.96-7.66 1.1-1.76-2.06-5.02-2.91-7.66-1.1-1.4.96-2.28 2.58-2.34 4.29-.14 3.88 3.3 6.99 8.55 11.76l.1.09c.76.69 1.93.69 2.69-.01l.11-.1c5.25-4.76 8.68-7.87 8.55-11.75-.06-1.7-.94-3.32-2.34-4.28zM12.1 18.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z"/></svg>
|
||||
|
After Width: | Height: | Size: 548 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#d89e76"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm2 4v-2H3c0 1.1.89 2 2 2zM3 9h2V7H3v2zm12 12h2v-2h-2v2zm4-18H9c-1.11 0-2 .9-2 2v10c0 1.1.89 2 2 2h10c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-1 12h-8c-.55 0-1-.45-1-1V6c0-.55.45-1 1-1h8c.55 0 1 .45 1 1v8c0 .55-.45 1-1 1zm-7 6h2v-2h-2v2zm-4 0h2v-2H7v2z"/></svg>
|
||||
|
After Width: | Height: | Size: 432 B |
1
libs/qdarktheme/widget_gallery/svg/folder_open_24dp.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#d89e76"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20 6h-8l-1.41-1.41C10.21 4.21 9.7 4 9.17 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm-1 12H5c-.55 0-1-.45-1-1V9c0-.55.45-1 1-1h14c.55 0 1 .45 1 1v8c0 .55-.45 1-1 1z"/></svg>
|
||||
|
After Width: | Height: | Size: 360 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#d89e76"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9.93 13.5h4.14L12 7.98 9.93 13.5zM20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-4.29 15.88l-.9-2.38H9.17l-.89 2.37c-.14.38-.5.63-.91.63-.68 0-1.15-.69-.9-1.32l4.25-10.81c.22-.53.72-.87 1.28-.87s1.06.34 1.27.87l4.25 10.81c.25.63-.22 1.32-.9 1.32-.4 0-.76-.25-.91-.62z"/></svg>
|
||||
|
After Width: | Height: | Size: 457 B |
1
libs/qdarktheme/widget_gallery/svg/palette_24dp.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#d89e76"><g><rect fill="none" height="24" width="24"/></g><g><path d="M12,2C6.49,2,2,6.49,2,12s4.49,10,10,10c1.38,0,2.5-1.12,2.5-2.5c0-0.61-0.23-1.2-0.64-1.67c-0.08-0.1-0.13-0.21-0.13-0.33 c0-0.28,0.22-0.5,0.5-0.5H16c3.31,0,6-2.69,6-6C22,6.04,17.51,2,12,2z M17.5,13c-0.83,0-1.5-0.67-1.5-1.5c0-0.83,0.67-1.5,1.5-1.5 s1.5,0.67,1.5,1.5C19,12.33,18.33,13,17.5,13z M14.5,9C13.67,9,13,8.33,13,7.5C13,6.67,13.67,6,14.5,6S16,6.67,16,7.5 C16,8.33,15.33,9,14.5,9z M5,11.5C5,10.67,5.67,10,6.5,10S8,10.67,8,11.5C8,12.33,7.33,13,6.5,13S5,12.33,5,11.5z M11,7.5 C11,8.33,10.33,9,9.5,9S8,8.33,8,7.5C8,6.67,8.67,6,9.5,6S11,6.67,11,7.5z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 758 B |
1
libs/qdarktheme/widget_gallery/svg/settings_24dp.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#d89e76"><rect fill="none" height="24" width="24"/><path d="M19.5,12c0-0.23-0.01-0.45-0.03-0.68l1.86-1.41c0.4-0.3,0.51-0.86,0.26-1.3l-1.87-3.23c-0.25-0.44-0.79-0.62-1.25-0.42 l-2.15,0.91c-0.37-0.26-0.76-0.49-1.17-0.68l-0.29-2.31C14.8,2.38,14.37,2,13.87,2h-3.73C9.63,2,9.2,2.38,9.14,2.88L8.85,5.19 c-0.41,0.19-0.8,0.42-1.17,0.68L5.53,4.96c-0.46-0.2-1-0.02-1.25,0.42L2.41,8.62c-0.25,0.44-0.14,0.99,0.26,1.3l1.86,1.41 C4.51,11.55,4.5,11.77,4.5,12s0.01,0.45,0.03,0.68l-1.86,1.41c-0.4,0.3-0.51,0.86-0.26,1.3l1.87,3.23c0.25,0.44,0.79,0.62,1.25,0.42 l2.15-0.91c0.37,0.26,0.76,0.49,1.17,0.68l0.29,2.31C9.2,21.62,9.63,22,10.13,22h3.73c0.5,0,0.93-0.38,0.99-0.88l0.29-2.31 c0.41-0.19,0.8-0.42,1.17-0.68l2.15,0.91c0.46,0.2,1,0.02,1.25-0.42l1.87-3.23c0.25-0.44,0.14-0.99-0.26-1.3l-1.86-1.41 C19.49,12.45,19.5,12.23,19.5,12z M12.04,15.5c-1.93,0-3.5-1.57-3.5-3.5s1.57-3.5,3.5-3.5s3.5,1.57,3.5,3.5S13.97,15.5,12.04,15.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
libs/qdarktheme/widget_gallery/svg/widgets_24dp.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#d89e76"><path d="M0 0h24v24H0z" fill="none"/><path d="M13 13v8h8v-8h-8zM3 21h8v-8H3v8zM3 3v8h8V3H3zm13.66-1.31L11 7.34 16.66 13l5.66-5.66-5.66-5.65z"/></svg>
|
||||
|
After Width: | Height: | Size: 251 B |
1
libs/qdarktheme/widget_gallery/ui/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Package including ui for WidgetGallery."""
|
||||
40
libs/qdarktheme/widget_gallery/ui/dock_ui.py
Normal file
@@ -0,0 +1,40 @@
|
||||
"""Module setting up ui of dock window."""
|
||||
from libs.qdarktheme.qtpy.QtCore import Qt
|
||||
from libs.qdarktheme.qtpy.QtWidgets import QDockWidget, QMainWindow, QTextEdit, QVBoxLayout, QWidget
|
||||
|
||||
|
||||
class DockUI:
|
||||
"""The ui class of dock window."""
|
||||
|
||||
def setup_ui(self, win: QWidget) -> None:
|
||||
"""Set up ui."""
|
||||
# Widgets
|
||||
left_dock = QDockWidget("Left dock")
|
||||
right_dock = QDockWidget("Right dock")
|
||||
top_dock = QDockWidget("Top dock")
|
||||
bottom_dock = QDockWidget("Bottom dock")
|
||||
|
||||
# Setup widgets
|
||||
left_dock.setWidget(QTextEdit("This is the left widget."))
|
||||
right_dock.setWidget(QTextEdit("This is the right widget."))
|
||||
top_dock.setWidget(QTextEdit("This is the top widget."))
|
||||
bottom_dock.setWidget(QTextEdit("This is the bottom widget."))
|
||||
for dock in (left_dock, right_dock, top_dock, bottom_dock):
|
||||
dock.setAllowedAreas(
|
||||
Qt.DockWidgetArea.LeftDockWidgetArea
|
||||
| Qt.DockWidgetArea.RightDockWidgetArea
|
||||
| Qt.DockWidgetArea.BottomDockWidgetArea
|
||||
| Qt.DockWidgetArea.TopDockWidgetArea
|
||||
)
|
||||
|
||||
# Layout
|
||||
main_win = QMainWindow()
|
||||
main_win.setCentralWidget(QTextEdit("This is the central widget."))
|
||||
main_win.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, left_dock)
|
||||
main_win.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, right_dock)
|
||||
main_win.addDockWidget(Qt.DockWidgetArea.TopDockWidgetArea, top_dock)
|
||||
main_win.addDockWidget(Qt.DockWidgetArea.BottomDockWidgetArea, bottom_dock)
|
||||
|
||||
layout = QVBoxLayout(win)
|
||||
layout.addWidget(main_win)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
75
libs/qdarktheme/widget_gallery/ui/frame_ui.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""Module setting up ui of frame window."""
|
||||
from libs.qdarktheme.qtpy.QtGui import QIcon
|
||||
from libs.qdarktheme.qtpy.QtWidgets import (
|
||||
QCalendarWidget,
|
||||
QCheckBox,
|
||||
QFrame,
|
||||
QGridLayout,
|
||||
QGroupBox,
|
||||
QHBoxLayout,
|
||||
QPushButton,
|
||||
QRadioButton,
|
||||
QScrollArea,
|
||||
QSpinBox,
|
||||
QToolButton,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
|
||||
class FrameUI:
|
||||
"""The ui class of frame window."""
|
||||
|
||||
def setup_ui(self, win: QWidget) -> None:
|
||||
"""Set up ui."""
|
||||
# Widgets
|
||||
group_box = QGroupBox("frameShape = Box")
|
||||
group_panel = QGroupBox("frameShape = Panel")
|
||||
group_none = QGroupBox("frameShape = NoFrame")
|
||||
group_line = QGroupBox("frameShape = VLine HLine")
|
||||
frame_box, frame_panel, frame_none, frame_v_line, frame_h_line = (QFrame() for _ in range(5))
|
||||
|
||||
# Setup widgets
|
||||
frame_box.setFrameShape(QFrame.Shape.Box)
|
||||
frame_panel.setFrameShape(QFrame.Shape.Panel)
|
||||
frame_none.setFrameShape(QFrame.Shape.NoFrame)
|
||||
frame_v_line.setFrameShape(QFrame.Shape.VLine)
|
||||
frame_h_line.setFrameShape(QFrame.Shape.HLine)
|
||||
|
||||
# Layout
|
||||
for frame in (frame_box, frame_panel, frame_none):
|
||||
push_btn_flat = QPushButton("Push button(flat)")
|
||||
push_btn_flat.setFlat(True)
|
||||
tool_btn = QToolButton()
|
||||
tool_btn.setIcon(QIcon("icons:favorite_border_24dp.svg"))
|
||||
calender = QCalendarWidget()
|
||||
|
||||
g_layout = QGridLayout(frame)
|
||||
g_layout.addWidget(QPushButton("Push button"), 0, 0)
|
||||
g_layout.addWidget(push_btn_flat, 0, 1)
|
||||
g_layout.addWidget(QSpinBox(), 1, 0)
|
||||
g_layout.addWidget(tool_btn, 1, 1)
|
||||
g_layout.addWidget(QRadioButton("Radio button"), 2, 0)
|
||||
g_layout.addWidget(QCheckBox("Check box"), 2, 1)
|
||||
g_layout.addWidget(calender, 3, 0, 1, 2)
|
||||
|
||||
for group, frame in ((group_box, frame_box), (group_panel, frame_panel), (group_none, frame_none)):
|
||||
v_layout = QVBoxLayout(group)
|
||||
v_layout.addWidget(frame)
|
||||
|
||||
h_layout = QHBoxLayout(group_line)
|
||||
h_layout.addWidget(frame_v_line)
|
||||
h_layout.addWidget(frame_h_line)
|
||||
|
||||
widget_container = QWidget()
|
||||
g_layout = QGridLayout(widget_container)
|
||||
g_layout.addWidget(group_box, 0, 0)
|
||||
g_layout.addWidget(group_panel, 0, 1)
|
||||
g_layout.addWidget(group_none, 1, 0)
|
||||
g_layout.addWidget(group_line, 1, 1)
|
||||
|
||||
scroll_area = QScrollArea()
|
||||
scroll_area.setWidget(widget_container)
|
||||
|
||||
v_main_layout = QVBoxLayout(win)
|
||||
v_main_layout.addWidget(scroll_area)
|
||||
293
libs/qdarktheme/widget_gallery/ui/widgets_ui.py
Normal file
@@ -0,0 +1,293 @@
|
||||
"""Module setting up ui of widgets window."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from libs.qdarktheme.qtpy.QtCore import QAbstractTableModel, QModelIndex, Qt
|
||||
from libs.qdarktheme.qtpy.QtGui import QIcon, QTextOption
|
||||
from libs.qdarktheme.qtpy.QtWidgets import (
|
||||
QCheckBox,
|
||||
QComboBox,
|
||||
QDateTimeEdit,
|
||||
QDial,
|
||||
QGridLayout,
|
||||
QGroupBox,
|
||||
QLabel,
|
||||
QLCDNumber,
|
||||
QLineEdit,
|
||||
QListWidget,
|
||||
QProgressBar,
|
||||
QPushButton,
|
||||
QRadioButton,
|
||||
QScrollArea,
|
||||
QSlider,
|
||||
QSpinBox,
|
||||
QSplitter,
|
||||
QTableView,
|
||||
QTabWidget,
|
||||
QTextEdit,
|
||||
QToolBox,
|
||||
QToolButton,
|
||||
QTreeWidget,
|
||||
QTreeWidgetItem,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
|
||||
|
||||
class _Group1(QGroupBox):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("Buttons")
|
||||
|
||||
# Widgets
|
||||
group_push = QGroupBox("Push Button")
|
||||
group_tool = QGroupBox("Tool Button")
|
||||
group_radio = QGroupBox("Radio Button")
|
||||
group_checkbox = QGroupBox("Check Box")
|
||||
|
||||
push_btn, push_btn_toggled = QPushButton("NORMAL"), QPushButton("TOGGLED")
|
||||
push_btn_flat, push_btn_flat_toggled = QPushButton("NORMAL"), QPushButton("TOGGLED")
|
||||
tool_btn, tool_btn_toggled, tool_btn_text = QToolButton(), QToolButton(), QToolButton()
|
||||
radio_btn_1, radio_btn_2 = QRadioButton("Normal 1"), QRadioButton("Normal 2")
|
||||
checkbox, checkbox_tristate = QCheckBox("Normal"), QCheckBox("Tristate")
|
||||
|
||||
# Setup widgets
|
||||
self.setCheckable(True)
|
||||
push_btn_flat.setFlat(True)
|
||||
push_btn_flat_toggled.setFlat(True)
|
||||
for btn in (push_btn_toggled, push_btn_flat_toggled):
|
||||
btn.setCheckable(True)
|
||||
btn.setChecked(True)
|
||||
|
||||
tool_btn.setIcon(QIcon("icons:favorite_border_24dp.svg"))
|
||||
tool_btn_toggled.setIcon(QIcon("icons:favorite_border_24dp.svg"))
|
||||
tool_btn_text.setIcon(QIcon("icons:favorite_border_24dp.svg"))
|
||||
tool_btn_text.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
|
||||
tool_btn_text.setText("Text")
|
||||
tool_btn_toggled.setCheckable(True)
|
||||
tool_btn_toggled.setChecked(True)
|
||||
|
||||
radio_btn_1.setChecked(True)
|
||||
checkbox.setChecked(True)
|
||||
checkbox_tristate.setTristate(True)
|
||||
checkbox_tristate.setCheckState(Qt.CheckState.PartiallyChecked)
|
||||
|
||||
# Layout
|
||||
g_layout_push = QGridLayout()
|
||||
g_layout_push.addWidget(QLabel("Normal"), 0, 0)
|
||||
g_layout_push.addWidget(push_btn, 1, 0)
|
||||
g_layout_push.addWidget(push_btn_toggled, 2, 0)
|
||||
g_layout_push.addWidget(QLabel("Flat"), 0, 1)
|
||||
g_layout_push.addWidget(push_btn_flat, 1, 1)
|
||||
g_layout_push.addWidget(push_btn_flat_toggled, 2, 1)
|
||||
group_push.setLayout(g_layout_push)
|
||||
|
||||
v_layout_tool = QVBoxLayout()
|
||||
v_layout_tool.addWidget(tool_btn)
|
||||
v_layout_tool.addWidget(tool_btn_toggled)
|
||||
v_layout_tool.addWidget(tool_btn_text)
|
||||
group_tool.setLayout(v_layout_tool)
|
||||
|
||||
v_layout_radio = QVBoxLayout()
|
||||
v_layout_radio.addWidget(radio_btn_1)
|
||||
v_layout_radio.addWidget(radio_btn_2)
|
||||
group_radio.setLayout(v_layout_radio)
|
||||
|
||||
v_layout_checkbox = QVBoxLayout()
|
||||
v_layout_checkbox.addWidget(checkbox)
|
||||
v_layout_checkbox.addWidget(checkbox_tristate)
|
||||
group_checkbox.setLayout(v_layout_checkbox)
|
||||
|
||||
g_layout_main = QGridLayout(self)
|
||||
g_layout_main.addWidget(group_push, 0, 0)
|
||||
g_layout_main.addWidget(group_tool, 0, 1)
|
||||
g_layout_main.addWidget(group_radio, 1, 0)
|
||||
g_layout_main.addWidget(group_checkbox, 1, 1)
|
||||
|
||||
|
||||
class _Group2(QGroupBox):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("Line boxes")
|
||||
# Widgets
|
||||
group_spinbox = QGroupBox("Spinbox")
|
||||
group_combobox = QGroupBox("Combobox")
|
||||
group_editable = QGroupBox("Line edit")
|
||||
group_date = QGroupBox("Date time edit")
|
||||
|
||||
spinbox, spinbox_suffix = QSpinBox(), QSpinBox()
|
||||
combobox, combobox_line_edit = QComboBox(), QComboBox()
|
||||
lineedit = QLineEdit()
|
||||
date_time_edit, date_time_edit_calendar = QDateTimeEdit(), QDateTimeEdit()
|
||||
|
||||
# Setup widgets
|
||||
self.setCheckable(True)
|
||||
spinbox_suffix.setSuffix(" m")
|
||||
|
||||
combobox.addItems(("Item 1", "Item 2", "Item 3"))
|
||||
combobox_line_edit.addItems(("Item 1", "Item 2", "Item 3"))
|
||||
combobox_line_edit.setEditable(True)
|
||||
|
||||
lineedit.setPlaceholderText("Placeholder text")
|
||||
date_time_edit_calendar.setCalendarPopup(True)
|
||||
|
||||
# Layout
|
||||
v_layout_spin = QVBoxLayout()
|
||||
v_layout_spin.addWidget(spinbox)
|
||||
v_layout_spin.addWidget(spinbox_suffix)
|
||||
group_spinbox.setLayout(v_layout_spin)
|
||||
|
||||
v_layout_combo = QVBoxLayout()
|
||||
v_layout_combo.addWidget(combobox)
|
||||
v_layout_combo.addWidget(combobox_line_edit)
|
||||
group_combobox.setLayout(v_layout_combo)
|
||||
|
||||
v_layout_lineedit = QVBoxLayout()
|
||||
v_layout_lineedit.addWidget(lineedit)
|
||||
group_editable.setLayout(v_layout_lineedit)
|
||||
|
||||
v_layout_date = QVBoxLayout()
|
||||
v_layout_date.addWidget(date_time_edit)
|
||||
v_layout_date.addWidget(date_time_edit_calendar)
|
||||
group_date.setLayout(v_layout_date)
|
||||
|
||||
g_layout_main = QGridLayout(self)
|
||||
g_layout_main.addWidget(group_spinbox, 0, 0)
|
||||
g_layout_main.addWidget(group_combobox, 0, 1)
|
||||
g_layout_main.addWidget(group_editable, 1, 0)
|
||||
g_layout_main.addWidget(group_date, 1, 1)
|
||||
|
||||
|
||||
class _TableModel(QAbstractTableModel):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._data = [[i * 10 + j for j in range(4)] for i in range(5)]
|
||||
|
||||
def data(self, index: QModelIndex, role: int) -> Any:
|
||||
if role == Qt.ItemDataRole.DisplayRole:
|
||||
return self._data[index.row()][index.column()]
|
||||
if role == Qt.ItemDataRole.CheckStateRole and index.column() == 1:
|
||||
return Qt.CheckState.Checked if index.row() % 2 == 0 else Qt.CheckState.Unchecked
|
||||
if role == Qt.ItemDataRole.EditRole and index.column() == 2:
|
||||
return self._data[index.row()][index.column()] # pragma: no cover
|
||||
return None
|
||||
|
||||
def rowCount(self, index) -> int: # noqa: N802
|
||||
return len(self._data)
|
||||
|
||||
def columnCount(self, index) -> int: # noqa: N802
|
||||
return len(self._data[0])
|
||||
|
||||
def flags(self, index: QModelIndex) -> Qt.ItemFlag:
|
||||
flag = super().flags(index)
|
||||
if index.column() == 1:
|
||||
flag |= Qt.ItemFlag.ItemIsUserCheckable
|
||||
elif index.column() in (2, 3):
|
||||
flag |= Qt.ItemFlag.ItemIsEditable
|
||||
return flag # type: ignore
|
||||
|
||||
def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...) -> Any: # noqa: N802
|
||||
if role != Qt.ItemDataRole.DisplayRole:
|
||||
return None
|
||||
if orientation == Qt.Orientation.Horizontal:
|
||||
return ["Normal", "Checkbox", "Spinbox", "LineEdit"][section]
|
||||
return super().headerData(section, orientation, role)
|
||||
|
||||
|
||||
class _Group3(QGroupBox):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("Scroll area and QTabWidget (QGroupBox.flat = True)")
|
||||
|
||||
# Widgets
|
||||
tab_widget = QTabWidget()
|
||||
tab_text_edit = QTextEdit()
|
||||
tab_table = QTableView()
|
||||
tab_list = QListWidget()
|
||||
tab_tree = QTreeWidget()
|
||||
|
||||
# Setup widgets
|
||||
self.setCheckable(True)
|
||||
self.setFlat(True)
|
||||
tab_widget.setTabsClosable(True)
|
||||
tab_widget.setMovable(True)
|
||||
tab_text_edit.append("<b>PyQtDarkTheme</b>")
|
||||
tab_text_edit.append("Dark theme for PySide and PyQt.")
|
||||
tab_text_edit.append("This project is licensed under the MIT license.")
|
||||
tab_text_edit.setWordWrapMode(QTextOption.WrapMode.NoWrap)
|
||||
|
||||
tab_table.setModel(_TableModel())
|
||||
tab_table.setSortingEnabled(True)
|
||||
|
||||
tab_list.addItems([f"Item {i+1}" for i in range(30)])
|
||||
tab_list.setAlternatingRowColors(True)
|
||||
|
||||
tab_tree.setColumnCount(2)
|
||||
for i in range(5):
|
||||
item = QTreeWidgetItem([f"Item {i+1}" for _ in range(2)])
|
||||
for j in range(2):
|
||||
item.addChild(QTreeWidgetItem([f"Child Item {i+1}_{j+1}" for _ in range(2)]))
|
||||
tab_tree.insertTopLevelItem(i, item)
|
||||
|
||||
# layout
|
||||
tab_widget.addTab(tab_table, "Table")
|
||||
tab_widget.addTab(tab_text_edit, "Text Edit")
|
||||
tab_widget.addTab(tab_list, "List")
|
||||
tab_widget.addTab(tab_tree, "Tree")
|
||||
|
||||
v_layout_main = QVBoxLayout(self)
|
||||
v_layout_main.addWidget(tab_widget)
|
||||
|
||||
|
||||
class _Group4(QGroupBox):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("QToolBox")
|
||||
# Widgets
|
||||
toolbox = QToolBox()
|
||||
slider = QSlider(Qt.Orientation.Horizontal)
|
||||
dial_ticks = QDial()
|
||||
progressbar = QProgressBar()
|
||||
lcd_number = QLCDNumber()
|
||||
|
||||
# Setup widgets
|
||||
self.setCheckable(True)
|
||||
toolbox.addItem(slider, "Slider")
|
||||
toolbox.addItem(dial_ticks, "Dial")
|
||||
toolbox.addItem(progressbar, "Progress Bar")
|
||||
toolbox.addItem(lcd_number, "LCD Number")
|
||||
slider.setValue(50)
|
||||
dial_ticks.setNotchesVisible(True)
|
||||
progressbar.setValue(50)
|
||||
lcd_number.setSegmentStyle(QLCDNumber.SegmentStyle.Flat)
|
||||
lcd_number.display(123)
|
||||
|
||||
# Layout
|
||||
v_layout = QVBoxLayout(self)
|
||||
v_layout.addWidget(toolbox)
|
||||
|
||||
|
||||
class WidgetsUI:
|
||||
"""The ui class of widgets window."""
|
||||
|
||||
def setup_ui(self, win: QWidget) -> None:
|
||||
"""Set up ui."""
|
||||
# Widgets
|
||||
h_splitter_1, h_splitter_2 = QSplitter(Qt.Orientation.Horizontal), QSplitter(Qt.Orientation.Horizontal)
|
||||
|
||||
# Setup widgets
|
||||
h_splitter_1.setMinimumHeight(350) # Fix bug layout crush
|
||||
|
||||
# Layout
|
||||
h_splitter_1.addWidget(_Group1())
|
||||
h_splitter_1.addWidget(_Group2())
|
||||
h_splitter_2.addWidget(_Group3())
|
||||
h_splitter_2.addWidget(_Group4())
|
||||
|
||||
widget_container = QWidget()
|
||||
v_layout = QVBoxLayout(widget_container)
|
||||
v_layout.addWidget(h_splitter_1)
|
||||
v_layout.addWidget(h_splitter_2)
|
||||
|
||||
scroll_area = QScrollArea()
|
||||
scroll_area.setWidget(widget_container)
|
||||
|
||||
v_main_layout = QVBoxLayout(win)
|
||||
v_main_layout.addWidget(scroll_area)
|
||||