В статье рассказывается о функционале, о котором забыли даже разработчики Qt. Дело в том, что этот функционал даёт уникальные возможности стилизации. В отличие от QML и CSS, использование QStyleFactory позволяет предоставить пользователю широкий выбор QStyle‑дизайнов виджетов без затрат времени на настройку QML и CSS. Я много раз видел, как программисты тратят много времени на настройку дизайна через QML и CSS, хотя в Qt 5 и Qt 6 есть база уже готовых дизайнов, которую никто не предоставляет пользователю в качестве выбора. Считаю необходимым рассказать об этом очень удобном и даже ленивом способе настройки дизайна.
Установка и запуск
Шаблон QWidget (QStyleSelect) для быстрого и простого изменения дизайна приложения с возможностью выбора между стилями. Скачать, собрать и запустить исходный код примера:
git clone https://github.com/PVE-Vladimir/QStyleSelect.git mkdir -p QStyleSelect/build cd QStyleSelect/build cmake .. make ./QStyleSelect
Установка стилей происходит через стандартный менеджер пакетов apt команды:
apt search styl | grep "qt5" | awk -F'/' '{print $1}' | sed 's/Development.*//g' | xargs sudo apt install -y apt search styl | grep "qt6" | awk -F'/' '{print $1}' | sed 's/Development.*//g' | xargs sudo apt install -y
Установка происходит в директорию /usr/lib/x86_64-linux-gnu/qt5/plugins/styles/ или /usr/lib/x86_64-linux-gnu/qt6/plugins/styles/ Сюда же можно установить стиль, собранный из исходного кода. К примеру, я собирал и использовал в локальном профиле Qt вот этот стиль: file:///usr/local/Qt-6.4.2/plugins/styles/k4oxygen.so

Как работает технология QStyle (QStyleFactory)
Установка стилей через QApplication не полностью поддерживается в Qt Creator. В руководстве Qt Creator сказано: «Внимание: в настоящее время таблицы стилей Qt не поддерживаются для пользовательских подклассов стилей QStyle. Мы планируем решить эту проблему в одном из будущих релизов». Однако это не значит, что этот функционал не работает. Как сказано в руководстве, вы можете вызвать QApplication::setStyle() в любое время, но, вызвав его до конструктора, вы гарантируете, что предпочтения пользователя, установленные с помощью ‑style, будут учтены. Пример использования:
QApplication::setStyle(QStyleFactory::create("Fusion"));
QStyleFactory создаёт объект QStyle с использованием функции create() и ключа, идентифицирующего стиль. Стили либо встроенные, либо динамически загружаются из плагина стилей.
В общем, как выяснилось далее, функционал CustomStyle действительно не доработан до конца. Однако мне удалось реализовать его для Qt5 полностью, а для Qt6 — частично. Всё равно считаю это хорошим результатом.
Qt5 как основной инструмент сборки располагает более развитым репозиторием стилей, чем Qt6.
Скомпилированные стили Qt 5 в репозитории Ubuntu, которые удалось найти: breeze5.so libkvantum.so libqgtk2style.so libqplastiquestyle.so libukui‑proxy‑style.so qtcurve.so breeze.so libqcleanlooksstyle.so libqmotifstyle.so libqt5-style‑ukui.so oxygen.so

Скомпилированные стили Qt6 в репозитории Ubuntu, которые удалось найти: adwaita.so libqt6ct‑style.so

