背景:
项目可能用到,也了解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,何等相似。扯远了。接受指正。
本文完。