QChart and series replace issue
-
Hi everyone,
thanks for your great support! I am not well versed with Qt, and at the moment I am developing a small utility real-time data recorder, which useQChart
as the data plotter control. Let me briefly explain the exact issue I am facing at the moment.I have a dedicated data receiver thread, running at 100ms period. In this thread I am receiving 16 channels of integer data samples For each data channel, there is one
QLineSeries
object, added to theQChart
object. In each thread processing cycle, I am converting integers toQPointF
and directly callingreplace
method for all series (in a for loop). Effectively I get received data to scroll right to left, as data arrives.While this approach works fine with 8 or 12 channels, as number of channels grows up, application starts to lag and becomes entirely non-responsive.
My understanding was that each
replace
call on series, will trigger implicitQChart
repainting, which takes processing place in the main Qt loop. Seems that my data receiver thread is requesting to many repaint events, which can't be processed in 100ms, and then burden accumulates, making my app unusable.I was wondering if there is any option to temporary disable
QChart
repaint, until I fully replace all series, and then trigger one single repaint, after all 16 series were fully replaced?Thanks for any hints!
-
Hi everyone,
thanks for your great support! I am not well versed with Qt, and at the moment I am developing a small utility real-time data recorder, which useQChart
as the data plotter control. Let me briefly explain the exact issue I am facing at the moment.I have a dedicated data receiver thread, running at 100ms period. In this thread I am receiving 16 channels of integer data samples For each data channel, there is one
QLineSeries
object, added to theQChart
object. In each thread processing cycle, I am converting integers toQPointF
and directly callingreplace
method for all series (in a for loop). Effectively I get received data to scroll right to left, as data arrives.While this approach works fine with 8 or 12 channels, as number of channels grows up, application starts to lag and becomes entirely non-responsive.
My understanding was that each
replace
call on series, will trigger implicitQChart
repainting, which takes processing place in the main Qt loop. Seems that my data receiver thread is requesting to many repaint events, which can't be processed in 100ms, and then burden accumulates, making my app unusable.I was wondering if there is any option to temporary disable
QChart
repaint, until I fully replace all series, and then trigger one single repaint, after all 16 series were fully replaced?Thanks for any hints!
@dmitttri said in QChart and series replace issue:
I have a dedicated data receiver thread, running at 100ms period. In this thread I am receiving 16 channels of integer data samples For each data channel, there is one QLineSeries object, added to the QChart object. In each thread processing cycle, I am converting integers to QPointF and directly calling replace method for all series (in a for loop)
Does this mean that a subsidiary thread is directly writing to/updating the
QChart
object in the UI? Or do you correctly do the updates in the main UI thread? If so do you send signals? How often? Do you "batch up" the data for update on the chart? I'm not understanding this area, need to understand when and where the chart is getting updated before advising on "repaints". -
@dmitttri said in QChart and series replace issue:
I have a dedicated data receiver thread, running at 100ms period. In this thread I am receiving 16 channels of integer data samples For each data channel, there is one QLineSeries object, added to the QChart object. In each thread processing cycle, I am converting integers to QPointF and directly calling replace method for all series (in a for loop)
Does this mean that a subsidiary thread is directly writing to/updating the
QChart
object in the UI? Or do you correctly do the updates in the main UI thread? If so do you send signals? How often? Do you "batch up" the data for update on the chart? I'm not understanding this area, need to understand when and where the chart is getting updated before advising on "repaints".@JonB said in QChart and series replace issue:
@dmitttri said in QChart and series replace issue:
I have a dedicated data receiver thread, running at 100ms period. In this thread I am receiving 16 channels of integer data samples For each data channel, there is one QLineSeries object, added to the QChart object. In each thread processing cycle, I am converting integers to QPointF and directly calling replace method for all series (in a for loop)
Does this mean that a subsidiary thread is directly writing to/updating the
QChart
object in the UI?Yes, thread sleeps 100ms, and when it wakes up, it reads latest arrived data, and then for each of 16 channels it directly calls
replace
for all 16 series in the QChartOr do you correctly do the updates in the main UI thread? If so do you send signals?
Series updates were performed directly in a dedicated thread, for performance reasons, and avoid signals.
How often?
Every 100ms, I receive approx 2000 points on each of 16 channels, and then I call
replace
for each channel in a loopDo you "batch up" the data for update on the chart?
No, received integer points are being converted to
QPointF
list, and passed directly to seriesreplace
method.I'm not understanding this area, need to understand when and where the chart is getting updated before advising on "repaints".
Thanks a lot for any kind of support, I may learn something useful here.
-
@JonB said in QChart and series replace issue:
@dmitttri said in QChart and series replace issue:
I have a dedicated data receiver thread, running at 100ms period. In this thread I am receiving 16 channels of integer data samples For each data channel, there is one QLineSeries object, added to the QChart object. In each thread processing cycle, I am converting integers to QPointF and directly calling replace method for all series (in a for loop)
Does this mean that a subsidiary thread is directly writing to/updating the
QChart
object in the UI?Yes, thread sleeps 100ms, and when it wakes up, it reads latest arrived data, and then for each of 16 channels it directly calls
replace
for all 16 series in the QChartOr do you correctly do the updates in the main UI thread? If so do you send signals?
Series updates were performed directly in a dedicated thread, for performance reasons, and avoid signals.
How often?
Every 100ms, I receive approx 2000 points on each of 16 channels, and then I call
replace
for each channel in a loopDo you "batch up" the data for update on the chart?
No, received integer points are being converted to
QPointF
list, and passed directly to seriesreplace
method.I'm not understanding this area, need to understand when and where the chart is getting updated before advising on "repaints".
Thanks a lot for any kind of support, I may learn something useful here.
@dmitttri said in QChart and series replace issue:
Yes, thread sleeps 100ms, and when it wakes up, it reads latest arrived data, and then for each of 16 channels it directly calls replace for all 16 series in the QChart
Before you go any further, that is not supposed to be allowed in Qt widgets. Threads may not update a widget directly, and I assume
replace
on a series counts as doing just that. You should wait to see if someone else confirms this, hopefully posting here. -
@dmitttri said in QChart and series replace issue:
Yes, thread sleeps 100ms, and when it wakes up, it reads latest arrived data, and then for each of 16 channels it directly calls replace for all 16 series in the QChart
Before you go any further, that is not supposed to be allowed in Qt widgets. Threads may not update a widget directly, and I assume
replace
on a series counts as doing just that. You should wait to see if someone else confirms this, hopefully posting here.@JonB said in QChart and series replace issue:
@dmitttri said in QChart and series replace issue:
Yes, thread sleeps 100ms, and when it wakes up, it reads latest arrived data, and then for each of 16 channels it directly calls replace for all 16 series in the QChart
Before you go any further, that is not supposed to be allowed in Qt widgets. Threads may not update a widget directly, and I assume
replace
on a series counts as doing just that. You should wait to see if someone else confirms this, hopefully posting here.Thanks for reply! I do realize that updating widgets concurrently with Qt main processing loop may look like a risky operation, and it was done primary because during recording process, QChart series will be updated only form this particular thread, and to avoid signals. Why? It makes things more complex, someone shall take care that data to be plotted (received data) is consistent and not touched while receiver thread is running. This means making a copy of all series contents, to be updated (replaced) in the main Qt loop context.
But even with such by book approach, we still have the same issue. One has to call
replace
in a loop (outside of thread context), and if it takes more time than receiver thread period (send update request signals), then I will end up with the same issue again (more plotting requests than QChart can serve in a given time period) -
@JonB said in QChart and series replace issue:
@dmitttri said in QChart and series replace issue:
Yes, thread sleeps 100ms, and when it wakes up, it reads latest arrived data, and then for each of 16 channels it directly calls replace for all 16 series in the QChart
Before you go any further, that is not supposed to be allowed in Qt widgets. Threads may not update a widget directly, and I assume
replace
on a series counts as doing just that. You should wait to see if someone else confirms this, hopefully posting here.Thanks for reply! I do realize that updating widgets concurrently with Qt main processing loop may look like a risky operation, and it was done primary because during recording process, QChart series will be updated only form this particular thread, and to avoid signals. Why? It makes things more complex, someone shall take care that data to be plotted (received data) is consistent and not touched while receiver thread is running. This means making a copy of all series contents, to be updated (replaced) in the main Qt loop context.
But even with such by book approach, we still have the same issue. One has to call
replace
in a loop (outside of thread context), and if it takes more time than receiver thread period (send update request signals), then I will end up with the same issue again (more plotting requests than QChart can serve in a given time period)@dmitttri
It's not just a "risky operation", it's not allowed. So if you use it I don't see how people can help you address its performance.I would assume that using
replace
(properly) would only cause the chart to update when its next paint event is processed.My understanding was that each replace call on series, will trigger implicit QChart repainting,
I would hope that it would only mark the chart for update, which is different from a direct repaint. Multiple calls can be coalesced into the next actual repaint.
If you do not get a better answer which achieves the responsiveness you require you might either "thin out" the datapoints or the frequency of updates. If your usage is really heavy/fast QChart is known to be perhaps not the greatest component, I don't know if something else or written yourself would work better, last resort.
-
I may reformulate my question as; If QChart has multiple series N attached, and we want to replace
all N series
at once, is there any method to do seriesreplace
in one go, and end up with only one QChart repaint? (and not callingreplace
N times, where each one triggers QChart repainting) -
I may reformulate my question as; If QChart has multiple series N attached, and we want to replace
all N series
at once, is there any method to do seriesreplace
in one go, and end up with only one QChart repaint? (and not callingreplace
N times, where each one triggers QChart repainting)Hi,
I don't know the module internals so I currently cannot comment on that part but one workaround that comes to mind is to block the widget updates while replacing all the series and then enable them again. That should avoid the "repaint storm" you are getting.
-
@dmitttri
It's not just a "risky operation", it's not allowed. So if you use it I don't see how people can help you address its performance.I would assume that using
replace
(properly) would only cause the chart to update when its next paint event is processed.My understanding was that each replace call on series, will trigger implicit QChart repainting,
I would hope that it would only mark the chart for update, which is different from a direct repaint. Multiple calls can be coalesced into the next actual repaint.
If you do not get a better answer which achieves the responsiveness you require you might either "thin out" the datapoints or the frequency of updates. If your usage is really heavy/fast QChart is known to be perhaps not the greatest component, I don't know if something else or written yourself would work better, last resort.
@JonB said in QChart and series replace issue:
@dmitttri
It's not just a "risky operation", it's not allowed. So if you use it I don't see how people can help you address its performance.Yes, and I will definitely try the option with proper approach, signals and keep any updates outside of the receiver thread. Thanks. At least, having the same issue with proper way of doing it, will make my inquiry easier to respond to.
I would assume that using
replace
(properly) would only cause the chart to update when its next paint event is processed.My understanding was that each replace call on series, will trigger implicit QChart repainting,
I would hope that it would only mark the chart for update, which is different from a direct repaint. Multiple calls can be coalesced into the next actual repaint.
If you do not get a better answer which achieves the responsiveness you require you might either "thin out" the datapoints or the frequency of updates. If your usage is really heavy/fast QChart is known to be perhaps not the greatest component, I don't know if something else or written yourself would work better, last resort.
Unfortunately seems you are right regarding
QChart
not being the best choice for high performance plotting of larger data sets. Anyhow, thanks for precise clarifications. -
@JonB said in QChart and series replace issue:
@dmitttri
It's not just a "risky operation", it's not allowed. So if you use it I don't see how people can help you address its performance.Yes, and I will definitely try the option with proper approach, signals and keep any updates outside of the receiver thread. Thanks. At least, having the same issue with proper way of doing it, will make my inquiry easier to respond to.
I would assume that using
replace
(properly) would only cause the chart to update when its next paint event is processed.My understanding was that each replace call on series, will trigger implicit QChart repainting,
I would hope that it would only mark the chart for update, which is different from a direct repaint. Multiple calls can be coalesced into the next actual repaint.
If you do not get a better answer which achieves the responsiveness you require you might either "thin out" the datapoints or the frequency of updates. If your usage is really heavy/fast QChart is known to be perhaps not the greatest component, I don't know if something else or written yourself would work better, last resort.
Unfortunately seems you are right regarding
QChart
not being the best choice for high performance plotting of larger data sets. Anyhow, thanks for precise clarifications. -
Hi,
I don't know the module internals so I currently cannot comment on that part but one workaround that comes to mind is to block the widget updates while replacing all the series and then enable them again. That should avoid the "repaint storm" you are getting.
@SGaist said in QChart and series replace issue:
Hi,
I don't know the module internals so I currently cannot comment on that part but one workaround that comes to mind is to block the widget updates while replacing all the series and then enable them again. That should avoid the "repaint storm" you are getting.
Yeah, I was trying to achieve such effect somehow, for example with
setViewportUpdateMode(QGraphicsView::NoViewportUpdate)
, update series, and then revert back the default UpdateMode, but this was not a successful approach.Anyway thanks a lot for your hints!
-
@SGaist said in QChart and series replace issue:
Hi,
I don't know the module internals so I currently cannot comment on that part but one workaround that comes to mind is to block the widget updates while replacing all the series and then enable them again. That should avoid the "repaint storm" you are getting.
Yeah, I was trying to achieve such effect somehow, for example with
setViewportUpdateMode(QGraphicsView::NoViewportUpdate)
, update series, and then revert back the default UpdateMode, but this was not a successful approach.Anyway thanks a lot for your hints!
@dmitttri I was rather thinking about QWidget::setUpdatesEnabled.
-
@dmitttri I was rather thinking about QWidget::setUpdatesEnabled.
@SGaist said in QChart and series replace issue:
@dmitttri I was rather thinking about QWidget::setUpdatesEnabled.
Thanks, I will try it!