QStyledItemDelegate drawing custom widget failed

半城伤御伤魂 提交于 2019-12-07 12:56:11

问题


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

回答1:


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.



来源:https://stackoverflow.com/questions/43112644/qstyleditemdelegate-drawing-custom-widget-failed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!