Qt与matlab混合编程
以下内容使用的操作系统、软件及编译器版本:win10、Qt5.9.6(msvc2015_64)、matlab R2018b
.m文件示例
matAdd.m
function [C]= matAdd(A, B)
% 两个矩阵数相加
C = A + B;
end
Qt工程中引用matlab相关的库和文件
.pro文件作如下设置,其中 D:/MATLAB/R2018b 为matlab的安装路径,可以根据自身实际安装的路径进行更改
# .h文件
INCLUDEPATH += D:/MATLAB/R2018b/extern/include
INCLUDEPATH += D:/MATLAB/R2018b/extern/include/Win64
# MATLAB 的.lib库文件
INCLUDEPATH += D:/MATLAB/R2018b/extern/lib/win64/microsoft
DEPENDPATH += D:/MATLAB/R2018b/extern/lib/win64/microsoft
win32: LIBS += -LD:/MATLAB/R2018b/extern/lib/win64/microsoft/-llibmex
win32: LIBS += -LD:/MATLAB/R2018b/extern/lib/win64/microsoft/-llibmx
win32: LIBS += -LD:/MATLAB/R2018b/extern/lib/win64/microsoft/-llibmat
win32: LIBS += -LD:/MATLAB/R2018b/extern/lib/win64/microsoft/-llibeng
win32: LIBS += -LD:/MATLAB/R2018b/extern/lib/win64/microsoft/-lmclmcr
win32: LIBS += -LD:/MATLAB/R2018b/extern/lib/win64/microsoft/-lmclmcrrt
Qt中调用matlab的几种方法
1.动态库方法
将.m文件编译成C++可以直接调用的动态库,之后在Qt程序中直接使用。这种方法最为简单,也最为常见。
maltlab函数编译动态库
- matlab中打开需要编译的.m文件,命令行中输入
mbuild -setup
输入命令后回车,根据弹出的选项进行对应的选择
之后在命令行中输入
deploytool
在弹出的窗口中选择 Library Compiler
之后在弹出的窗口中选择要编译的动态库的类型,点击 + 添加.m文件(可一次添加多个),设置生成的动态库的名称,保存当前工程(若此时不保存,在点击生成之前系统会提示保存),最后点击 Package 生成动态库。
生成成功后,在matlab工程下将看到生成的动态库
Qt中调用matlab生成的动态库
Qt工程中.pro引用生成的动态库和对应头文件
LIBS += F:/Programs/QT/MatlabTest/link/matAdd.lib
INCLUDEPATH += F:/Programs/QT/MatlabTest/link/
Qt程序中调用动态库中的函数
if (!matAddInitialize()) // DLL初始化
{
qDebug() << "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); // 调用函数计算,返回的结果保存在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个元素
// 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列
小结
在Qt中使用matlab编译生成的动态库是最为简单和常见的方法,但是在一些情形下matlab无法将程序编译成动态库(脚本程序或某些函数不支持编译等),这种情况下则必须采用其它办法在C++中调用matlab程序。
2. Qt调用matlab引擎
在Qt中可以直接启动matlab引擎,此后可从Qt程序中向matlab发送指令进行相关操作,指令的语法和matlab命令窗口语法一致。
Qt启动matlab引擎
头文件需要包含
#include "engine.h"
启动matlab引擎
Engine *ep = nullptr;
if(!(ep = engOpen(nullptr))) // 启动matlab引擎
{
qDebug() << "无法启动matlab引擎";
return;
}
engSetVisible(ep, false); // 隐藏matlab自带命令窗口
其中,当matlab引擎启动的时候,默认是会弹出matlab的命令行窗口的,此时若不需要,可以使用 engSetVisible(ep, false) 函数将该窗口隐藏。
Qt和matlab之间数据交互
仍以matAdd函数为例,在Qt中定义A和B两个变量,调用matAdd.m文件计算得到结果C,将其返回Qt程序并显示。
// 给变量赋值
double init1 = 3.0;
mxArray *a = nullptr;
a = mxCreateDoubleMatrix(1, 1, mxREAL);
memcpy((void*) mxGetPr(a), (void*)&init1, sizeof (double));
engPutVariable(ep, "A", a);
mxDestroyArray(a);
double init2 = 1.0;
mxArray *b = nullptr;
b = mxCreateDoubleMatrix(1, 1, mxREAL);
memcpy((void*) mxGetPr(b), (void*)&init2, sizeof (double));
engPutVariable(ep, "B", b);
mxDestroyArray(b);
engEvalString(ep, "cd F:/Programs/others/MatlabTest"); // 向matlab发送字符串命令
engEvalString(ep, "one=matAdd(A,B);");
mxArray *result = engGetVariable(ep, "one"); //将MATLAB结果传回qt
qDebug() << *mxGetPr(result); // 打印结果
engEvalString 函数用于从Qt向matlab发送命令,engGetVariable 函数用于获取matlab计算得到的结果,返回值是 mxArray* 类型,mxGetPr 用于将matlab支持的mxArray类型转换为C++支持的数据类型(double*)。
关闭matlab引擎
engClose(ep); // 关闭matlab引擎
其他
matlab函数中有多个返回值时Qt指令写法
.m文件
function [C, D]= matAdd(A, B)
% 两矩阵相加和相减
C = A + B;
D = A - B;
end
Qt程序写法如下,指令的语法和在matlab命令行中写法一致。
engEvalString(ep, "[one, two]=matAdd(A,B);");
mxArray *result = engGetVariable(ep, "one"); //将MATLAB结果传回qt
mxArray *result1 = engGetVariable(ep, "two");
直接调用.m文件
当.m文件中是脚本程序时,可以直接执行.m文件
engEvalString(ep, "load('XXX/XXX/XXX/xxx.m');");
mxArray类型与C++数组互转
上面的示例中传入matlab的变量只是两个double型的数(也可以理解为1行1列的矩阵),当传入或者返回的数据是多行多列的矩阵时,写法与上文略有不同。
二维数组转mxArray*
double init3[2][5] = {{1, 2, 3, 4, 5},{11, 22, 33, 44, 55}};
mxArray *c = nullptr;
c = mxCreateDoubleMatrix(2, 5, mxREAL);
memcpy((void*) mxGetPr(c), (void*)init3, sizeof (init3));
mxArray*转二维数组
int M = mxGetM(c); // 行数
int N = mxGetN(c); // 列数
double *ans = mxGetPr(c); // 转为数组类型
// 打印数组结果
for(int i = 0; i < M; ++i)
{
for(int j = 0; j < N; ++j)
{
cout << *(ans + (i * N + j)) << " ";
cout.flush();
}
cout << "\n";
}
小结
使用Qt调用matlab引擎方法比较比较灵活,相当于在Qt中打开了一个matlab,但是matlab引擎启动较慢,并且需要进行二者之间的数据交互,容易出错,此外还需要一定的matlab语法基础。
3.使用txt等中间文件在Qt和matlab之间进行数据交互
这种方法比较简单,Qt和matlab两者基本互相独立,将Qt获取到的数据保存到txt等中间文件当中,matlab程序中需要的参数从txt文件中读取,并将返回结果同样保存为txt文件,供Qt程序读取和显示。
(若在运行Qt程序过程中,全程不想打开matlab软件,则可以使用Qt启动带命令窗口的matlab引擎,之后的操作在命令行窗口中进行。)
简单粗暴最有效。