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

内容简介

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

本文就介绍使用Qt 5.9和Matlab 2017b进行混合编程的基本流程,主要包括:
  • 如何在Matlab中将m文件编译为C++语言的DLL文件
  • 如何在Qt项目中加入自定义DLL相关的LIB文件,以及MATLAB相关的LIB文件和H文件搜索路径
  • MATLAB运行时DLL文件所在路径,及系统相关的环境变量设置
  • 如何在Qt中调用自定义DLL中的函数,如何通过mwArray类传递输入输出参数

1. Matlab中m文件编译为C++语言的DLL文件

在MATLAB中编写一个简单的两个矩阵相加的函数matAdd.m,其代码如下:


function  [C]= matAdd(A,B)
% C= matAdd(A,B)
%  两个矩阵相加
C=A+B;
end

在编译m文件之前,需要保证MATLABCompiler已经设置好了编译器,在MATLAB命令行窗口中使用mbuild –setup 设置使用C编译器。

>> mbuild -setup
MBUILD 配置为使用 'Microsoft Visual C++ 2015 (C)' 以进行 C 语言编译。
	
要选择不同的语言,请从以下选项中选择一种命令:
 mex -setup C++ -client MBUILD 
 mex -setup FORTRAN -client MBUILD

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

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

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

在命令行中输入 deploytool,出现如下的对话框

Qt 5.9 与 Matlab 2017b 混合编程基本流程_第1张图片

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

Qt 5.9 与 Matlab 2017b 混合编程基本流程_第2张图片

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

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

打包完成后,在项目文件matAdd.prj的目录下生成与项目同名的子目录,即\matAdd,该目录下有3个文件夹。

Qt 5.9 与 Matlab 2017b 混合编程基本流程_第3张图片

matAdd\for_redistribution目录下是文件MyAppInstaller_web.exe,这个是MATLAB运行时库的和本项目的安装文件,运行后可从网上下载MATLAB的运行时库进行安装,还会安装本项目生成的DLL、LIB和h文件。

Qt 5.9 与 Matlab 2017b 混合编程基本流程_第4张图片

matAdd\for_redistribution_files_only目录下是编译生成的.dll 、.lib和.h文件,其中.lib和.h文件是在Qt项目编译时需要用到的,.dll文件是程序运行时需要用到的。

Qt 5.9 与 Matlab 2017b 混合编程基本流程_第5张图片

matAdd\for_testing  目录下是用于测试的,但是dll不是可执行文件,不能直接进行进行测试。

2.  Qt 5.9项目中使用matAdd.dll

2.1 Qt项目创建与UI设计

创建一个QtWidget Application项目testAdd,主窗口基于QWidget。程序运行时如下图。

Qt 5.9 与 Matlab 2017b 混合编程基本流程_第6张图片

2.2  库文件matAdd.lib 的加入

在项目目录下新建一个include目录,将前面编译生成的matAdd\for_redistribution_files_only目录下的matAdd.lib和matAdd.h文件复制到此目录下。

在Qt Creator里,testAdd项目节点上单击右键,选择“Add Library...”,在出现的向导中首先选择“External Library”,后续出现下图的界面,选择库testAdd\include\目录下的库文件matLib,其他选择如图。

Qt 5.9 与 Matlab 2017b 混合编程基本流程_第7张图片

在此对话框完成后,在testAdd.pro文件中会增加如下的几行:


win32: LIBS += -L$$PWD/include/ -lmatAdd
	
INCLUDEPATH += $$PWD/include
DEPENDPATH += $$PWD/include

为了方便查看matAdd.h的内容,还可以将matAdd.h文件添加到项目中,但是要注意不要修改matAdd.h文件的内容。

2.3  Matlab其他依赖库和头文件搜索路径的加入

除了自己编译生成的DLL相关的.lib文件和头文件,要编译此Qt项目,还需要用到MATLAB的几个.lib文件和.h文件。

