Qt 5.9 VS2017 与 Matlab 2018b 混合编程基本流程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qing666888/article/details/85157325

内容简介

最近在用Qt编写一个数据处理软件,涉及到很多信号处理的算法,这些都是很成熟的算法,本着找轮子的思路找可用的源程序。GitHub上相关的源代码倒是很多,但是要么缺少实例和文档、要么在Windows下编译出现问题,即便是较大的完整的源程序库,学习其使用也是个比较耗时间的事情。找来找去,最后还是决定用Matlab与Qt混合编程。

本文就介绍使用Qt 5.9 VS2017和Matlab 2018b进行混合编程的基本流程,主要包括:

  1. 如何在Matlab中将m文件编译为C++语言的DLL文件。
  2. 如何在Qt项目中加入自定义DLL相关的LIB文件,以及MATLAB相关的LIB文件和H文件搜索路径MATLAB运行时DLL文件所在路径,及系统相关的环境变量设置
  3. 如何在Qt中调用自定义DLL中的函数,如何通过mwArray类传递输入输出参数

一、Matlab中m文件编译为C++语言的DLL文件

在MATLAB中编写一个简单的矩阵卷积函数以及画图调用matAdd.m,其代码如下:

function  [C]= matAdd(n,B)
A = ones(n);
C = conv2(A,B);
mesh(C);
end

在编译m文件之前,需要保证MATLABCompiler已经设置好了编译器,在MATLAB命令行窗口中使用mbuild -setup设置使用C++编译器,默认是c语言编译器。

>> mbuild -setup
MBUILD 配置为使用 'Microsoft Visual C++ 2017 (C)' 以进行 C 语言编译。

要选择不同的语言,请从以下选项中选择一种命令:
 mex -setup C++ -client MBUILD 
 mex -setup FORTRAN -client MBUILD

要选择C++编译,请从以下选项中选择一种命令,鼠标点击对应的指令可能切换:

 mex -setup C++ -client MBUILD 
 mex -setup FORTRAN -client MBUILD

要使用C++编译器,也可以用命令 mbuild –setup C++ 设置

>> mbuild -setup C++
MBUILD 配置为使用 'Microsoft Visual C++ 2017' 以进行 C++ 语言编译。

我的电脑上安装了Visual Studio 2017,MATLAB会自己查找可用的编译器,若没有自动找到编译器,可能版本兼容问题或其他问题,可搜索相关资料解决问题。

在命令行中输入 deploytool,出现如下的对话框
在这里插入图片描述

“ApplicationCompiler”用于将m文件编译为exe文件直接运行,“Library Compiler”用于将m文件编译为DLL、COM组件等形式。我们要生成DLL文件,所以选择“Library Compiler”。
在这里插入图片描述

在上图的MATLAB Compiler窗口中,“TYPE”部分选择C++ shared Library,“EXPORTED FUNCTIONS”是需要导出的m文件,点击右侧的“Add”按钮选择编写的文件matAdd.m。右侧是MATLAB运行时库的安装打包方式,在本机上测试可选择“Runtime downloaded from web”。

保存项目为matAdd.prj,然后点击“Package”按钮进行编译和打包。

打包完成后,在项目文件matAdd.prj的目录下生成与项目同名的子目录,即\matAdd,该目录下有3个文件夹。
在这里插入图片描述

matAdd\for_redistribution目录下是文件MyAppInstaller_web.exe,这个是MATLAB运行时库的和本项目的安装文件,运行后可从网上下载MATLAB的运行时库进行安装,还会安装本项目生成的DLL、LIB和h文件。
在这里插入图片描述
matAdd\for_redistribution_files_only目录下是编译生成的.dll 、.lib和.h文件,其中.lib和.h文件是在Qt项目编译时需要用到的,.dll文件是程序运行时需要用到的。

在这里插入图片描述
matAdd\for_testing 目录下是用于测试的,但是dll不是可执行文件,不能直接进行进行测试。

