The value of Zmotion is to bring customers more success!
QT是目前众多软件开发工程师使用的C++应用程序开发框架。QT具有完整的GUI图形库,界面控件的样式和布局可直接通过控件属性进行设置,简化了代码的使用。对于需要人机界面操作的机器视觉程序而言,QT强大的界面设计功能很大程度地减少了视觉软件开发工程师设计操作界面的开发难度。正运动技术VPLC系列控制器可搭载Linux系统,且支持QT调用正运动软件的ZBasic指令。
本节课程我们将和大家一起分享ZBasic结合QT实现图像采集功能的方法。
一 认识QT
Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器“Meta Object Compiler, moc”)以及一些宏,Qt很容易扩展,并且允许真正地组件编程。
二 ZBasic and QT
正运动技术将ZDevelop软件主要使用的ZBasic语言封装成C++动态链接库,QT可以通过调用动态库的形式调用ZBasic指令。
三 软件实现
(一)准备
(二)实例演示
1.打开QTCreator软件,新建QT项目:点击“New Project”→项目选择“Application”→选择“QT Widgets Application”→点击“Choose...”按钮进入下一步→输入项目名称“ZVision_QT_1”,选择项目存储的路径(路径不能包含中文),点击“下一步”按钮→默认选择“下一步”,最后点击“完成”按钮,完成项目的建立。
2.添加库相关文件:将相关库文件拷贝到项目所在文件夹下→鼠标左键点击项目名称,再点击鼠标右键弹出快捷菜单,选择“添加现有文件”→依次将“zaux.h”文件、“zmotion.h”文件、“zaux.cpp”文件添加到项目→双击“.pro”文件,手动添加动态链接库代码“win32: LIBS += -L$$PWD/ -lzmotion”→在“mainwindow.cpp”文件中定义调用库相关头文件,即添加代码:#include “zmotion.h”和#include “zaux.h”。
3.在mainwindow.cpp文件中定义程序中使用到的全局变量。
ZMC_HANDLE g_handle;//控制器句柄
int Times=0;
uint8 zv0_pBuff[400*300*2]={0};//锁存0缓存大小
int ContinuousGetImgFlag=0; //连续采集标志
4.设计UI界面。
5.添加界面初始化代码。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->ZV0->setText("");//清空字体
ui->ZV0->setStyleSheet("QLabel{background-color:rgb(0, 0, 100);}"); //设置样式表
ui->ExposureValue->setText("5000");
}
6.添加【扫描控制器IP】槽函数:选择【扫描控制器IP】按钮,右击弹出快捷菜单栏,选择“转到槽”选项→弹出“转到槽”窗口,选择对应的信号源“clicked()”,点击“OK”确认,即可完成槽函数的定义和声明→在槽函数的声明函数中添加函数实现的具体代码内容。
//扫描控制器IP
void MainWindow::on_Scan_Control_IP_clicked()
{
char buffer[ 10240];
int32 iresult;
const char *ipaddress;
iresult = ZMC_SearchEth(buffer, 10230, 100); //扫描控制器IP,返回错误码信息
if(ERR_OK != iresult)
{
return ; //错误
//将扫描到的所有IP地址进行分割,并按照IP地址格式显示在下拉列表中
int ipos = 0;
const char * pstring;
pstring = buffer;
for( int j= 0; j< 100;j++) //最多100个IP列表
{
char buffer2[ 256];
buffer2[ 0] = '\0';
while( ' ' == pstring[ 0])
{
pstring++;
}
ipos = sscanf(pstring , "%s", &buffer2);
if(EOF == ipos)
{
break;
}
//跳过字符
while(( ' ' != pstring[ 0]) && ( '\t' != pstring[ 0]) && ( '\0' != pstring[ 0]))
{
pstring++;
}
if( 0 == strcmp(buffer2, ipaddress))
{
return ;
}
ui->ComboBoxIP->addItem(buffer2);
}
}
7.添加【连接】槽函数:选择【连接】按钮,右击弹出快捷菜单栏,选择“转到槽”选项→弹出“转到槽”窗口,选择对应的信号源“clicked()”,点击“OK”确认,即可完成槽函数的定义和声明→在槽函数的声明函数中添加函数实现的具体代码内容。
//连接控制器
void MainWindow::on_Link_Control_clicked()
{
//get ip from current list
int32 iresult; //错误码
int nIndex = 0;
char * tmp_buff = new char[ 16];
char str1[ 64];
QString str;
str = ui->ComboBoxIP->currentText(); //获取IP列表里的IP地址数据
qDebug()<< "current ip"<<str;//打印当前选择的IP地址数据
QByteArray ba = str.toLatin1(); //将IP的字符串格式转到char * 格式
tmp_buff = ba.data();
if( 0 != g_handle) //如果控制器句柄不为0
{
ZAux_Close(g_handle); //关闭控制器链接
}
iresult=ZAux_OpenEth(tmp_buff,&g_handle); //根据获取的IP地址与控制器建立链接,返回错误码
if (iresult== 0)
{
sprintf(str1, "控制器连接成功,当前IP:%s", tmp_buff);
setWindowTitle(str1);
qDebug()<< "Link Success! ip:"<<tmp_buff;
}
else
{
qDebug()<< "Link Fail!";
setWindowTitle( "控制器已断开");
}
}
8.添加【断开】槽函数:选择【断开】按钮,右击弹出快捷菜单栏,选择“转到槽”选项→弹出“转到槽”窗口,选择对应的信号源“clicked()”,点击“OK”确认,即可完成槽函数的定义和声明→在槽函数的声明函数中添加函数实现的具体代码内容。
//断开连接控制器
void MainWindow::on_Disconnect_clicked()
{
if(0 != g_handle)
{
ZAux_Close(g_handle);//关闭控制器链接
g_handle = NULL;//清空句柄
setWindowTitle("控制器已断开");
}
}
9.添加【扫描相机】槽函数:选择【扫描相机】按钮,右击弹出快捷菜单栏,选择“转到槽”选项→弹出“转到槽”窗口,选择对应的信号源“clicked()”,点击“OK”确认,即可完成槽函数的定义和声明→在槽函数的声明函数中添加函数实现的具体代码内容。
//扫描相机
void MainWindow::on_Scan_Camera_clicked()
{
float CamNum= 0;
int32 iresult; //错误码
QString str;
char buffer[ 2048];
iresult=ZAux_RunTask(g_handle, 0, "cam_scan_all()"); //运行控制器里面指定的SUB
if (iresult== 0)
{
qDebug()<< "run cam_scan_all() success";
}
else
{
qDebug()<< "run cam_scan_all() fail !!"<<iresult;
}
ZAux_Direct_GetTable(g_handle, 0, 1,&CamNum); //读取控制器table变量
str = QString( "扫描到的相机个数:%1").arg(CamNum);
ui->CamNum->setText(str); //显示扫描到的相机数量
}
10.添加【采集图像】槽函数:选择【采集图像】按钮,右击弹出快捷菜单栏,选择“转到槽”选项→弹出“转到槽”窗口,选择对应的信号源“clicked()”,点击“OK”确认,即可完成槽函数的定义和声明→在槽函数的声明函数中添加函数实现的具体代码内容(此前需要在头文件中定义使用到的QPixmap fPixmap 变量)。
//单次采集
void MainWindow::on_Camera_Grab_clicked()
{
int32 iresult; //错误码
iresult=ZAux_RunTask(g_handle, 0, "btn_grab()"); //运行控制器里面指定的SUB
if (iresult== 0)
{
qDebug()<< "run btn_grab() success";
}
else
{
qDebug()<< "run btn_grab() fail !!"<<iresult;
}
ZV0_Show(); //将控制器获取到的图像转换成Qt可显示的图片格式,并显示图像
}
//将获取到的图像显示到ZV0控件中
void MainWindow::ZV0_Show()
{
int latin= 0,buffsize= 400* 300* 2;
int width, height,bite,WrTimes;
int flag= 1, count= 0;
//获取zvlatch的信息
ZMC_ZVLatinImgGetInfo(g_handle, latin, &width, &height, &bite, &WrTimes); //获取图像信息
if(WrTimes== 0)
{
return; //时间没有改变退出函数
}
//获取到的锁存0的缓存数据
buffsize = width*height* 2;
ZMC_ZVLatinImgGet(g_handle, latin, &width, &height, &bite, zv0_pBuff, buffsize); //获取图像数据
//将获取到的锁存0的缓存数据在qt中显示出来
QImage Image ((uchar *)zv0_pBuff,width, height,QImage::Format_RGB555);
Image = Image.mirrored( false, true);
QPixmap Pixmap = QPixmap:: fromImage (Image);
fPixmap = Pixmap.scaled(width, height);
ui->ZV0->clear();
ui->ZV0->setPixmap(fPixmap);
}
11.添加显示函数ZV0_Show()的定义和声明函数。
private slots:
void on_Scan_Control_IP_clicked();
void on_Link_Control_clicked();
void on_Disconnect_clicked();
void on_Scan_Camera_clicked();
void on_Camera_Grab_clicked();
void on_Contiue_Grab_clicked();
void on_Stop_Grab_clicked();
void on_Set_Exposure_clicked();
void ZV0_Show(); //锁存0显示
//将获取到的图像显示到ZV0控件中
void MainWindow::ZV0_Show()
{
int latin= 0,buffsize= 400* 300* 2;
int width, height,bite,WrTimes;
int count= 0;
int flag= 0;
ZMC_ZVLatinImgGetInfo(g_handle, latin, &width, &height, &bite, &WrTimes);
qDebug()<<WrTimes;
if(Times == WrTimes)
{
return; //时间没有改变退出函数
}
else
{
Times = WrTimes;
}
QThread::msleep( 100);
while( 0 == flag) //等待
{
ZAux_Direct_GetVariableInt(g_handle, "CamGetPhotoFlag",&flag);
QThread::msleep( 10);
count++;
if( 1000 < count) //等待时间大于10s
{
break; //退出循环
}
}
flag= 0;
//获取zvlatch的信息
ZAux_Direct_SetVariableInt(g_handle, "CamGetPhotoFlag",flag);
//获取到的锁存0的缓存数据
buffsize = width*height* 2;
ZMC_ZVLatinImgGet(g_handle, latin, &width, &height, &bite, zv0_pBuff, buffsize); //获取图像数据
//将获取到的锁存0的缓存数据在qt中显示出来
flag= 1;
//获取zvlatch的信息
ZAux_Direct_SetVariableInt(g_handle, "CamGetPhotoFlag",flag);
QImage Image( (uchar *)zv0_pBuff,width, height,QImage::Format_RGB555);
Image = Image.mirrored( false, true);
QPixmap Pixmap = QPixmap:: fromImage (Image);
fPixmap = Pixmap.scaled(width, height);
ui->ZV0->clear();
ui->ZV0->setPixmap(fPixmap);
}
12.添加【连续采集】槽函数:选择【连续采集】按钮,右击弹出快捷菜单栏,选择“转到槽”选项→弹出“转到槽”窗口,选择对应的信号源“clicked()”,点击“OK”确认,即可完成槽函数的定义和声明→在槽函数的声明函数中添加函数实现的具体代码内容。
//连续采集
void MainWindow::on_Contiue_Grab_clicked()
{
int32 iresult; //错误码
iresult=ZAux_RunTask(g_handle, 0, "btn_cgrab()"); //运行控制器里面指定的SUB
if (iresult== 0)
{
qDebug()<< "run btn_cgrab() success";
}
else
{
qDebug()<< "run btn_cgrab() fail !!"<<iresult;
}
ContinuousGetImgFlag= 1; //连续采集标志置1
}
13.添加定时刷新函数:在“mainwindow.h”文件中添加包含QTimer定时器的类型库,并定义“QTimer UpZV_0”定时器变量→在界面初始化函数中添加定时器触发条件和信号与槽连接函数→添加定时采集图像的定义和声明函数,并在声明函数中添加具体的实现代码内容。
#include
#include
void ZV0_Show(); //锁存0显示
void UpZV_0show();
private:
Ui::MainWindow *ui;
QPixmap fPixmap;
QTimer UpZV_0;
MainWindow::MainWindow(QWidget * parent) :
QMainWindow( parent),
ui( new Ui::MainWindow)
{
ui->setupUi(this);
ui->ZV0->setText( ""); //清空字体
ui->ZV0->setStyleSheet( "QLabel{background-color:rgb(0, 0, 100);}"); //设置样式表
ui->ExposureValue->setText( "5000");
UpZV_0.start( 20); //200ms定时判断采集并显示图像
connect(&UpZV_0, SIGNAL(timeout()), this, SLOT(UpZV_0show()));
}
//定时器开启连续采集
void MainWindow::UpZV_0show()
{
if( 1 == ContinuousGetImgFlag)
{
ZV0_Show();
}
}
14.添加【停止采集】槽函数:选择【停止采集】按钮,右击弹出快捷菜单栏,选择“转到槽”选项→弹出“转到槽”窗口,选择对应的信号源“clicked()”,点击“OK”确认,即可完成槽函数的定义和声明→在槽函数的声明函数中添加函数实现的具体代码内容。
//停止采集
void MainWindow::on_Stop_Grab_clicked()
{
int32 iresult; //错误码
iresult=ZAux_RunTask(g_handle, 0, "btn_stopCgrab()"); //运行控制器里面指定的SUB
if (iresult== 0)
{
qDebug()<< "run btn_stopCgrab() success";
}
else
{
qDebug()<< "run btn_stopCgrab() fail !!"<<iresult;
}
ContinuousGetImgFlag= 0; //连续采集标志置0
}
15.添加【设置】槽函数:选择【设置】按钮,右击弹出快捷菜单栏,选择“转到槽”选项→弹出“转到槽”窗口,选择对应的信号源“clicked()”,点击“OK”确认,即可完成槽函数的定义和声明→在槽函数的声明函数中添加函数实现的具体代码内容。
//设置曝光时间
void MainWindow::on_Set_Exposure_clicked()
{
int32 iresult; //错误码
QString str_tmp;
float ExposureValue;
str_tmp = ui->ExposureValue->text(); //获取QLineEdit控件显示的曝光值
ExposureValue = str_tmp.toFloat(); //字符串转成浮点数
ZAux_Direct_SetTable(g_handle, 1, 1,&ExposureValue); //设置控制器的table变量
iresult=ZAux_RunTask(g_handle, 0, "Camera_Exposure()"); //运行控制器里面指定的SUB
if (iresult== 0)
{
qDebug()<< "run Camera_Exposure() success";
}
else
{
qDebug()<< "run Camera_Exposure() fail !!"<<iresult;
}
}
16.查看运行效果:将控制器接入电源,使用网线将控制器与PC连接,将相机通电并接入控制器→使用ZDevelop软件打开ZBasic指令编写包含采集函数的项目文件,将该项目下载到控制器→运行QT程序,并连接到控制器,进行对应采集操作。
(三)仿真效果演示
本次,正运动技术ZBasic结合Qt实现机器视觉图像采集,就分享到这里。
更多精彩内容请关注“正运动小助手”公众号,需要相关开发环境与例程代码,请咨询正运动技术销售工程师:400-089-8936。
本文由正运动技术原创,欢迎大家转载,共同学习,一起提高中国智能制造水平。文章版权归正运动技术所有,如有转载请注明文章来源。