540 lines
20 KiB
Markdown
540 lines
20 KiB
Markdown
BreezeStyleSheets
|
|
=================
|
|
|
|
Configurable Breeze and BreezeDark-like stylesheets for Qt Applications.
|
|
|
|
BreezeStyleSheets is a set of beautiful light and dark stylesheets that render consistently across platforms, including high DPI screens. Each stylesheet is generated from a theme file and can be extended with a plugin system, simplifying the generation custom stylesheets for your application. The stylesheets are comprehensively tested with most Qt widgets and widget properties, providing a consistent, stylish feel on any platform, including different operating systems, desktop environments, and Qt versions.
|
|
|
|
**Table of Contents**
|
|
|
|
- [Gallery](#gallery)
|
|
- [Customization](#customization)
|
|
- [Features](#features)
|
|
- [Plugins](#plugins)
|
|
- [Extending Stylesheets](#extending-stylesheets)
|
|
- [Installing](#installing)
|
|
- [CMake Installation](#cmake-installation)
|
|
- [QMake Installation](#qmake-installation)
|
|
- [PyQt5 Installation](#pyqt5-installation)
|
|
- [PyQt6 Installation](#pyqt6-installation)
|
|
- [Debugging](#debugging)
|
|
- [Development Guide](#development-guide)
|
|
- [Configuring](#configuring)
|
|
- [Testing](#testing)
|
|
- [Distribution Files](#distribution-files)
|
|
- [Git Ignore](#git-ignore)
|
|
- [Known Issues and Workarounds](#known-issues-and-workarounds)
|
|
- [License](#license)
|
|
- [Contributing](#contributing)
|
|
- [Acknowledgements](#acknowledgements)
|
|
- [Contact](#contact)
|
|
|
|
# Gallery
|
|
|
|
**Breeze/BreezeDark**
|
|
|
|
Example user interface using the Breeze and BreezeDark stylesheets side-by-side.
|
|
|
|
![BreezeDark](/assets/Breeze.gif)
|
|
|
|
For an extensive view of screenshots of the theme, see the [gallery](assets/gallery.md).
|
|
|
|
# Customization
|
|
|
|
It's easy to design your own themes using `configure.py`. First, add the styles you want into [theme](/theme/), then run configure with a list of styles you want to include.
|
|
|
|
**Theme**
|
|
|
|
Here is a sample theme, with the color descriptions annotated. Please note that although there are nearly 40 possibilities, for most applications, you should use less than 20, and ~10 different hues.
|
|
|
|
```jsonc
|
|
// NOTE: This is a custom JSON file, where lines leading
|
|
// with `//` are removed. No other comments are valid.
|
|
{
|
|
// Main foreground color.
|
|
"foreground": "#eff0f1",
|
|
// Lighter foreground color for selected items.
|
|
"foreground-light": "#ffffff",
|
|
// Main background color.
|
|
"background": "#31363b",
|
|
// Alternate background color for styles.
|
|
"background:alternate": "#31363b",
|
|
// Main color to highlight widgets, such as on hover events.
|
|
"highlight": "#3daee9",
|
|
// Color for selected widgets so hover events can change widget color.
|
|
"highlight:dark": "#2a79a3",
|
|
// Alternate highlight color for hovered widgets in QAbstractItemViews.
|
|
"highlight:alternate": "#369cd1",
|
|
// Main midtone color, such as for borders.
|
|
"midtone": "#76797c",
|
|
// Lighter color for midtones, such as for certain disabled widgets.
|
|
"midtone:light": "#b0b0b0",
|
|
// Darker midtone, such as for the background of QPushButton and QSlider.
|
|
"midtone:dark": "#626568",
|
|
// Lighter midtone for separator hover events.
|
|
"midtone:hover": "#8a8d8f",
|
|
// Color for checked widgets in QAbstractItemViews.
|
|
"view:checked": "#334e5e",
|
|
// Hover background color in QAbstractItemViews.
|
|
// This should be fairly transparent.
|
|
"view:hover": "rgba(61, 173, 232, 0.1)",
|
|
// Background for a horizontal QToolBar.
|
|
"toolbar:horizontal:background": "#31363b",
|
|
// Background for a vertical QToolBar.
|
|
"toolbar:vertical:background": "#31363b",
|
|
// Background color for the corner widget in a QAbstractItemView.
|
|
"view:corner": "#31363b",
|
|
// Border color between items in a QHeaderView.
|
|
"view:header:border": "#76797c",
|
|
// Background color for a QHeaderView.
|
|
"view:header": "#31363b",
|
|
// Border color Between items in a QAbstractItemView.
|
|
"view:border": "#31363b",
|
|
// Background for QAbstractItemViews.
|
|
"view:background": "#1d2023",
|
|
// Background for widgets with text input.
|
|
"text:background": "#1d2023",
|
|
// Background for the currently selected tab.
|
|
"tab:background:selected": "#31363b",
|
|
// Background for non-selected tabs.
|
|
"tab:background": "#2c3034",
|
|
// Color for the branch/arrow icons in a QTreeView.
|
|
"tree": "#afafaf",
|
|
// Color for the chunk of a QProgressBar, the active groove
|
|
// of a QSlider, and the border of a hovered QSlider handle.
|
|
"slider:foreground": "#3daee9",
|
|
// Background color for the handle of a QSlider.
|
|
"slider:handle:background": "#1d2023",
|
|
// Color for a disabled menubar/menu item.
|
|
"menu:disabled": "#76797c",
|
|
// Color for a checked/hovered QCheckBox or QRadioButton.
|
|
"checkbox:light": "#58d3ff",
|
|
// Color for a disabled or unchecked/unhovered QCheckBox or QRadioButton.
|
|
"checkbox:disabled": "#c8c9ca",
|
|
// Color for the handle of a scrollbar. Due to limitations of
|
|
// Qt stylesheets, any handle of a scrollbar must be treated
|
|
// like it's hovered.
|
|
"scrollbar:hover": "#3daee9",
|
|
// Background for a non-hovered scrollbar.
|
|
"scrollbar:background": "#1d2023",
|
|
// Background for a hovered scrollbar.
|
|
"scrollbar:background:hover": "#76797c",
|
|
// Default background for a QPushButton.
|
|
"button:background": "#31363b",
|
|
// Background for a pressed QPushButton.
|
|
"button:background:pressed": "#454a4f",
|
|
// Border for a non-hovered QPushButton.
|
|
"button:border": "#76797c",
|
|
// Background for a disabled QPushButton, or fallthrough
|
|
// for disabled QWidgets.
|
|
"button:disabled": "#454545",
|
|
// Color of a dock/tab close icon when hovered.
|
|
"close:hover": "#b37979",
|
|
// Color of a dock/tab close icon when pressed.
|
|
"close:pressed": "#b33e3e",
|
|
// Default background color for QDockWidget and title.
|
|
"dock:background": "#31363b",
|
|
// Color for the float icon for QDockWidgets.
|
|
"dock:float": "#a2a2a2",
|
|
// Background color for the QMessageBox critical icon.
|
|
"critical": "#80404a",
|
|
// Background color for the QMessageBox information icon.
|
|
"information": "#406880",
|
|
// Background color for the QMessageBox question icon.
|
|
"question": "#634d80",
|
|
// Background color for the QMessageBox warning icon.
|
|
"warning": "#99995C"
|
|
}
|
|
```
|
|
|
|
Once you've saved your custom theme, you can then build the stylesheet, icons, and resource file with:
|
|
|
|
```bash
|
|
python configure.py --styles=dark,light,<custom> --resource custom.qrc
|
|
```
|
|
|
|
Then, you can use `custom.qrc`, along with the generated icons and stylesheets in each folder, in place of `breeze.qrc` for any style.
|
|
|
|
The `--styles` command flag takes a comma-separated list of values, or `all`, which will configure every theme present in the [themes](/theme) directory.
|
|
|
|
**Generating Colors**
|
|
|
|
As a reference point, see the pre-generated [themes](/theme). In general, to create a good theme, modify only the highlight colors (blues, greens, purples) to a new color, such that the saturation and lightness stay the same (only the hue changes). For example, the color `rgba(51, 164, 223, 0.5)` becomes `rgba(164, 51, 223, 0.5)`.
|
|
|
|
**Extensions**
|
|
|
|
We also allow customizable extensions to extend the default stylesheets with additional style rules, using the colors defined in your theme. This also enables the integration of third-party Qt plugins/widgets into the generated stylesheets.
|
|
|
|
For example, to configure with extensions for the [Advanced Docking System](https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System), run:
|
|
|
|
```bash
|
|
python configure.py --extensions=advanced-docking-system --resource custom.qrc
|
|
```
|
|
|
|
Like with styles, `--extensions` takes a comma-separated list of values, or `all`, which will add every extension present in the [extensions](/extension) directory. For a detailed introduction to creating your own extensions, see the extensions [tutorial](/extension/README.md).
|
|
|
|
# Features
|
|
|
|
- Complete stylesheet for all Qt widgets, including esoteric widgets like `QCalendarWidget`.
|
|
- Customizable, beautiful light and dark themes.
|
|
- Cross-platform icon packs for standard icons.
|
|
- Extensible stylesheets: add your own plugins or rules and automatically configure them using the same configuration syntax.
|
|
|
|
## Plugins
|
|
|
|
The supported plugins can be found in the [extensions](/extension/README.md) directory and include theme support for:
|
|
- [Advanced Docking System](/extension/README.md#advanced-docking-system)
|
|
- [QDockWidget Tooltips](/extension/README.md#qdockwidget-tooltips)
|
|
- [Complete Standard Icon Set](/extension/README.md#standard-icons)
|
|
|
|
# Extending Stylesheets
|
|
|
|
There are some limitations of using Qt stylesheets in general, which cannot be solved by stylesheets. To get more fine-grained style control, you should subclass `QCommonStyle`:
|
|
|
|
```c++
|
|
class ApplicationStyle: public QCommonStyle
|
|
{
|
|
...
|
|
}
|
|
```
|
|
|
|
The limitations of stylesheets include:
|
|
|
|
- Using custom standard icons.
|
|
- Scaling icons with the theme size.
|
|
- QToolButton cannot control the icon size without also affecting the arrow size.
|
|
- Close and dock float icon sizes scale poorly with font size.
|
|
|
|
For an example of using QCommonStyle to override standard icons in a PyQt application, see [standard_icons.py](/example/standard_icons.py). An extensive reference can be found [here](https://doc.qt.io/qt-5/style-reference.html). A reference of QStyle, and the default styles Qt provides can be found [here](https://doc.qt.io/qt-5/qstyle.html).
|
|
|
|
# Installing
|
|
|
|
Here are detailed instructions on how to install Breeze Style Sheets for a variety of build systems and programming languages.
|
|
|
|
## Configuring
|
|
|
|
By default, BreezeStyleSheets comes with the `dark` and `light` themes pre-built. In order to build all pre-packaged themes including PyQt5 and PyQt6 support, run:
|
|
|
|
```bash
|
|
python configure.py --styles=all --extensions=all --pyqt6 \
|
|
--resource breeze.qrc --compiled-resource breeze_resources.py
|
|
```
|
|
|
|
All generated themes will be in the [dist](/dist) subdirectory, and the compiled Python resource will be in `breeze_resouces.py`. Note that using the `--compiled-resource` flag requires `pyrcc5` to be installed.
|
|
|
|
## CMake Installation
|
|
|
|
Using CMake, you can download, configure, and compile the resources as part part of the build process. The following configurations are provided by @ruilvo. First, save the following as `BreezeThemes.cmake`
|
|
|
|
```cmake
|
|
# Setup Qt: this works with both Qt5 and Qt6
|
|
set(CMAKE_AUTOMOC ON)
|
|
set(CMAKE_AUTORCC ON)
|
|
set(CMAKE_AUTOUIC ON)
|
|
|
|
find_package(
|
|
QT NAMES Qt6 Qt5
|
|
COMPONENTS Core
|
|
REQUIRED)
|
|
find_package(
|
|
Qt${QT_VERSION_MAJOR}
|
|
COMPONENTS ${AE_REQUIRED_QT_COMPONENTS}
|
|
REQUIRED)
|
|
# -------------------
|
|
|
|
# Get Python to compile the stylesheets.
|
|
# Fetch the repository, configure, compile the stylesheets.
|
|
find_package(Python COMPONENTS Interpreter)
|
|
|
|
include(FetchContent)
|
|
|
|
set(FETCHCONTENT_QUIET
|
|
OFF
|
|
CACHE BOOL "Silence fetch content" FORCE)
|
|
|
|
FetchContent_Declare(
|
|
breeze_stylesheets
|
|
GIT_REPOSITORY https://github.com/Alexhuszagh/BreezeStyleSheets.git
|
|
GIT_TAG origin/main
|
|
GIT_PROGRESS ON
|
|
USES_TERMINAL_DOWNLOAD TRUE)
|
|
|
|
FetchContent_GetProperties(breeze_stylesheets)
|
|
if(NOT breeze_stylesheets_POPULATED)
|
|
FetchContent_Populate(breeze_stylesheets)
|
|
|
|
add_library(breeze_themes STATIC "${breeze_stylesheets_SOURCE_DIR}/dist/qrc/breeze.qrc")
|
|
|
|
add_custom_target(
|
|
run_python_breeze ALL
|
|
COMMAND ${Python_EXECUTABLE} configure.py --extensions=<EXTENSIONS>
|
|
--styles=<STYLES> --resource breeze.qrc
|
|
WORKING_DIRECTORY ${breeze_stylesheets_SOURCE_DIR}
|
|
BYPRODUCTS "${breeze_stylesheets_SOURCE_DIR}/dist/qrc/breeze.qrc"
|
|
COMMENT "Generating themes")
|
|
|
|
add_dependencies(breeze_themes run_python_breeze)
|
|
endif()
|
|
```
|
|
|
|
Next, make sure the path to `breeze_themes.cmake` is in your module search [path](https://cgold.readthedocs.io/en/latest/tutorials/cmake-sources/includes.html), and add the following to your CMakeLists.txt:
|
|
|
|
```cmake
|
|
include(BreezeThemes)
|
|
|
|
add_executable(myapp WIN32 MACOSX_BUNDLE "main.cpp")
|
|
target_link_libraries(myapp PRIVATE Qt${QT_VERSION_MAJOR}::Widgets breeze_themes)
|
|
```
|
|
|
|
And then in your application start point, add the following:
|
|
|
|
```cpp
|
|
int main()
|
|
{
|
|
// ...
|
|
QApplication app(argc, argv);
|
|
|
|
// Need to initialize the resource, since we're using an external
|
|
// build system and this isn't automatically handled by CMake.
|
|
Q_INIT_RESOURCE(breeze_themes);
|
|
QFile file(":/dark-green/stylesheet.qss");
|
|
file.open(QFile::ReadOnly | QFile::Text);
|
|
QTextStream stream(&file);
|
|
app.setStyleSheet(stream.readAll());
|
|
|
|
// ...
|
|
}
|
|
```
|
|
|
|
## QMake Installation
|
|
|
|
Copy the contents of the `dist/qrc` subdirectory into your project directory and add the qrc file to your project file.
|
|
|
|
For example:
|
|
|
|
```qmake
|
|
TARGET = app
|
|
SOURCES = main.cpp
|
|
RESOURCES = breeze.qrc
|
|
```
|
|
|
|
To load the stylesheet in C++, load the file using QFile and read the data. For example, to load BreezeDark, run:
|
|
|
|
```cpp
|
|
|
|
#include <QApplication>
|
|
#include <QFile>
|
|
#include <QTextStream>
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
QApplication app(argc, argv);
|
|
|
|
// set stylesheet
|
|
QFile file(":/dark/stylesheet.qss");
|
|
file.open(QFile::ReadOnly | QFile::Text);
|
|
QTextStream stream(&file);
|
|
app.setStyleSheet(stream.readAll());
|
|
|
|
// code goes here
|
|
|
|
return app.exec();
|
|
}
|
|
```
|
|
|
|
## PyQt5 Installation
|
|
|
|
To compile the stylesheet for use with PyQt5, ensure you configure with the `--compiled-resource` flag (which requires `pyrcc5` installed). The compiled resource Python file now contains all the stylesheet data. To load and set the stylesheet in a PyQt5 application, import that file, load the contents using QFile and read the data. For example, to load BreezeDark, first configure using:
|
|
|
|
```bash
|
|
python configure.py --compiled-resource breeze_resources.py
|
|
```
|
|
|
|
Then load the stylesheet and run the application using:
|
|
|
|
```python
|
|
from PyQt5 import QtWidgets
|
|
from PyQt5.QtCore import QFile, QTextStream
|
|
import breeze_resources
|
|
|
|
|
|
def main():
|
|
app = QtWidgets.QApplication(sys.argv)
|
|
|
|
# set stylesheet
|
|
file = QFile(":/dark/stylesheet.qss")
|
|
file.open(QFile.ReadOnly | QFile.Text)
|
|
stream = QTextStream(file)
|
|
app.setStyleSheet(stream.readAll())
|
|
|
|
# code goes here
|
|
|
|
app.exec_()
|
|
```
|
|
|
|
## PyQt6 Installation
|
|
|
|
Since [pyrcc](https://www.riverbankcomputing.com/pipermail/pyqt/2020-September/043209.html) is no longer being maintained, using local Python paths is the preferable solution. For a detailed description on how to package these resources, see this StackOverflow [answer](https://stackoverflow.com/a/20885799/4131059).
|
|
|
|
First, package your code using setuptools. Make sure `zip_safe` is off, so we can properly load the files from a search path, and include the necessary package directories to your `MANIFEST.in` file.
|
|
|
|
```python
|
|
from setuptools import setup
|
|
|
|
setup(
|
|
# Either option is valid here.
|
|
# Either use `package_data` with enumerating the values, or
|
|
# set `include_package_data=True`.
|
|
include_package_data=True,
|
|
package_data={
|
|
'breeze_theme': ['dist/pyqt6/*'],
|
|
},
|
|
zip_safe=False,
|
|
)
|
|
```
|
|
|
|
Then, you can import the resources as follows:
|
|
|
|
```python
|
|
import importlib.resources
|
|
from Qt6 import QtWidgets, QtCore
|
|
from Qt6.QtCore import QFile, QTextStream
|
|
|
|
|
|
def main():
|
|
app = QtWidgets.QApplication(sys.argv)
|
|
|
|
# set stylesheet
|
|
# Note that the search path name must be the theme name.
|
|
# dark => dark, light => light, dark-purple => dark-purple, ...
|
|
breeze_theme = importlib_resources.files('breeze_theme.dark')
|
|
QtCore.QDir.addSearchPath('dark', breeze_theme)
|
|
file = QFile("dark:stylesheet.qss")
|
|
file.open(QFile.OpenModeFlag.ReadOnly | QFile.OpenModeFlag.Text)
|
|
stream = QTextStream(file)
|
|
app.setStyleSheet(stream.readAll())
|
|
|
|
# code goes here
|
|
|
|
app.exec()
|
|
```
|
|
|
|
# Debugging
|
|
|
|
Have an issue with the styles? Here's a few suggestions, prior to filing a bug report:
|
|
|
|
- Modified the application font? Make sure you do **before** setting the application stylesheet.
|
|
- Modified the application style? Make sure you do **after** you creating a `QApplication instance` but **before** you show the window or add widgets.
|
|
|
|
# Development Guide
|
|
|
|
## Configuring
|
|
|
|
To configure the assets and the stylesheets, run `python configure.py`. To compile the assets and stylesheets for PyQt5, ensure `pyrcc5` is installed and run:
|
|
|
|
```bash
|
|
python configure.py --compiled-resource breeze_resources.py
|
|
```
|
|
|
|
## Testing
|
|
|
|
The unittest suite is [ui.py](test/ui.py). By default, the suite runs every test, so to test changes to a specific widget, pass the `--widget $widget` flag. To test other configurations, see the options for `--stylesheet`, `--widget`, `--font-size`, and `--font-family`, and then run the tests with the complete UI in [widgets.py](/example/widgets.py). If the widget you fixed the style for does not exist in the test suite or [widgets.py](/example/widgets.py), please add it.
|
|
|
|
```bash
|
|
# Test all widgets
|
|
$ python test/ui.py --stylesheet $theme
|
|
# Test only a single widget.
|
|
$ python test/ui.py --widget $widget --stylesheet $theme
|
|
# Get the help options.
|
|
$ python test/ui.py --help
|
|
usage: ui.py [-h] [--widget WIDGET] [--stylesheet STYLESHEET] [--style STYLE]
|
|
[--font-size FONT_SIZE] [--font-family FONT_FAMILY] [--width WIDTH]
|
|
[--height HEIGHT] [--alignment ALIGNMENT] [--compress] [--scale SCALE] [--pyqt6]
|
|
[--use-x11]
|
|
|
|
Configurations for the Qt application.
|
|
|
|
options:
|
|
-h, --help show this help message and exit
|
|
--widget WIDGET widget to test. can provide `all` to test all widgets
|
|
--stylesheet STYLESHEET
|
|
stylesheet name (`dark`, `light`, `native`, ...)
|
|
--style STYLE application style (`Fusion`, `Windows`, `native`, ...)
|
|
--font-size FONT_SIZE
|
|
font size for the application
|
|
--font-family FONT_FAMILY
|
|
the font family
|
|
--width WIDTH the window width
|
|
--height HEIGHT the window height
|
|
--alignment ALIGNMENT
|
|
the layout alignment
|
|
--compress add stretch on both sides
|
|
--scale SCALE scale factor for the UI
|
|
--pyqt6 use PyQt6 rather than PyQt5.
|
|
--use-x11 force the use of x11 on compatible systems
|
|
--print-tests print all available tests (widget names).
|
|
# Get a complete list of available tests.
|
|
$ python test/ui.py --print-tests
|
|
aero_wizard
|
|
all_focus_tree
|
|
alpha_colordialog
|
|
...
|
|
wizard
|
|
yes_button
|
|
```
|
|
|
|
To see the complete list of Qt widgets covered by the unittests, see [Test Coverage](Test%20Coverage.md).
|
|
|
|
## Distribution Files
|
|
|
|
When pushing changes, only the `light` and `dark` themes should be configured, without any extensions. To reset the built resource files to the defaults (this requires `pyrcc5` to be installed), run:
|
|
|
|
```bash
|
|
python configure.py --clean --pyqt6 \
|
|
--compiled-resource breeze_resources.py
|
|
```
|
|
|
|
If no changes are being made to the icons or stylesheets, you may want to ensure that the `dist` directory is assumed to be unchanged in git, no longer tracking changes to these files. You can turn tracking distribution files off with:
|
|
|
|
```bash
|
|
python vcs.py --no-track-dist
|
|
```
|
|
|
|
To turn back on tracking, run:
|
|
|
|
```bash
|
|
python vcs.py --track-dist
|
|
```
|
|
|
|
## Git Ignore
|
|
|
|
Note that the `.gitignore` is auto-generated via `vcs.py`, and the scripts to track or untrack distribution files turn off `.gitignore` tracking. Any changes should be made in `vcs.py`, and ensure that `.gitignore` is tracked, and commit any changes:
|
|
|
|
```bash
|
|
python vcs.py --track-gitignore
|
|
git add .gitignore
|
|
git commit -m "..."
|
|
```
|
|
|
|
# Known Issues and Workarounds
|
|
|
|
For known issues and workarounds, see [issues](/ISSUES.md).
|
|
|
|
# License
|
|
|
|
MIT, see [license](/LICENSE.md).
|
|
|
|
# Contributing
|
|
|
|
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in BreezeStyleSheets by you shall be licensed under the MIT license without any additional terms or conditions.
|
|
|
|
# Acknowledgements
|
|
|
|
BreezeStyleSheets is a fork of [QDarkStyleSheet](https://github.com/ColinDuquesnoy/QDarkStyleSheet). Some of the icons are modified from [Material UI](https://github.com/google/material-design-icons) and [Material Design Icons](https://materialdesignicons.com/) (both of which use an Apache 2.0 license), and are redistributed under the MIT license.
|
|
|
|
# Contact
|
|
|
|
Email: ahuszagh@gmail.com
|
|
Twitter: KardOnIce
|