二、Qt 5.9 VS2017项目中使用matAdd.dll

  1. Qt项目创建与UI设计
    创建一个QtWidget Application项目testAdd,主窗口基于QWidget。程序运行时如下图。
    在这里插入图片描述
  2. 库文件matAdd.lib 的加入
    在项目目录下新建一个include目录,将前面编译生成的matAdd\for_redistribution_files_only目录下的matAdd.lib和matAdd.h文件复制到此目录下。然后添加VS项目包含目录,包含了自己编译的matlab库头文件目录,matlab的依赖头文件目录,如下图:
.\include
D:\MATLAB\R2018b\extern\include
D:\MATLAB\R2018b\extern\include\win64

在这里插入图片描述

  1. 自己编译的库文件以及Matlab依赖库文件目录
.\include
D:\MATLAB\R2018b\extern\lib\win64\microsoft

在这里插入图片描述

  1. 添加项目属性附加依赖项,及所依赖的库文件名称。
libmex.lib
libmx.lib
libmat.lib
libeng.lib
mclmcr.lib
mclmcrrt.lib
matAdd.lib

在这里插入图片描述

  1. 系统环境变量的设置
    额外加入的一些库实际上对应于MATLAB运行时的一些DLL文件,这些运行时文件主要在以下几个目录下,所以要保证这些目录添加到了Windows的环境变量PATH里。默认安装matlab会自动增加了这些环境变量的。
    D:\MATLAB\R2018b\runtime\win64;
    D:\MATLAB\R2018b\bin;

若是程序发布到没有安装MATLAB的电脑上,需要用Matlab Compiler编译生成的安装包,本例就是 matAdd\for_redistribution目录下的MyAppInstaller_web.exe。

若只是要独立安装MATLAB运行时库,在MATLAB 命令行里输入 mcrinstaller可以得到离线的MATLAB运行时库安装文件的路径。

>> mcrinstaller
ans ='D:\MATLAB2017b\toolbox\compiler\deploy\win64\MCRInstaller.exe'
  1. 编写使用DLL内函数matAdd()的代码
    在Qt Creator里,为widget.ui上的按钮编写如下的代码。
void Widget::on_btnAdd_clicked()
{
   if (!matAddInitialize()) //DLL 初始化
   {
      ui->textEdit->setText("DLL initialization failed");
      return;
   }


   double   vectA[] = { 4 }; //向量A
   mwArray matrixA(1,1,mxDOUBLE_CLASS, mxREAL);//定义数组
   matrixA.SetData(vectA,1); //将C++ 的一维数组存储到 MATLAB的二维数组

   double   vectB[] = { 5,6,7};  //向量B
   mwArray matrixB(1,3,mxDOUBLE_CLASS, mxREAL);//定义数组
   matrixB.SetData(vectB,3); //

//保存计算结果
   mwArray matrixC(4,6,mxDOUBLE_CLASS, mxREAL);//定义数组,double类型
   
   int nargout=1;//输出变量个数
   matAdd(nargout,matrixC,matrixA,matrixB);//

//读取结果

//   int dim=1; //按照维度一维数组读出matrixC  
//   double   av=matrixC.Get(dim,1);
//   double   bv=matrixC.Get(dim,2);
//   double   cv=matrixC.Get(dim,3);

   ui->textEdit->setText("");
   int dim=2;  //按照维度二维数组读出matrixC
   for (int i = 1; i < 5; i++) //索引从1开始。不是从0开始
   {
	   double   av = matrixC.Get(dim, i, 1);
	   double   bv = matrixC.Get(dim, i, 2);
	   double   cv = matrixC.Get(dim, i, 3);
	   double   dv = matrixC.Get(dim, i, 4);
	   double   ev = matrixC.Get(dim, i, 5);
	   double   fv = matrixC.Get(dim, i, 6);
	   QString  str = QString::asprintf("%.0f, %.0f, %.0f, %.0f, %.0f, %.0f", av, bv, cv, dv, ev, fv);
	   ui->textEdit->append(str);
   }
}

  1. DLL的初始化
    在使用matAdd.dll里的函数之前,必须先初始化。matAddInitialize()是matAdd.h文件里面的库初始化函数。

  2. matAdd函数的输入输出参数
    mwArray是MATLAB的数组类,MATLAB编译生成的DLL的接口函数的参数都是采用mwArray类型,例如matAdd.h文件中的函数matAdd()的函数原型为:

