最近因为被派了开发光纤解调仪软件开发的活,花了大概两个月的时间从零开始学QT写软件,总体完成的差不多之后在这里把遇到的困难总结一下。
一、动态链接库的调用
我手上的资料有公司之前很老的用MFC写的软件的源码,根据那个软件的源码来进行开发的。首先遇到的问题是无法调用光纤解调仪的软件,公司源码中给了.dll,.lib,.h文件,可是按照网上的方法一直调用不了dll里面的函数实现。一直在报错,后来看到网上说dll只能用MSVC编译器编译,之前在网上学习的时候教的都是用MinGW这个编译器,所以我就换成了MSVC,发现还是不行。又看到网上说可能是因为编译器位数的问题所以换成了32位的试了一下成功了。我在这个地方我卡了大概一个星期,人都快崩溃了。具体的调用方法可以参照网上的帖子,有很多介绍的已经很详细了。所以如果调用不了,可以考虑是编译器的问题。
二、子线程的运用
我的程序中要实现从光纤解调仪里面采集数据的实时显示和用不同采样频率储存的功能,这里就要运用子线程了,因为一直采集数据是用while循环来实现的,而在主线程中使用while循环的话程序会卡死,所以使用子线程的目的是建立一个随时可以中断的while循环。子线程的运用也是一个很麻烦的地方,我在B站是跟着播放量最高的那个QT视频学着做的,发现里面好像没有介绍子线程的运用。这里给大家介绍一下我试出来方法。
(1)通过重写QThread中的run()函数来实现
首先说明一下,QT官方是不推荐使用这种方法的,而且在我运用的过程中发现了一个很具体的缺陷就是用这用重写run()函数的方法只能开启一个子线程。在这里我还是介绍一下这种方法。
第一步先建一个头文件getInfo.h,创建一个类继承QThread
#ifndef GETINFO_H
#define GETINFO_H#include "mainwindow.h"class getInfo:public QThread//类getInfo继承自QThread,这表示它可以在单独的线程中执行操作
{Q_OBJECT //Q_OBJECT宏,允许类中使用信号和槽的机制
public:explicit getInfo(QObject *parent = nullptr);//构造函数以一个QWidget对象的指针为参数,父对象的默认值为nullptr,这样就可以创建带有或不带有父对象的MainWindowvoid run();//重写了QThread类的run()函数private:};#endif // GETINFO_H
第二步建一个源文件来写构造和run()函数
#include "getinfo.h"
#include "QThread"getInfo::getInfo(QObject *parent) : QThread(parent)
{//构造函数中可以初始化变量也可以空着
}void getInfo::run()
{while(m_isCanRun){//子线程中要实现的代码}
}
第三步通过按钮开启子线程或者在主线程中开启,通过子线程结束
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "getinfo.h"bool m_isCanRun = true;MainWindow::MainWindow(QWidget *parent)//构造函数: QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{getInfo * gt = new getInfo();//创建子线程对象gt->start();//启动子线程
}void MainWindow::on_pushButton_2_clicked()
{m_isCanRun = false;//结束循环gt = nullptr;
}
第四步可以发现我通过一个主线程中m_isCanRun的改变实现了子线程中循环的结束,一个类中参数的改变是怎样影响到另一个类呢?这里我用一个全局变量头文件实现,建了一个global_para.h头文件。记住还要在主线程cpp文件最上面声明一下才可以用,如第三步第五行。
#ifndef GLOBAL_PARA_H
#define GLOBAL_PARA_Hextern bool m_isCanRun;#endif // GLOBAL_PARA_H
(2)通过moveToThread()函数实现子线程
第一步建立一个头文件并且在其中创建一个类,非常普通的一个类。
#ifndef GETINFO_H
#define GETINFO_H#include <QApplication>class getInfo:public QObject
{Q_OBJECT //Q_OBJECT宏,允许类中使用信号和槽的机制
public:explicit getInfo(QObject *parent = nullptr);//构造函数以一个QWidget对象的指针为参数,父对象的默认值为nullptrvoid doWork();private:signals:public slots:void workFinished();};#endif // GETINFO_H
第二步建一个cpp文件具体写dowork()函数要实现的功能,这里同样通过新建一个全局变量类的方式来控制循环的关闭,上面已经说过了这里不再贴代码。dowork()函数结尾发出一个信号。
#include "getInfo.h"getInfo::getInfo(QObject *parent)
{m_isGetInfoCanRun = true;
}void getInfo::doWork()
{while (m_isGetInfoCanRun){//想实现的功能相应的代码}emit workFinished();
}
第三步在主线程头文件中引入”QTread“,源文件中声明这个类和一个子线程,具体代码和注释如下
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);//初始化读取数据的子线程QThread* thread = new QThread;//创建一个子线程getInfo* getinfo = new getInfo;//创建刚刚自己建立的类getinfo->moveToThread(thread);//将这个类放入子线程中QObject::connect(thread, &QThread::started, getinfo, &getInfo::doWork);//子线程开启时调用新建类中的dowork函数QObject::connect(getinfo, &getInfo::workFinished, thread, &QThread::quit);//类中的一个信号与子线程的退出通过信号与槽连接在一起QObject::connect(thread, &QThread::finished, getinfo, &getInfo::deleteLater);//子线程结束,类删除释放空间QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);//子线程结束,子线程删除释放空间thread->start();}
三 、代码实现过程中常见的问题
(1)报错C2001是因为用了中文字符
解决方法是在头文件中加
#pragma execution_character_set("utf-8")
如果还是解决不了就是因为编译器是MSCV的问题,要在.pro文件中加入
msvc {QMAKE_CFLAGS += /utf-8QMAKE_CXXFLAGS += /utf-8}
(2)报错LNK2019就是写错了,不符合语法但是编译的时候检查不出来,可以根据报错信息提示查找相应位置。
(3)子线程中不能使用messgebox和创建文件夹
(4)多线程同时调用一个函数程序大概率崩溃
(5)不能直接通过信号与槽来开启子线程
四、总结
总的来说整个项目完成下来还是学到了很多东西,过程中遇到了很多困难网上都找不到解决问题,只能靠自己摸索,要不是这个任务实在是推不掉早就放弃了。不得不说chatgpt和new bing真的是生产力工具,把我的编程效率提升了50%以上,省去了我许多在无用且满是广告的搜索引擎中屎里淘金的时间。最后放一张最终效果图,大家共勉!