1

One of my earlier Question deals with a customized QStyledItemDelegate containing a QWidget with two QLabels side by side.

I was really satisfied with the easy solution given by Joseph Ireland. Unfortunately, the given is solution is broken, but I didn't realized this immediately. If the QTableWidget containing my QStyledItemDelegate gets too small, the scrollbars are activated.

Now scrolling destroys the correct drawing of my cell elements. This seems to be some kind of update problem. I realized this, after I draw a rectangle around the viewport region of my QAbstractScollArea.

My table looks like the following, if you will scroll violently:

Violent scrolling destroys my table

Cells contents are not drawn at the right place or seemingly not draw after all. Happens strangely for every second row. Even more the rectangle drawn around the viewport region of my QAbstractScrollArea is messed up. If the window is redrawn (hide/show window) everything is fine.

What might the solution to this kind of update/repaint problem? Maybe I need to repaint after scrolling is finished?

My adapted solution posted by Joseph Ireland was:

Header-File: 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;
    };

Source File: 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();
}

The following program served me as a test runner:

#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();
}
Community
  • 1
  • 1
Aleph0
  • 5,816
  • 4
  • 29
  • 80
  • Please add the code to this question as well. – Kuba hasn't forgotten Monica Apr 03 '17 at 13:13
  • Try to use clipping – Dmitry Sazonov Apr 04 '17 at 14:50
  • Obviously. But how to do it? And how to make it correctly? – Aleph0 Apr 04 '17 at 15:30
  • 1
    Only a thought: the item delegate is for drawing items, not for drawing elements for the entire tablewidget. It is called once for each item. Thus, you should move the painting of the rectangle to the `paintEvent` of the tableWidget. This will (should) solve the problem with the rectangle. Or to the `paintEvent` of the ViewPort, I am not quite sure. If this would be the case, then you should use a new viewport class and use the tableview method `setViewport`. – LoPiTaL Apr 05 '17 at 07:18
  • Thanks for the hint. Actually, I don't want to draw the rectangle. I would be satisfied with the content. But also my content gets messed up. But deriving from `QTableWidget` might be still a solution anyway. Somehow the `paintEvent` is not handled correctly. – Aleph0 Apr 05 '17 at 07:25

0 Answers0