如何重新绘制QChart?

6

我想知道如何在将新点添加到其中的QLineSeries后重新绘制QChart 目标是用于显示高速获取的数据(最高达400,000个点/秒),并在数据以数据包形式到达时更新图表。

这是我一直在开发的测试程序:

MainWindow:

class MainWindow : public QMainWindow{
    Q_OBJECT

    QLineSeries *series;
    QChart *chart;
    QChartView *chartView;

    int cnt=0;


public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pB_Start_clicked();

private:
    Ui::MainWindow *ui;
};

MainWindow构造函数:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){
    ui->setupUi(this);

    series = new QLineSeries();

    chart = new QChart();
    chart->setBackgroundRoundness(0);

    chart->addSeries(series);

 // A bunch of formatting
    chart->setBackgroundVisible(false);
    chart->setMargins(QMargins(0,0,0,0));
    chart->layout()->setContentsMargins(0,0,0,0);
    chart->legend()->hide();
    chart->setPlotAreaBackgroundBrush(QBrush(Qt::black));
    chart->setPlotAreaBackgroundVisible(true);
    chartView = new QChartView(chart);
    ui->gridLayout->addWidget(chartView);

}

并且有一个 clicked 事件来将点添加到系列中的 pushButton:

void MainWindow::on_pB_Start_clicked(){
    series->append(cnt,qSin(cnt/10));
    cnt++;
    // Update plot here << ======== HOW?
}

OpenGLSeries示例以某种方式实现了这一点。我不明白具体是如何实现的。但在这种情况下,它有些不同,因为它用新的点替换了系列中的所有点,而不是将它们附加到末尾。


当调用 on_pB_Start_clicked 时,实际上会发生什么?它是仅向系列添加一个点还是开始以高速率添加点? - Sergey
在这个例子中,它只是添加了另一个点。这是我构建的一个测试程序,以了解QChart的功能。但目标是以高速率绘制。 - A. Vieira
2个回答

4

显然,QCharts不需要repaint()。向系列中添加新点似乎就足够了。我之前没有设置图表的轴,也因为数值没有正确计算而看不到数据。

已更正的代码:

头文件:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){
    ui->setupUi(this);

    series = new QLineSeries();

    chart = new QChart();    
    chart->addSeries(series);

    chart->createDefaultAxes(); // Preparing the axis
    chart->axisX()->setRange(0,10); 
    chart->axisY()->setRange(0,10); 

    // Same formatting
    chart->setBackgroundVisible(false);
    chart->setMargins(QMargins(0,0,0,0));
    chart->layout()->setContentsMargins(0,0,0,0);
    chart->legend()->hide();
    chart->setPlotAreaBackgroundBrush(QBrush(Qt::black));
    chart->setPlotAreaBackgroundVisible(true);
    chartView = new QChartView(chart);
    ui->gridLayout->addWidget(chartView);
}

并且在 pushButton 代码中,在计算之前将 cnt 强制转换为 double 类型。

void MainWindow::on_pB_Start_clicked(){
    double val = 3*(qSin((double)cnt*2)+2);
    series->append(cnt,val); // Enough to trigger repaint!
    cnt++;
}

如果你正在使用AreaSeries,那么你可以使用setUpperSeries(new_useries_here)setLowerSeries(new_lseries_here)进行更新。你的areaSeries必须被存储为全局变量。 - lenooh
easy_chart.cpp:72:12: warning: 'axisX' is deprecated qchart.h:109:5: note: 'axisX' has been explicitly marked deprecated here qcompilerdetection.h:227:45: note: expanded from macro 'Q_DECL_DEPRECATED' - euraad

2
首先,如果您在GUI线程中以400000 pts/sec接收和附加点,则应用程序将完全冻结。因此,您需要专门为数据接收和处理分配另一个线程,并使用(例如)与QueuedConnection连接的信号/插槽将处理过的图形数据发送到GUI线程。通过“处理”,我至少指平均值、丢弃和抽取(就像那些DSP人员理解的那样),因为400000 pts/sec似乎太快了,您会浪费内存和GUI性能。但是,如果您不想抽取,那就由您决定。在这种情况下,您可以考虑比QueuedConnectioned信号/插槽更轻量级的数据传递机制。
第二个问题是什么时候绘制?不久前,我在更低的速率下使用QCustomPlot实现了类似的功能。我面临的主要问题是,当我尝试在接收每个点之后重新绘制时,存在巨大(且变化的)延迟,特别是在绘制抗锯齿图时。在您的情况下,解决方案是子类化QChartView(我假设您已经完成了),并在其中覆盖timerEvent,当您需要开始/停止重新绘制时调用startTimer()/killTimer()。或者,您可以在拥有QChartView对象的对象中保持一个定时器,并从那里发出重新绘制,但与子类化QChartView相比,它看起来像是抽象泄漏。总之,这种方法使您可以实现几乎恒定的帧速率,并使其尽可能平滑,而不会冻结应用程序的界面。
最后,如何重新绘制?QChartView似乎具有从QWidget继承的repaint()方法,该方法可以实现您所需的功能。

就是这样。repaint() 看起来可以做到。我只需要预定义比例或类似的东西。是的,每秒 400,000 个点很多,我从未在我的主应用程序中绘制过这么多点。到目前为止,我一直使用 qwt 来绘制图形,使用 QwtPlotDirectPainter 一次只绘制几个点,并结合 QCoreApplication::processEvents() 保持 GUI 运行良好。但是,由于我看到 QCharts 使用 OpenGL,所以我计划进行更改。 - A. Vieira
@A.Vieira Qwt经常被认为比QCustomPlot快得多。也许它也比QCharts更快,我不知道。如果要谈论比例尺,请在每次重新绘制之前考虑基于数据的重新缩放。这可能会在绘图的前几秒钟让用户感到困惑,但它可以解决预定义比例尺错误可能带来的问题。 - Sergey
从OpenGLSeries中我所看到的情况来看,使用OpenGL在每次绘制新数组时比我使用Qwt更快。我不知道如何在Qwt中使用OpenGL,它似乎仍在开发中但可能是可行的(不要引用我)。我在Qwt上取得了成功,但QCharts可能更稳定,可以在许多电脑上进行开发并将我的工作传递给同事。 - A. Vieira

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接