欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-10 16:13:28
...

内容简介

最近在用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 混合编程基本流程

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

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

在上图的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 混合编程基本流程

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

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

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

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

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

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

2.1 Qt项目创建与UI设计

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

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

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 混合编程基本流程

在此对话框完成后,在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 混合编程基本流程

编译后,还需要将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文件后无法运行的问题及其解决方法