高速高效地更新 QTableView
Posted
技术标签:
【中文标题】高速高效地更新 QTableView【英文标题】:Efficiently updating a QTableView at high speed 【发布时间】:2010-09-22 14:10:09 【问题描述】:我正在使用带有 QItemDelegate 子类的 QTableView 来控制 tableview 单元格的外观。
每个单元格显示外接设备的名称和状态,一次最多可连接100台设备。
每个设备的名称和类型基本上是静态的,很少更新(可能每小时一次),但每个单元格都需要显示设备输入的实时值,我目前每 50 毫秒轮询一次。该值显示为画家绘制的基本条形图,提供给 TableView 的 Delegate::paint() 方法。
每秒更新我的模型 20 次的问题是每次都重绘整个表格,效率非常低。将绘制方法限制为仅绘制条形图表明大部分 CPU 时间用于在每个单元格上绘制名称、状态和相关图像,而不是图形。
我需要找到一种方法来定期更新每个单元格的图表而不重绘单元格,但我不知道该怎么做。
实现这一目标的最有效方法是什么?
编辑:附加到帮助的图片。
图像代表一个 QTableView 中的 10 个传感器。号码、姓名和状态几乎是静态的,几乎从不更新。 “传感器值”文本旁边的条形图每 50 毫秒更新一次。我只想绘制这个栏,而不是文本、状态和单元格背景。状态灯和背景是复杂的图像,因此比简单地绘制和填充矩形需要更多的 CPU 时间。
【问题讨论】:
状态是否需要与其他所有小部件位于同一个小部件中?我的第一个想法是在它旁边粘贴一个来自同一模型的 ListView。 是的,不幸的是。每个设备都有许多需要在图表旁边的参数。我确实考虑过有两个视图,也许是重叠的,但这似乎是实现我想要的一种非常混乱的方式,并且使更改模型、编辑等变得更加困难。 【参考方案1】:由于你的QTableView继承了QWidget,你可以在上面调用如下:
setUpdatesEnabled(false);
changeAllYourData();
setUpdatesEnabled(true);
当 setUpdatesEnabled 为 false 时,对其进行的任何 paint() 或 update() 调用均无效。因此,您可以阻止它更新,更改所有数据然后重新启用它,可能通过手动调用 paint() 或 update() 手动对其进行,我不确定这部分。
这里是 setUpdatesEnabled 方法的文档。
QWidget updatesEnabled
希望这会有所帮助。
用户发表评论后编辑:
您可以通过在执行原始paint() 或update() 之前测试一个标志来为您的QItemDelegate 子类实现您自己的setUpdatesEnabled(bool)(因为它不继承QWidget 并且没有)。 之后,您可以为 QTableView 的每个单元格(或行或列)指定它们是否必须更新或重新绘制。
通过这样做,您可以阻止其他单元格(委托)重新绘制,除非您更改手动创建的 setUpdatesEnabled 标志,但保留包含图表的单元格上的更新。
我必须说我从未测试过这个或类似的东西,所以我希望它按我认为的方式工作。
祝你好运
用户编辑后编辑:
按照我之前的评论,您可以为每个代表设置一个标志,以仅绘制您的图表或整个图像,而不是为每个单元格设置一个标志(我认为您的图表位于单独的单元格中)。
希望这会有所帮助,
编辑:
我偶然发现了 Qt 4.7 中的一个新功能(我不知道您是否可以使用它,但它可以解决您的一些问题。) 该功能是 QStaticText。它是一个允许您缓存文本(字体和效果)并更快地绘制它们的类。见链接here。
希望它能解决你的问题。
【讨论】:
我实际上已经在这样做了,所以我每秒只更新模型 20 次,而不是 20 x numberOfDevices,但这并没有解决问题以及图表,我正在绘制背景图像、名称字符串、状态字符串和其他各种显示,它们本身仅以低得多的速率更新。 图表是要单独写在一个单元格中吗? 不,它包含在其他信息中。我已将图像附加到原始问题以帮助可视化 GUI。【参考方案2】:我很少建议这条路径而不是委托,但在您的情况下,这可能是值得的。我会考虑制作自己的视图,该视图足够了解仅更新需要更新的屏幕部分。像这样的视图小部件显然比通常情况更具有特殊用途,但如果您真的需要效率,这是一种方法。
如果您需要稍微提高效率,则需要考虑的其他事项是确保只标记实际更改的已更改行(如果传感器值不经常更改,并且只是经常轮询)或考虑添加实际未重绘的滞后值(如果传感器值的变化不够快,无法抵消这一点)。
【讨论】:
这是我走的路线;创造我自己的观点。我采纳了 Roku 的建议并使用 QGLWidget 来处理渲染,结果我看到数百台设备的 CPU 使用率只有 3%,而且刷新间隔比以前更短。 @Dani 参加聚会有点晚了,但是这个问题可以从您按照这种方法学到的知识中受益匪浅。【参考方案3】:将背景图像(单元格背景图像、状态和名称)作为 QPixmap 缓存到模型中。仅当状态或名称更改时才重绘该像素图。在一般情况下,您只需要在其上绘制缓存的 QPixmap 和传感器值。
编辑:
将 fullRepaintNeeded 标志添加到您的数据类。当状态或名称更改时,fullRepaintNeeded 设置为 true。
当代理正在绘制项目时,代理首先检查项目的 fullRepaintNeeded 标志。如果 fullRepaintNeeded 为真,则创建一个新的 QPixmap,并将所有内容绘制到最终绘制到 tableview 的 QPixmap。然后使用模型的 setData 函数(但不调用 dataChanged)将 QPixmap 缓存到模型(这意味着您的数据类)。 fullRepaintNeeded 现在设置为 false。
但如果在委托的绘制函数中 fullRepaintNeeded 为 false,则从模型中询问先前缓存的 QPixmap,绘制到 tableview 并在其上绘制最终的传感器值。
【讨论】:
好主意,没想到。 这是我想做的,但我该怎么做呢?只有一个绘制例程,并且只有一个 dataChanged() 插槽。理想情况下,我需要两个,但我应该为另一个 QPainter 对象使用哪个?我已经开始使用 Live 的 flag 方法来实现这一点,我会看看它是如何进行的。 我刚试过这个。绘制例程与您描述的完全一样,结果:设置标志时,单元格被擦除,然后绘制图形。在更新其他值(或更改选择)时,最多只有 50 毫秒,直到下一次图表更新,然后清除单元格并再次仅绘制条形图。 你有必要使用QTableView吗?如果你真的需要快速更新并使用低 CPU 使用率,请使用 QGLWidget 使用 OpenGL 绘制所有内容。您可以轻松地以非常低的 CPU 使用率每秒绘制每 20 次。但这比使用 QTableView 需要更多的编码。 在用尽所有其他可能性后,我接受了您的建议并开始重现使用 OpenGL 渲染的 QTableView。它实际上花费的时间比我想象的要少得多,而且从长远来看会更加灵活,因为我可以完全控制它的外观和操作方式。以上是关于高速高效地更新 QTableView的主要内容,如果未能解决你的问题,请参考以下文章
更高效地从 qtableWidget 更新和插入 MYSQL 数据库