使用 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->end();
移动到itemWidget->render(...)
之前的行会怎样?
@putu 仍然可以看到选择,但看不到小部件。
您是否尝试过类似示例中的方法,例如Star Delegate Example?也许你错过了一些东西,例如sizeHint
。或者最好使用painter->save()
和painter->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有三种方式(不使用,动态使用,静态使用,默认是动态使用)