Пользовательский интерфейс в Qt

Кастомизация стандартных виджетов через QSS: чекбоксы, скроллбары

Введение

В этой статье соберём более полные, законченные примеры кастомизации сложных составных виджетов — чекбокса с собственными иконками состояний и полностью переработанного скроллбара, которые на практике одни из самых частых объектов кастомизации, поскольку их вид по умолчанию особенно сильно отличается от современного, «плоского» дизайна большинства приложений.

Концепция

Скроллбар — один из самых составных стандартных виджетов с точки зрения количества псевдоэлементов: помимо ::handle (ползунок) у него есть ::add-line/::sub-line (кнопки прокрутки на краях, которые в современном дизайне обычно скрывают вовсе) и ::add-page/::sub-page (область трека до и после ползунка). Полная кастомизация скроллбара под «плоский» современный стиль обычно означает скрытие кнопок прокрутки и явную стилизацию только трека и ползунка.

Пример кода: Кастомизация стандартных виджетов через QSS

app.setStyleSheet(R"(
    /* Чекбокс с разными иконками для каждого состояния */
    QCheckBox {
        spacing: 8px;
        font-size: 14px;
    }

    QCheckBox::indicator {
        width: 20px;
        height: 20px;
        border-radius: 4px;
        border: 2px solid #d1d5db;
        background-color: white;
    }

    QCheckBox::indicator:hover {
        border-color: #2563eb;
    }

    QCheckBox::indicator:checked {
        background-color: #2563eb;
        border-color: #2563eb;
        image: url(:/icons/check-white.svg);
    }

    /* Промежуточное состояние "частично выбрано" — отдельная иконка */
    QCheckBox::indicator:indeterminate {
        background-color: #93c5fd;
        border-color: #93c5fd;
        image: url(:/icons/dash-white.svg);
    }

    QCheckBox::indicator:disabled {
        background-color: #f3f4f6;
        border-color: #e5e7eb;
    }

    /* Полностью переработанный вертикальный скроллбар в современном плоском стиле */
    QScrollBar:vertical {
        background: transparent;
        width: 10px;
        margin: 0;
    }

    /* Скрытие кнопок прокрутки сверху и снизу — характерно для современного минималистичного дизайна */
    QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
        height: 0px;
        background: none;
    }

    /* Трек до и после ползунка — делаем прозрачным, видимым остаётся только сам ползунок */
    QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
        background: transparent;
    }

    /* Сам ползунок скроллбара */
    QScrollBar::handle:vertical {
        background-color: #cbd5e1;
        border-radius: 5px;
        min-height: 30px;
    }

    QScrollBar::handle:vertical:hover {
        background-color: #94a3b8;
    }
)");

Пояснения к коду

Для чекбокса показаны четыре различных состояния индикатора: обычное, при наведении (:hover), отмеченное (:checked), и специфичное для трёхпозиционных чекбоксов промежуточное состояние (:indeterminate, используемое, например, в чекбоксе «выбрать все», когда выбрана только часть элементов списка) — каждое состояние получает собственную комбинацию цвета фона и иконки, что даёт пользователю чёткую визуальную обратную связь о текущем состоянии без необходимости вглядываться в мелкие детали.

Стилизация скроллбара демонстрирует распространённый паттерн «минималистичного» скроллбара: кнопки прокрутки (::add-line/::sub-line) скрываются заданием нулевой высоты и пустого фона, область трека (::add-page/::sub-page) делается прозрачной, и визуально остаётся только сам ползунок (::handle) — именно так выглядит большинство современных скроллбаров в популярных приложениях, разительно отличаясь от объёмного, с явными кнопками стрелок скроллбара классического стиля Windows.

Подводные камни

  • Полное скрытие кнопок прокрутки скроллбара через нулевую высоту может оставить нефункциональную, но пустую область на месте бывшей кнопки, если не задать background: none дополнительно — в некоторых версиях/стилях Qt может остаться нежелательная остаточная рамка или фон даже при нулевом размере элемента, что нужно явно проверять визуально после применения стиля.
  • Указание иконок через image: url(...) требует, чтобы соответствующие файлы были встроены в приложение через Qt Resource System (основной цикл статей, файл .qrc) — путь, начинающийся с :/, ссылается именно на встроенный ресурс, а не на файл в файловой системе; забытая регистрация ресурса в .qrc-файле приводит к тому, что иконка просто не отображается, без явной ошибки в QSS.
  • Стилизация QCheckBox::indicator:indeterminate имеет смысл только если код приложения действительно использует трёхпозиционный режим чекбокса (setCheckState(Qt::PartiallyChecked) или setTristate(true)) — если виджет используется только в обычном двухпозиционном режиме, написанные стили для промежуточного состояния просто никогда не будут применены, и это не ошибка, а признак избыточного, не используемого кода стилей.
  • Слишком тонкий или почти невидимый ползунок скроллбара (типичная «ультра-минималистичная» эстетика) может ухудшить доступность для пользователей с нарушениями зрения или для пользователей, использующих устройства с не самой точной координацией движений мыши/тач-панели — стоит балансировать эстетические предпочтения дизайна с практической применимостью элемента управления, особенно учитывая темы доступности, разобранные в отдельном цикле статей (200–209).