Эти стили имеют в своём составе несколько вариаций цветовой палитры, чаще всего это вариации светлой и чёрной тем. В моём примере программы отсутствует удаление повторяющихся названий стилей. В случае необходимости вы можете сами изменить переменную списка стилей по своему усмотрению. Может быть, различия и есть, но я их просто не заметил. Список стилей в коде формируется через QStringList:
QStringList styles = QStyleFactory::keys();
Далее для Qt5 я добавил в код логику обработки свойств имеющихся стилей для тёмных версий:
Добавление в список темных версий стилей:
QStringList styles = QStyleFactory::keys(); //qDebug() << "Current Style:" << styles; int indexwindows = styles.indexOf("Windows"); int indexmotif = styles.indexOf("motif"); int indexcde = styles.indexOf("cde"); int indexdark = styles.indexOf("kvantum-dark"); // Insert a new style. Combination of properties of different topics Qt 5 if (indexwindows != -1 && indexdark != -1) { styles.insert(indexwindows + 1, "Windows-dark"); } if (indexmotif != -1 && indexdark != -1 ){ styles.insert(indexmotif + 1, "motif-dark"); } if (indexcde != -1 && indexdark != -1 ){ styles.insert(indexcde + 1, "cde-dark"); } qDebug() << "Current Style:" << styles;
Логика создания темных версий стилей:
//Combination of properties of different topics Qt 5 if (style == "Windows-dark") { QApplication::setStyle(QStyleFactory::create("kvantum-dark")); QApplication::setStyle(QStyleFactory::create("Windows")); } else if (style == "ukui-dark") { QApplication::setStyle(QStyleFactory::create("kvantum-dark")); QApplication::setStyle(QStyleFactory::create("ukui")); } else if (style == "motif-dark") { QApplication::setStyle(QStyleFactory::create("kvantum-dark")); QApplication::setStyle(QStyleFactory::create("motif")); } else if (style == "cde-dark") { QApplication::setStyle(QStyleFactory::create("kvantum-dark")); QApplication::setStyle(QStyleFactory::create("cde")); } else { QApplication::setStyle(QStyleFactory::create(style)); }
Как видно из кода, возможно совмещать свойства нескольких тем в одну. Это противоречит тому, что указано в электронном руководстве Qt Creator, но тут жизнь расходится с теорией. Дело в том, что некоторые свойства пользовательских стилей несовместимы с обычными стилями Qt. Поэтому в коде примера QStyleSelect применяются такого рода уловки, чтобы всё работало.
Уловка QPalette
Некоторые пользовательские стили по какой‑то причине необратимо изменяют QPalette. Класс QPalette содержит цветовые группы для каждого состояния виджета. Поэтому в коде перед применением цвета сохраняются системные цветовые группы.
oldPalette = new QPalette(QApplication::palette());
После чего перед применением нового стиля происходит сброс цветовых групп и на всякий случай устанавливается стиль Fusion. Fusion — базовый стиль Qt.
QApplication::setPalette(*oldPalette); QApplication::setStyle(QStyleFactory::create("Fusion")); //Disables QPalette from the system in Qt 5 //white_QPalette();
Метод white_QPalette() работает только в Qt 5. По неизвестной мне причине в Qt 6, если цвет установить через setColor, этот параметр будет доминировать над setStyle, а в Qt 5 существует последовательность установки цвета. Метод white_QPalette() реализует другой подход к сбросу цветовой группы. Если взять системную палитру, есть вероятность, что в системе будет выбрана чёрная тема, и тогда некоторые стили применят чёрную цветовую группу. Чтобы сделать программу независимой от системы, создан метод white_QPalette(), который устанавливает светлую цветовую группу по умолчанию.
void MainWindow::white_QPalette() { QPalette Theme; // QPalette palette = QApplication::palette(); // for (int role = 0; role < QPalette::NColorRoles; ++role) { // qDebug() << "Role" << role << ":" << palette.color((QPalette::ColorRole)role); // } Theme.setColor(QPalette::WindowText, QColor::fromRgbF(0.137255, 0.14902, 0.160784, 1)); Theme.setColor(QPalette::Button, QColor::fromRgbF(0.988235, 0.988235, 0.988235, 1)); Theme.setColor(QPalette::Light, QColor::fromRgbF(1, 1, 1, 1)); Theme.setColor(QPalette::Midlight, QColor::fromRgbF(1, 1, 1, 1)); Theme.setColor(QPalette::Dark, QColor::fromRgbF(0.494118, 0.494118, 0.494118, 1)); Theme.setColor(QPalette::Mid, QColor::fromRgbF(0.658824, 0.658824, 0.658824, 1)); Theme.setColor(QPalette::Text, QColor::fromRgbF(0.137255, 0.14902, 0.160784, 1)); Theme.setColor(QPalette::BrightText, QColor::fromRgbF(1, 1, 1, 1)); Theme.setColor(QPalette::ButtonText, QColor::fromRgbF(0.137255, 0.14902, 0.160784, 1)); Theme.setColor(QPalette::Base, QColor::fromRgbF(1, 1, 1, 1)); Theme.setColor(QPalette::Window, QColor::fromRgbF(0.937255, 0.941176, 0.945098, 1)); Theme.setColor(QPalette::Shadow, QColor::fromRgbF(0.462821, 0.462821, 0.462821, 1)); Theme.setColor(QPalette::Highlight, QColor::fromRgbF(0.239216, 0.682353, 0.913725, 1)); Theme.setColor(QPalette::HighlightedText, QColor::fromRgbF(1, 1, 1, 1)); Theme.setColor(QPalette::Link, QColor::fromRgbF(0.160784, 0.501961, 0.72549, 1)); Theme.setColor(QPalette::LinkVisited, QColor::fromRgbF(0.607843, 0.34902, 0.713726, 1)); Theme.setColor(QPalette::AlternateBase, QColor::fromRgbF(0.968627, 0.968627, 0.968627, 1)); Theme.setColor(QPalette::NoRole, QColor::fromRgbF(0, 0, 0, 1)); Theme.setColor(QPalette::ToolTipBase, QColor::fromRgbF(0.968627, 0.968627, 0.968627, 1)); Theme.setColor(QPalette::ToolTipText, QColor::fromRgbF(0.137255, 0.14902, 0.160784, 1)); Theme.setColor(QPalette::PlaceholderText, QColor::fromRgbF(0, 0, 0, 0.501961)); QApplication::setPalette(Theme); }
Этот цикл выводит последовательность всех цветовых групп:
QPalette palette = QApplication::palette(); for (int role = 0; role < QPalette::NColorRoles; ++role) { qDebug() << "Role" << role << ":" << palette.color((QPalette::ColorRole)role); }
Уловка QApplication::setStyle
Существует проблема с тем, что некоторые стили используют прозрачные окна. Поэтому необходимо инициализировать их первыми в конструкторе класса, как рекомендуют в руководстве Qt. После этого можно устанавливать другие стили в любой последовательности.
//Installing the first theme of a custom theme. Otherwise, transparency issues occur. if ( styles.indexOf("kvantum-dark") != -1 ) { QApplication::setStyle(QStyleFactory::create("kvantum-dark")); } else { QApplication::setStyle(QStyleFactory::create("Adwaita-HighContrastInverse")); }
Также в некоторых случаях возможно совмещать свойства двух стилей в один пример:
QApplication::setStyle(QStyleFactory::create("kvantum-dark")); QApplication::setStyle(QStyleFactory::create("Windows"));
Алгоритм работы QStyleSelect
В конструкторе класса основного окна при первом запуске применяется начальная тема «kvantum‑dark» или «Adwaita‑HighContrastInverse», а если их нет, то остаётся базовая. Затем, после создания объекта MainWindow, запускается метод history_QStyle(), который создаёт файл истории config_style.json в случае его отсутствия. Через файл config_style.json реализовано сохранение выбранной пользователем предыдущей темы по умолчанию после закрытия программы. Начальная тема при первом запуске — ukui. Если темы нет, метод setStyle установит тему по умолчанию «Fusion». Если файл существует, тема по умолчанию будет установлена из файла config_style.json. В методе history_QStyle также выполняется сброс цветовой группы.
int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); w.history_QStyle(); return a.exec(); }
Меню стилей формируется из списка QStringList styles и добавляется в стандартный menubar
ui->menubar->addMenu(menu);
Для каждого стиля в конструкторе класса MainWindow создаётся свой слот:
// Add actions for each style for (const QString &style : styles) { QAction *action = menu->addAction(style); // Set up a slot to handle style selection connect(action, &QAction::triggered, [this, style, configFilePath]() { //Reset QPalette values to default QApplication::setPalette(*oldPalette); QApplication::setStyle(QStyleFactory::create("Fusion")); //Disables QPalette from the system in Qt 5 //white_QPalette(); QFile file(configFilePath); if (file.open(QIODevice::ReadWrite | QIODevice::Text)) { QByteArray fileData = file.readAll(); QJsonDocument jsonDoc = QJsonDocument::fromJson(fileData); QJsonObject jsonObj = jsonDoc.object(); jsonObj["style"] = style; file.resize(0); QJsonDocument updatedDoc(jsonObj); file.write(updatedDoc.toJson()); file.close(); }else{ QMessageBox::information(this, "Style .json", "Failed to create file: " + style); } //Combination of properties of different topics Qt 5 if (style == "Windows-dark") { QApplication::setStyle(QStyleFactory::create("kvantum-dark")); QApplication::setStyle(QStyleFactory::create("Windows")); } else if (style == "ukui-dark") { QApplication::setStyle(QStyleFactory::create("kvantum-dark")); QApplication::setStyle(QStyleFactory::create("ukui")); } else if (style == "motif-dark") { QApplication::setStyle(QStyleFactory::create("kvantum-dark")); QApplication::setStyle(QStyleFactory::create("motif")); } else if (style == "cde-dark") { QApplication::setStyle(QStyleFactory::create("kvantum-dark")); QApplication::setStyle(QStyleFactory::create("cde")); } else { QApplication::setStyle(QStyleFactory::create(style)); } QMessageBox::information(this, "Style changed", "Style changed to " + style); }); }

