VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线

  • VS2017+QT5.9.2中使用多媒体定时器+qcustomplot绘制动态曲线
    • 1. VS2017+QT5.9.2的配置
    • 2. QT工程的创建
    • 3. 导入多媒体定时器和qcustomplot
      • 3.1 多媒体定时器
      • 3.2 qcustomplot
    • 4. 动态绘图
    • 5. 程序的打包发布
    • 6. 相关资料

VS2017+QT5.9.2中使用多媒体定时器+qcustomplot绘制动态曲线

  VS2017+QT5.9.2中使用多媒体定时器+qcustomplot绘制动态曲线,从环境搭建到程序打包发布的一个相对完整的流程。定时器的精度实际没有测试,按我参考的博主的说法应该是ms级别的,我实际使用的时候是定时2ms。
  也是初学QT的小白,下面的操作可能也不是很规范。都是按实际操作经验总结瞎掰出来,最后应该还是能用吧。

1. VS2017+QT5.9.2的配置

  对于VS+QT的环境配置网上相关的教程比较多,这里就不再赘述。
  对于这一部分,我也没用参考什么教程,就直接无脑安装,先安装QT5.9.2,好像是全部勾选(但可能有点组件并不需要),最终大小为12G左右;然后在安装VS2017社区版,我安装的时候也是无脑选择前面三个组件,或许QT开发不需要这么多,需要注意的是C++通用windows平台工具是必要的,默认似乎不会勾选这个选项,然后等待它安装成功,大概有20G吧。

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线_第1张图片
  然后在VS中配置QT环境,在 工具>扩展和更新>联机 中搜索QT,然后就可以看见QT Visual Studio Tools,点击下载即可,然后关闭VS2017后会弹出一个更新界面,点击 修改 就安装成功了,重新打开VS2017,界面的选项栏就会多一个 QT VS Tools 的选项,点击该选项,点击 Qt Options,在其中添加安装QT的路径对应的编译器。

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线_第2张图片
  我对应是这样的路径 C:\Qt\Qt5.9.6\5.9.6\msvc2017_64,然后点击确定就基本完成了。

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线_第3张图片

2. QT工程的创建

  在配置好环境后,在VS2017中新建项目时,就会在 Visual C++ 中多出一个 QT 的选项。

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线_第4张图片
  我们选择QT的窗体程序,命名为qt_demo,新建工程。之后还会弹出一些关于QT基本设置的窗体选项,主要在于下面这个选项配置。

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线_第5张图片
  我们选择debug,然后 QT Version 就现在之前配置的 msvc2017_64,然后对于Qt Modules 默认会帮你选上三个必要的module,点击那个表格框,就会弹出全部的选项。

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线_第6张图片
  对于我需要实现的动态绘图功能,QT官方有一个 Charts 的库,但是用过后感觉很一般吧,点数多的时候,刷新频率就不能太快,论坛上面说似乎只有50hz吧。所以这里没有勾选它。然后一路 Next Finish,QT工程就创建完毕了。
  最后,需要留意的是工程对应的SDK版本,在项目上 右键>属性>配置属性>常规 中查看,SDK版本需要换为 SDK10。(另外,由于我电脑里面同时装了VS2013和VS2017,所以在平台工具集会默认选择2013,需要自己手动换成2017)。

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线_第7张图片

3. 导入多媒体定时器和qcustomplot

3.1 多媒体定时器

  由于甲方的需要,我需要一个大概2ms的定时器,虽然QT里面自带定时器控件,但是定时精度似乎只有20ms,然后解决方案是调用Windows的多媒体定时器来达到ms精度的定时功能。我参考这位博主的方法,十分感谢这位博主:
  Qt之高精度多媒体定时器:https://blog.csdn.net/iliukunpeng/article/details/79211201
  直接根据这个博文,建立相应的头文件和源文件,我命名为了performancetimer,将它们放入项目文件夹中,然后将它们添加到项目中即可。
  经过个人的实践,在纯QT环境中,可以直接使用,但是需要在工程对应的 .pro 文件中添加 LIBS += -lwinmm ,添加这个库才可以编译成功使用。

LIBS += -lwinmm

  在VS+QT的环境中,上述博文中头文件中的

friend WINAPI void  CALLBACK PeriodCycle(uint,uint,DWORD_PTR,DWORD_PTR,DWORD_PTR);
// 需要替换为
friend void WINAPI CALLBACK PeriodCycle(uint,uint,DWORD_PTR,DWORD_PTR,DWORD_PTR);

然后在项目的头文件中需要添加 #pragma comment(lib,“Winmm.lib”),来添加对应的库。就可以正常使用了。

// qt_demo.h中
#include "performancetimer.h"
#pragma comment(lib,"Winmm.lib")

