Пользовательский виджет QStyledItemDelegate отображается неправильно при прокрутке

Один из моих предыдущих Вопросов касается настраиваемого QStyledItemDelegate, содержащего QWidget с двумя QLabel рядом.

Я был очень доволен простым решением, предложенным Джозефом Айрлендом. К сожалению, данное решение сломано, но я не сразу это понял. Если QTableWidget, содержащий мой QStyledItemDelegate, становится слишком маленьким, активируются полосы прокрутки.

Теперь прокрутка уничтожает правильный рисунок элементов моей ячейки. Похоже, это какая-то проблема с обновлением. Я понял это после того, как нарисовал прямоугольник вокруг области просмотра моего QAbstractScollArea.

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

Насильственная прокрутка уничтожает мою таблицу

Содержимое ячеек прорисовывается не там, где надо, или вообще не прорисовывается. Странно происходит для каждой второй строки. Еще больше испортился прямоугольник, нарисованный вокруг области просмотра моего QAbstractScrollArea. Если окно перерисовывается (скрыть/показать окно), все в порядке.

Какое может быть решение такой проблемы обновления/перекраски? Может быть, мне нужно перерисовать после завершения прокрутки?

Мое адаптированное решение, опубликованное Джозефом Айрлендом, было:

Заголовочный файл: TwoNumbersDelegate.h

#pragma once

#include <QStyledItemDelegate>
class QLabel;

    class TwoNumbersDelegate : public QStyledItemDelegate {
    public:
        TwoNumbersDelegate(QObject* parent = nullptr);

        virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;

        virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;

    private:
        QLabel* mLeft;
        QLabel* mRight;
        QFrame* mFrame;
    };

Исходный файл: TwoNumbersDelegate.cpp

#include "TwoNumbersDelegate.h"
#include <QLabel>
#include <QPainter>
#include <QDebug>
#include <QHBoxLayout>
#include <QTableWidget>

TwoNumbersDelegate::TwoNumbersDelegate(QObject* parent /*= nullptr*/) : QStyledItemDelegate(parent)
{
    mLeft = new QLabel("%1");
    mRight = new QLabel("%2");
    mFrame = new QFrame;
    mFrame->setLayout(new QHBoxLayout);
    mFrame->layout()->addWidget(mLeft);
    // you could add a spacer here maybe
    mFrame->layout()->addWidget(mRight);

    mFrame->setAttribute(Qt::WA_DontShowOnScreen, true);
    mFrame->show();
}

void TwoNumbersDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    auto data = index.data(Qt::EditRole);
    auto list = data.toList();
    if (list.size() != 2) {
        QStyledItemDelegate::paint(painter, option, index);
    }

    mLeft->setText(list.at(0).toString());
    mRight->setText(list.at(1).toString());
    mFrame->resize(option.rect.size());
    qDebug() << option.rect.size();
//  mFrame->layout()->invalidate();
//  mFrame->layout()->activate();

    if (auto tableWidget = qobject_cast<QTableWidget*>(parent())) {
        auto cellRegion = QRegion(option.rect);
        auto viewportRegion = QRegion(tableWidget->viewport()->rect());
        auto intersectedRegion = cellRegion.intersected(viewportRegion);
        intersectedRegion.translate(-option.rect.topLeft());
        painter->drawRect(tableWidget->viewport()->rect().adjusted(0,0,-1,-1));
        painter->save();
        painter->translate(option.rect.topLeft());
        mFrame->render(painter, QPoint(), intersectedRegion, QWidget::DrawChildren);
        qDebug() << cellRegion << viewportRegion << intersectedRegion;
        painter->restore();
    }
}

QSize TwoNumbersDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    return mFrame->minimumSizeHint();
}

Следующая программа послужила мне тестовым бегуном:

#include <QApplication>
#include <QTableWidget>
#include <QVBoxLayout>
#include "TwoNumbersDelegate.h"

int main(int argc, char** args) {
    QApplication q(argc, args);
    auto frame = new QFrame;
    frame->setLayout(new QVBoxLayout);
    //frame->setStyleSheet("QFrame { background: green}"); // Activate this to see overdrawing
    auto table = new QTableWidget;
    table->setAlternatingRowColors(true);
    table->setItemDelegate(new TwoNumbersDelegate);
    table->setRowCount(20);
    table->setColumnCount(10);
    for (auto iter = 0; iter < 20; iter++) {
        for (auto colIter = 0; colIter < 10; colIter++) {
            auto item = new QTableWidgetItem;
            QVariantList map;
            map << iter << iter*colIter;
            item->setData(Qt::EditRole, map);
            table->setItem(iter, colIter, item);
        }
    }
    frame->layout()->addWidget(table);
    frame->show();
    q.exec();
}

person Aleph0    schedule 03.04.2017    source источник
comment
Пожалуйста, добавьте код и в этот вопрос.   -  person Kuba hasn't forgotten Monica    schedule 03.04.2017
comment
Попробуйте использовать отсечение   -  person Dmitry Sazonov    schedule 04.04.2017
comment
Очевидно. Но как это сделать? И как это сделать правильно?   -  person Aleph0    schedule 04.04.2017
comment
Только мысль: делегат элемента предназначен для рисования элементов, а не для рисования элементов всего табличного виджета. Он вызывается один раз для каждого элемента. Таким образом, вы должны переместить рисование прямоугольника в paintEvent tableWidget. Это (должно) решить проблему с прямоугольником. Или к paintEvent ViewPort, я не совсем уверен. Если это так, вам следует использовать новый класс окна просмотра и использовать метод tableview setViewport.   -  person LoPiTaL    schedule 05.04.2017
comment
Спасибо за подсказку. На самом деле, я не хочу рисовать прямоугольник. Я был бы доволен содержанием. Но и мой контент портится. Но выход из QTableWidget все равно может быть решением. Каким-то образом paintEvent обрабатывается неправильно.   -  person Aleph0    schedule 05.04.2017