Заключение
Мне очень интересно узнать мнение общественности в комментариях о шаблоне QStyleSelect. В этой статье, как мне кажется, удалось представить интересный пример использования QStyle (QStyleFactory). Данный шаблон QStyleSelect благодаря своим вариантам стилей даёт возможность расширенной стилизации приложений. В качестве существенного плюса можно отметить то, что не нужно тратить время на QML и CSS, а можно использовать уже готовые стили.
Комментарии (19)

Zifix
06.09.2026 21:47Для кастомных интерфейсов виджеты на сегодня уже не актуальны, и не развиваются со времён Qt4, страшно сказать.

PVE-Vladimir Автор
06.09.2026 21:47Дизайн в целом со времён Qt 4 (2006-2016) стал умирать. Причина- векторная графика. К примеру, Windows Aero - последняя версия Windows, в которой был дизайн (он растровый). В Windows 8.1 и Windows 10 дизайн векторный и меняется только цвет. Во времена Qt 4 даже установка драйвера на принтер с диска имела свой дизайн. Сейчас любое банковское приложение имеет один и тот же дизайн, меняется только цвет. Дизайн сейчас существует в основном на китайских Android-смартфонах: Xiaomi и т. д. Для Apple добавить шрифты в качестве выбора для пользователя, это целое событие.

