[Qt-interest] Drawing huge amounts of data into a waterfall

Valentina Masi valentina.masi at altran.it
Fri Dec 18 09:54:10 CET 2009


Code of main window:

// .h
#ifndef DEMOCONSOLE_H

#define DEMOCONSOLE_H



#include <QTimer>

#include "ui_democonsole.h"

#include "renderArea.h"



class DemoConsole : public QMainWindow {

      Q_OBJECT



public:

      DemoConsole(QWidget *parent = 0, Qt::WFlags flags = 0);

      ~DemoConsole();



private slots:

      void on_action_Waterfall_triggered();

  void generateSamples();



private:

      Ui::DemoConsoleClass ui;

      RenderArea* renderArea;

      float** signalRows;

      int** colorRows;

      int rowNumber;  // Max number of rows in signalRows e colorRows

      int sampleNumber;  // Size of each row in signalRows e colorRows

  int currentRowNumber;  // Current number of rows inserted in signalRows e 
colorRows

      QTimer* timer;



private:

      void initializeSignalAndColorRows();

      void initializeColorRows();

      void resetWaterfall();

};



#endif // DEMOCONSOLE_H



// .cpp
#include "democonsole.h"



DemoConsole::DemoConsole(QWidget *parent, Qt::WFlags flags)

      : QMainWindow(parent, flags) {



      ui.setupUi(this);



      // Create my custom widget RenderArea and add it to main window

      this->renderArea = new RenderArea(ui.centralWidget);

      this->renderArea->move(10, 180);



      this->signalRows = NULL;

      this->colorRows = NULL;

      this->rowNumber = 10000;

      this->sampleNumber = 2731;

      this->currentRowNumber = 0;



      // Timer (I use it to simulate arrival of signals at rate of 250ms)

      timer = new QTimer(this);

      // Connect timer out to generateSamples() slot

      connect(timer, SIGNAL(timeout()), this, SLOT(generateSamples()));

}



DemoConsole::~DemoConsole() {

}



void DemoConsole::on_action_Waterfall_triggered() {

      resetWaterfall();



      // Allocate signal matrix

      this->signalRows = new float*[this->rowNumber];

      for (int i = 0; i < this->rowNumber; i++) {

            this->signalRows[i] = new float[this->sampleNumber];

      }

      // Initialize to 0 each element in signal matrix

      for (int i = 0; i < this->rowNumber; i++) {

            memset(this->signalRows[i], 0, this->sampleNumber);

      }



  // Allocate color matrix

      this->colorRows = new int*[this->rowNumber];

      for (int i = 0; i< this-> rowNumber; i++) {

            this->colorRows[i] = new int[this->sampleNumber];

      }

      // Initialize to 0 each element in color matrix

      for (int i = 0; i < this->rowNumber; i++) {

            memset(this->colorRows[i], 0, this->sampleNumber);

      }



      // Initialize my custom widget

      this->renderArea->initialize(this->rowNumber, this->sampleNumber);



      // Start timer with a timeout interval of 250ms

  timer->start(250);

}



// Slot connected to timer timeout

// Whenever timer fires, a new row of this->sampleNumber samples is to be 
generated;

// each sample is a random number between 0 and 1000; the new row is 
inserted into

// this->signalRows matrix at position of this->currentRowNumber index

void DemoConsole::generateSamples() {



      if (this->signalRows == NULL) {

            return;

      }



      // Create a new row and insert it into signal and color matrices

      initializeSignalAndColorRows();



      // Pass the new row to my custom widget

      this->renderArea->setNewDataToBeDrawed(this->colorRows[this->currentRowNumber 
 - 1], this->currentRowNumber - 1);

}



void DemoConsole::resetWaterfall() {

      // Stop timer

      this->timer->stop();



      // Deallocated signal matrix

      if (this->signalRows != NULL) {

            for (int i = 0; i < this->rowNumber; i++) {

                  delete[] this->signalRows[i];

            }

            delete[] this->signalRows;

            this->signalRows = NULL;

      }



      // Deallocate color matrix

      if (this->colorRows != NULL) {

            for (int i = 0; i < this->rowNumber; i++) {

                  delete[] this->colorRows[i];

            }

            delete[] this->colorRows;

            this->colorRows = NULL;

      }



      this->currentRowNumber = 0;

}



