[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