3.2 qcustomplot

  对于QT中动态绘图的功能,即我想要的示波器功能不像NI的一些仪器仪表软件封装的那么方便,QT中自带的绘图库QCharts确实不太行(也可能是我不太会使用),然后我选择了第三方绘图库QCustomplot,它的使用和配置都比较方便而且性能也能达到我的需求。
  对于qcustomplot的基本配置网上有很多教程,这里不再赘述。不能说毫无难度,但可以说是非常简单。只需要去qcustomplot官网下载压缩包。好像里面就这么点东西。。。

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线_第8张图片
  然后将qcustomplot.h和qcustomplot.cpp放入工程文件夹,在将它们添加到工程中,然后还需要添加一个库,在项目上 右键>属性>链接器>输入>附加依赖项 中添加“Qt5PrintSupportd.lib”,然后对应 release 里面是“Qt5PrintSupport.lib”,这里可以顺手添加,不然以后发布程序的时候,忘记这茬事就很尴尬了。。。然后就可以用啦。
  我们小试一波,打开QT界面文件(qt_demo.ui),然后在主界面中拖入一个Widget控件,缩放到合适的大小,点击 右键>提升为…,然后出现如下界面,然后在 提升类的名称:中输入 QCustomPlot(一定要注意大小写哈,不然会很尴尬的,,),点击 添加,然后选中刚刚添加的类,点击 提升。

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线_第9张图片
  在保存ui文件后,在debug下点击运行,就会出现如下效果了。

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线_第10张图片
在纯QT环境中,操作也类似,就是对于 printsupport 库的添加方式不太一样,只需要在.pro文件中添加:

QT += printsupport

4. 动态绘图

  对于动态绘图,我的思路就很简单,就是创建两个定时器,一个用于往向量中更新数据,我设置了2ms一次;一个用于刷新绘图,包括更新坐标轴和更新曲线,我设置了100ms一次。然后我在界面中放置了两个示波器,都绘制一个简单的正弦函数,也放置了一些按钮,用了开启和停止定时器,增大和缩小正弦波幅值。最后运行结果大概是这个样子的。

VS+QT中使用多媒体定时器+qcustomplot绘制动态曲线_第11张图片
  我就直接上代码了,主要是qt_demo.h和qt_demo.cpp文件,关键的地方也做了一些必要的注释;剩下就是ui文件,按上图自己搭建就好。然后整个工程文件的下载链接在文末,跳转自行下载哈。
qt_demo.h

#pragma once

#include 
#include "ui_qt_demo.h"
#include 
#include "performancetimer.h"

#pragma comment(lib,"Winmm.lib")

class qt_demo : public QMainWindow
{
    Q_OBJECT

public:
    qt_demo(QWidget *parent = Q_NULLPTR);

private:
    Ui::qt_demoClass ui;

	PerformanceTimer* timer_chart;
	PerformanceTimer* timer_data;


private slots:
	void on_pushButton_clicked();		// 点击启动绘图
	void on_pushButton_2_clicked();		// 点击停止绘图
	void on_pushButton_3_clicked();		// 点击增大正弦函数幅值
	void on_pushButton_4_clicked();		// 点击减小正弦函数幅值
	void handleTimeout_chart();			// 绘图的定时回调函数
	void handleTimeout_data();			// 控制的定时回调函数
};

qt_demo.cpp

#include "qt_demo.h"
#include "qcustomplot.h"

int timer_interval = 2;				// 数据添加定时器的定时间隔,2ms
float timer_interval_f = timer_interval / 1000.0;
int timer_interval_chart = 100;		// 绘图定时器的定时间隔,100ms

QVector data_x1, data_y1, data_x2, data_y2;

int axisTime = 40;									// 横坐标显示时间长度
int dataSzie = axisTime * 1000 / timer_interval;	// 图标显示点的个数

int flag_axis = 0;					// 是否更新坐标轴的标志位
int counter = 0;					// 数据添加时间的计数器
int counter_chart = 0;				// 绘图计数器

float F = 1.0;

qt_demo::qt_demo(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
	QFont font, font_tick;
	font.setPointSize(12);
	font.setWeight(50);
	font_tick.setPointSize(10);

	// 初始化绘图界面
	ui.widget_1->addGraph();					// 加入一条曲线
	ui.widget_1->yAxis->setRange(0, 10);		// 设置横纵坐标轴范围
	ui.widget_1->xAxis->setRange(0, axisTime);
	ui.widget_1->xAxis->setLabel(QStringLiteral("时间/s"));		// 设置横纵坐标轴标签
	ui.widget_1->yAxis->setLabel(QStringLiteral("压力/MPa"));
	ui.widget_1->xAxis->setLabelFont(font);
	ui.widget_1->xAxis->setTickLabelFont(font_tick);
	ui.widget_1->yAxis->setLabelFont(font);
	ui.widget_1->yAxis->setTickLabelFont(font_tick);

	ui.widget_2->addGraph();
	ui.widget_2->yAxis->setRange(0, 10);
	ui.widget_2->xAxis->setRange(0, axisTime);
	ui.widget_2->xAxis->setLabel(QStringLiteral("时间/s"));
	ui.widget_2->yAxis->setLabel(QStringLiteral("压力/MPa"));
	ui.widget_2->xAxis->setLabelFont(font);
	ui.widget_2->xAxis->setTickLabelFont(font_tick);
	ui.widget_2->yAxis->setLabelFont(font);
	ui.widget_2->yAxis->setTickLabelFont(font_tick);

	// 给开始和停止按钮贴图
	QIcon ico("./picture/start.png");
	ui.pushButton->setIcon(ico);
	ui.pushButton->setIconSize(QSize(50, 50));
	ui.pushButton->setFlat(true);

	QIcon ico_stop("./picture/stop.png");
	ui.pushButton_2->setIcon(ico_stop);
	ui.pushButton_2->setIconSize(QSize(50, 50));
	ui.pushButton_2->setFlat(true);


	// 创建多媒体定时器
	timer_chart = new PerformanceTimer(this);
	connect(timer_chart, SIGNAL(timeout()), this, SLOT(handleTimeout_chart()));	// 绑定槽函数

	timer_data = new PerformanceTimer(this);
	connect(timer_data, SIGNAL(timeout()), this, SLOT(handleTimeout_data()));
}

