Qt与Matlab混合编程中mwArray数组使用详解

内容简介

在《Qt 5.9 与 matlab 2017b 混合编程基本流程》里介绍了MATLAB与C++混合编程的基本流程,流程走通之后,关键就是通过DLL里的函数实现功能了。

MATLAB编译后的函数具有统一的输入输出参数的接口形式,主要是用到mwArray类型数组。在前一博文里没有对mwArray详细介绍,实际使用中还有些细节的问题,在本文里就对mwArray的使用做义工详细的介绍。

主要的内容包括:

(1)       mwArray数组的创建,设置实数数据

(2)       mwArray主要函数方法

(3)       给mwArray数组传递复数数据

(4)       给mwArray数组传递字符串数据

Qt 5.9 测试项目运行界面如下图所示,用到的工具和环境是:

  •   matlab 2017b
  •   Qt 5.9
  •   MSVC 2015 64位编译器
  •   Windows 7  64位

Qt与Matlab混合编程中mwArray数组使用详解_第1张图片

 

1. 准备m文件,并编译生成DLL

在Matlab里编写三个m函数文件

 

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

function  [C]= matAbs(A)
% 求矩阵各元素的绝对值或模, 若A为复数矩阵,求各个元素的模
C=abs(A);
end

function  [C]= matLoadDataFile(filename)
% 从一个文件载入数组
C=load(filename);
end

注意,这三个函数需要分别保存为m文件。

使用deploytool启动MATLAB Compiler,加入这3个m文件,编译为C++ Shared Library。编译项目保存为matBasics.prj,编译后的\for_redistribution_files_only目录下的文件如下图

Qt与Matlab混合编程中mwArray数组使用详解_第2张图片

 

2. Qt项目里使用matBasics.dll

2.1 Qt项目创建与.pro文件设置

在Qt Creator里创建一个Qt Widget Application,项目名称为test1,主窗口基于QMainWindow。在项目的文件目录下创建一个\include目录,将matBasics.h和matBasics.lib文件复制到此。

通过Add  Library 导入库文件matBasics.lib,并添加Matlab相关的库文件和头文件搜索路径。设MATLAB 2017b安装在D:\MATLAB 2017b目录下,设置后的test1.pro文件如下:

 

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = test1
TEMPLATE = app

DEFINES += QT_DEPRECATED_WARNINGS


SOURCES += \
        main.cpp \
        MainWindow.cpp \

HEADERS += \
        MainWindow.h \
    include/matBasics.h \

FORMS += \
        MainWindow.ui


#用户自定义的MATLAB程序的DLL库
win32: LIBS += -L$$PWD/include/ -lmatBasics

INCLUDEPATH += $$PWD/include
DEPENDPATH += $$PWD/include

# Matlab 运行库的头文件
# .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

2.2  matBasics.dll的初始化

在使用matBasics.dll里的函数之前,需要调用matBasics.h里的函数matBasicsInitialize()进行初始化,我们将初始化在窗口的构造函数里完成。下面是构造函数的代码:

 

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
   ui(new Ui::MainWindow)
{
   ui->setupUi(this);

   if (matBasicsInitialize()) //必须调用初始化
      ui->plainTextEdit->appendPlainText("matlab程序DLL初始化成功.");
   else
   {
      ui->plainTextEdit->appendPlainText("*** matlab程序DLL初始化失败");
      return;
   }
   qsrand(QTime::currentTime().msec()); //初始化随机数种子
}

 

3.  mwArray类的使用

3.1 mwArray类变量的常用构造函数

mwArray类常用的构造函数如下:

