Пользовательский QStyledItemDelegate для рисования текста несколькими цветами - PullRequest
1 голос
/ 30 апреля 2019

Я хочу отобразить два столбца внутри QTableWidget, показывающих различия между двумя значениями (рассчитанные ранее некоторыми алгоритмами расстояния Левенштейна). Части хранятся внутри данных каждого QTableWidgetItem, как QStringList. Первая часть должна отображаться черным, следующая - красным, а затем снова чередоваться черным, красным и т. Д.

Для этой цели я реализовал QStyledItemDelegate с пользовательской функцией paint(), которая в конечном итоге вызывает метод drawText():

void DifferencesDelegate::drawText(QPainter *painter,
                                   const QStyleOptionViewItem &option,
                                   const QModelIndex &index) const
{
    painter->save();

    const QPen defaultPen = painter->pen();

    QStyleOptionViewItem opt = option;
    initStyleOption(&opt, index);
    opt.text.clear();

    QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
    style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);

    opt.rect.moveRight(opt.rect.right() + 3);

    int color = 1;
    for (const QString &part : index.data(Qt::UserRole).toStringList()) {
        color++;
        color = color % 2;
        if (color) {
            painter->setPen(Qt::red);
        } else {
            painter->setPen(defaultPen);
        }

        style->drawItemText(painter, opt.rect, opt.displayAlignment, opt.palette, true, part);
        opt.rect.moveRight(opt.rect.right() + painter->fontMetrics().width(part));
    }

    painter->restore();
}

Это приводит к правильной окраске, пока ширина столбца достаточна:

Column's width sufficient

но как только столбец становится меньше, я получаю грязное переполнение:

Column too narrow

Это, безусловно, вызвано применением opt.rect для каждой части экрана, но не для текста в целом.

Единственная проблема в том, что я понятия не имею, как это исправить ;-) Любая помощь будет принята с благодарностью! Заранее спасибо!

1 Ответ

0 голосов
/ 30 апреля 2019

Совершенно неожиданно мне удалось ее решить; -)

void DifferencesDelegate::drawText(QPainter *painter, const QStyleOptionViewItem &option,
                                const QModelIndex &index) const
{
    if (! index.data(Qt::UserRole).isValid()) {
        TableDelegate::drawText(painter, option, index);
        return;
    }

    painter->save();

    const QPen defaultPen = painter->pen();

    QStyleOptionViewItem opt = option;
    initStyleOption(&opt, index);
    opt.text.clear();

    QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
    style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, opt.widget);

    constexpr int offset = 3;
    const QString ellipsis = QStringLiteral("…");
    const int ellipsisWidth = painter->fontMetrics().width(ellipsis);
    const int rightBorder = opt.rect.left() + opt.rect.width() - offset;

    QRect drawRect;
    int color = 1;
    int partWidth;
    bool overflow = false;

    opt.rect.moveRight(opt.rect.right() + offset);

    const QStringList parts = index.data(Qt::UserRole).toStringList();
    const int partsCount = parts.count();
    for (int i = 0; i < partsCount; i++) {
        color++;
        color = color % 2;
        if (color) {
            painter->setPen(Qt::red);
        } else {
            painter->setPen(defaultPen);
        }

        partWidth = painter->fontMetrics().width(parts.at(i));

        drawRect = opt.rect;
        if (drawRect.left() + partWidth + (i == partsCount - 1 ? 0 : ellipsisWidth) > rightBorder) {
            drawRect.setWidth(rightBorder - drawRect.left() - ellipsisWidth);
            overflow = true;
        }

        style->drawItemText(painter, drawRect, opt.displayAlignment, opt.palette, true,
                            parts.at(i));

        if (overflow) {
            drawRect.setLeft(rightBorder - ellipsisWidth);
            drawRect.setWidth(ellipsisWidth);
            style->drawItemText(painter, drawRect, opt.displayAlignment, opt.palette, true,
                                ellipsis);
            break;
        }

        opt.rect.moveRight(opt.rect.right() + partWidth);
    }

    painter->restore();
}
...