zum
06.09.2026 21:47и не развиваются со времён Qt4
С другой стороны, а что там развивать?

PVE-Vladimir Автор
06.09.2026 21:473D и скевоморфизм, к примеру. QPushButton с полноценной 3D-графикой, шейдерами и ещё больше наркомании.

Sazonov
06.09.2026 21:47Вы просто немного поиграли со стилями, слово «шаблон» в контексте вашей статьи мне непонятно. Сразу хочется у вас спросить, а вы исходники Qt в той части где идёт работа с qss открывали?
В целом, поддержка css (qss) в Qt - это чистого вида натягивание совы на глобус. Причём в кулуарах одной из конференций мне довелось об этом поговорить с разработчиками. Просто в то время это был тренд и не было нормальных альтернатив для быстрого прототипирования стилизованных окон.
На текущем проекте (энтерпрайз, куча легаси) у нас обвязка, которая на основе json генерирует qss со всеми нужными стилями. И я задолбался подпирать костылями то, что где-то изначально упустили. Наиболее классическая ситуация - это когда разработчики виджетов не совсем аккуратно реализуют поддержку setFocus, setParent внутри каких либо обработчиков и повреждается внутренний кэш qstylesheet (там вектор указателей на QObject) - и в процессе смены стилей в лучшем случае всё дико тормозит, а в худшем - access violation из-за обращений к уже удалённым объектам.
Мой личный вывод - если вы всё ещё сидите на виджетах, то наследуйтесь от QStyle либо QProxyStyle и ручками, под каждую платформу, пилите нужный стиль. Программно, без всяких там qss. Будет намного более предсказуемо и быстро.