mwArray(mwSizenum_rows,mwSizenum_cols,mxClassIDmxID,mxComplexitycmplx=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

3.2 mwArray类变量的定义、赋值和常用函数的意义

窗口上“矩阵A的参数”按钮的响应代码如下:

 

void MainWindow::on_btnParams_clicked()
{// 求矩阵A的各种参数, 即mwArray类的主要函数的作用
   int   rowCntA=ui->spinBoxA_Row->value();//行数
   int   colCntA=ui->spinBoxA_Col->value();//列数

//读取矩阵A
   int   elementCntA=rowCntA*colCntA;//元素个数
   double   *arrayA=new double[elementCntA]; //一维数组

   int N=0;//一维数组的元素索引号
   for(int i=0;itableA->columnCount();i++) //逐列读取,按列存储
      for (int j=0; jtableA->rowCount();j++)
      {
         arrayA[N]=ui->tableA->item(j,i)->text().toDouble();
         N++;
      }

   mwArray matrixA(rowCntA,colCntA,mxDOUBLE_CLASS, mxREAL);//定义数组
   matrixA.SetData(arrayA,elementCntA); //设置数据 

//  mwArray类的主要函数的意义
   ui->plainTextEdit->clear();
   mwSize  dims=matrixA.NumberOfDimensions();
//矩阵的维数,标量,值1=一维序列,2=二维数组
   ui->plainTextEdit->appendPlainText(tr("mwArray::NumberOfDimensions() 矩阵的维数,标量"));
   ui->plainTextEdit->appendPlainText(tr("       1 表示一维矩阵,  2 表示二维矩阵"));
   ui->plainTextEdit->appendPlainText(tr("  matrixA.NumberOfDimensions()=%1").arg(dims));

   mwArray arrayDim=matrixA.GetDimensions();//是个一维数组,元素个数=NumberOfDimensions()
   ui->plainTextEdit->appendPlainText(tr("\n mwArray::GetDimensions()是矩阵的具体大小,即行数和列数"));
   ui->plainTextEdit->appendPlainText("  matrixA.GetDimensions()的结果:");
   for(int i=1;i<=dims;i++)
   {
      int cnt=arrayDim.Get(dims,i);//即使dims=2, i 表示元素索引,也是可行的
//      int cnt=arrayDim.Get(1,i); //一维向量
      ui->plainTextEdit->appendPlainText(tr("  第%1维大小 = %2").arg(i).arg(cnt));
   }

   mwSize   elementCnt=matrixA.NumberOfElements();//元素个数
   ui->plainTextEdit->appendPlainText(tr("\n mwArray::NumberOfElements() 元素个数=行数*列数"));
   ui->plainTextEdit->appendPlainText(tr("  matrixA.NumberOfElements()=%1").arg(elementCnt));
}

这里,主要需要注意以下问题:

(1)mwArray数组的赋值

mwArray:: SetData(mxUint64* buffer, mwSizelen) 用于给数组赋值

其中,buffer必须是一维数组,即便mwArray变量是一个二维数组,len是一维数组的元素个数,等于行数乘以列数。在给二维数组赋值时,buffer必须按列存储数据(见代码内容)

(2)mwSize  mwArray::NumberOfDimensions()函数返回一个整数标量,表示数组的维数

返回值为1表示一维数组,2表示二维数组。

程序测试各种情况下都返回2,即便是一个行向量或列向量。

(3)mwArray   mwArray ::GetDimensions()返回一个数组,表示数组各维数的大小,对于向量或二维数组,返回值都是两个元素。第1个元素表示行数,第2个元素表示列数。

(4)mwSize   mwArray::NumberOfElements() 返回数组的元素个数,等于行数乘以列数

(5)mwArray的其他常用函数

bool  IsComplex() const  返回值表示数组是不是复数类型

bool  IsEmpty() const  返回值表示数组是否为空

bool  IsNumeric() const 返回值表示矩阵是否为数值型

Qt与Matlab混合编程中mwArray数组使用详解_第3张图片

1行1列的数组的参数

Qt与Matlab混合编程中mwArray数组使用详解_第4张图片

1行4列的数组的参数

Qt与Matlab混合编程中mwArray数组使用详解_第5张图片

3行1列的数组的参数

3.3 mwArray数组数据的读取

界面上“C=A”按钮的响应代码如下,其功能是将数组A直接复制给数组C,并读取出C的内容进行显示。

 

void MainWindow::on_btnAssign_clicked()
{//C=A,用操作符赋值
   int   rowCnt=ui->spinBoxA_Row->value();
   int   colCnt=ui->spinBoxA_Col->value();

   int elementCnt=rowCnt*colCnt;//元素个数
   mwArray matrixA(rowCnt,colCnt,mxDOUBLE_CLASS, mxREAL);//定义数组,2行,3列,double类型

//读取数组A,
   double   *arrayA;
   arrayA=new double[elementCnt];

   int N=0;
   for(int i=0;itableA->columnCount();i++)
      for (int j=0; jtableA->rowCount();j++)
      {
//         matrixA(j,i)=ui->tableA->item(j,i)->text().toDouble(); //不能如此赋值
         arrayA[N]=ui->tableA->item(j,i)->text().toDouble();
         N++;
      }

   matrixA.SetData(arrayA,elementCnt);

//读取结果
//   mwArray matrixC(rowCnt,colCnt,mxDOUBLE_CLASS, mxREAL);//定义数组,2行,3列,double类型

   mwArray  matrixC=matrixA;

   ui->spinBoxC_Row->setValue(rowCnt);
   ui->spinBoxC_Col->setValue(colCnt);

   int dim=2; //数组维数,表示二维数组
   N=1;
   for(int i=1; i<=colCnt;i++)
      for(int j=1; j<=rowCnt;j++)
      {
//         double   value=matrixC(j,i); //直接用数组下标索引,第j行,第i列
//         double   value=matrixC(N); //直接按元素序号读取,第N个元素
//         double   value=matrixA.Get(dim,j,i); //按照dim维数数组读出,获取第j行,第i列的数据
         double   value=matrixA.Get(dim,N); //按照dim维数数组读出,读取序号为N的元素
         N++;

         QTableWidgetItem *item=new QTableWidgetItem(QString::asprintf("%.0f",value));
         item->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
         ui->tableC->setItem(j-1,i-1,item);
      }
}

这段代码注意以下问题:

(1)matrixA使用了SetData()函数赋值

   mwArray matrixA(rowCnt,colCnt,mxDOUBLE_CLASS, mxREAL);
   matrixA.SetData(arrayA,elementCnt);

(2)matrixC直接用了mwArray 的“=”操作符赋值,即

   mwArray  matrixC=matrixA;

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

3.3 mwArray复数型数组赋值

mwArray也可以是复数性数组,定义数组时使用mxCOMPLEX 类型。

mwArray mwArray::real() 获取数组的实部,也是一个mwArray类型数组

mwArray mwArray::imag() 获取数组的虚部,也是一个mwArray类型数组

下面是界面上“A+B*j复数矩阵求模”按钮的响应代码,它将矩阵A作为复数矩阵的实部,矩阵B作为实数矩阵的虚部,然后代用DLL里的matAbs()函数求模。

void MainWindow::on_btnAbs_clicked()
{// A+B*j复数矩阵求模
   int   rowCntA=ui->spinBoxA_Row->value();
   int   colCntA=ui->spinBoxA_Col->value();

   int   rowCntB=ui->spinBoxB_Row->value();
   int   colCntB=ui->spinBoxB_Col->value();

   if ((rowCntA != rowCntB || (colCntA != colCntB)))
   {
      QMessageBox::critical(this, "错误", "矩阵A和B的维数不一致,不能构造复数矩阵",
                            QMessageBox::Ok,QMessageBox::NoButton);
      return;
   }

//读取矩阵A
   int   elementCntA=rowCntA*colCntA;//元素个数
   double   *arrayA=new double[elementCntA]; //一维数组

   int N=0;//一维数组的元素索引号
   for(int i=0;itableA->columnCount();i++) //逐列读取,序列化存储到一维数组
      for (int j=0; jtableA->rowCount();j++)
      {
         arrayA[N]=ui->tableA->item(j,i)->text().toDouble();
         N++;
      }

   mwArray matrixComplex(rowCntA,colCntA,mxDOUBLE_CLASS, mxCOMPLEX);//定义数组,行,列,double类型复数矩阵
   matrixComplex.Real().SetData(arrayA,elementCntA); //为复数矩阵的实部赋值

//读取数组B
   int   elementCntB=rowCntB*colCntB;//元素个数
   double   *arrayB=new double[elementCntB];

   N=0;
   for(int i=0;itableB->columnCount();i++) //逐列读取,序列化存储到一维数组
      for (int j=0; jtableB->rowCount();j++)
      {
         arrayB[N]=ui->tableB->item(j,i)->text().toDouble();
         N++;
      }

   matrixComplex.Imag().SetData(arrayB,elementCntB); //为复数矩阵的虚部赋值

//计算, 复数矩阵的模
  int   rowCntC=rowCntA;
  int   colCntC=colCntA;

   mwArray matrixAbs(rowCntC,colCntC,mxDOUBLE_CLASS, mxREAL);//定义数组,行,列,double类型
   int nargout=1;//输出变量个数
   matAbs(nargout,matrixAbs,matrixComplex);

//读取结果
   ui->spinBoxC_Row->setValue(rowCntC);
   ui->spinBoxC_Col->setValue(colCntC);

   int dim=2; //按照二维数组读出matrixC
   for(int i=1; i<=colCntC;i++)
      for(int j=1; j<=rowCntC;j++)
      {
         double   value=matrixAbs.Get(dim,j,i); //二维数组,第j行,第i列的数据
         QTableWidgetItem *item=new QTableWidgetItem(QString::asprintf("%.4f",value));
         item->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
         ui->tableC->setItem(j-1,i-1,item);
      }
}

若是要读取复数型数组的内容,也是通过mwArray::real()和mwArray::imag()分别获取其实部和虚部数组,再读取元素的方法就是一样的了。

3.4 mwArray传递字符串数据

创建的matBasics.dll中的函数matLoadDataFile()是接收一个txt文件名,通过load指令载入文件,并作为数组返回。

function  [C]= matLoadDataFile(filename)
% 从一个文件载入数组
C=load(filename);
end

在matlab的指令窗口里生成一个随机数矩阵,并保存为文本文件

>> B=10*rand(3,4)
B =
       7.0936        6.797         1.19       3.4039
       7.5469        6.551       4.9836       5.8527
       2.7603       1.6261       9.5974       2.2381

>> save dataA.txt B -ascii

matBasics.h文件里的 matLoadDataFile()函数的原型是:

bool MW_CALL_CONV mlxMatLoadDataFile(int nlhs, mxArray *plhs[], int nrhs, mxArray    *prhs[]);

与其他接口函数的参数形式也是一样的,只是输入参数*prhs[] 需要传递字符串表示的文件名,返回数组plhs的行数、列数是未知的。

Qt 编写程序界面上的“打开数组txt文件”按钮的响应代码如下:

void MainWindow::on_btnOpenFile_clicked()
{//传递字符串型数据
    QString aFileName=QFileDialog::getOpenFileName(this,"打开文件","","txt文件(*.txt)");
    if (aFileName.isEmpty())
       return;

    ui->plainTextEdit->clear();
    ui->plainTextEdit->appendPlainText(aFileName);

//打开文件,并在plainTextEdit里显示
    QString str;
    QFile aFile(aFileName);  //以文件方式读出
    if (aFile.open(QIODevice::ReadOnly | QIODevice::Text)) //以只读文本方式打开文件
    {
       QTextStream aStream(&aFile); //用文本流读取文件
       while (!aStream.atEnd())
       {
          str=aStream.readLine();//读取文件的一行
          ui->plainTextEdit->appendPlainText(str); //添加到文本框显示
       }
       aFile.close();//关闭文件
    }

//使用DLL里的matLoadDataFile()打开文件,并返回数组
    char*   charStr=aFileName.toLocal8Bit().data();
    mwArray matrixFilename(charStr);//字符串数据赋值

    mwArray matrixC(mxDOUBLE_CLASS, mxREAL);//返回值是未知大小的数组
    int nargout=1;//输出变量个数
    matLoadDataFile(nargout,matrixC,matrixFilename);

//读取结果数组
    mwSize  dims=matrixC.NumberOfDimensions();//矩阵的维数,2表示二维数组
    mwArray arrayDim=matrixC.GetDimensions();//各维的具体大小
    int rowCnt=arrayDim.Get(dims,1); //行数
    int colCnt=arrayDim.Get(dims,2); //列数

    ui->spinBoxC_Row->setValue(rowCnt);
    ui->spinBoxC_Col->setValue(colCnt);

    for(int i=1; i<=colCnt;i++)
       for(int j=1; j<=rowCnt;j++)
       {
          double   value=matrixC(j,i); //直接用数组下标索引,可行
          QTableWidgetItem *item=new QTableWidgetItem(QString::asprintf("%.4f",value));
          item->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
          ui->tableC->setItem(j-1,i-1,item);
       }
}

这段代码先通过Qt的QFileDialog对话框选择文件并显示其内容,

这段代码要注意以下内容:

(1)字符串数据的mwArray数组的赋值

通过QFileDialog::getOpenFileName()获取需要打开的文件名aFileName,aFileName是QString类型,转换为char*类型变量charStr,定义数组matrixFilename时直接在构造函数里赋值。代码如下:

    QString aFileName=QFileDialog::getOpenFileName(this,"打开文件","","txt文件(*.txt)");
    char*   charStr=aFileName.toLocal8Bit().data();
    mwArray matrixFilename(charStr);//字符串数据赋值

(2)未知行数、列数的mwArray的定义

调用matLoadDataFile()函数打开的数组的行列数是不确定的,所以定义返回数组matrixC时未指定行数和列数,定义和调用DLL里函数的代码如下:

    mwArray matrixC(mxDOUBLE_CLASS, mxREAL);//返回值是未知大小的数组
    int nargout=1;//输出变量个数
    matLoadDataFile(nargout,matrixC,matrixFilename);

(3)未知大小的mwArray数组的数据读取

matrixC数组的大小预先是不知道的,调用matLoadDataFile()函数返回数组matrixC后,需要通过一些函数获取数组的行数和列数,才可以从数组里完整的读出数据。获取数组大小的代码如下:

    mwSize  dims=matrixC.NumberOfDimensions();//矩阵的维数,2表示二维数组
    mwArray arrayDim=matrixC.GetDimensions();//各维的具体大小

    int rowCnt=arrayDim.Get(dims,1); //行数
    int colCnt=arrayDim.Get(dims,2); //列数

    ui->spinBoxC_Row->setValue(rowCnt);
    ui->spinBoxC_Col->setValue(colCnt);

选择文件并打开后的运行效果如下图所示。

Qt与Matlab混合编程中mwArray数组使用详解_第6张图片

 

 

 

【本文实例Qt 5.9项目完整源程序下载地址】https://download.csdn.net/download/hongandyi/10416823

或百度网盘下载:  https://pan.baidu.com/s/1pM2PxrH

相关阅读:

 

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

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

 

 

 

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