QHeaderView иконки

Давайте разберемся как заголовках колонок таблицы (QHeaderView) помимо текста выводить разную дополнительную информацию. Например в нашем случае есть необходимость выводить значок, означающий что по какой-то  колонке сделан отбор.

QHeaderView отрисовывает каждую секцию заголовка в функции QHeaderView::paintSection, давайте ее разберем, тут все вроде бы на первый взгляд очевидно:

void QHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
    Q_D(const QHeaderView);
    if (!rect.isValid())
        return;
    // get the state of the section
    QStyleOptionHeader opt;
    initStyleOption(&opt);
    QStyle::State state = QStyle::State_None;
    if (isEnabled())
        state |= QStyle::State_Enabled;
    if (window()->isActiveWindow())
        state |= QStyle::State_Active;
    if (d->clickableSections) {
        if (logicalIndex == d->hover)
            state |= QStyle::State_MouseOver;
        if (logicalIndex == d->pressed)
            state |= QStyle::State_Sunken;
        else if (d->highlightSelected) {
            if (d->sectionIntersectsSelection(logicalIndex))
                state |= QStyle::State_On;
            if (d->isSectionSelected(logicalIndex))
                state |= QStyle::State_Sunken;
        }

    }
    if (isSortIndicatorShown() && sortIndicatorSection() == logicalIndex)
        opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder)
                            ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp;

    // setup the style options structure
    QVariant textAlignment = d->model->headerData(logicalIndex, d->orientation,
                                                  Qt::TextAlignmentRole);
    opt.rect = rect;
    opt.section = logicalIndex;
    opt.state |= state;
    opt.textAlignment = Qt::Alignment(textAlignment.isValid()
                                      ? Qt::Alignment(textAlignment.toInt())
                                      : d->defaultAlignment);

    opt.iconAlignment = Qt::AlignVCenter;
    opt.text = d->model->headerData(logicalIndex, d->orientation,
                                    Qt::DisplayRole).toString();
    if (d->textElideMode != Qt::ElideNone)
        opt.text = opt.fontMetrics.elidedText(opt.text, d->textElideMode , rect.width() - 4);

    QVariant variant = d->model->headerData(logicalIndex, d->orientation,
                                    Qt::DecorationRole);
    opt.icon = qvariant_cast(variant);
    if (opt.icon.isNull())
        opt.icon = qvariant_cast(variant);
    QVariant foregroundBrush = d->model->headerData(logicalIndex, d->orientation,
                                                    Qt::ForegroundRole);
    if (foregroundBrush.canConvert())
        opt.palette.setBrush(QPalette::ButtonText, qvariant_cast(foregroundBrush));

    QPointF oldBO = painter->brushOrigin();
    QVariant backgroundBrush = d->model->headerData(logicalIndex, d->orientation,
                                                    Qt::BackgroundRole);
    if (backgroundBrush.canConvert()) {
        opt.palette.setBrush(QPalette::Button, qvariant_cast(backgroundBrush));
        opt.palette.setBrush(QPalette::Window, qvariant_cast(backgroundBrush));
        painter->setBrushOrigin(opt.rect.topLeft());
    }

    // the section position
    int visual = visualIndex(logicalIndex);
    Q_ASSERT(visual != -1);
    if (count() == 1)
        opt.position = QStyleOptionHeader::OnlyOneSection;
    else if (visual == 0)
        opt.position = QStyleOptionHeader::Beginning;
    else if (visual == count() - 1)
        opt.position = QStyleOptionHeader::End;
    else
        opt.position = QStyleOptionHeader::Middle;
    opt.orientation = d->orientation;
    // the selected position
    bool previousSelected = d->isSectionSelected(this->logicalIndex(visual - 1));
    bool nextSelected =  d->isSectionSelected(this->logicalIndex(visual   1));
    if (previousSelected && nextSelected)
        opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected;
    else if (previousSelected)
        opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected;
    else if (nextSelected)
        opt.selectedPosition = QStyleOptionHeader::NextIsSelected;
    else
        opt.selectedPosition = QStyleOptionHeader::NotAdjacent;
    // draw the section
    style()->drawControl(QStyle::CE_Header, &opt, painter, this);

    painter->setBrushOrigin(oldBO);
}

На практике получается, что цвет текста в колонке заголовка меняется нормально через роль TextColorRole. А вот цвет фона секции через роль BackgroundColorRole не устанавливается.

Вот кусок стека вызовов функций при отрисовки секции:

QHeaderView::paintEvent
QHeaderView::paintSection // для каждой секции отдельно, тут мы видим, что backgroundBrush устанавливается при использовании роли Qt::BackgroundRole (то есть пока все нормально)
QWindowsVistaStyle::drawControl
QWindowsXPStyle::drawControl
QWindowsStyle::drawControl
QCommonStyle::drawControl // 

фотка 1

Итак что именно не срабатывает в QCommonStyle::drawControl  а именно в case CE_Header, чтобы изменился фон секции? Переопределяем функцию и выясняем, что:

style()->drawControl(QStyle::CE_HeaderLabel, &subopt, painter, this);  // рисует текст в секции - это понятно.

style()->drawControl(QStyle::CE_HeaderSection, &header, painter, this); // отвечает каким-то образом за фон (но как цвет фона изменить по прежнему не понятно). 

Как подсказку используем лог функции data и подсмотрим какие роли запрашивает QTableView у модели данных (для отрисовки себя конечно же). Оказывается это:

Qt::FontRole
Qt::TextAlignmentRole
Qt::TextColorRole
Qt::CheckStateRole
Qt::DecorationRole
Qt::BackgroundColorRole

DecorationRole отвечает за иконку например и этим можно воспользоваться. То есть можно штатными средствами установить заголовку колонки иконку слева или справа. Вот пример:

фотка 2

Нашу тестовую программу можно скачать здесь Развитие Sql Qt 4.8.1

.