QtChart做个动态更新的曲线

背景:

项目可能用到,也了解customplot,但是没用过,现在这里环境封闭,尽量用qt自带的chart做。

最终我想做个类似插件的widget,只要输入数据就可以绘制曲线。但前提得先看看chart咋用。于是,实验之后记录下来。大家看过以后共同交流,也为后人铺路。

很多东西谁都不可能生下来就会,闭门造车不是每次运气都那么好。互联网给了大家交流的平台,少走很多弯路。非常感谢csdn各位的奉献。

QChart原理:

先说我测试后的总结。

qt需要把图表chart画在画布chartview上,所以界面上得先有个chartview,就是个widget的提升。

新建一个chart对象,添加到chartview上。

新建点集对象series,添加到chart。

新建坐标轴对象,把series锚定到坐标轴。

网上各种代码说注意顺序,实际上无所谓,先new哪个都行,注意依赖关系即可。

如果想让它动起来,就是更新点集series和重新锚定坐标axis即可。

下面开始操练,保证复制下来直接就是个完整demo。老鸟略过,如果看了请多指正。

界面:

默认新建一个widget项目,就在默认的mainwindow上折腾。放几个按钮和一个widget,将widget提升为QChartView。

.ui文件源码如下:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>513</width>
    <height>392</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <widget class="QPushButton" name="pushButton">
      <property name="text">
       <string>start</string>
      </property>
     </widget>
    </item>
    <item row="0" column="1">
     <widget class="QPushButton" name="pushButton_2">
      <property name="text">
       <string>stop</string>
      </property>
     </widget>
    </item>
    <item row="0" column="2">
     <widget class="QPushButton" name="pushButton_3">
      <property name="text">
       <string>reset</string>
      </property>
     </widget>
    </item>
    <item row="0" column="3">
     <spacer name="horizontalSpacer">
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
      </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>249</width>
        <height>20</height>
       </size>
      </property>
     </spacer>
    </item>
    <item row="1" column="0" colspan="4">
     <widget class="QChartView" name="wChartView" native="true"/>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>513</width>
     <height>23</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <customwidgets>
  <customwidget>
   <class>QChartView</class>
   <extends>QWidget</extends>
   <header>qchartview.h</header>
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>

代码:

头文件:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtCharts>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();

    void on_pushButton_3_clicked();

private:
    Ui::MainWindow *ui;

    QLineSeries *m_series = nullptr;
    QChart* m_chart = nullptr;
    QValueAxis *m_axisX = nullptr;
    QValueAxis *m_axisY = nullptr;
    int m_iMaxX = 100;

    void f_Init();
    void f_Reset();
    void f_UpdateAxis();

    int m_iTimerID = 0;
    void timerEvent(QTimerEvent *event);
};
#endif // MAINWINDOW_H

源文件:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    f_Init();
    f_Reset();
}
MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::f_Init()
{
    //为界面上的画布(QChartView),创建一个图表对象(QChart),并设置它的相关属性。
    m_chart = new QChart();
    ui->wChartView->setChart(m_chart);
    m_chart->setTitle("数据走势图");
    m_chart->setTheme(QChart::ChartThemeDark);

    //创建一个点集序列(series),添加到图表中。
    m_series = new QLineSeries(m_chart);
    m_chart->addSeries(m_series);
    m_series->setUseOpenGL(true);//OpenGl加速

    //初始化坐标轴(Axis),并添加到图表中。
    m_axisX = new QValueAxis;
    m_axisY = new QValueAxis;
//    m_axisX->setLabelFormat("%g");
    m_axisX->setTitleText("X轴");
    m_axisY->setRange(0, 1000);//y轴值范围0-1000,x轴之后动态设置
    m_axisY->setTitleText("Y轴");
    m_chart->addAxis(m_axisX, Qt::AlignBottom);//x轴放在底部
    m_chart->addAxis(m_axisY, Qt::AlignLeft);//y轴放在左侧
}
void MainWindow::f_Reset()
{
    m_series->clear();
    m_series->append(0, 0);//点集默认就一个原点,待会儿曲线就从这里开始
    f_UpdateAxis();
}
void MainWindow::f_UpdateAxis()
{
    //根据点集的数据变化,动态更新x轴的值范围。
    qreal realX_first = m_series->pointsVector().first().x();
    m_axisX->setRange(realX_first, realX_first + m_iMaxX);

    //由于坐标轴已经更新,需要重新锚定点集。别问我咋知道的,为了面子,肯定不能说我试出来的。
    m_series->attachAxis(m_axisY);
    m_series->attachAxis(m_axisX);
}
void MainWindow::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == m_iTimerID)
    {
        /***
         * 这里是生成一个0-1000的随机数。如果有兴趣,你可以让它按照自己需要的趋势走,比如正弦。
         *
         * 说明:
         *
         * 任何语言的随机数函数,肯定都是算法,理论上不可能绝对随机。所以就有了种子值的概念,让程序员能干预它。
         * 上学时自己做纸牌游戏玩,想到了计算机报上看到过,某浏览器的随机算法中,采用了流逝的时间作为种子值。
         * 所以很受启发,真正的随机在于现实世界的触发,所以想要每次洗牌随机,就采用玩家开始操作时的时间毫秒数
         * 作为种子值,基本上就没问题了。你想,哪有那么巧合的人为操作?所以日常这绝对够了。当然,这里是在定时
         * 器里取秒数,仔细看曲线还是有规律的,只是个示意。
         *
         * 由于随机数不一定是多少,所以取模1000,保证取值肯定0-1000。
         */
        QRandomGenerator rg(QTime::currentTime().second());
        quint32 iRand = rg.generate() % 1000;

        //新点位就让它x轴移动1,y轴等于随机数。
        qreal realInterval = 0.1;
        qreal realX_last = m_series->pointsVector().last().x();
        qreal realX_new = realX_last + realInterval;
        qreal realY_new = iRand;

        //先让点集脱离坐标系,更新完再锚定。
        m_series->detachAxis(m_axisX);
        m_series->detachAxis(m_axisY);

        //如果曲线已经绘制到x轴最右侧,则先删除最左侧的点。
        if (m_series->count() > (m_iMaxX / realInterval))
        {
            m_series->remove(0);
        }

        //追加新点位。
        m_series->append(realX_new, realY_new);

        //更新坐标轴。
        f_UpdateAxis();

        //如此,曲线会一直随着时间流逝,不断往右画。
    }
}
void MainWindow::on_pushButton_clicked()
{
    m_iTimerID = this->startTimer(100);
}
void MainWindow::on_pushButton_2_clicked()
{
    this->killTimer(m_iTimerID);
    m_iTimerID = 0;
}
void MainWindow::on_pushButton_3_clicked()
{
    f_Reset();
}
#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

虽然东西不多,但开箱即用。

总结:

其实不难用,主要是qt手册说明的不够仔细。知道chartview,chart,series,axis这几个东西如何搭配就可以了。就像xml和json一样,也是有几个类。

注意事项已经写在注释里了。主要的再提一下,更新界面这种操作一定要数据和界面分离,处理完数据再绑定。

20多年前的vs ado,十年前的vs datatable-bind,和今天的mvc,何等相似。扯远了。接受指正。

本文完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值