QCustomPlot Discussion and Comments

Patch: Ignoring frames, when QCP is too slowReturn to overview

Hello,

I've created an (ugly) patch against situations when there are too many objects on the plot, and you want to pan the plot with left mouse click. In such situations, the replots are queued, and every registered mouse position change gets reflected... with a large delay.
This patch helps in detecting how much time a given replot has taken, and commands the next replot only after this time (and some more) has passed. The only drawback is that your last mouse position will be ignored, but this only means that you're already in the "danger zone" of having too many objects visible, and need to zoom in anyway. At least your program is responsive and allows you to perform this zoom immediately.

Instructions:

1) Save the following patch as qcustomplot.cpp.patch, in the same directory as qcustomplot.cpp:

--- qcustomplot.cpp	2015-04-25 01:54:48.000000000 +0200
+++ qcustomplot.cpp	2015-09-29 09:04:57.228196916 +0200
@@ -9014,6 +9014,7 @@
 */
 QCustomPlot::QCustomPlot(QWidget *parent) :
   QWidget(parent),
+	 m_delayed(false),
   xAxis(0),
   yAxis(0),
   xAxis2(0),
@@ -9035,6 +9036,7 @@
   mPaintBuffer(size()),
   mMouseEventElement(0),
   mReplotting(false)
+
 {
   setAttribute(Qt::WA_NoMousePropagation);
   setAttribute(Qt::WA_OpaquePaintEvent);
@@ -10357,32 +10359,52 @@
   signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite
   recursion.
 */
+static unsigned tdiff = 0;
+static unsigned tprev = 0;
 void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
 {
   if (mReplotting) // incase signals loop back to replot slot
     return;
   mReplotting = true;
   emit beforeReplot();
+
+	unsigned diffNow = QDateTime::currentMSecsSinceEpoch() - tprev;
+        if (diffNow > tdiff * 1.02 || ! m_delayed)
+	//if (true)
+        {
+            tprev = QDateTime::currentMSecsSinceEpoch();
+
+	  mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
+	  QCPPainter painter;
+	  painter.begin(&mPaintBuffer);
+	  if (painter.isActive())
+	  {
+	    painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
+	    if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
+	      painter.fillRect(mViewport, mBackgroundBrush);
+	    draw(&painter);
+	    painter.end();
+	    if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
+	      repaint();
+	    else
+	      update();
+	  } else // might happen if QCustomPlot has width or height zero
+	    qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
+	  
+	  emit afterReplot();
+	  mReplotting = false;
+
+            tdiff = QDateTime::currentMSecsSinceEpoch() - tprev;
+
+        }
+	else
+{
+	  emit afterReplot();
+	mReplotting = false;
+	return;
+}
   
-  mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
-  QCPPainter painter;
-  painter.begin(&mPaintBuffer);
-  if (painter.isActive())
-  {
-    painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
-    if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
-      painter.fillRect(mViewport, mBackgroundBrush);
-    draw(&painter);
-    painter.end();
-    if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
-      repaint();
-    else
-      update();
-  } else // might happen if QCustomPlot has width or height zero
-    qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
-  
-  emit afterReplot();
-  mReplotting = false;
+
 }
 
 /*!

2) and the following patch as qcustomplot.h.patch ins the same dir as qcustomplot.h:

--- qcustomplot.h	2015-04-25 01:54:48.000000000 +0200
+++ qcustomplot.h	2015-09-29 08:52:51.071275234 +0200
@@ -1714,6 +1714,8 @@
   
   explicit QCustomPlot(QWidget *parent = 0);
   virtual ~QCustomPlot();
+
+ bool m_delayed;
   
   // getters:
   QRect viewport() const { return mViewport; }

3) Apply the patches with:
patch < qcustomplot.cpp.patch
patch < qcustomplot.h.patch

4) Compile and install the library, including the altered header file

5) In your application, register the following signals:

connect(ui->customPlot, &QCustomPlot::mousePress, this, &MyMainWindow::mousePress);
connect(ui->customPlot, &QCustomPlot::mouseRelease, this, &MyMainWindow::mouseRelease);

6) And implement them with:

void MyMainWindow::mousePress(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        ui->customPlot->m_delayed = true;
        event->accept();
    }
};

void MyMainWindow::mouseRelease(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        event->accept();
        ui->customPlot->m_delayed = false;
    }
};


I'm sure that Emanuel can make this better in the end. This is just a proof of concept, which works good enough.

The patch above worked for version 1.3.0 Beta.
The patch version below is updated for QCP 1.3.2:

qcustomplot.cpp.patch

--- qcustomplot.cpp	2015-12-22 01:02:50.000000000 +0100
+++ qcustomplot.cpp	2017-03-31 19:07:54.290680098 +0200
@@ -9016,6 +9016,7 @@
 */
 QCustomPlot::QCustomPlot(QWidget *parent) :
   QWidget(parent),
+  m_delayed(false),
   xAxis(0),
   yAxis(0),
   xAxis2(0),
@@ -10363,32 +10364,50 @@
   signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite
   recursion.
 */
+static unsigned tdiff = 0;
+static unsigned tprev = 0;
 void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
 {
   if (mReplotting) // incase signals loop back to replot slot
     return;
   mReplotting = true;
   emit beforeReplot();
-  
-  mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
-  QCPPainter painter;
-  painter.begin(&mPaintBuffer);
-  if (painter.isActive())
-  {
-    painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
-    if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
-      painter.fillRect(mViewport, mBackgroundBrush);
-    draw(&painter);
-    painter.end();
-    if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
-      repaint();
-    else
-      update();
-  } else // might happen if QCustomPlot has width or height zero
-    qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
-  
-  emit afterReplot();
-  mReplotting = false;
+
+	unsigned diffNow = QDateTime::currentMSecsSinceEpoch() - tprev;
+        if (diffNow > tdiff * 1.02 || ! m_delayed)
+   //if (true)
+        {
+            tprev = QDateTime::currentMSecsSinceEpoch();
+
+     mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
+     QCPPainter painter;
+     painter.begin(&mPaintBuffer);
+     if (painter.isActive())
+     {
+       painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
+       if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
+         painter.fillRect(mViewport, mBackgroundBrush);
+       draw(&painter);
+       painter.end();
+       if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
+         repaint();
+       else
+         update();
+     } else // might happen if QCustomPlot has width or height zero
+       qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
+     
+     emit afterReplot();
+     mReplotting = false;
+
+            tdiff = QDateTime::currentMSecsSinceEpoch() - tprev;
+
+        }
+   	else
+	{
+	     emit afterReplot();
+	   mReplotting = false;
+	   return;
+	}
 }
 
 /*!

qcustomplot.h.patch

--- qcustomplot.h	2017-03-31 19:02:45.730688186 +0200
+++ qcustomplot.h	2017-03-31 19:03:06.286687647 +0200
@@ -1715,6 +1715,8 @@
   explicit QCustomPlot(QWidget *parent = 0);
   virtual ~QCustomPlot();
   
+  bool m_delayed;
+
   // getters:
   QRect viewport() const { return mViewport; }
   QPixmap background() const { return mBackgroundPixmap; }