void DemoConsole::initializeSignalAndColorRows() {

      // Initialize the currentRowNumber-index row in signalRows

      for (int j = 0; j < this->sampleNumber; j++) {

        // Initialize the (currentRowNumber, j) element to random number 
between 0 and 1000

            this->signalRows[this->currentRowNumber][j] = 
(rand()/(float(RAND_MAX)+1)) * 1000;

      }



      // Inizialize the currentRowNumber-index row in colorRows

      initializeColorRows();



      // A new row was inserted in signalRows and colorRows matrices; so 
this->currentRowNumber is to be increased

      this->currentRowNumber++;

}



// Initialize the (currentRowNumber, j) element of colorRows matrix as 
follows:

// - if (currentRowNumber, j) element of signalRows matrix is in [0, 
62.5)  -> red color (#FF0000)

// - if (currentRowNumber, j) element of signalRows matrix is in [62.5, 
125)   -> blue color (#0000FF)

// - if (currentRowNumber, j) element of signalRows matrix is in [125, 
187.5) -> yellow color (#FFFF00)

// - if (currentRowNumber, j) element of signalRows matrix is in [187.5, 
250)   -> green color (#00FF00)

// - if (currentRowNumber, j) element of signalRows matrix is in [250, 
312.5) -> fuchsia color (#FF33CC)

// - if (currentRowNumber, j) element of signalRows matrix is in [312.5, 
375)   -> light green color (#CCFF66)

// - if (currentRowNumber, j) element of signalRows matrix is in [375, 
437.5) -> light orange color (FFCC00)

