[原创教程]LabWindows/CVI入门之第二章:GUI开发
导航目录
2.1 六步构建简单的GUI程序
在本节中,我们要做的例子很简单,是创建一个如下图所示的应用程序。
图 2‑1 程序运行效果
2.1.1 创建工程文件
运行LabWindows/CVI,单击菜单File-New-Project(*.prj)…,将会出现新建工程的选项对话框。在对话框中可以选择是否同时新建一个工作空间,是否保持跟上个工程同样的配置。
图 2‑2 创建新的工程
2.1.2 创建用户界面文件
单击菜单File-New-User Interface(*.uir)…,将会弹出用户界面编辑器。用户界面编辑器是一个利用交互式拖放控件的方法来设计图形用户界面的编辑器。对于一个新建的用户界面文件,编辑器将自动产生一个空面板,如下图所示。
双击面板 ,弹出属性编辑框。在属性编辑框中可以修改程序的标题、窗口位置、窗口按钮/菜单、窗口风格等特性。
图 2‑4 属性编辑框
初始状态下,面板属性对话框的Callback Function(回调函数)留空。此处可以填写回调函数的名称(此示例填写“MainCallBack”),以方便后面添加窗口关闭、窗口激活或者失活的响应代码。
2.1.3 在用户界面文件中添加控件
在该窗口中Untitled Panel的灰色区域中单击鼠标右键,便会弹出一个如图 2‑5所示的控件选择的快捷菜单。LabWindows/CVI 的控件种类见表 2‑1。
从该菜单中选择所需的控件,并摆放到面板的适当位置(也可通过Create 菜单项来实现上述操作)。每个控件通过一系列属性设置对话框来设定,可以通过设置控件的属性来改变其外观、设置、热键和标签等。
在此,我们选择添加两个Numeric菜单下的Numeric控件,添加一个Text菜单下的String控件,Command Button菜单下的Square Command Button控件。添加完毕后,双击控件(或者右击-Edit Control)修改控件的相关属性。
分别在属性编辑框中,将两个Numeric控件的Constant Name属性修改为“PARA_A”、“PARA_B”,Label Appearance的Label属性修改为“参数A”、“参数B”。将String控件的Constant Name属性修改为“RESULT”,Label Appearance的Label属性修改为“A+B”。将Command Button控件的Constant Name属性修改为“CALCULATE”,Callback Function属性修改为“Calculate”,以方便后面生成回调函数。
图 2‑6 修改控件的属性
当然,为了美观,可以在Quick Edit Window中修改按钮等控件的背景颜色,在Label Appearance中修改按钮中文字的颜色。上图中OK按钮即普通按钮修改颜色之后的效果。
2.1.4 生成框架源代码
在用户界面编辑窗口中,选择菜单Code-Generate-All Code,此时将会弹出一个提示存储.uir文件的对话框,在此我们存为1.uir(CVI将会生成同名的.h文件。不建议大家像示例一样起无意义的命名)。保存后CVI会弹出生成代码的对话框,一般情况下直接点击OK,框架源代码会自动保存在跟.uir同名的C文件中。
图 2‑7 生成所有代码
至此,我们点击菜单Run-Debug Project之后,程序UI已经可以运行了。运行结果如下图所示。当然了,因为尚未编写按钮的回调函数的响应代码,此时点击任何按钮都不会执行对应的操作。
图 2‑8 程序UI运行结果
2.1.5 添加回调函数
生成框架代码之后,我们应该可以在uir同名的C文件中看到CVI为我们生成了如下代码:
我们可以在CVI为我们生成的框架中添加或修改代码,以实现具体的功能。在添加或修改代码之前,我们先来逐一分析CVI生成的框架代码做了什么。
cvirte.h、userint.h分别是CVI运行时库(CVI Run-Time Engine)、CVI用户界面库(CVI User Interface)的头文件,分别包含了CVI框架运行、CVI用户界面运行所需要的基本函数的声明与定义。1.h是界面文件1.uir的头文件库,包含了界面中的控件的定义与声明。而panelHandle为面板的全局句柄,当对面板或者面板上的控件进行操作时经常用到。
main函数是用户程序运行时首先被执行的函数。main函数实现的功能分别是CVI运行引擎初始化(InitCVIRTE函数)、加载面板(LoadPanel函数)、显示面板(DisplayPanel函数)、开始消息队列循环(RunUserInterface函数)。其中RunUserInterface会不断的检查面板的消息或者操作系统发送的消息,永远不会返回,除非用户主动调用QuitUserInterface函数中止消息循环。所以,正常运行状态下,main函数中的DiscardPanel永远不会被执行,main函数永远不会退出,直到程序被关闭。
MainCallBack是我们在面板属性编辑框中填写的Callback Function的名字,负责执行主程序面板消息的响应代码。在switch语句中,我们可以看到,CVI已经事先为我们生成好了三个面板最常用的事件:EVENT_GOT_FOCUS、EVENT_LOST_FOCUS以及EVENT_CLOSE。分别在对应的case中添加代码,即可在窗口被激活、窗口失活、以及点击窗口右上角“关闭”按钮时执行的代码。
CVI初学者可能会被MainCallBack前面的从未见过的CVICALLBACK吓倒。其实我们只需要选中CVICALLBACK之后右击,选择Go To Defination即可看到:
CVICDECL仍然不是我们的C语言关键字。继续选中CVICDECL之后右击,选择Go To Defination查看CVICDECL的原型:
至此,CVICALLBACK真相大白。其实CVICALLBACK只是__cdecl的宏定义。什么?不懂__cdecl是什么意思?赶紧拿起手中的手机,访问Google搞清楚它们的神奇功效吧!
CVI初学者也可能被MainCallBack后面跟着的一堆参数吓倒,不知道他们是做何用,“不管他们行不行”。panel好理解,就是消息发生所在的面板的句柄。callbackData表示消息发出时所附带的数据。可是eventData1以及eventData2却容易让初学者云里雾里。其实我们只需要选中任意case语句中的事件(此处可以选择EVENT_GOT_FOCUS),右击选择Go To Defination之后即可发现,CVI User Interface的事件定义全在这里。对eventData1以及eventData2的说明,也在定义的注释中有所说明。
在此,为了让用户点击程序右上角的关闭按钮时,程序可以顺利关闭,在case EVENT_CLOSE语句后面添加QuitUserInterface函数:
Calculate函数是我们在面板中创建的按钮(Command Button)控件的消息响应函数,函数框架跟前面的主面板的消息响应函数类似。我们需要在此添加按钮按下之后需要执行的代码。Calculate函数添加响应之后的代码如下:
需要提醒大家的是,作为CVI的初学者,请在对照示例进行实际操作时,不要直接Copy+Paste上面的代码。自己对照着上面的代码一个字母一个字母的敲进去,会对扎实、牢固的学习掌握CVI具有很大的帮助的。
GetCtrlVal是获取控件的当前值的函数,其函数声明如下:
其中value为控件的当前值变量的指针。panelHandle参数为函数面板的句柄,之前已经提到。可是控件的句柄controlID如何获得呢?
最简单的方法就是,控件的句柄即[控件所在的面板的Constant Name]_[控件的Constant Name]。若控件所在面板的Constant Name为PANEL,控件的Constant Name为NAME,那么控件的句柄即为PANEL_NAME。其中,面板或者控件的Constant Name都可以通过双击面板或者双击控件来进行设置。
另外一种方法即打开.uir同名的.h文件,查找对应的控件的定义。我们在1.h文件中发现如下代码:
即可得知,“参数A”控件的句柄为PANEL_PARA_A。
SetCtrlVal是设置控件的当前值的函数,其函数声明如下:
同GetCtrlVal一样,调用SetCtrlVal并填入面板、控件句柄之后即可设置控件的值。
GetCtrlVal、SetCtrlVal、GetCtrlAttribute以及SetCtrlAttribute四个函数,是对几乎所有控件都适用的函数,在CVI的GUI程序中,使用极其频繁,是学习CVI的GUI编程必须掌握的四个函数。
2.1.6 运行、调试
点击菜单Run-Debug Project(或点击工具栏的绿色三角形按钮)即可直接编译并运行当前工程,同时也可以在工程所在目录下生成exe文件。
右击工程名并选择Build(或点击菜单Build-Create Debuggable Executable)即可编译并生成exe文件。
在CVI代码编辑窗口中单击代码前空白部分或者按F9按键,则可以在对应的C语言语句处设置断点。
在程序中断时,单击菜单Window即可控制开启或关闭Memory(内存)、Variables(变量)、Watch(监控)窗口,通过查看或者修改对应的参数的值来方便的调试程序。
在本节中,我们已经通过编写并运行一个GUI的实例,了解了在CVI下用C语言开发GUI程序的基本过程。至此,大家已经算是对CVI入门了,大家已经可以尝试着自行开发一些有用、好玩的程序了。
2.2 借助帮助文档使用更多控件更多功能
一般软件在设计时都会遵循二八定律,即80%的人通常只使用一个软件20%的功能,但是另外80%的功能却对20%的人来说很重要。对于CVI初学者也一样,任何教程只会告诉初学者20%的功能如何去使用,而剩下的80%的功能只能由使用者根据CVI软件提供的帮助文档或者自行查找相关的资料来使用/实现。
授之以鱼不如授之以渔,下面我们将针对CVI初学者经常遇到的几个问题,提供解决问题的方法。
2.2.1 随心使用更丰富的CVI控件
CVI具有丰富的控件,从一般的按钮、界面到图表、开关。在使用CVI提供的丰富、强大的控件之前,我们必须先对CVI提供了哪些控件有一个大致的了解。
CVI控件的类型列举如表 2‑1所示。
数值型控件(Numeric) |
文本型控件(Text ) |
命令按钮(Command Button) | 双态按钮(Toggle Button) |
指示灯(LED) | 二值开关(Binary Switch) |
列表控件(Ring) | 列表框(List Box) |
装饰控件(Decoration) | 图形控件(Graph) |
图片控件(Picture) | 表格控件(Table) |
定时器控件(Timer) | 画布控件(Canvas) |
分隔线控件(Splitter) | 标签页控件(Tab) |
ActiveX控件(ActiveX) | 经典样式控件(Classic-Style…) |
自定义控件(Custom Controls) |
常用控件的功能如下:
(1) 数值型(Numeric)控件可作为用户参数输入窗或程序参数回显窗;
(2) 字符串(Text )控件用来输入或显示一个字符串;
(3) 文本型控件包括文本框控件(Text Box Control)和信息框控件(Text Message Control),用于显示大量的文本信息;
(4) 命令按钮(Command Button)控件用于出发一个事件,用于仪器的控制。命令按钮一般必须定义回调函数功能的名称;
(5) 双态按钮(Toggle Button)控件包括双态按钮、文本/图形双态按钮和单选/复选按钮,它们都有两种状态,分别为“0”和“1”。
(6) 二值开关(Binary Switch)控件可以在两种状态(On/Off)下工作,并为两种状态设置控件值和标题,与触发按钮类似;
(7) 列表(Ring)控件用于在一组值中进行选择,既可以用鼠标单击控件框或单击控件的上下箭头选择,也可以用键盘的Up和Down键来选择;
(8) 时钟(Timer)控件用来设置特定的时间段内触发事件,它可以在无限长的时间里等间隔地重复执行给定的操作;
(9) 图形(Graph)控件分为曲线图Graph控件和带状图Strip Charts控件。带状图用来实时显示图形数据。
(10)标签页(Tab)控件用来给程序添加多个可以切换的标签页。
(11)经典样式控件(Classic-Style…)罗列了早期版本中CVI的控件。
在.uir文件中添加控件之后,右击控件,都会弹出类似图 2‑9所示的菜单:
选择“Control Help”,则CVI会自动打开帮助文档的该控件的帮助部分。如图 2‑10所示。点击Programming… Controls 链接即可查看操作该控件所需要的一些函数。
2.2.2 使用丰富的CVI函数库
在第一章中,我们提到了CVI的强大之处在于它提供了丰富的函数库支持。利用CVI的库,我们可以轻松的实现数据采集、数据分析、GUI交互、通信以及多线程等功能。
如何使用CVI丰富的函数库呢?细心的同学可能已经注意到了,在CVI开发环境的左下角有一个Libraries Tree窗口(如图 2‑11),提供了CVI提供的函数库(fp,Function Tree)的浏览功能。
例如,若想实现以下弹窗(Popup)的功能。则可在函数库浏览窗口的User Interface Library中打开Pop-up Panels,然后在 Message/Prompt Popups中找到ConfirmPopup函数,右击该函数,选择Function Help,即可获得使用该函数的相关帮助。如果直接双击该函数,还会在右侧出现函数面板,辅助大家填写函数的参数。
图 2‑12 确认是否关闭的弹窗
图 2‑13 获得函数的帮助信息
一个典型的帮助窗口如所示。帮助窗口中一般具有该函数功能的简单描述,并且具有该函数的声明。
图 2‑14 ConfirmPopup的帮助窗口
使用该帮助窗口足够满足一般情况的需求。但若想要寻求更加详细帮助文档,可以点击菜单Help-Contents,在“索引”中输入函数名,寻求更加详细的说明文档。
此外CVI还提供了其他丰富的函数库。CVI的函数库不光可以实现用户交互的操作,支持ANSI C,还支持以下诸多功能:
菜单创建、打印管理、绘图、剪切板操作、最小化到系统托盘、鼠标控制、按键检测、定时器、显示器兼容管理、信号生成、矩阵操作、复数运算、信号处理、信号测量、数据统计、拟合、向量运算、字符串操作、文件操作、物理内存访问、任务管理、调用外部程序、多线程管理、调用外部模块管理、VXI通信、GPIB通信、RS232通信、VISA功能、TCP通信、访问Internet、直接数据访问控制、调用ActiveX控件、调用.Net功能、套接字通信等等等等。
2.2.3 获取丰富的CVI事件与用户交互
CVI应用程序是以事件来驱动的(事件驱动是指当事件发生后对应的代码才会被执行)。CVI中的事件有三种来源:用户操作引起的事件(如鼠标点击)、操作系统发送的事件(如定时器)、应用程序之间发送的或自己给自己发送的事件。
在2.1.5 添加回调函数中,我们已经初步接触了控件以及面板的事件。通过userint.h中对事件的宏定义我们可以知道CVI中有哪些事件,但是我们却无法知道面板此时正在发生的事件是什么。
用户界面文件编辑器就提供了方便我们查看面板当前发生的事件的查看器。当我们打开一个.uir文件时,在用户界面文件编辑器的上方有四个状态切换按钮(如所示)。从左到右分别是预览按钮、界面编辑按钮字体编辑按钮与背景编辑按钮。
图 2‑15 uir文件的状态切换按钮
点击预览按钮,将状态切换到预览。此时,尝试鼠标或者键盘操作,在CVI的右上角(不同版本可能有所区别)会出现以“EVENT_”开头的字样,该字样即提示了当前发生的事件。
当知道了某种操作导致的事件之后,可以再次切换到界面编辑模式,右击该控件-View Control Callback则CVI会自动跳到该控件的回调函数中。若View Control Callback不可用,则双击该控件,给该控件添加回调函数(Callback Function)然后右击控件-Generate Control Callback后即可使用。
2.3 探索与实验
2.3.1 实验
设计虚拟信号发生器,如图 2‑16所示。
功能描述及测试表格如表 2‑2所示。
功能要求 | 测试要求 |
波形选择 | 在三角波、正弦波、方波、任意波之间切换 |
波形参数设置 | 设置采样率、频率、幅值 |
偏移量设置 | 设置波形的偏移量 |
显示颜色设置 | 设置波形显示的颜色 |
2.3.2 探究
探究在CVI下菜单创建、打印管理、绘图、剪切板操作、最小化到系统托盘、鼠标控制、按键检测、定时器、显示器兼容管理、信号生成、矩阵操作、复数运算、信号处理、信号测量、数据统计、拟合、向量运算、字符串操作、物理内存访问、任务管理、调用外部程序、多线程管理、调用外部模块管理等函数库的函数。
自己写iRunUserInterface函数,要求实现跟RunUserInterface函数完全相同的功能。