QCustomPlot Discussion and Comments

QCircularBuffer support for graphingReturn to overview

I have a project where I want to be able to display a variety of time-varying data. It is expected that the amount of time that will be displayed is constant and new values will arrive roughly periodically, but they will have corresponding timestamps. It seems that calling a function of the type

QCPGraph::setData(cost QCircularBuffer<double>& keys, const QCircularBuffer<double>& values, bool alreadySorted = false)

would be a useful use case for plotting this type of data. Unfortunately, QCPDataContainer doesn't seem to be written in a way that supports this type of work. Is there an alternative recommended way to handle this use case that avoids frequent reshuffling of memory?

Since QCP maintains not only a post-allocation buffer, but also a pre-allocation buffer in its default data storage, removing a slice from the front of the data and adding data there is very fast. In fact, the case where the incoming data is already sorted and has smaller keys than the existing data is detected and handled as efficient as possible: it just copies the keys/values to the existing pre-allocation space and avoids any unnecessary sorting or merging.

Also removing and inserting slices in the middle is usually efficient. It is true though that in this case, the preceding or succeeding data is moved in memory. For usual data densities (let's say below 100k points), this has a negligible impact on performance compared with the rendering, again due to pre-/post-allocation buffers and memory layout. QCP doesn't need to re-allocate memory as you re-add new data into the previously removed slice, just move between allocated and continuous/adjacent memory locations.

If your keys are always increasing (as is the case for a timestamp for example), a circular buffer doesn't really make sense as default. The fastest data structure for QCP's purpose has the data sorted by key, which for a circular structure would require extra work in terms of sorting across memory boundaries with an artificial boundary somewhere in the middle. I thus recommend simply adding the new data to the container using the pre-sorted argument if possible (it will recognize that it is always only appending data in terms of key). The unused data can be removed by the key that scrolls out of view, if you don't wish to keep it. In this situation, post-allocation will make memory de-/re-allocations a rare occasion.

Thank you. That was a very thorough explanation. It seems that the method you described should work for my purposes.

It appears as though this technique cannot be applied to QCPColorMapData containers? In my use case, I have a realtime spectogram showing radio data across a given band. So, you'll have new slices of data coming in, one at a time, at the top (or bottom) of the map, and everything else shifting down (or up) by one row. To avoid the constant shifting of memory data, I was tossing around the idea of subclassing QCPColorMapData and reimplementing it using boost::circular_buffer.

Am I in for a world of hurt here?