1. 内容简介
师兄留下了一些matlab编写的程序,老师希望用Qt绘制界面,matlab完善算法功能,两者混合编程。任务布置下来,对于编程能力薄弱的我是一个极大的挑战,参考网上很多大佬的文档,初步实现了一个小小的自测试混合编程,中间也遇到了很多问题,在此记录一下,给自己警醒也希望可以给后来者提供一些帮助。
2. 软件环境
2.1QT编译器
软件环境很重要,不然很容易遇到各种问题,很多大佬实现混合编程时在QT中使用的是MSVC编译器,考虑到我这边有一部分Qt程序已经使用MinGW编写好了,不想再切换成MSVC编译修改,所以决定使用Qt5.10.1的MinGW编译器(32位)。
2.2Matlab2015b(32位)
2.3Visual studio 2015
3.主要流程
3.1如何在Matlab中将m文件编译为C++语言的DLL文件
3.2如何在Qt项目中加入自定义DLL相关的LIB文件,以及MATLAB相关的LIB文件和H文件搜索路径
3.3MATLAB运行时DLL文件所在路径,及系统相关的环境变量设置
3.4如何在Qt中调用自定义DLL中的函数,如何通过mwArray类传递输入输出参数
4.具体步骤
4.1Matlab中m文件编译为C++语言的DLL文件
在MATLAB中编写一个简单的myFunc.m,其代码如下:
function C=myFunc(A,B)
C=A+B;
end
在编译m文件之前,需要保证MATLABCompiler已经设置好了编译器,在MATLAB命令行窗口中使用mbuild –setup 设置使用C编译器。
mbuild -setup
MBUILD 配置为使用 ‘Microsoft Visual C++ 2015 ©’ 以进行 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,出现如下的对话框
“ApplicationCompiler”用于将m文件编译为exe文件直接运行,“Library Compiler”用于将m文件编译为DLL、COM组件等形式。我们要生成DLL文件,所以选择“Library Compiler”。
在上图的MATLAB Compiler窗口中,“TYPE”部分选择C++ shared Library,“EXPORTED FUNCTIONS”是需要导出的m文件,点击右侧的“Add”按钮选择编写的文件myFunc.m。右侧是MATLAB运行时库的安装打包方式,在本机上测试可选择“Runtime downloaded from web”。
保存项目为myFunc.prj,然后点击“Package”按钮进行编译和打包。
打包完成后,在项目文件myFunc.prj的目录下生成与项目同名的子目录,即\myFunc,该目录下有3个文件夹。
muFunc\for_redistribution_files_only目录下是编译生成的.dll 、.lib和.h文件,其中.lib和.h文件是在Qt项目编译时需要用到的,.dll文件是程序运行时需要用到的。
4.2 Qt 5.10.1项目中使用myFunc.dll
4.2.1Qt项目创建
创建一个QtWidget Application项目test01,类名是Test,主窗口基于QWidget。
4.2.2库文件myFunc.lib 的加入
在项目目录下新建一个include目录,将前面编译生成的myFunc\for_redistribution_files_only目录下的myFunc.lib,myFunc.h文件复制到此目录下。
在Qt Creator里,myFunc项目节点上单击右键,选择“Add Library…”,在出现的向导中首先选择“External Library”,后续出现下图的界面,选择库myFunc\include\目录下的库文件matLib,其他选择如图。
在此对话框完成后,在test01.pro文件中会增加如下的几行:
win32: LIBS += -L$$PWD/include/ -lmyFunc
INCLUDEPATH += $$PWD/include
DEPENDPATH += $$PWD/include
4.2.3Matlab其他依赖库和头文件搜索路径的加入
除了自己编译生成的DLL相关的.lib文件和头文件,要编译此Qt项目,还需要用到MATLAB的几个.lib文件和.h文件。
我的电脑上,MATLAB2015b安装在C:\Program Files (x86)\MATLAB\R2015b目录下,在test01.pro文件中需要加入如下的设置:
INCLUDEPATH += $$quote(C:/Program Files (x86)/MATLAB/R2015b/extern/include)
DEPENDPATH += $$quote(C:/Program Files (x86)/MATLAB/R2015b/extern/include)
LIBS+= -L$$quote(C:/Program Files (x86)/MATLAB/R2015b/extern/lib/win32/microsoft) -llibmx
LIBS+= -L$$quote(C:/Program Files (x86)/MATLAB/R2015b/extern/lib/win32/microsoft) -llibmat
LIBS+= -L$$quote(C:/Program Files (x86)/MATLAB/R2015b/extern/lib/win32/microsoft) -lmclmcr
LIBS+= -L$$quote(C:/Program Files (x86)/MATLAB/R2015b/extern/lib/win32/microsoft) -lmclmcrrt
除此之外,由于使用的时MinGW的编译器,还需要在头文件中添加
DEFINES += __MW_STDINT_H__
这个东西是针对matlab的mclmcr.h头文件中第170行到第190行中编译器的一些宏的设定,一般来说如果是mingW编译器我们一般写+= MING_W,但是这个头文件中并没有给出mingW编译器的情况,所以一般选择加上上面这行代码,选择一个编译器的类型。
4.2.4系统环境变量的设置
额外加入的一些库实际上对应于MATLAB运行时的一些DLL文件,这些运行时文件主要在以下几个目录下,所以要保证这些目录添加到了Windows的环境变量PATH里。
C:\Program Files (x86)\MATLAB\R2015b\runtime\win32;
C:\Program Files (x86)\MATLAB\R2015b\bin
C:\Program Files (x86)\MATLAB\R2015b\extern\lib\win32\microsoft
D:\Qt\5.10.1\msvc2015\bin
4.2.5编写使用DLL内函数myFunc()的代码
QLibrary myLib("myFunc.dll");
typedef bool MW_CALL_CONV(*Fun)(int,class mwArray const &,class mwArray const &,class mwArray const &);
Fun myFunc = (Fun) myLib.resolve("?myFunc@@YAXHAAVmwArray@@ABV1@1@Z");
if(!myFuncInitialize())
{
qDebug()<<"could not initialize libmyfuncdll\n";
exit(0);
}
QString result;
double para1,para2;
para1=ui->lineEdit->text().toDouble();
para2=ui->lineEdit_2->text().toDouble();
mwArray a(1,1,mxDOUBLE_CLASS);
mwArray b(1,1,mxDOUBLE_CLASS);
mwArray c(1,1,mxDOUBLE_CLASS);
a(1,1)=para1;
b(1,1)=para2;
myFunc(1,c,a,b);
result=c.ToString();
ui->lineEdit_3->setText(result);
还要记得在test.h头文件中添加
#include <QLibrary>
#include "myFunc.h"
#include <QDebug>
最后,将myFunc.dll文件复制粘贴到编译生成的debug文件夹下,到此为止,所有准备工作都完成了。
对于
QLibrary myLib("myFunc.dll");
typedef bool MW_CALL_CONV(*Fun)(int,class mwArray const &,class mwArray const &);
Fun myFunc = (Fun) myLib.resolve("?myFunc@@YAXHAAVmwArray@@ABV1@1@Z");
需要说明的是由于matlab使用vs的msvc编译器生成的dll文件,生成后我们使用mingW调用,但生成的头文件中指定各种编译器对应的情况,但唯独没有MinGW,这就导致了在使用MinGW编译器时我们使用了:
DEFINES += MW_STDINT_H
使用msvc编译器调用matlab生成的dll时,编译器会自动识别函数名,因此也不需要使用resolve函数。
4.2.6编译和运行
在lineEdit中输入任意数字,点击计算按钮,就可以看到结果了!
5 特别提醒
刚开始向别人学习按照步骤操作的时候,遇到了一些问题和注意事项:
1、Qt的编译器一定要注意,是MSVC还是MinGW很重要,不同的编译器需要添加的代码是不一样的,如果Qt选择MSVC64位的,电脑安装的VS也要是64位的,并且Matlab的版本年份一般要高于VS的版本年份,比如选择matlab2017b和VS2015两个64位进行搭配,避免在mbuild-setup时出现matlab无法识别VS的情况。
2、编译没有错误,运行的时候出现强制结束程序。可能是如下两个原因:
第一个原因:请参考https://blog.csdn.net/hongandyi/article/details/79427078
第二个原因:请下载一个的Dependency Walker 2.2查看dll文件,resolve的文件名是否一致,程序强制退出,这个问题卡了我好久,后来发现下面的@1@Z,我打成了@@Z。改正过来,程序正常运行。
QLibrary myLib(“myFunc.dll”);
typedef bool MW_CALL_CONV(*Fun)(int,class mwArray const &,class mwArray const &,class mwArray const &);
Fun myFunc = (Fun) myLib.resolve("?myFunc@@YAXHAAVmwArray@@ABV1@1@Z");
建议参考:https://blog.csdn.net/Justice132/article/details/83830151
6 代码链接
https://download.csdn.net/download/China_Rocky/12205984
7 总结
Qt调用matlab动态链接库
好处:
1、qt做界面,matlab做算法,二者相得益彰
2、相对于将算法C++化,这样做当然节约时间和成本
缺点:
程序执行速度慢,容易出错,步骤比较繁琐
补充:后来遇到一个问题,就是matlab中有多个m文件,m1文件是主程序文件,它的函数参数是其他m文件的函数,而不是矩阵或数组,在调用过程中编译没有出错,运行时程序崩溃。如果遇到这种情况,是因为参数类型不匹配的关系,声明中的参数是矩阵类型,而代入的实参是函数,只需要把matlab程序中的函数参数去掉,重新生成dll文件并替换掉include文件夹中原来的dll文件,然后在Qt中直接调用即可。
谢谢链接中诸多大佬的总结,通过学习,避免了很多问题,但是自己在实现的过程中,也遇到了各种各样的问题,看到网上主要是QtMSVC编译下matlab混合编程比较多,而MinGW编译下的混合编程较少,步骤流程有欠完善,所以写了这篇文章,也是我写的第一篇博客,希望可以帮助到有需要的朋友!不足之处,还请多多指教!