在QWidgetDelegate的paint()方法中为QListView渲染QWidget

Adr*_*hon 12 qt qwidget qpainter qlistview qstyleditemdelegate

我在实现自定义窗口小部件渲染时遇到困难QListView.我目前有一个QListView显示我的自定义模型PlayQueue基于QAbstractListModel.

这对于简单的文本工作正常,但现在我想为每个元素显示一个自定义小部件.所以我将a子类化为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);
}
Run Code Online (Sandbox Code Playgroud)

正确呈现选择背景但不显示小部件.我尝试使用QPainterQt示例中的简单命令,这很好用:

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");
}
Run Code Online (Sandbox Code Playgroud)

所以我尝试了一些改变:

  • 更改QStyledItemDelegateQItemDelegate
  • 添加painter->save()painter->restore()渲染
  • 将窗口小部件几何设置为可用大小

但我现在有点卡住了,我在互联网上搜索了一段时间,但找不到任何做我想要的例子,他们都谈论编辑小部件(这很容易)或自定义绘制控件(预定义的控件) ,像进度条).但在这里我真的需要一个我创建的自定义小部件,包含一些布局,标签和pixmaps.谢谢你的帮助!

我在Ubuntu 11.04上使用Qt 4.7.3 for GCC.

小智 15

只是为了完成整个画面:进一步可以找到使用委托管理QWidget作为QListView项的代码.

我终于找到了如何使用paint(...)方法使它在QStyledItemDelegate的子类中工作.

它似乎比以前的解决方案更有效,但是这个语句需要通过调查setIndexWidget()对创建的QWidget做什么来验证.

最后,这是代码:

class PackageListItemWidget: public QWidget
Run Code Online (Sandbox Code Playgroud)

.....

class PackageListItemDelegate: public QStyledItemDelegate
Run Code Online (Sandbox Code Playgroud)

.....

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);   
};
Run Code Online (Sandbox Code Playgroud)

  • 稍微搞砸了一下之后,我发现我必须手动将deviceTransform()应用到偏移点以使其排队.在`painter-> end()之前;`我创建了`QPoint mappedorigin = painter-> deviceTransform().map(QPoint(option.rect.x(),option.rect.y()))`,然后将mappedorigin传递给QWidget :: render()的第二个arg.如果我在此函数中创建了窗口小部件,那么这可能不是必需的,但我将其创建为父窗口小部件的一部分. (3认同)

Adr*_*hon 4

好吧,我终于知道如何做我想做的事了。这是我所做的:

  1. 删除委托类
  2. 致电QListView::setIndexWidget()data()来设置小部件
  3. 通过检查确保设置时没有小部件已存在QListView::indexWidget()
  4. 处理Qt::SizeHintRole角色以返回小部件的大小提示
  5. Qt::DisplayRole返回角色的空白 QVariant

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