【问题标题】:Qt: Best way to implement "oscilloscope-like" realtime-plottingQt:实现“类似示波器”实时绘图的最佳方式
【发布时间】:2010-10-03 03:19:46
【问题描述】:

我正在基于Qwt 为 Qt 开发一个 Gui-Module 来绘制实时测量值,就像在数字示波器中一样。到目前为止一切正常,但也许还有一些功能需要添加;-)

在数据按列存储在 QVectors 中的那一刻,以及一个单独的 QObject 中的一个全局 timeReference QVector。因此,可以逐行丢弃数据,以仅将 Meusurement 保留到某个过去。所有 QVector 总是具有相同的长度。然后可以在 QwtPlot 中按行按时间正确绘制完整数据。

我想更多地封装数据存储,以便更独立地处理测量。因此,最好为每个测量添加一个单独的时间坐标列表,并将它们放在一个单独的 QObject 中,该 QObject 接受和传递数据。然后会有 10 或 20 个这样的 QObject,每个数据通道一个,由 QwtPlot 上的上覆 QObject 单独绘制。

数据现在可以是动态的——数据如何存储、更改或丢弃,外部不可见。

我的问题是:这很聪明吗? 20 或 30 个 QObjects,包含每 10000 个测量值、10000 个时间值,以及一个类似大小(动态填充)的单独内存区域,其中显示数据的子集用于绘图......?将 QObject 中的每个测量值作为信号接收,以 1kHz 左右的频率发射是否合理?信号/槽部分来自于稍后使每个对象成为 QThread 的想法,并实现实时过滤,如对数据的低通或 FFT——因此,信号/槽连接可以方便地控制输出多线程环境?

如何将数据有效地存储在我的对象中?我正在考虑两个 QList,一个用于时间,一个用于宝贵数据。然后动态分配两个普通的双数组以进行动态访问,其指针与长度一起放入一个结构中并由 accessData(pastTime) 方法返回。动态存储器充满了从“现在”到过去某个点的 timeVal/测量组合,可由信号设置。由 QObject 内部的互斥锁保护的一切脆弱。

当丢弃旧值时,必须从头开始搜索 QList 以找到第一个足够年轻的值以保留,并丢弃位于该索引之前的值。 QMap 是否因为它的 upperBound() 函数而更智能?我认为隐藏的开销不值得。

专业人士将如何尝试巧妙、高效或轻松地解决此问题?我应该知道的特殊 Qt 功能?或者甚至有免费的解决方案吗?无论如何,对于这样一个基本问题,有很多文字......感谢您阅读这里;-)

提前致谢

马文

edith:在 stijns cmets 之后对论证进行了一些清理。

【问题讨论】:

  • 不完全理解这个问题:为什么要在 QObject 中进行测量?并为时间坐标保留一个额外的 QObject?两者都是静态数据,所以我不明白你为什么要将信号/插槽连接到它们,而且对我来说听起来很奇怪,你想要一个与时间分开的测量。您能否更详细地解释/提供一个示例,说明您将如何处理此类对象上的信号?
  • 无论如何,我过去在半实时绘图和 QwtPlot 方面有一些经验。让这个为我们工作的最方便的方法是生产者/消费者场景。简而言之,生产者线程从 ADC 读取数据并进行一些处理,然后将数据包放入循环缓冲区中。消费者线程从缓冲区读取大块,决定如何处理它,切断信号并传递给 QwtPlot。请注意,所有数据采集、缓冲、处理都是在没有 Qt 的情况下编写的:它实际上没有现成的类型来存储 2D 数据或与音频硬件通信,因此这些都是仅使用 C++ 自制的。
  • 嗯,不——我想将一种类型的所有测量值与所有记录时间一起放入一个子类/对象中,但隐藏这种复杂性。启用异步录制、实时内容等等...

标签: c++ user-interface qt


【解决方案1】:

