K.Friend
K.Friend

Reputation: 31

Qt | Creating a dynamic Chart / plot with QGraphicsView, QGraphics Scene | realtime data

I am currently developing a small programm in Qt. To show a plot you can use qwt or qcustomplot or the qpainterevent or QChart. But I am interessted in a solution for a dynamic plot which is writen with the QGraphicsView.

My preferences -width of my chart should be constant -realtime plotting -the first sample should be deleted or overwriten if the end of the chart is reached, so it is a dynamic and fluent chart

My example beneath is able to be dynamic and fluent... but just for the number, which is in my if clause. I do not get why.

The idea is to delete the first lineitem, so I have constantly 99 items. If I delete a item I want to give the next item the position from the item before. So x=99 will be x=98 ......x=1 will be x=0;

Do I have a mistake in my idea? I also have had several ideas, but this is probably the best.

Thanks in advance Konrad

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        scene = new QGraphicsScene(this);
        ui->graphicsView->setScene(scene);
        vectorPoint = new QVector<QPoint>;
        line = new QVector<QGraphicsLineItem*>;

    yDatai = 0;
    xDatai = 0;
    Grenzenlaufvariable = 0;

    timer = new QTimer(this);
    timer->start(10);
    connect (timer, SIGNAL(timeout()),this,SLOT(newData()));
    connect(this,SIGNAL(newPaint()),this,SLOT(paint()));
}

MainWindow::~MainWindow()
{
    delete ui;
    delete scene;
    delete vectorPoint;
    delete line;
    }

    void MainWindow::newData()
    {

    if (yDatai == 100 || yDatai == -100)                
    {
        Grenzenlaufvariable++;
    }
    if (Grenzenlaufvariable%2==0)
    {
        yDatai+=1;
    }
    else
    {
        yDatai-=1;
    }
    xDatai++;

    point = {xDatai,yDatai};                            
    vectorPoint->append(point);

    if(vectorPoint->size()>1)
    {
        item = scene->addLine(QLineF(vectorPoint->at(ix-1),vectorPoint->at(ix)));
        line->append(item);
    }
       ix++;
    emit newPaint();                                    
}

void MainWindow::paint()
{
    if(line->size()==99)
    {

        scene->removeItem(line->at(0));
        line->removeAt(0);
        qDebug()<<line->size();
        for (int ip= 0;ip <line->size();ip++)
        {
            oldx = line->at(ip)->x();
            line->at(ip)->setX(oldx-1);
            qDebug()<<ip;

        }
    }
}

Upvotes: 2

Views: 5395

Answers (3)

K.Friend
K.Friend

Reputation: 31

CAUTION!! These Solutions are not the fastest ones. Use QPainterPath instead of QLineF, it is like you take a pen, draw a line, put the pen away, and this 1000 times. Better to safe all QPoints in a QPainterPath and take the pen once to draw. That increases the performance to Realtime Plotting with a 4 min trend and more without problems.

Upvotes: 0

K.Friend
K.Friend

Reputation: 31

Update:

Code stable for more than 50 min with 800 samples in view, with 100 Hz samplerate and 20 Hz framerate. Using a Thread for the simulatordata. Feel free to ask me nearly everything about this topic, I worked through it for nearly 2 months :D