extern LIB_matAdd_CPP_API void MW_CALL_CONV matAdd(int nargout, mwArray& C, const mwArray& A, const mwArray& B);

不去管那一堆的前缀,函数的4个参数:
int nargout 是输出参数个数,表示后面紧跟着的nargout个变量是输入参数,由matAdd.m文件可知,matAdd()有1个输出参数C
mwArray &C 输出参数,对应于matAdd.m文件里的输出参数C
mwArray &A, const mwArray &B 两个输入参数,对应于matAdd.m文件里的两个输入参数A 和B

由m文件的函数编译生成的C++函数都是这样的参数定义顺序,格式比较统一。

三、mwArray类的使用

Qt 程序要调用matAdd.dll里的函数matAdd(),就需要按照函数原型传递输入和输出参数,主要是mwArray类的使用。例输入参数数组matrixA的定义和传递数据的代码是:

mwArray matrixA(rowCnt,colCnt,mxDOUBLE_CLASS, mxREAL); 
matrixA.SetData(vectA,elementCnt); 

定义mwArray类型的变量matrixA时,利用构造函数传递了数组的行数、列数、元素类型、实数或复数类型
语句matrixA.SetData(vectA,elementCnt)将 一维向量vectA的数据内容赋值给matrixA,元素个数为elementCnt,等于行数*列数。即使matrixA是一个二维数组,使用SetData赋值时,输入vectA也必须是一个向量,是逐列存储的。

同样赋值matrixB,定义返回矩阵matrixC。
调用matAdd()函数进行计算的代码是:

int nargout=1;//输出变量个数
matAdd(nargout,matrixC,matrixA,matrixB);//C=A+B

得到matrixC之后,需要读取出matrixC的数据内容进行显示。

读取mwArray的某个元素的值用mwArray::Get()函数。函数的第一个变量表示数组维数,维数为1表示数组是向量,获取元素只需一个索引即可,matrixC是一维向量,可以用下面的代码读取数据:

int dim=1; //按照向量读出matrixC
double   av=matrixC.Get(dim,1); //第1个元素
double   bv=matrixC.Get(dim,2); //第2个元素
double   cv=matrixC.Get(dim,3); //第3个元素

维数为2表示数组是二维数组,获取一个元素需要行号和列号。matrixC是向量,也可以看做只有1行的二维数组,所以下面的代码也是可行的

int dim=2; //按照二维数组读出matrixC
double   av=matrixC.Get(dim,1,1); //第1行,第1列
double   bv=matrixC.Get(dim,1,2);// 第1行,第2列
double   cv=matrixC.Get(dim,1,3); // 第1行,第3列

注意:mwArray数组的下标都是从1开始的,与C/C++的数组元素下标从0开始不同。

  • 编译和运行
    代码无语法错误后编译并运行,但是运行时闪退。这是Matlab2017b的一个少见的错误,在本人另一篇博文《Matlab 2017b编译成exe或DLL文件后无法运行的问题及其解决方法》里有描述和解决办法。

    注意:在Qt Creator里编译时,应选择MSVC2017 64bit编译器,因为在Matlab里是用MSVC 2017 64位编译生成的DLL文件,若在Qt里使用其他的32位编译器将无法正常运行。

    编译后,还需要将3个DLL文件复制到编译后的\release目录下,才可以正常运行。其中,matAdd.dll就是我们在MATLAB里编译生成的DLL。另外的SSLEAY32.DLL和LIBEAY32.DLL是从Matlab的安装目录 \bin\win64下复制过来的。

  • 一切正常后,运行时点击“卷积计算”按钮,就可以得到计算结果,以及绘图。

  • 按F5调试运行可能会弹出引发异常类型中断弹窗,把弹窗上的勾去掉。再F5运行即可正常调试运行。

本文Qt 5.9 +VS2017 测试项目完整源码下载地址
https://download.csdn.net/download/qing666888/10866757

注:按上述文章修改成自己matlab安装目录才可正常运行

参考文章:
https://blog.csdn.net/hongandyi/article/details/79433623
https://blog.csdn.net/qq_20515461/article/details/84850636

猜你喜欢

转载自blog.csdn.net/qing666888/article/details/85157325