2026, Jan 04 21:01
Почему QSS в PySide6 не перекрашивает SVG-иконки и как правильно переключать темы
Почему в PySide6 QSS не меняет цвет SVG-иконок в QIcon, и как грамотно переключать темы: два набора иконок и QDir.setSearchPaths() без костылей. Подробно.
Переключение приложения PySide6 между светлой и тёмной темами почти всегда приводит к одному и тому же вопросу: можно ли управлять цветом SVG-иконок через QSS — по аналогии с браузерами и currentColor? Короткий ответ: нет. И если ваш интерфейс опирается на QIcon с SVG-ресурсами, попытка унаследовать цвет из таблицы стилей не даст ожидаемого результата.
Постановка задачи
Легко предположить, что правило стиля ниже перекрасит SVG-иконку, используемую виджетом, особенно если внутри SVG задано fill="currentColor" и вы рассчитываете, что иконка унаследует свойство color:
#nav_btn {
color: white;
}
На практике в PySide6 это не работает. SVG, загруженные через QIcon, не получают контекста о цвете виджета, поэтому иконка не изменится вместе со свойством color в QSS.
Почему так происходит
QIcon — это не изображение, а абстракция, к которой виджеты обращаются, когда им нужен pixmap определённого размера или состояния. Под капотом у каждого QIcon есть движок иконок; для SVG он умеет парсить и рендерить содержимое SVG в pixmap по запросу.
Когда виджет рисует свою иконку, Qt вызывает QIcon::pixmap(), который перенаправляет запрос в движок. Ключевой момент — сигнатура, которую получает движок:
QIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
В этом вызове нет никакой информации о цвете переднего плана виджета или его таблице стилей. Движок иконок рендерит, не зная, какой цвет задан у целевого виджета, поэтому изменение палитры или свойства color в QSS не влияет на результат. Да, есть ещё функция paint(), которая получает QPainter, но полагаться на неё для передачи цвета в данном случае — неподдерживаемый путь; даже собственная обработка style sheet в Qt для иконок на кнопках использует именно pixmap(). В итоге «перекрашивание» иконок через QSS здесь недостижимо.
Ещё один частый источник путаницы — ожидание, что таблицы стилей Qt ведут себя как CSS один к одному. Синтаксис похож, но это не браузерный движок: полной каскадности и всех семантик CSS там нет. В частности, нельзя передать «CSS-контекст» цвета в движок иконок.
Практическое решение
Надёжный подход — держать отдельные наборы иконок под каждую цветовую схему и переключать их на лету. В Qt для удобства есть небольшой, но полезный механизм: QDir.setSearchPaths(). Вы задаёте логический префикс и сопоставляете его с физической директорией в зависимости от активной темы. Тогда один и тот же код загрузки будет брать нужные ресурсы без условной логики по всему UI.
Последовательность такая: подготовьте две папки с одинаковыми именами SVG-файлов — для светлой и для тёмной темы — и привяжите префикс поискового пути к активной папке в соответствии с текущей схемой. Работает как с файловой системой, так и с ресурсами Qt.
Вот инициализация:
ui_meta = QApplication.styleHints()
if ui_meta.colorScheme() == Qt.ColorScheme.Dark:
asset_dir = '<path-to-light-icons>'
else:
asset_dir = '<path-to-dark-icons>'
QDir.setSearchPaths('gfx', [asset_dir])
После этого назначать иконки просто и независимо от физического расположения файлов:
main_button.setIcon(QIcon('gfx:toolbar_play.svg'))
Важно помнить: setSearchPaths() не обновляет задним числом уже созданные экземпляры QIcon. Вызывайте его до создания любых иконок — обычно при старте приложения. Если вы поддерживаете переключение темы на ходу, переустанавливайте иконки, вызывая setIcon() после смены схемы. Держите только логические имена и повторно применяйте их при изменении.
Минимальный пример переустановки иконки после смены системной цветовой схемы:
hint_src = QApplication.styleHints()
icon_key = 'toolbar_play.svg'
main_button.setIcon(QIcon('gfx:' + icon_key))
def on_scheme_flip():
main_button.setIcon(QIcon('gfx:' + icon_key))
hint_src.colorSchemeChanged.connect(on_scheme_flip)
Почему это важно
В Qt рендеринг иконок определяется QIcon и его движком, а не CSS-подобной каскадностью. Ожидание, что QSS перекрасит SVG, ведёт в тупик и к хрупким костылям. Разделение ассетов по темам и выбор через QDir.setSearchPaths() делает код чище, предсказуемее и поддерживаемее, а решение о том, какой набор активен, — централизованным.
Итоги
Если ваше приложение на PySide6 поддерживает светлый и тёмный режимы, не рассчитывайте на свойство color в QSS для влияния на SVG-иконки. Подготовьте два набора иконок с одинаковыми именами файлов, выберите активный через QDir.setSearchPaths() до создания иконок, а при смене темы на лету переустановите иконки, чтобы они подхватили новый путь поиска. Такой подход соответствует реальной работе QIcon и QIconEngine и избавляет от привязки рендеринга иконок к неподдерживаемым ожиданиям от стилей.