void MainWindow::drawChart()
{
//To check the framerate I implemented a framecounter
    framerunner++;
    ui->Framerate->setText(QString::number(int(framerunner/double(DurationTimer->elapsed()/1000))));

//Using to stay focused on the scene, not neccesary if you define the x values from [startview-endview]
    QRect a;
    if(Samplevector.size()!=0)
    {
        a.setRect(Samplevector.at(Samplevector.size()-1).getX()-850,0,900,200);
        qDebug()<<Samplevector.at(Samplevector.size()-1).getX();
    ui->LinegraphView->setSceneRect(a);
    }
//delete everything in the scene and redraw it again
scene->clear();
if(Samplevector.size()>1)
{
    for(int i=1;i<Samplevector.size();i++)
    scene->addLine(QLineF(Samplevector.at(i-1).getX(),Samplevector.at(i-1).getY(),Samplevector.at(i).getX(),Samplevector.at(i).getY()));
}

}
void MainWindow::start()
{
    framerate->start(50);
    DurationTimer->start();
    hegsimulator->moveToThread(thread);
    thread->start();
   qDebug()<<"Request "<<this->QObject::thread()->currentThreadId();
}
void MainWindow::stop()
{
    framerate->stop();
    hegsimulator->stopDevice();
}
void MainWindow::prepareGraph()
{
    samplerunner++;
    ui->Samplerate->setText(QString::number(int(samplerunner/double(DurationTimer->elapsed()/1000))));
    Samplevector.append(hegsimulator->getSample());
    if(Samplevector.size()>800)
    {
        //graphlinevector.first()->hide();
        //scene->removeItem(graphlinevector.first());
//        graphlinevector.removeFirst();
      Samplevector.removeFirst();
    }
//    if(Samplevector.size()>1)
//    {
//    item = scene->addLine(QLineF(Samplevector.at(Samplevector.size()-2).getX(),Samplevector.at(Samplevector.size()-2).getY(),Samplevector.at(Samplevector.size()-1).getX(),Samplevector.at(Samplevector.size()-1).getY()));
//    graphlinevector.append(item);

//    }

}

Upvotes: 0

K.Friend
K.Friend

Reputation: 31

So far, thats the best answer, pay attention that if you use 100Hz as samplerate, my performance is just stable with 50 samplesInView. You can decrease the samplerate and increase the samplesInView to have more values in the plot.

Important: xDatashort is a QVector<double> which includes all the x-values yDatashort is a QVector<double> which includes all the y-values both are filled with values in the programm class, this class is emitting the signal to the connection which start the slot drawGraph().

You can also just use an QVector<QPoint> which makes it more easy to handle, but its not what I want in my case.

lineVector is a QVector<QGraphicsLineItem> which inclues all the Lines from the view

xScale is used to extend the plot, yScale as well.

width is the width of the Coordinationsystem xAxisMark is the pixeldistance between to distance marks marksVector is a QVector<double> which includes the distance marks of the x Axis, which should be dynamic

iCurrentVectorPoint is a runtimevariable, which helps me to add the lines.

!!This code is good to use for realtime plotting, but it has not the best performance, so if anybody is having ideas to unleash potential, feel free to achieve the best answer :) !!

For further questions just comment and I will try to help you to get a nice handmade plot on your device.

void Plot::drawGraph()
{
    if(programm->xDatashort.size()>1)
        {
            if(lineVector->size()==programm->samplesInView)
            {
                for (int ip =0;ip<programm->samplesInView;ip++)
                {
                    lineVector->at(ip)->setLine((ip)*xScale,(programm->yDatashort.at(ip))*yScale*(-1),(ip+1)*xScale,(programm->yDatashort.at(ip+1))*yScale*(-1));
                }
                for (int iy=1 ; iy<(width/xAxisMarks)+1 ; iy++)
                {
                    int oldx = marksVector->at(iy)->x();
                    oldx-=1;
                    if(oldx%xAxisMarks==0 || oldx==0)
                    {
                   marksVector->at(iy)->setX(oldx+xAxisMarks);
                    }
                    else
                    {
                    marksVector->at(iy)->setX(oldx);
                    }
                }
            }
            else
           {
                item = scene->addLine(QLineF(programm->xDatashort.at(iCurrentVectorPoint-1)*xScale,  programm->yDatashort.at(iCurrentVectorPoint-1)*yScale*(-1),   programm->xDatashort.at(iCurrentVectorPoint)*xScale, programm->yDatashort.at(iCurrentVectorPoint)*yScale*(-1)));
                lineVector->append(item);
            }

       }
        iCurrentVectorPoint++;

}

Upvotes: 1

Related Questions