Qt C++ Matlab 混合编程测试使用文档
Qt C++ Matlab 混合编程测试使用文档
环境版本
- Qt:5.9.0
- MSVC 2017 64bit 编译器
- Matlab:R2019b (64bit)
- Windows 11 64位
开发步骤
本文就介绍使用 Qt 5.9 和 Matlab 进行混合编程的基本流程,主要包括:
- 如何在Matlab中将m文件编译为C++语言的DLL文件
- 如何在Qt项目中加入自定义DLL相关的LIB文件,以及MATLAB相关的LIB文件和H文件搜索路径
- 如何在Qt中调用自定义DLL中的函数,如何通过mwArray类传递输入输出参数
Matlab 动态链接库的生成
Matlab 函数的编写
在 Matlab 中编写函数,这里编者以两个矩阵相加函数 matAdd
作为测试函数。并保存为 matAdd.m 文件。
function C= matAdd(A,B)
% C= matAdd(A,B), 两个矩阵相加
C=A+B;
end
注意:需要多个 Matlab 函数时,需要将各个文件保存到不同的Matlab文件中,在下面的步骤中统一集合成相关动态链接库和头文件。
设置MATLABCompiler 编译器
在 Matlab 命令行输入下面的内容。
>> mbuild -setup C++
MBUILD 配置为使用 'Microsoft Visual C++ 2017' 以进行 C++ 语言编译。
注意:编者采用 'Microsoft Visual C++ 2017' 进行 C++ 编译,和 Qt 编译器保持一致,暂不清楚不同编译器是否会对结果造成影响。电脑上安装了Visual Studio 2017,MATLAB会自己查找可用的编译器。
Library Compiler
在 Matlab 命令行输入下面的内容:
>> deploytool
并选择 ‘Library Compiler’ :“ApplicationCompiler”用于将m文件编译为exe文件直接运行,“Library Compiler”用于将m文件编译为DLL、COM组件等形式。我们要生成DLL文件,所以选择“Library Compiler”。
‘TYPE’ 部分选择 C++ Shared Library ,“EXPORTED FUNCTIONS” 选择待添加的文件(这里可以添加多个函数文件)。右侧是MATLAB运行时库的安装打包方式,在本机上测试可选择“Runtime downloaded from web”。添加完毕后点击‘Package’ 进行编译和打包。
Qt 调用环境配置
打包完毕后,项目文件 matAdd.prj 目录下生成与其项目同名的子目录,即 \matAdd,该目录下有 3 个文件夹。我们主要需要 matAdd\for_redistribution_files_only 目录下是编译生成的.dll 、.lib和.h文件。其中.lib和.h文件是在Qt项目编译时需要用到的,.dll文件是程序运行时需要用到的。
这三个文件,需要复制到 Qt 的 pro 目录下。
matAdd.lib 文件的加入
打开 pro 库,右键选择添加库,选择添加外部库,加入目录中的 matAdd.lib 文件,其他选择如图所示:
在 pro 文件中,将会出现下面几段代码:
win32: LIBS += -L$$PWD/./ -lmatAdd
INCLUDEPATH += $$PWD/.
DEPENDPATH += $$PWD/.
win32:!win32-g++: PRE_TARGETDEPS += $$PWD/./matAdd.lib
else:win32-g++: PRE_TARGETDEPS += $$PWD/./libmatAdd.a
为了方便查看matAdd.h的内容,还可以将matAdd.h文件添加到项目中,但是要注意不要修改matAdd.h文件的内容。
Matlab 依赖库和头文件搜索路径的加入。
除了自己编译生成的DLL相关的.lib文件和头文件,要编译此Qt项目,还需要用到MATLAB的几个.lib文件和.h文件。
我的电脑上,MATLAB2019b安装在 D:/Program/matlab2019 目录下,在 testAdd.pro 文件中需要加入如下的设置:
INCLUDEPATH += 'D:/Program/matlab2019/matlab/extern/include'
INCLUDEPATH += 'D:/Program/matlab2019/matlab/extern/include/win64'
win32: LIBS += -L'D:/Program/matlab2019/matlab/extern/lib/win64/microsoft/' -llibmex
win32: LIBS += -L'D:/Program/matlab2019/matlab/extern/lib/win64/microsoft/' -llibmx
win32: LIBS += -L'D:/Program/matlab2019/matlab/extern/lib/win64/microsoft/' -llibmat
win32: LIBS += -L'D:/Program/matlab2019/matlab/extern/lib/win64/microsoft/' -llibeng
win32: LIBS += -L'D:/Program/matlab2019/matlab/extern/lib/win64/microsoft/' -lmclmcr
win32: LIBS += -L'D:/Program/matlab2019/matlab/extern/lib/win64/microsoft/' -lmclmcrrt
INCLUDEPATH += 'D:/Program/matlab2019/matlab/extern/lib/win64/microsoft'
DEPENDPATH += 'D:/Program/matlab2019/matlab/extern/lib/win64/microsoft'
INCLUDEPATH += 'D:/Program/matlab2019/matlab/extern/lib/win64'
DEPENDPATH += 'D:/Program/matlab2019/matlab/extern/lib/win64'
注意:注意,若路径名称中含有空格,需要使用quote(),如
#INCLUDEPATH+=$$quote(D:/MATLAB2017b/extern/include)
系统环境变量的配置
若是程序发布到没有安装MATLAB的电脑上,需要用Matlab Compiler编译生成的安装包,本例就是 matAdd\for_redistribution目录下的MyAppInstaller_web.exe。
若只是要独立安装MATLAB运行时库,在MATLAB 命令行里输入 mcrinstaller可以得到离线的MATLAB运行时库安装文件的路径。
>> mcrinstaller
更多相关内容见:在没有安装MATLAB的电脑上运行MATLAB程序
C++ 对 Matlab 函数调用
测试设计 UI 界面如下所示:
所用到的控件如下图所示:
matAdd.dll 的初始化
在使用 matAdd.dll 函数之前,需要调用 matAdd.h 里的函数 matAddInitialize
进行初始化
我们将初始化在窗口的构造函数里完成。下面是构造函数的代码:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 不执行matAddInitialize()会报错,注意matAdd为文件名
if(matAddInitialize())
{
ui->TextEdit->setText("matlab程序DLL初始化成功.");
}
else
{
ui->TextEdit->setText("*** matlab程序DLL初始化失败");
return;
}
}
mwArray 类的使用
(1)构造函数如下所示:
mwArray(num_rows,num_cols,mxID,cmplx=mxREAL)
num_rows表示行数, mwSize是整数类型
num_cols表示列数
mxID是mxClassID类型,表示元素的基本数据类型,常见的有如下的一些取值mxLOGICAL_CLASS
mxCHAR_CLASS
mxDOUBLE_CLASS
mxSINGLE_CLASS
mxINT8_CLASS
mxUINT8_CLASS
mxINT16_CLASS
mxUINT16_CLASS
mxINT32_CLASS
mxUINT32_CLASS
mxINT64_CLASS
mxUINT64_CLASS
- cmplx是mxComplexity类型,有mxREAL和mxCOMPLEX两种取值,标书数组元素是实数或复数,缺省为mxREAL
(2)mwArray数组的赋值
mwArray:: SetData(mxUint64* buffer, mwSizelen)
其中,buffer必须是一维数组,即便mwArray变量是一个二维数组,len是一维数组的元素个数,等于行数乘以列数。在给二维数组赋值时,buffer必须按列存储数据(见代码内容)。
void MainWindow::on_pushButton_clicked()
{// 两个矩阵相加, C=A+B
//读取矩阵A
int rowCntA=ui->spinBoxA_Row->value();
int colCntA=ui->spinBoxA_Col->value();
int elementCntA=rowCntA*colCntA; //元素个数
//一维数组,用于C++向 MATLAB数组传递数据
double *arrayA=new double[elementCntA];
int N=0; //C++的一维数组的元素索引号
for(int i=0;i<ui->tableA->columnCount();i++) //逐列读取,序列化存储到一维数组
for (int j=0; j<ui->tableA->rowCount();j++)
{
arrayA[N]=ui->tableA->item(j,i)->text().toDouble();
N++;
}
//定义数组,行,列,double类型
mwArray matrixA(rowCntA,colCntA,mxDOUBLE_CLASS, mxREAL);
//将C++ 的一维数组arrayA存储到 MATLAB的二维数组matrixA
matrixA.SetData(arrayA,elementCntA);
(3)mwArray数组元素的读取
可以使用mwArray::Get()函数读取数组的元素,
例如,对于二维数组,采用Get()函数读取数据的代码一般是
int dim=2; // 二维数组
double value=matrixA.Get(dim,j,i); //按照dim维数数组读出,第j行, 第i列
也可以不用行号、列号,而用序号读取,如
int dim=2; // 二维数组
double value=matrixA.Get(dim,N); //按照dim维数数组读出,第N个元素
这里的N是按列排列的元素的总的序号。对于二维数组,还是按照行号、列号更直观一些。
也可以直接使用mwArray的“()”操作符读取数组元素,如
double value=matrixC(j,i); //直接用数组下标索引,第j行,第i列
double value=matrixC(N); //直接按元素序号读取, 第N个元素
Matlab 函数的调用
在我们已经通过上述方式进行 mwArray 进行 数组的定义与赋值后,我们可以直接通过 Matlab 函数文件的原函数名进行函数的调用,需要传入的参数为:
- nargout:输出变量个数
- matrixC: 保存返回矩阵的 mwArray类,需要指定相关属性。
- matrixA、matrixB:matlab 原函数中的两个参数矩阵。
//计算, C=A+B
int rowCntC=rowCntA;
int colCntC=colCntA;
//定义数组,行,列,double类型
mwArray matrixC(rowCntC,colCntC,mxDOUBLE_CLASS, mxREAL);
int nargout=1;//输出变量个数
// 调用函数,计算 C = A + B
matAdd(nargout,matrixC,matrixA,matrixB);
其他官方文档及常见问题
mwArray 类官方文档
Class used to pass input/output arguments to C++ functions generated by MATLAB Compiler SDK
常见问题
错误使用 mbuild (line 166) Unable to complete successfully. 未找到支持的编译器。
学习笔记:Qt与Matlab混合编程及遇到的诸多问题(附DEMO)
以上链接内容仅供参考,实际操作过程会遇到无数的坑。。。下面的网址可以解决百分之八九十的问题: