PDA

View Full Version : real time data display



hammer256
12th February 2009, 22:52
Hello, I need to write an application that accepts a stream of data and display it on a graph in real time. I want to be able to append the new data to be displayed with the data already displayed, and when the graph reaches the end of the screen it wraps around, sort of like a rolling graph. The question is, is there a way to paint only the new data, without repainting everything? I want this to be pretty fast. What kind of facility should I use? I mean, QPainter and paintEvent in the QWidgets seemed like the right thing to use, but paintevent seems to clear the window before it draws, and I really need to avoid drawing everything every time.
Thanks for the help.

jacek
13th February 2009, 00:14
Take a look at QWidget::setAttribute(). Esp. the Qt::WA_NoSystemBackground and Qt::WA_OpaquePaintEvent attributes.

You could paint everything on a pixmap and then just shift it and draw new stuff on exposed area. You can check out how Qwt (http://qwt.sf.net) does it.

agostain
10th August 2009, 10:08
i have the same problem of hammer256. do you resolve your problem?? can you help me please? thanks in advance!

hammer256
10th August 2009, 16:52
Hey, I did solve my problem. I don't know if it's the right way to do it, but it's fast (especially with qt 4.5) and does what I want.

Basically the idea is that the display widget has a back buffer (I use QPixmap) in which all my graph updates gets drawn on. In the function which actually generates the data and draws the data, I have a QPainter that draws to that back buffer. The paintEvent function for the display widget simply copies what's on the back buffer to the screen, but only for the area specified in the "event" parameter passed to it. So every time new data is generated, I call the Qpainter to draw on the back buffer, then, I call update on the display widget and pass a QRect parameter to it that specifies the area I want to update on, and I'm done.

I don't have access to my code right now, (a little busy also) but if you need I can show you the code later.

agostain
10th August 2009, 16:58
thank you for the reply. it would be great if you can show me the code. i have no hurry. thank you a lot .hope the code will help me more.

hammer256
10th August 2009, 21:59
This is the header file for my display widget. The backBuf and paintEvent are the ones to note here. I use a pointer for the backBuf so I can decide later what the size should be.


#ifndef SIMDISPW_H
#define SIMDISPW_H

#include <QtGui/QWidget>
#include <QtGui/QPainter>
#include <QtGui/QPaintEvent>
#include <QtGui/QColor>
#include <QtGui/QPixMap>
#include <QtCore/QMutex>
#include "ui_simdispw.h"
#include "common.h"
#include "globalvars.h"

//widget class to display realtime data generated by the simulator
class SimDispW : public QWidget
{
Q_OBJECT //ignore

public:
SimDispW(QWidget *parent = 0);
~SimDispW();
//returns the pixmap buffer that should be drawn on
QPixmap *getBackBuf();

private:
Ui::SimDispWClass ui; //autogenerated by ui_simdispw.h, ignore
QPixmap *backBuf; //the pixmap buffer that the widget updates from

protected:
void paintEvent(QPaintEvent *); //custom paint event that paints the window
};

#endif // SIMDISPW_H


the source file for the display widget. I have some threading stuff in there because my data generation function is in a different thread, so you can ignore it if you want.



#include "../includes/simdispw.h"
#include "../includes/moc_simdispw.h"

SimDispW::SimDispW(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
this->setAttribute(Qt::WA_DeleteOnClose); //when the window is closed, delete the object
this->setFixedSize(2000, NUMMF);
backBuf=new QPixmap(this->width(), this->height());
backBuf->fill(QColor(0,0,0)); //sets the whole thing to black

}

SimDispW::~SimDispW()
{
delete backBuf;
}

QPixmap *SimDispW::getBackBuf()
{
return backBuf;
}


void SimDispW::paintEvent(QPaintEvent * event)
{
QPainter p(this);
p.setViewTransformEnabled(true);
p.setWindow(0, 0, backBuf->width(), backBuf->height());
p.setViewport(0, 0, this->width(), this->height());
//p.setWorldMatrixEnabled(false);

bufLock.lock(); //for thread management, locks buffer mutex
p.drawPixmap(event->rect(), *backBuf, event->rect());
//cout<<event->rect().x()<<" "<<event->rect().y()<<" "<<event->rect().width()<<" "<<event->rect().height()<<endl;
//p.drawPixmap(0,0, *backBuf);
bufLock.unlock(); //unlocks when done
}


And here is the data generation function.


/*
* simthread.cpp
*
* Created on: Feb 16, 2009
* Author: wen
*/

#include "../includes/simthread.h"
#ifdef INTELCC
#include <omp.h>
#endif

SimThread::SimThread(QObject *parent, SimDispW *panel)
:QThread(parent)
{
dispW=panel;
}


void SimThread::run()
{
if(dispW==NULL || !initialized)
{
return;
}
simLoop();
}

void SimThread::simLoop()
{
QPixmap *buf;
QPainter p;
CRandomSFMT0 randGen(time(NULL));
int trialTime;

bool *mfsAP=new bool[NUMMF];
bool *granulesAP=new bool[NUMMF];
bool *golgisAP=new bool[NUMMF];

if(dispW==NULL)
{
return;
}
buf=dispW->getBackBuf();
if(buf==NULL)
{
return;
}

while(true)
{
simStopLock.lock();
if(simStop)
{
simStopLock.unlock();
break;
}
simStopLock.unlock();

trialTime=time(NULL);
for(int i=0; i<NUMMF; i++)
{
if(mossyFibers[i].getMFType()==MossyFiber::activeCS+1 || mossyFibers[i].getMFType()==MossyFiber::activeCS+5)
{
mossyFibers[i].setCSOn(1);
}
else
{
mossyFibers[i].setCSOn(0);
}
}

bufLock.lock();
buf->fill(Qt::black);
bufLock.unlock();
dispW->update();

for(int i=0; i<TRIALTIME; i++)
{
bool tempAP;
bool *drawAP;

simStopLock.lock();
if(simStop)
{
simStopLock.unlock();
break;
}
simStopLock.unlock();

simPauseLock.lock();

for(int j=0; j<NUMMF; j++)
{
//mossyFibers[j].setThresh(mossyFibers[j].getThresh()+(1-mossyFibers[j].getThresh())*MossyFiber::threshDecay);

// mfsSpike[j]=randGen.fRandom()<((int)(mossyFibers[j].getCSOn() && i>=mossyFibers[j].getCSStart() && i<mossyFibers[j].getCSEnd())*mossyFibers[j].getIncFreq()+mossyFibers[j].getbgFreqCont(MossyFiber::activeContext))*mossyFi bers[j].getThresh();
//
// if(mfsSpike[j])
// {
// mossyFibers[j].setThresh(0);
//
// {
// for(int k=0; k<mossyFibers[j].getNumSynGR(); k++)
// {
// granuleCells[mossyFibers[j].getConGRInd(k)].setExI(mossyFibers[j].getConGRDen(k),mfsSpike[j]);
// }
// }
// }
mfsAP[j]=mossyFibers[j].calcActivity(i, randGen);
}

#ifdef INTELCC
#pragma omp parallel shared(granuleCells, golgiCells)
#endif
{
#ifdef INTELCC
#pragma omp for schedule(static, NUMGR/16) nowait
#endif
for(int j=0; j<NUMGR; j++)
{
tempAP=granuleCells[j].calcActivity();
if(j<NUMMF)
{
granulesAP[j]=tempAP;
}
}
#ifdef INTELCC
#pragma omp for schedule(static, NUMGO/16) nowait
#endif
for(int j=0; j<NUMGO; j++)
{
golgisAP[j]=golgiCells[j].calcActivity();
}
}

simDispTypeLock.lock();
if(simDispType==0)
{
drawAP=mfsAP;
}
else if(simDispType==1)
{
drawAP=granulesAP;
}
else
{
drawAP=golgisAP;
}
simDispTypeLock.unlock();

bufLock.lock();
p.begin(buf);
p.setWindow(0,0, TRIALTIME, NUMMF);
p.setViewport(0, 0, buf->width(), buf->height());

p.setPen(Qt::white);
for(int j=0; j<NUMMF; j++)
{
if(drawAP[j])
{
p.drawPoint(i, j);
}
}

if(i>=MossyFiber::csOnset[MossyFiber::activeCS] && i<MossyFiber::csOnset[MossyFiber::activeCS]+MossyFiber::csDuration[MossyFiber::activeCS])
{
p.setPen(Qt::blue);
}
else
{
p.setPen(Qt::black);
}
for(int j=0; j<NUMMF; j++)
{
if(!drawAP[j])
{
p.drawPoint(i, j);
}
}
p.scale((float)buf->width()/5000, (float)buf->height()/NUMMF);

QRect updateArea(p.worldTransform().mapRect(QRect(i, 0, 1, NUMMF)));
if(updateArea.width()<1)
{
updateArea.setWidth(1);
}
p.end();
bufLock.unlock();

dispW->update(updateArea);

simPauseLock.unlock();
}

cout<<"trial run time: "<<time(NULL)-trialTime<<endl;
}

// for(int i=0; i<NUMMF; i++)
// {
// delete &mfsAP[i];
// delete &granulesAP[i];
// delete &golgisAP[i];
// }
delete mfsAP;
delete granulesAP;
delete golgisAP;
}


the really relevant part of that function is the following:


bufLock.lock();
p.begin(buf);
p.setWindow(0,0, TRIALTIME, NUMMF);
p.setViewport(0, 0, buf->width(), buf->height());

p.setPen(Qt::white);
for(int j=0; j<NUMMF; j++)
{
if(drawAP[j])
{
p.drawPoint(i, j);
}
}

if(i>=MossyFiber::csOnset[MossyFiber::activeCS] && i<MossyFiber::csOnset[MossyFiber::activeCS]+MossyFiber::csDuration[MossyFiber::activeCS])
{
p.setPen(Qt::blue);
}
else
{
p.setPen(Qt::black);
}
for(int j=0; j<NUMMF; j++)
{
if(!drawAP[j])
{
p.drawPoint(i, j);
}
}
p.scale((float)buf->width()/5000, (float)buf->height()/NUMMF);

QRect updateArea(p.worldTransform().mapRect(QRect(i, 0, 1, NUMMF)));
if(updateArea.width()<1)
{
updateArea.setWidth(1);
}
p.end();
bufLock.unlock();

dispW->update(updateArea);

simPauseLock.unlock();


I do some scaling coordinate transform stuff with the display, but you can ignore that if you don't need it. The code is a bit long and has a lot of irrelevant stuff in there for you, so feel free to ask if something doesn't make sense.

agostain
11th August 2009, 09:31
thanks a lot. i will take a look on it now. if i have question i won't renounce to ask ;)

agostain
11th August 2009, 14:48
what's the meaning of TRIALTIME and NUMMF ? do you write on x-axis from 0 to TRIALTIME ?

agostain
11th August 2009, 23:00
QWidget: Cannot create a QWidget when no GUI is being used

i used your simDisp class and then i implemet my own thread.

i got this error. do you know what's the problem?

wysota
12th August 2009, 08:58
Just a side note - this code is not safe as it references a pixmap from a worker thread. It will probably fail sooner or later, especially on X11 systems.

honestapple
25th March 2013, 10:37
Hi, wysota
I had met the same problems as hammer256. I am working on Windows XP. I have some questions.
1. Why is it not safe to reference pixmap from worker thread? Would this cause big porblems on Windows.
2. Please give me some advice if you have better solution.

wysota
25th March 2013, 14:09
1. Why is it not safe to reference pixmap from worker thread?
Because QPixmap could potentially access GUI data.


Would this cause big porblems on Windows.
It might crash your app.


2. Please give me some advice if you have better solution.
Use a shared QImage instance and generate a pixmap from it in the main thread.

honestapple
25th March 2013, 16:29
I knew that there was a pwt library which is used to plot. Which is better between the pwt library and the solution you advised?

wysota
25th March 2013, 16:47
I don't know what pwt is. I only know Qwt and it is hard to compare a home crafted solution with a library that has been on the market for years.