使用 QStyledItemDelegate::paint() 直接在 QListView 上绘制小部件

Posted

技术标签:

【中文标题】使用 QStyledItemDelegate::paint() 直接在 QListView 上绘制小部件【英文标题】:Paint widget directly on QListView with QStyledItemDelegate::paint() 【发布时间】:2017-06-03 13:53:12 【问题描述】:

经过几个小时的工作,我可以在QListView 上绘制一个小部件。但是,这幅画是通过QPixmap 完成的。小部件出现,我可以看到一个进度条。但是,它有点“像素化”(由于使用了QPixmap)。是否可以直接绘制为普通小部件?这是我的问题。

以下是我的工作:

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

    QPaintDevice* original_pdev_ptr = painter->device();

    FileQueueListItem* itemWidget = reinterpret_cast<FileQueueListItem*>(index.data(Qt::UserRole).value<void*>());

    itemWidget->setGeometry(option.rect);
    painter->end();

    QPixmap pixmap(itemWidget->size());
    if (option.state & QStyle::State_Selected)
        pixmap.fill(option.palette.highlight().color());
    else
        pixmap.fill(option.palette.background().color());
    itemWidget->render(&pixmap,QPoint(),QRegion(),QWidget::RenderFlag::DrawChildren);

    painter->begin(original_pdev_ptr);
    painter->drawPixmap(option.rect, pixmap);

我通过here 的提示学会了如何做我所做的事情。在那里,绘画是直接在QListView 上完成的,这就是我想要实现的。以下尝试不起作用,我做错了什么:

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

    std::cout<<"Painting..."<<std::endl;
    QPaintDevice* original_pdev_ptr = painter->device();

    FileQueueListItem* itemWidget = reinterpret_cast<FileQueueListItem*>(index.data(Qt::UserRole).value<void*>());

    itemWidget->setGeometry(option.rect);
    painter->end();

    if (option.state & QStyle::State_Selected)
        painter->fillRect(option.rect, option.palette.highlight());
    else
        painter->fillRect(option.rect, option.palette.background());

    itemWidget->render(painter->device(),
                       QPoint(option.rect.x(), option.rect.y()),
                       QRegion(0, 0, option.rect.width(), option.rect.height()),
                       QWidget::RenderFlag::DrawChildren);
    painter->begin(original_pdev_ptr);

列表仍然是空的,没有任何反应。虽然可以看到选择,但小部件不显示。

【问题讨论】:

如果将painter-&gt;end(); 移动到itemWidget-&gt;render(...) 之前的行会怎样? @putu 仍然可以看到选择,但看不到小部件。 您是否尝试过类似示例中的方法,例如Star Delegate Example?也许你错过了一些东西,例如sizeHint。或者最好使用painter-&gt;save()painter-&gt;restore() 而不是begin, end。我无法提供更多建议,因为没有完整的代码。 @putu 另外 save() + restore() (在删除了所有 begin() 和 end() 之后)不起作用...相同的结果...选择有效,而渲染不起作用不行。 【参考方案1】:

让我们澄清一些事情:

    您不应该创建小部件并将它们放入模型中。这是有充分理由的。 Qt 事件循环中涉及到小部件,这意味着拥有太多小部件会显着减慢您的程序。

    小部件不仅仅是一堆控件(这似乎是您对它们的看法)。它们参与事件循环,这就是为什么您不应该将小部件作为数据模型的一部分。

    如果您使用的是多线程程序,并且我们的模型与视图分离,那么内存管理将成为一场噩梦。 Qt 永远不会容忍尝试从其他线程构造或删除任何小部件(这是有道理的,因为从事件循环中分离线程通常不是线程安全的)。

根据这些信息,您想要做的事情的正确方法是什么?可悲的是,唯一正确的方法是自己绘制控件。如果您的小部件很简单,那很容易做到。如果您的小部件很复杂,您将需要大量数学来计算每个小部件的位置。

在Qt Torrent Example 中,您将看到进度条是如何绘制的。绘制控件所需要做的就是计算位置,并使用rect 成员变量作为控件的包含矩形,然后绘制它们(当然,在设置它们的值之后)。函数paint()里面有个option.rect参数,就是整个item的矩形。您所要做的就是使用一些数学来计算每个小部件在该矩形内的位置。

PS:切勿对位置使用绝对值。你永远不会做对,尤其是对于不同的 DPI。

这将在没有小部件的情况下绘制控件,并且即使对于数千个元素也能保证您需要的速度。

【讨论】:

以上是关于使用 QStyledItemDelegate::paint() 直接在 QListView 上绘制小部件的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)