QtCharts依赖于QtChartView实现,但是在UI文件中是没有 QtChartView这个类型的。所以我们需要进行一些额外的操作来实现我们的目的。因为QtChartView继承自QGraphicsView,所以我们可以在界面添加一个Graphics View
然后在右边的对象查看器中,选中这个QGraphicsView然后右键,就会出现菜单,选择提升为,这个操作就可以实现添加继承自QGraphicsView的模块。
进行下方的操作就可以在UI文件中添加一个继承自QGraphicsView的QtChartView了
记得在头文件中包含下方的代码,这样才可以正常使用QtCharts。
#include
为了达到心电图的效果,首先我们根据期望的目标对表格进行初始化,实现图标的红色虚线线框,让心电波形更加凸显。
其中设置线型函数需要说明一下
axisX_ECG->setLinePen(QPen(Qt::red, 1, Qt::DashDotDotLine, Qt::SquareCap, Qt::RoundJoin))
折线图初始化函数
void DynamicECG::initEcgWaveLineChart() {
axisY_ECG = new QValueAxis();
axisX_ECG = new QValueAxis();
ecgSeries = new QLineSeries();//这里也可以也考虑改为平滑曲线
ecgWaveLineChart = new QChart();
//添加曲线到chart中
ecgWaveLineChart->addSeries(ecgSeries);
//设置坐标轴显示范围
axisY_ECG->setRange(-350, 1200);
axisX_ECG->setRange(0, 600);
axisX_ECG->setTickCount(30);//横坐标30个数据点
axisY_ECG->setTickCount(10);//纵坐标分为10块
//设置坐标轴的颜色,粗细和设置网格显示
axisX_ECG->setGridLinePen(QPen(Qt::red, 1, Qt::DashDotDotLine, Qt::SquareCap, Qt::RoundJoin)); //网格样式
axisY_ECG->setGridLinePen(QPen(Qt::red, 1, Qt::DashDotDotLine, Qt::SquareCap, Qt::RoundJoin));
axisX_ECG->setLinePen(QPen(Qt::red, 1, Qt::DashDotDotLine, Qt::SquareCap, Qt::RoundJoin));//坐标轴样式
axisY_ECG->setLinePen(QPen(Qt::red, 1, Qt::DashDotDotLine, Qt::SquareCap, Qt::RoundJoin));
axisY_ECG->setGridLineVisible(true);//显示线框
axisX_ECG->setGridLineVisible(true);
axisX_ECG->setLabelsVisible(false);//不显示label具体数值
axisY_ECG->setLabelsVisible(false);
//把坐标轴添加到chart中,第二个参数是设置坐标轴的位置,
//只有四个选项,下方:Qt::AlignBottom,左边:Qt::AlignLeft,右边:Qt::AlignRight,上方:Qt::AlignTop
ecgWaveLineChart->addAxis(axisX_ECG, Qt::AlignBottom);
ecgWaveLineChart->addAxis(axisY_ECG, Qt::AlignLeft);
//把曲线关联到坐标轴
ecgSeries->attachAxis(axisX_ECG);
ecgSeries->attachAxis(axisY_ECG);
ecgSeries->setColor(QColor(Qt::black));//设置线的颜色
//ecgSeries->setUseOpenGL(true);//openGL加速 在我的电脑上如果采用加速,绘制速度反倒会变慢
setLineChartMargins(ecgWaveLineChart, 2);//设置折线图边距
ecgWaveLineChart->legend()->hide();//不显示注释
ui->ecgWaveLineChart->setChart(ecgWaveLineChart);//这里就可以把我们设置好的QrChart与QChartView进行绑定
}
}
因为我的心电传感器的数据大概是8ms一个点,所以这里我使用定时器,每8ms进行一次任务:绘制一个新的点,8ms的时候,定时器会触发timeout信号,然后我们可以自定义每次定时器触发执行的任务oneTimeOutAction。
void DynamicECG::onShowPushButtonClick()
{
originList.clear();
QString origin = ui->inputTextEdit->toPlainText();
originList = origin.split(",");
originListSize = originList.count();
qDebug() << QString("数据大小%1").arg(originListSize);
ecgWaveDrawTimer = new QTimer(this);//定时任务
connect(ecgWaveDrawTimer, SIGNAL(timeout()), this, SLOT(oneTimeOutAction()));
ecgWaveDrawTimer->start(8);//8ms执行一次
}
下方是oneTimeOutAction具体执行的函数
void DynamicECG::oneTimeOutAction() {
//这里主要是实验用,当文本框数据绘制完毕后就停止绘制,在实际中可以没有这一行代码
if (originListIndex >= originListSize)
{
ecgWaveDrawTimer->stop();
}
else {
qint16 tempInt16 = originList.at(originListIndex).toInt();
//qDebug() << QString("数据为:%1").arg(tempInt16);
drawEcgWave(originListIndex, tempInt16);
originListIndex++;
}
}
绘制波形图的函数,主要是利用pointF的向量值赋值给series,然后通知series整体进行更改
//注意axis_x的输入,不要超过int的上限
void DynamicECG::drawEcgWave(int axis_x, qint16 data) {
int timesCounts = axis_x / 600;//查看数据是否超过了600
if (timesCounts > 0)//如果第一次界面绘制结束,之后存在了600个点
{
axis_x = axis_x - timesCounts * 600;
ecgPointBuffer[axis_x].setY(data);//更改已有点的Y值
}
else//如果是第一次界面
{
ecgPointBuffer.append(QPointF(axis_x, data));
}
ecgSeries->replace(ecgPointBuffer);
}
其实写到这里,已经发现了一些问题了,比如定时器的频繁调用会造成系统响应变慢,以及很不方便控制绘制速度,有可能会出现延迟越来越大的现象,但是这个案例的确可以作为一个初步的使用,我后面的实现也是在这个的基础上一步步走过来的。
运行程序,只需复制心电波形数据.csv中的内容,在下方的文本框中粘贴(其实可以写一个读取CSV文件数据的函数,这里为了方便就采用文本框的方式其实主要是偷懒),然后点击显示曲线即可实现波形的动态显示
程序及心电数据下载连接
说明:程序提供的下载(本程序是利用VS2017+Qt 5.11.2进行编写,建议用相同环境打开,不同的Qt版本可能会导致编译不成功,最近时间挺紧张,后面有时间可能再考虑转换成Qt版本)
我发现CSDN真的有趣,积分怎么越变越高。。。我还改不了需要积分,如果需要程序就留言邮箱吧 我有空发给你们。
这一节已经实现了动态心电图,但是针对实时的心电数据,该程序还是需要做出一定的修改,下一章将会讲解动态曲线和数据的环形队列,才能真正实现数据的接收和实时绘制。
快速跳转链接:《QtCharts绘制动态心电图[2]——利用队列进行实时绘制》