我的电脑上,MATLAB2017b安装在D:\ MATLAB2017b目录下,在testAdd.pro文件中需要加入如下的设置:


# .h文件搜索路径
INCLUDEPATH += D:/MATLAB2017b/extern/include
INCLUDEPATH += D:/MATLAB2017b/extern/include/Win64
	
# 用到的MATLAB 的.lib库文件 及其搜索路径
INCLUDEPATH += D:/MATLAB2017b/extern/lib/win64/microsoft
DEPENDPATH += D:/MATLAB2017b/extern/lib/win64/microsoft
	
win32: LIBS += -LD:/MATLAB2017b/extern/lib/win64/microsoft/ -llibmex
win32: LIBS += -LD:/MATLAB2017b/extern/lib/win64/microsoft/ -llibmx
win32: LIBS += -LD:/MATLAB2017b/extern/lib/win64/microsoft/ -llibmat
win32: LIBS += -LD:/MATLAB2017b/extern/lib/win64/microsoft/ -llibeng
win32: LIBS += -LD:/MATLAB2017b/extern/lib/win64/microsoft/ -lmclmcr
win32: LIBS += -LD:/MATLAB2017b/extern/lib/win64/microsoft/ -lmclmcrrt

注意,若路径名称中含有空格,需要使用quote(),如

     #INCLUDEPATH+=$$quote(D:/MATLAB2017b/extern/include)

2.4 系统环境变量的设置

额外加入的一些库实际上对应于MATLAB运行时的一些DLL文件,这些运行时文件主要在以下几个目录下,所以要保证这些目录添加到了Windows的环境变量PATH里。

D:\MATLAB2017b\runtime\win64;
D:\MATLAB2017b\bin\win64;

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

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

>> mcrinstaller
ans ='D:\MATLAB2017b\toolbox\compiler\deploy\win64\MCRInstaller.exe'

2.5  编写使用DLL内函数matAdd()的代码

在Qt Creator里,为widget.ui上的按钮编写如下的代码。


void Widget::on_btnAdd_clicked()
{
   if (!matAddInitialize()) //DLL 初始化
   {
      ui->lineEdit_C->setText("DLL initialization failed");
      return;
   }

   double   vectA[]={1,2,3}; //向量A
   double   vectB[]={5,6,7};  //向量B

   int   rowCnt=1;   //行数
   int   colCnt=3;   //列数
   int   elementCnt=3; //元素个数

   mwArray matrixA(rowCnt,colCnt,mxDOUBLE_CLASS, mxREAL);//定义数组
   matrixA.SetData(vectA,elementCnt); //将C++ 的一维数组存储到 MATLAB的二维数组

   mwArray matrixB(rowCnt,colCnt,mxDOUBLE_CLASS, mxREAL);//定义数组
   matrixB.SetData(vectB,elementCnt); //

//计算
   mwArray matrixC(rowCnt,colCnt,mxDOUBLE_CLASS, mxREAL);//定义数组,double类型
   int nargout=1;//输出变量个数
   matAdd(nargout,matrixC,matrixA,matrixB);//C=A+B

//读取结果
   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列

//   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个元素

   QString  str=QString::asprintf("%.0f, %.0f, %.0f", av,bv,cv); 
   ui->lineEdit_C->setText(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++函数都是这样的参数定义顺序,格式比较统一。

(3)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开始不同。

2.6  编译和运行

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

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

Qt 5.9 与 Matlab 2017b 混合编程基本流程_第8张图片

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

一切正常后,运行时点击“matAdd()计算”按钮,就可以得到计算结果。


本文Qt 5.9 测试项目完整源码下载地址

    http://download.csdn.net/download/hongandyi/10268476

或    https://pan.baidu.com/s/1brdfC31


参考:

Matlab 2017b编译成exe或DLL文件后无法运行的问题及其解决方法












你可能感兴趣的:(Matlab,Qt)