void qt_demo::on_pushButton_clicked()
{
	timer_data->start(timer_interval);
	timer_chart->start(timer_interval_chart);
	ui.pushButton->setEnabled(false);
}

void qt_demo::on_pushButton_2_clicked()
{
	timer_data->stop();
	timer_chart->stop();
	ui.pushButton->setEnabled(true);
}

void qt_demo::on_pushButton_3_clicked()
{
	F = F + 0.1;
}

void qt_demo::on_pushButton_4_clicked()
{
	F = F - 0.1;
}

void qt_demo::handleTimeout_data()
{
	float y1, y2;
	float T;
	T = timer_interval_f * counter;
	y1 = F * sin(T) + 1;
	y2 = F * sin(T) - 1;
	// 添加新的数据进入数据向量
	data_x1.append(T);
	data_y1.append(y1);
	data_x2.append(T);
	data_y2.append(y2);
	// 移除不在显示范围的数据
	if (data_x1.size() > dataSzie)
	{
		data_x1.pop_front();
		data_y1.pop_front();
		data_x2.pop_front();
		data_y2.pop_front();
		flag_axis = 1;
	}
	counter++;
}

void qt_demo::handleTimeout_chart()
{
	float axis_min;
	float axis_max;
	float y_max, y_min;

	// 自动按照y值最大值、最小值更新y轴范围
	y_max = (*(std::max_element(std::begin(data_y1), std::end(data_y1))));
	y_max = y_max>0 ? y_max*1.05 : y_max*0.95;
	y_min = (*(std::min_element(std::begin(data_y1), std::end(data_y1))));
	y_min = y_min>0 ? y_min*0.95 : y_min*1.05;
	ui.widget_1->yAxis->setRange(y_min, y_max);

	y_max = (*(std::max_element(std::begin(data_y2), std::end(data_y2))));
	y_max = y_max > 0 ? y_max * 1.05 : y_max * 0.95;
	y_min = (*(std::min_element(std::begin(data_y2), std::end(data_y2))));
	y_min = y_min > 0 ? y_min * 0.95 : y_min * 1.05;
	ui.widget_2->yAxis->setRange(y_min, y_max);

	// 更新x轴范围
	if (flag_axis == 1)
	{
		axis_min = counter_chart * timer_interval_chart / 1000.0 - axisTime;
		axis_max = counter_chart * timer_interval_chart / 1000.0;
		ui.widget_1->xAxis->setRange(axis_min, axis_max);
		ui.widget_2->xAxis->setRange(axis_min, axis_max);
	}
	// 刷新图表
	ui.widget_1->graph(0)->setData(data_x1, data_y1);
	ui.widget_1->replot();
	ui.widget_2->graph(0)->setData(data_x2, data_y2);
	ui.widget_2->replot();

	counter_chart++;
}

5. 程序的打包发布

  对于VS+QT的程序的打包发布网上也有许多教程,可以参考这个博文:
  VS+Qt应用开发-发布Release程序打包发布:https://blog.csdn.net/qq_36170958/article/details/108717691
  1、首先把原先的程序使用 release 进行编译,注意!!!原先在debug中添加的库也需要在release里面添加一遍,这个demo里面就只有“Qt5PrintSupport.lib”了。其实使用debug发布也是可以的,可以参考这个博文:vs 项目发布Debug和Release区别。
  2、打开cmd,进入Qt安装目录下对应编译器的bin文件路径,我的是 C:\Qt\Qt5.9.2\5.9.2\msvc2017_64\bin,
  3、输入命令 windeployqt (工程的release路径),例如 windeployqt C:\Users\135\source\repos\qt_demo\x64\Release 回车,即生成成功了,然后工程的release路径中的.exe文件就可以双击运行了。
  4、如果工程使用了一些额外的图片或者dll资源时,也需要把对应的资源复制到.exe所在的文件夹中。这个demo工程里面使用两个贴图,所以也需要将存放图片的picture文件夹复制到.exe文件夹中,不然图片无法正常显示。然后将这个文件夹压缩打包就算结束辽。

6. 相关资料

工程文件的百度云链接:https://pan.baidu.com/s/1aOUrMf93Lfouyl4twCLFcg
提取码:42ia

你可能感兴趣的:(qt,其他)