QStyledItemDelegate 绘制自定义小部件失败

Posted

技术标签:

【中文标题】QStyledItemDelegate 绘制自定义小部件失败【英文标题】:QStyledItemDelegate drawing custom widget failed 【发布时间】:2017-03-30 08:40:37 【问题描述】:

在我的一个项目中,我使用QTableWidget 来显示一些复杂的计算结果。为了增加表格的可读性,我需要在单个表格单元格内显示两个对齐的值。

稍后我想通过使用颜色或箭头等更多地自定义小部件。

为此,我从QStyledItemDelegate 派生,并在我的QTableWidget 实例上调用了table ->setItemDelegate(new TwoNumbersDelegate)

由于某些原因,QFrame 永远不会显示。我真的什么都试过了。奇怪的是,调用drawLine 会给出一些结果,但只在顶部的左侧单元格中。

我的想法是,调用mFrame->render(...) 不是正确的方法,但正确的方法是什么?

我的包含文件是:

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

我的cpp-文件是:

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

【问题讨论】:

但是QStyledItemDelegate不是派生自QWidget?怎么会有布局?我应该把它变成一个 QWidget 吗? 非常感谢。我真的迷失了这些东西。 :-) 我建议你使用QTextDocument::drawContents来绘制格式化的内容。示例代码可以在这里找到:***.com/questions/16444558/… 这还不够,因为我打算增强这个小部件。例如。使用图标或其他工具来增强可读性。 QTextDocument中使用图标没有问题。它支持富文本格式和 Qt-html 子集。因此,您可以输出到QTextEdit 的所有内容都可用于快速渲染。所以你可以尝试这样的事情:&lt;img src=":/myres/someicon.png" /&gt;. 【参考方案1】:

这里有几个潜在的问题:

布局

由于小部件是不可见的,因此不会计算布局,因此可能会画出不合适的位置,忽略调整大小的调用等。

为确保布局更新,在构造函数中,添加

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

这使得小部件表现得好像它是可见的(这是我们想要的),但不会直接在屏幕上绘制任何东西。

绘制位置

使用代理进行绘图被剪切到单元格,因此如果您在错误的位置绘图,您将什么也看不到。 单元格的边界由options.rect 给出,这些坐标以qtableview 为单位。所以你的 drawline 命令只在左上角的单元格中绘制。

这种剪辑还意味着您无需担心要渲染小部件的哪个区域,只需将绘制器转换为使用单元格的坐标,然后绘制整个小部件。

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

总而言之,这是一个更新的 .cpp 文件:

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

如果您需要更多帮助,请告诉我。另外,警告:我还没有通过编译器运行它,所以如果您仍然需要帮助,或者是否有任何小错误需要纠正,请告诉我。

【讨论】:

太棒了。按照你的承诺工作。 :-) 也许您可以更正以下错别字。 widgetTarget 应该是 mFramemFrame.resize(...) 应该是 mFrame-&gt;resize(...)

以上是关于QStyledItemDelegate 绘制自定义小部件失败的主要内容,如果未能解决你的问题,请参考以下文章

Qt表格中的自定义编辑组件---------------自定义代理QStyledItemDelegate

使用 QStyledItemDelegate paint() 在表格中绘制图标

QStyledItemDelegate中如何根据qStylesheet进行绘制

重新实现 QStyledItemDelegate::paint - 如何获取子元素坐标?

自定义 QStyledItemDelegate - 将编辑应用到模型

继承QAbstractTableModel QStyledItemDelegate实现自定义表格,添加进度条和选中框。