简体   繁体   中英

QStyledItemDelegate drawing custom widget failed

In one of my projects I'm using a QTableWidget in order to display some complex computational results. In order to increase the readability of the table I'm in need to display two aligned values inside of a single table cell.

Later on I want to customize the widget even more by using colors or arrows etc..

For this I derived from QStyledItemDelegate and I called table ->setItemDelegate(new TwoNumbersDelegate) on my QTableWidget instance.

For some reasons the QFrame is never display. I really tried everything. Strangely, a call to drawLine gives some result, but only in the left cell on the top.

My idea is, that calling mFrame->render(...) is not the correct way to do it, but what is the correct way?

My include file is:

#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;
    };

My cpp -File is:

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

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);
    mFrame->layout()->addWidget(mRight);
}

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);
    }
    auto leftValue=list.at(0).toDouble();
    auto rightValue=list.at(1).toDouble();
    mLeft->setText(QString("%1").arg(leftValue));
    mRight->setText(QString("%2").arg(rightValue));
    mLeft->render(painter, QPoint(), option.rect);
    painter->drawLine(4, 4, 7, 7); // Draws Line, but not in every cell of my table?
}

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

A few potential issues here:

Layout

Since the widget is invisible, the layout isn't being calculated, so things might be drawn out of place, calls to resize ignored, etc.

To ensure the layout is updated, in the constructor, add

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

This makes the widget behave as if it is visible (which we want), but doesnt draw anything to the screen directly.

Paint location

Drawing with a delegate is clipped to the cell, so if you draw in the wrong place, you won't see anything at all. The bounds of the cell are given by options.rect , and these coordinates are in terms of the qtableview. So your drawline command only draws in the top-left cell.

This clipping also means you don't need to worry about what region of the widget to render, just translate the painter to use the cell's coordinates, and paint the whole widget.

painter->save();
painter->translate(option.rect.topLeft());
mFrame->render(painter, QPoint(), QRegion(), QWidget::DrawChildren );
painter->restore();

All together, here is an updated .cpp file:

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(opt.rect.size());

    // if there are still layout problems maybe try adding:
    // mFrame->layout()->invalidate();
    // mFrame->layout()->activate();

    painter->save();
    painter->translate(option.rect.topLeft());
    mFrame->render(painter, QPoint(), QRegion(), QWidget::DrawChildren );
    // painter->drawLine(4, 4, 7, 7); // Draws Line in every cell now
    painter->restore();
}

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

Let me know if you need any more help. Also, a warning: I haven't so much as run this through a compiler, so let me know if you still need help, or if there are any little errors to correct.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM