QCustomPlot Discussion and Comments

patch: optimise and significantly improve performance/responsivness of replot()Return to overview

Hi

This patch changes way replot() works.
It schedules replot on timer only once per event loop and invoke it on next timer call.
That makes it possible for application to keep responsivness snappy and smooth gui updates even when processing ticks on very high load.
I tried to keep changes minimalistic, so some functionality is lost, but it may be worth an effort to turn this hack into fullblown new refresh mode.

patch for qcustomplot.cpp

-void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
+void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority, const bool _runFromTimer)
 {
-  if (refreshPriority == QCustomPlot::rpQueuedReplot)
-  {
-    if (!mReplotQueued)
-    {
-      mReplotQueued = true;
-      QTimer::singleShot(0, this, SLOT(replot()));
-    }
-    return;
+  /* schedule replot on timer only once per event loop  and invoke it on next timer call*/
+  if (!mReplotQueued) {
+    mReplotQueued = true;
+    /* we could schedule with refreshPriority==rpImmediateRefresh to force always repaint()
+       but in my usage i didnt find any rendering glitches passing just QCustomPlot::rpRefreshHint
+    */
+    QTimer::singleShot(0, this, [this]{ replot(QCustomPlot::rpRefreshHint, true); });
   }
-  
+
   if (mReplotting) // incase signals loop back to replot slot
     return;
+
+  if (mReplotQueued && not(_runFromTimer)) { return; }
+
   mReplotting = true;
   mReplotQueued = false;
   emit beforeReplot();
@@ -14445,12 +14452,12 @@
     layer->drawToPaintBuffer();
   for (int i=0; i<mPaintBuffers.size(); ++i)
     mPaintBuffers.at(i)->setInvalidated(false);
-  
+
   if ((refreshPriority == rpRefreshHint && mPlottingHints.testFlag(QCP::phImmediateRefresh)) || refreshPriority==rpImmediateRefresh)
     repaint();
   else
     update();
-  
+
   emit afterReplot();
   mReplotting = false;
 }

patch for qcustomplot.h

+  Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint, const bool _runFromTimer = false);

This is exctly what replot(QCustomPlot::rpQueuedReplot) already does, no? If you call that multiple times per event loop, it will cancel the call due to mReplotQueued being true already.

Hi

Yes it is very similar.
Our app uses replot(QCustomPlot::rpQueuedReplot) in all places.
It works well, but in some corner cases (eg. under very high load) it causes replot stalls.
The version i proposed(which additionally blocks all replot requests of no rpQueuedReplot type) mitigates that flaw, which origin i am not sure.
Maby it is caused by some stray replot() of no rpQueuedReplot type.
Our app does not emit those (maby qcustomplot lib internally emits it sometimes)

Either way, maby someone will find it usefull in their setups.
Thanks for your time!

marek