// - if (currentRowNumber, j) element of signalRows matrix is in [437.5, 
500)   -> turquoise color (#00FFFF)

// - if (currentRowNumber, j) element of signalRows matrix is in [500, 
562.5) -> light blue color (#8080FF)

// - if (currentRowNumber, j) element of signalRows matrix is in [562.5, 
625)   -> black color (#000000)

// - if (currentRowNumber, j) element of signalRows matrix is in [625, 
687.5) -> cherry color (#C20041)

// - if (currentRowNumber, j) element of signalRows matrix is in [687.5, 
750)   -> pink color (#FF40A0)

// - if (currentRowNumber, j) element of signalRows matrix is in [750, 
812.5) -> 20% gray color (#D2D2D2)

// - if (currentRowNumber, j) element of signalRows matrix is in [812.5, 
875)   -> 30% gray color (#B2B2B2)

// - if (currentRowNumber, j) element of signalRows matrix is in [875, 
937.5) -> 40% gray color (#8F8F8F)

// - if (currentRowNumber, j) element of signalRows matrix is in [937.5, 
1000)  -> 60% gray color(#5F5F5F)

void DemoConsole::initializeColorRows() {

      for (int j = 0; j < this->sampleNumber; j++) {

            if (signalRows[this->currentRowNumber][j] >= 0 && 
signalRows[this->currentRowNumber][j] < 62.5) {

                   // red color

                   colorRows[this->currentRowNumber][j] = 0xFF0000;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 62.5 && 
signalRows[this->currentRowNumber][j] < 125) {

                   // blue color

                   colorRows[this->currentRowNumber][j] = 0x0000FF;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 125 && 
signalRows[this->currentRowNumber][j] < 187.5) {

                   // yellow color

                   colorRows[this->currentRowNumber][j] = 0xFFFF00;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 187.5 && 
signalRows[this->currentRowNumber][j] < 250) {

                   // green color

                   colorRows[this->currentRowNumber][j] = 0x00FF00;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 250 && 
signalRows[this->currentRowNumber][j] < 312.5) {

                   // fuchsia color

                   colorRows[this->currentRowNumber][j] = 0xFF33CC; 
//0xFF8080;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 312.5 && 
signalRows[this->currentRowNumber][j] < 375) {

                   // light green color

                   colorRows[this->currentRowNumber][j] = 0xCCFF66; 
//0x80FF80;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 375 && 
signalRows[this->currentRowNumber][j] < 437.5) {

                   // ligth orange color

                   colorRows[this->currentRowNumber][j] = 0xFFCC00; 
//0xFF8100;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 437.5 && 
signalRows[this->currentRowNumber][j] < 500) {

                   // turquoise color

                   colorRows[this->currentRowNumber][j] = 0x00FFFF;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 500 && 
signalRows[this->currentRowNumber][j] < 562.5) {

                   // light blue color

                   colorRows[this->currentRowNumber][j] = 0x8080FF;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 562.5 && 
signalRows[this->currentRowNumber][j] < 625) {

                   // black color

                   colorRows[this->currentRowNumber][j] = 0x000000;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 625 && 
signalRows[this->currentRowNumber][j] < 687.5) {

                   // cherry color

                   colorRows[this->currentRowNumber][j] = 0xC20041;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 687.5 && 
signalRows[this->currentRowNumber][j] < 750) {

                   // pink color

                   colorRows[this->currentRowNumber][j] = 0xFF40A0;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 750 && 
signalRows[this->currentRowNumber][j] < 812.5) {

                   // 20% gray color

                   colorRows[this->currentRowNumber][j] = 0xD2D2D2;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 812.5 && 
signalRows[this->currentRowNumber][j] < 875) {

                   // 30% gray color

                   colorRows[this->currentRowNumber][j] = 0xB2B2B2;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 875 && 
signalRows[this->currentRowNumber][j] < 937.5) {

                   // 40% gray color

                   colorRows[this->currentRowNumber][j] = 0x8F8F8F;

                   continue;

            }

            if (signalRows[this->currentRowNumber][j] >= 937.5) {

                   // 60% gray color

                   colorRows[this->currentRowNumber][j] = 0x5F5F5F;

                   continue;

            }

      }

}




Code of my custom widget:

// .h
#ifndef RENDERAREA_H

#define RENDERAREA_H



#include <QScrollArea>

#include <QPen>

#include <QWidget>



class RenderArea : public QWidget {

      Q_OBJECT

public:

      RenderArea(QWidget *parent = 0);

      QSize minimumSizeHint() const;

      QSize sizeHint() const;

      void setNewDataToBeDrawed(int* colorRow, int rowIndex);

      void initialize(int rowNumber, int sampleNumber);



protected:

      void paintEvent(QPaintEvent *event);



private:

      int** dataToBeDrawed;

      int rowNumber;  // Max number of rows in dataToBeDrawed

      int sampleNumber;  // Size of each row in dataToBeDrawed

      int currentRowNumber;  // Current number of rows inserted in 
dataToBeDrawed

      bool newRow;  // true if a new row is to be drawed

};

#endif


// .cpp

#include <QtGui>



#include "renderArea.h"



RenderArea::RenderArea(QWidget *parent)

: QWidget(parent) {



      setBackgroundRole(QPalette::Base);

      setAutoFillBackground(true);



      this->dataToBeDrawed = NULL;



      this->setAttribute(Qt::WA_PaintOnScreen);



      this->newRow = false;

}



QSize RenderArea::minimumSizeHint() const {

      return QSize(100, 100);

}



QSize RenderArea::sizeHint() const {

      return QSize(400, 200);

}



void RenderArea::paintEvent(QPaintEvent* event) {

      if (this->dataToBeDrawed == NULL) {

        // No data to be drawed

        return;

      }



      if (this->newRow == false) {

        // No new row to be drawed

        return;

      }

      this->newRow = false;





      QPainter painter(this);

      QColor penColor;

      QPen pen;

      QPoint point;



      int actualRowNumber = (this->currentRowNumber < this->rowNumber ? 
this->currentRowNumber : this->rowNumber);

      int y = 0;

      for (int i = (actualRowNumber - 1); i >= 0; i--) {  // Il waterfall 
scende

            for (int j = 0; j < this->sampleNumber; j++) {

                  // Setto il colore e la larghezza

                  penColor.setRgb(this->dataToBeDrawed[i][j]);

                  pen.setColor(penColor);

                  painter.setPen(pen);

                  point.setX(j);

                  point.setY(y);

                  painter.drawPoint(point);

            }

            y++;

      }

}



// Called by parent widget whenever a new row is available

void RenderArea::setNewDataToBeDrawed(int* colorRow, int rowIndex) {

      // Copy the new row into this->dataToBeDrawed at the index rowIndex

      for (int j = 0; j < this->sampleNumber; j++) {

            this->dataToBeDrawed[rowIndex][j] = colorRow[j];

  }



      // Update the real number of rows inserted in this->dataToBeDrawed

      this->currentRowNumber = rowIndex + 1;



      // Rember a new row is to be drawed

      this->newRow = true;



      update();

}



void RenderArea::initialize(int rowNumber, int sampleNumber) {

      // Deallocate matrix if allocated

      if (this->dataToBeDrawed != NULL) {

            for (int i = 0; i < this->rowNumber; i++) {  // Note 
this->rowNumber hs still old value             delete[] 
this->dataToBeDrawed[i];

            }

            delete[] this->dataToBeDrawed;

            this->dataToBeDrawed = NULL;

      }



      // Set to new value input parameters

      this->rowNumber = rowNumber;

      this->sampleNumber = sampleNumber;



      // Create this->dataToBeDrawed

      this->dataToBeDrawed = new int*[this->rowNumber];

      for (int i = 0; i < this->rowNumber; i++) {

            this->dataToBeDrawed[i] = new int[this->sampleNumber];

      }



      // Initialize each element of this->dataToBeDrawed to 0

      for (int i = 0; i < this->rowNumber; i++) {

            memset(this->dataToBeDrawed[i], 0, this->sampleNumber);

      }

}



Whenever the timer fires (see method generateSamples() in DemoConsole 
class), I insert a new row into signalRows matrix: each element of the new 
row is a rondom number between 0 e 1000. Then I insert a new row in 
colorRows matrix: I initialize the element j of the new row to a hexadecimal 
RGB color according to value of element j of signalRows matrix. Finally, I 
pass the new row of color matrix to my custom widget that updates its data 
structures and calls update(). In the paintEvent() method of my custom 
widget, I repaint all color rows according to following rule: for each 
element I draw a pixel; the color of the pixel is the element value; the 
coordinates of the pixel depend on (i, j) indices of the element in 
colorRows matrix.

The posted code regards just a demo application. The final application will 
have to be able to draw and manage a lot of waterfall charts (about a 
hundred charts).

Thanks
Valentina






----- Original Message ----- 
From: "Sean Harmer" <sean.harmer at maps-technology.com>
To: <qt-interest at trolltech.com>
Sent: Thursday, December 17, 2009 4:20 PM
Subject: Re: [Qt-interest] Drawing huge amounts of data into a waterfall


> Hi,
>
> On Thursday 17 December 2009 15:16:35 Valentina Masi wrote:
>> Hi all
>>
>> I am developing an application drawing a huge amount of data: every 250ms
>>  my application receives a new array of 2731 floats and has to update a
>>  waterfall chart moving down the chart content of 1 pixel and showing a 
>> new
>>  line (of 2731 points) at the top of the chart. In order to do this, I 
>> have
>>  created a custom widget extending QWidget and overridden paintEvent
>>  handler. My problem is that running my application is too selfish in 
>> term
>>  of CPU and gets very slow just after a few seconds.
>>
>> Can anyone give me a suggest?
>
> Are you able to post some sample code that shows what you are doing? Or 
> even
> better a complete working example that uses random data for e.g.?
>
> Sean
> _______________________________________________
> Qt-interest mailing list
> Qt-interest at trolltech.com
> http://lists.trolltech.com/mailman/listinfo/qt-interest 




More information about the Qt-interest-old mailing list