PVE-Vladimir Автор
06.09.2026 21:47Тут цель статьи была в том, чтобы показать, что уже есть собранные стили Qt, которые легко подключить к проекту. Многие делают программы с ui вообще без стилизации или неделю тратят на создание аналога стиля из репозитория. Мне, конечно, понятно, что ручками лучше получится, но столько времени есть не у всех. А хороший дизайн программы, это вообще не всем по силам.

Sazonov
06.09.2026 21:47Ок. Вы показали как загрузить стиль. По сути вы описали два метода из документации по Qt - перечислить стили, установить стиль.
И да, заголовок про ненужность QML - слишком громкий. Можете на qss и виджетах сделать переливающийся фон у кнопки? На QML это пару строк кода и будет с большего делаться на GPU, на чистом qss вы не сделаете вообще никаких анимаций.

PVE-Vladimir Автор
06.09.2026 21:47Да, я привёл пример с описанием. Туториалов по QML много, а про стили мало. Я не пробовал, но мне что-то подсказывает, что всё же это можно сделать в стилях .so. QML - это отличный инструмент для стилизации, я не спорю, я показал готовую альтернативу. Ясно, что ручками можно сделать всё, что угодно.

Sazonov
06.09.2026 21:47Я не знаю, что такое “стили .so”. Поясните, что вы имеете в виду? Обычно под .so понимают расширение для динамических библиотек в линуксе. Это никак не связано с Qt и темами.
В документации Qt примеров достаточно. Впрочем, как я уже написал, вы просто вызвали setStyle :)
Попробуйте сделать анимации на qss. Единственный способ, который я знаю, это использование QPropertyAnimation с ручным переопределением обработчика paint event. Но это уже не qss.
Если вы попробуете разработку с QML, то возвращаться на виджеты не захотите. Ну разве что вы любите мешать UI код и бизнес логику. И документации предостаточно. Попробуйте открыть официальную документацию по Qt. Там про стили и QML написано даже больше, чем про виджеты.
Ждём ваших результатов.

PVE-Vladimir Автор
06.09.2026 21:47В документации Qt примеров недостаточно, там нет того, что написано в статье. Стили .so используют QPainter, QSvgRenderer, QPixmapCache, возможно и QML можно подключить. Стили .so - это библиотеки, содержащие код и ресурсы для отображения интерфейсов (кнопки, окна, шрифты и т. д.). Я так глубоко не копал, как именно работает setStyle со стилем .so. Но анимации через QAbstractAnimation там точно присутствуют.

