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

Paint widget directly on QListView with QStyledItemDelegate::paint()

经过几个小时的工作,我可以在 QListView 上绘制一个小部件。然而,绘画是通过QPixmap完成的。小部件出现,我可以看到一个进度条。但是,它有点 "pixelated"(由于使用 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);
}

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

让我们弄清楚一些事情:

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

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

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

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

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

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

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