在QListView的QWidgetDelegate的paint()方法中呈现QWidget

13

我在实现在QListView中自定义部件渲染时遇到了困难。 目前,我有一个以QAbstractListModel为基础的自定义模型PlayQueueQListView上显示。

这对于简单文本运作良好,但现在我想为每个元素显示一个自定义小部件。 因此,我继承了QStyledItemDelegate并实现了paint方法,代码如下:

void QueueableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
    if (option.state & QStyle::State_Selected)
        painter->fillRect(option.rect, option.palette.highlight());
    QWidget *widget = new QPushButton("bonjour");
    widget->render(painter);
}

选择背景已正确地呈现,但未显示任何小部件。 我尝试了像Qt示例中那样使用简单的QPainter命令,这很好用:


void QueueableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{
    if (option.state & QStyle::State_Selected)
        painter->fillRect(option.rect, option.palette.highlight());
    if (option.state & QStyle::State_Selected)
        painter->setPen(option.palette.highlightedText().color());
    painter->setFont(QFont("Arial", 10));
    painter->drawText(option.rect, Qt::AlignCenter, "Custom drawing");
}

所以我尝试了一些更改,例如:

  • QStyledItemDelegate更改为QItemDelegate
  • 在渲染周围添加painter->save()painter->restore()
  • 将小部件的几何形状设置为可用大小

但是我现在有些卡住了,我在互联网上搜寻了一段时间,但找不到任何做我想做的事情的例子,它们都谈论小部件的编辑(这要容易得多)或自定义绘制控件(预定义的控件,例如进度条)。但是这里我真的需要一个包含一些布局、标签和图片的自定义小部件。谢谢你的帮助!

我正在使用Qt 4.7.3为GCC在Ubuntu 11.04上。

3个回答

17

为了完整地呈现整个内容:进一步可以找到关于如何使用代理将QWidget管理为QListView项的代码。

最终,我找到了如何在QStyledItemDelegate子类中使用其paint(...)方法使其工作。

看起来这比以前的解决方案更有效率,但需要通过调查setIndexWidget()对创建的QWidget执行了什么来验证这个说法。

因此,最终的代码如下:

class PackageListItemWidget: public QWidget

.....

class PackageListItemDelegate: public QStyledItemDelegate

.....

void PackageListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const
{

// here we have active painter provided by caller

// by the way - we can't use painter->save() and painter->restore()
// methods cause we have to call painter->end() method before painting
// the QWidget, and painter->end() method deletes
// the saved parameters of painter

// we have to save paint device of the provided painter to restore the painter
// after drawing QWidget
QPaintDevice* original_pdev_ptr = painter->device();

// example of simple drawing (selection) before widget
if (option.state & QStyle::State_Selected)
    painter->fillRect(option.rect, option.palette.highlight());

// creating local QWidget (that's why i think it should be fasted, cause we 
// don't touch the heap and don't deal with a QWidget except painting)
PackageListItemWidget item_widget;

// Setting some parameters for widget for example
    // spec. params
item_widget.SetPackageName(index.data(Qt::DisplayRole).toString());     
    // geometry
item_widget.setGeometry(option.rect);

// here we have to finish the painting of provided painter, cause
//     1) QWidget::render(QPainter *,...) doesn't work with provided external painter 
//          and we have to use QWidget::render(QPaintDevice *,...)
//          which creates its own painter
//     2) two painters can't work with the same QPaintDevice at the same time
painter->end(); 

// rendering of QWidget itself
item_widget.render(painter->device(), QPoint(option.rect.x(), option.rect.y()), QRegion(0, 0, option.rect.width(), option.rect.height()), QWidget::RenderFlag::DrawChildren);   

// starting (in fact just continuing) painting with external painter, provided
// by caller
painter->begin(original_pdev_ptr);  

// example of simple painting after widget
painter->drawEllipse(0,0, 10,10);   
};

4
在尝试了一些操作后,我发现我必须手动将deviceTransform()应用于偏移点才能使其对齐。在 painter->end(); 之前,我创建了 QPoint mappedorigin = painter->deviceTransform().map(QPoint(option.rect.x(), option.rect.y())) ,然后将mappedorigin作为第二个参数传递给QWidget::render()。如果我是在这个函数中创建widget,这可能不是必需的,但我将其创建为parent widget的一部分。 - Andrew Domaszek
这是唯一对我有用的东西,谢谢。我不得不添加来自@AndrewDomaszek的映射。 - Kuba Beránek

4

好的,我最终找到了如何实现我想要的功能。以下是我的操作步骤:

  1. 放弃使用委托类
  2. 在我的模型的data()方法中调用QListView::setIndexWidget()来设置小部件
  3. 通过检查QListView::indexWidget()确保在设置时没有任何小部件存在
  4. 处理Qt::SizeHintRole角色以返回小部件的大小提示
  5. 对于Qt::DisplayRole角色,返回空的QVariant

这样,我就可以在QListView中显示自定义小部件,并且它们会被正确地延迟加载(这就是我使用模型/视图模式的原因)。但我不知道如何在不显示时卸载它们,这是另一个问题。


你最终找到了最佳的卸载方法吗? - lyschoening

0

这里有一个例子给你。 看起来你需要使用QStylePainter,但是据我所知它只是用于绘制,不像真正的按钮那样起作用。


是的,我看到了这个方法,但它只适用于绘制预定义控件,而不是自定义小部件。顺便说一下,我不需要一个可工作的按钮,这只是为了简化示例,我的小部件相当静态,如果可能的话,仅绘制它就可以了。 - Adrien Rey-Jarthon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接