zum
06.09.2026 21:47В документации Qt примеров недостаточно, там нет того, что написано в статье.
В документации Qt много чего нет интересного, но что-то да есть
Стили .so используют QPainter, QSvgRenderer, QPixmapCache, возможно и QML можно подключить. Стили .so - это библиотеки, содержащие код и ресурсы для отображения интерфейсов (кнопки, окна, шрифты и т. д.). Я так глубоко не копал, как именно работает setStyle со стилем .so.
У вас смешалось всё в кучу.
«Стиль.so» — это динамически подключаемая библиотека, в которой, если посмотреть исходники Qt, находится, (обычно), два класса — класс плагина, унаследованный от QStylePlugin, и сам класс стиля, унаследованный от QProxyStyle. Первый класс нужен для того, чтоб Qt загрузила данное расширение и зарегистрировала стиль для доступа к нему через QStyleFactory, (поиск осуществляется по стандартным путям).
Но анимации через QAbstractAnimation там точно присутствуют.
Вот как уже реализовывать отрисовку в drawPrimitive, drawComplexControl и так далее — это уже дело программиста: вот тебе QPainter*, вот тебе QStyleOption* — рисуй.
Стили винды вообще дёргают виндовый код для отрисовки виджетов.
Читайте сорцы, там всё написано и объяснено.

zum
06.09.2026 21:47Единственный способ, который я знаю, это использование QPropertyAnimation с ручным переопределением обработчика paint event. Но это уже не qss.
в QPropertyAnimation::updateCurrentValue вызывать setStylesheet у targetObject, преобразованного к QWidget* ))

Sazonov
06.09.2026 21:47Увы, при более-менее нагруженном UI (например, таббар с градиентами) будет дико тормозить. Непрактично. И, главное, это не чистый qss.
Виндовые кнопки, к примеру, отрисовывается с анимациями, но это платформозависимое решение, там под капотом winapi дёргается. И это тоже не чистый qss.
Я тут попробовал еще раз перечитать то, что написал автор. Как я понял, он взял исходники некоторых тем из Qt5 и скомпилил их в свой пакет в слегка видоизменённом стиле, опустив при этом вопросы лицензирования. При этом слегка упустив то, что Qt может собираться статически. До изучения системы плагинов Qt автор пока ещё не дошел.

zum
06.09.2026 21:47Увы, при более-менее нагруженном UI (например, таббар с градиентами) будет дико тормозить. Непрактично. И, главное, это не чистый qss.
Ну я же пошутил...
И это тоже не чистый qss.
Конечно нет, как только устанавливается QSS через setStyleSheet, то стиль виджета меняется на QStyleSheetStyle, (что логично).
Я тут попробовал еще раз перечитать то, что написал автор. Как я понял, он взял исходники некоторых тем из Qt5 и скомпилил их в свой пакет в слегка видоизменённом стиле, опустив при этом вопросы лицензирования.
Как я понял из исходников, то
аффтар жжотавтор просто устанавливает пакеты из репозитория ииии... всё. Потом просто устанавливает палитру. Практической ценности не имеет, имхо. Как прототип функционала смены тем и палитры - пойдёт, но, (опять же имхо), лучше в стол.А так да, читать автора сложно, может быть, в его случае, прогнать текст через LLM было бы хорошим решением.

Siemargl
06.09.2026 21:47Такой путь выбрали в Dephi.
Но иметь выбор вариантов - стили или css (даже с костылями) лучше.
flass
Вовсе она не забытая. Мы в своё время смотрели на неё но отказались. Очень слабенькая стилизация в очень небольшом диапазоне. Если дизайнеры просят что-то эдакое (а у нас именно что было очень эдакое для встраиваемых систем), то тут лучше QML ничего не придумали (полная свобода действий фактически). CSS, точнее его устаревшее подмножество QSS это отстой, согласен. Работали и с ним, потом отказались, ибо чтобы сделать что-то из ряда вон в нём, надо прям запариться
PVE-Vladimir Автор
Туториалов по QStyle фактически нет, все используют QML или что-то другое. Ну, стилизация вовсе не слабая, можно же свой стиль придумать и подключить. Я собрал k4oxygen.so и подключил, всё заработало. Да, есть сложность в том, что стили (по душе) нужно искать и собирать, но в пакетных менеджерах есть уже собранные. Я согласен, что QML - это отличный инструмент для стилизации чего-то эдакого. Но для QML нужны время и дизайнерские способности. А тут сразу готовая база стилей.