qt |使用 QGraphicsView、QGraphics Scene 创建动态图表/绘图 |实时数据
Qt | Creating a dynamic Chart / plot with QGraphicsView, QGraphics Scene | realtime data
我目前正在用 Qt 开发一个小程序。要显示绘图,您可以使用 qwt 或 qcustomplot 或 qpainterevent 或 QChart。但我对用 QGraphicsView 编写的动态图的解决方案感兴趣。
我的喜好
- 我图表的宽度应该是恒定的
-实时绘图
-如果到达图表末尾,则应删除或覆盖第一个示例,因此它是动态流畅的图表
我下面的例子是动态和流畅的...但只是为了数字,它在我的 if 子句中。我不明白为什么。
想法是删除第一个lineitem,所以我一直有99个item。如果我删除一个项目,我想为下一个项目提供与之前项目的位置。
所以
x=99 将是 x=98 ......x=1 将是 x=0;
我的想法有错吗?
我也有过一些想法,但这可能是最好的。
提前致谢
康拉德
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;
}
}
}
到目前为止,这是最佳答案,请注意,如果您使用 100Hz 作为采样率,我的性能仅在 50 samplesInView
时稳定。
您可以降低采样率并增加 samplesInView 以在图中包含更多值。
重要:
xDatashort
是包含所有 x 值的 QVector<double>
yDatashort
是一个包含所有 y 值的 QVector<double>
两者都填充了 programm
class 中的值,此 class 正在向启动插槽 drawGraph()
.
的连接发出信号
你也可以只使用一个 QVector<QPoint>
这样更容易处理,但这不是我想要的。
lineVector
是一个 QVector<QGraphicsLineItem>
,它包括视图中的所有行
xScale
用于扩展剧情,yScale
也是。
width
是坐标系的宽度
xAxisMark
是距离标记之间的像素距离
marksVector
是一个QVector<double>
,其中包括x轴的距离标记,应该是动态的
iCurrentVectorPoint
是一个运行时变量,它可以帮助我添加行。
!!这段代码很适合用于实时绘图,但它的性能不是最好的,所以如果有人有想法释放潜力,请随时获得最佳答案:) !!
如有其他问题,请发表评论,我会尽力帮助您在您的设备上制作精美的手工绘图。
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++;
}
更新:
代码稳定超过 50 分钟,查看 800 个样本,100 Hz 采样率和 20 Hz 帧率。为模拟器数据使用线程。随时问我关于这个话题的几乎所有问题,我已经研究了将近 2 个月 :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);
// }
}
注意!!
这些解决方案不是最快的。使用 QPainterPath 而不是 QLineF,就像你拿了一支笔,画了一条线,又把笔收起来,如此重复 1000 次。
最好将所有 QPoints 保存在一个 QPainterPath 中,然后拿笔画一次。这提高了 4 分钟趋势实时绘图的性能,并且没有问题。
我目前正在用 Qt 开发一个小程序。要显示绘图,您可以使用 qwt 或 qcustomplot 或 qpainterevent 或 QChart。但我对用 QGraphicsView 编写的动态图的解决方案感兴趣。
我的喜好 - 我图表的宽度应该是恒定的 -实时绘图 -如果到达图表末尾,则应删除或覆盖第一个示例,因此它是动态流畅的图表
我下面的例子是动态和流畅的...但只是为了数字,它在我的 if 子句中。我不明白为什么。
想法是删除第一个lineitem,所以我一直有99个item。如果我删除一个项目,我想为下一个项目提供与之前项目的位置。 所以 x=99 将是 x=98 ......x=1 将是 x=0;
我的想法有错吗? 我也有过一些想法,但这可能是最好的。
提前致谢 康拉德
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;
}
}
}
到目前为止,这是最佳答案,请注意,如果您使用 100Hz 作为采样率,我的性能仅在 50 samplesInView
时稳定。
您可以降低采样率并增加 samplesInView 以在图中包含更多值。
重要:
xDatashort
是包含所有 x 值的 QVector<double>
yDatashort
是一个包含所有 y 值的 QVector<double>
两者都填充了 programm
class 中的值,此 class 正在向启动插槽 drawGraph()
.
你也可以只使用一个 QVector<QPoint>
这样更容易处理,但这不是我想要的。
lineVector
是一个 QVector<QGraphicsLineItem>
,它包括视图中的所有行
xScale
用于扩展剧情,yScale
也是。
width
是坐标系的宽度
xAxisMark
是距离标记之间的像素距离
marksVector
是一个QVector<double>
,其中包括x轴的距离标记,应该是动态的
iCurrentVectorPoint
是一个运行时变量,它可以帮助我添加行。
!!这段代码很适合用于实时绘图,但它的性能不是最好的,所以如果有人有想法释放潜力,请随时获得最佳答案:) !!
如有其他问题,请发表评论,我会尽力帮助您在您的设备上制作精美的手工绘图。
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++;
}
更新:
代码稳定超过 50 分钟,查看 800 个样本,100 Hz 采样率和 20 Hz 帧率。为模拟器数据使用线程。随时问我关于这个话题的几乎所有问题,我已经研究了将近 2 个月 :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);
// }
}
注意!! 这些解决方案不是最快的。使用 QPainterPath 而不是 QLineF,就像你拿了一支笔,画了一条线,又把笔收起来,如此重复 1000 次。 最好将所有 QPoints 保存在一个 QPainterPath 中,然后拿笔画一次。这提高了 4 分钟趋势实时绘图的性能,并且没有问题。