photo_tom 的回答非常概括:我会远离 QObjects 来实现数据处理和处理。

  • 如果您决定为您的 gui 使用 Qt 以外的其他东西,您将很难重构代码。像 QList 和 QVector 这样的类可以被 STL 对应物替换而没有太大问题,但信号/插槽部分是另一回事。
  • 任何用于信号处理(如过滤/fft)的第 3 部分实现都可能采用指向 1D 或 2D 数据的原始指针,因此您必须将它们从 QVector 中取出,我什至不确定这是否可行。如果不是,则必须从 QVector 中取出每个样本并将其复制到内存块中,然后进行处理,然后将其放回 QVector。
  • 这给我们带来了关于 QList/QMap 的问题:它可以用它们中的任何一个来完成,但它们实际上被设计为具有随机访问迭代器的动态容器,而您拥有固定大小的内存块来保存 2D 数据。可能值得研究一个完全满足您需求的自定义数据容器类。拿一张纸,写下你真正需要的东西,理清思路,忘记 Qt/STL/... 然后想想你需要哪些组件来实现它,然后进一步思考如何实现这些组件(最终在 Qt 方面)。
  • 对于这样的代码,最好永远不要重新分配。预先设置最大历史记录和您将允许的样本数量限制(或将其设为配置设置),并在程序开始时分配所需的数组,进一步重用相同的内存。
  • 考虑一个循环缓冲区。当数据以其他速率进入而不是取出时,这很方便(就像数据采集的大多数情况一样),它不需要重新分配,它会自动保留历史记录,并且可以用最少数量的内存副本来实现,如果它被读取/ write 方法直接返回指向底层内存的指针。
  • 也许重新考虑你的线程想法,它可能会使你的代码变得不必要地复杂:最终,来自不同通道的所有数据必须同时出现在屏幕上。如果用户在时间 x 看到来自通道 1 的数据,则来自通道 2 的数据也必须来自时间 x,否则它作为范围没有多大意义。但是假设您在不同的线程中处理来自这些通道的数据,您需要在执行实际显示的线程中进行额外的同步,因为并非所有数据线程都会同时在时间 x 完成处理块。除此之外,考虑一下可能没有任何性能提升:如果 CPU 必须以 100% 计算 30 个通道和剪辑的 FFT,如果将它们拆分为 30 个线程,是否真的会更快地计算这些 FFT?李>
  • 顺便说一句,您说这是一个基本问题,我认为实际上并非如此。我已经在不同设备上完成了许多用于数据采集/处理/可视化/保存的应用程序,我认为这些应用程序是他最难开发的.​​.....

【讨论】:

  • 很多开发人员没有预先考虑的优秀点。我同意你关于这些类型的应用程序最难的最后一点。
  • 是的,非常感谢。在我当前的解决方案中,40000 个最新元素保存在一个类中,其中的一个子集动态地提供给 QwtPlot。这启用了“旧”数据的回滚、缩放和过滤。现在这个基础正在起作用,我开始 FFT——仍在考虑线程——看看它有多快。目前,它正在用 40000 个元素实时绘制我的音频(9000Hz),两个程序并行运行——现在是 100% CPU。没关系 ;-) 好的 Tipp 保持分配的内存和代码中的一些小清理!
【解决方案2】:

乍一看,QObjects 似乎是处理此类数据存储问题的绝佳方式。当我第一次开始使用 Qt 时,我也有同样的想法。但是,这不是它们的用途。在http://www.informit.com/articles/article.aspx?p=667415 上有一篇很好的关于 QObjects 的真正含义和功能的文章。

如果您要做的只是将数据存储在一个类中,就像我读到您的问题一样,那么不要使用基于 QObject 的类。开销会严重影响您的性能。

至于 Qt 的哪些特定功能可以帮助您,Qt 并没有什么特别的功能可以帮助您。我发现 Qt 的容器比标准模板库或一些 boost 的专用容器更容易使用,因为当时我对 stl 还不够了解。

从性能的角度来看,我最好的建议是通过编写内存池系统或使用Boost Pool 来最小化新/删除的数量。并且还可以最大限度地减少移动数据。

【讨论】:

  • 谢谢,我从数据存储中删除了 QObject,这确实加快了速度;-) 感谢您的文章,除了 Qt 文档之外的新视图。但我不会使用 boost——我在一个更大的项目中工作,无法做出这样的决定。 Qt 很好 ;-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-24
  • 2021-12-23
  • 2023-03-07
  • 2016-06-14
  • 1970-01-01
  • 2021-11-08
相关资源
最近更新 更多