1.4 Tab 控件
1.4.1 Tab 控件设计方法
Tab 控件类似分隔标签或一组文件夹上的标签,使用Tab 控件可以在应用程序窗口或对话框中的同一区域定义多个页面。该控件包含了一个或多个panel 面板集合。在设计时和运行时,可以设置其属性来控制Tab 控件的外观,如可设置标签以多行形式显示,将标签放在Tab 控件的上下左右各种位置,并可在标签上显示图标。
LabWindows/CVI 中的Tab 控件最初是由Panel 面板转换而来,控件中的每个页面都有自己的句柄(类似于面板句柄),可以认为Tab 控件是一个容器,里面包含了面板的集合,因此,Tab 控件内也可重复内嵌Tab 控件。正是由于Tab 控件具有这些特点与属性,在使用某些函数时,如SetCtrlVal 、GetCtrlVal 等时,参数中的面板句柄设置应区分是一般面板句柄还是Tab 页面句柄。
在LabWindows/CVI 中,Tab 控件是最为特殊的一个控件,这主要是因为引入了容器中的容器概念(笔者定义)。与其他语言最大的区别是,在LabWindows/CVI 中,一个面板即为一个容器,所有的控件必须放置在这个容器内,不允许在这个面板上存在其他的容器,如果换个角度,把容器看作句柄,也就是说,一个面板只有一个句柄,控件没有句柄,只有ID(AcTIveX 控件除外),即使是Radio Group 这类控件,严格上来说也是由Tree 控件衍生而来,不具有控件句柄。这样设计的好处是,LabWindows/CVI 的消息传递是扁平化的,只在一个容器中进行消息循环,事件响应及时;缺点也显而易见,不能设计出像Visual Basic 或Delphi 中非常完美的控件组或页框(如Frame),但这也是LabWindows/CVI 设计的一大优点,简化设计思路,使程序设计者更加专注于逻辑系统设计而非语言本身。举例说明,如果在Visual Basic 或Delphi 中建有一个工程,Form (窗体)里面有一个Tab 控件,Tab 控件内嵌套一个Numeric 控件,可以对其进行属性操作,即获得或设置属性值;同理,在LabWindows/CVI 集成开发环境中也建有一个工程,Panel(面板)里面有一个Tab 控件,控件内嵌套一个Numeric 控件,则引用方法如下:
(1)Visual Basic 语句
赋值
Form.Tab.Numeric.value = 1
取值
x = Form.Tab.Numeric.value
(2)Delphi 语句
赋值
Form.Tab.Numeric.value := 1
取值
x := Form.Tab.Numeric.value
(3)LabWindows/CVI 函数
赋值(函数语句取自本例程)
panelHandle = LoadPanel (0, "Tab 控件.uir", PANEL); GetPanelHandleFromTabPage (panelHandle, PANEL_TAB, 0, &TabPanelHandle); SetCtrlVal (TabPanelHandle, TABPANEL_NUMERICTHERM, temp); 取值(函数语句取自本例程)
panelHandle = LoadPanel (0, "Tab 控件.uir", PANEL); GetPanelHandleFromTabPage (panelHandle, PANEL_TAB, 0, &TabPanelHandle); GetCtrlVal (TabPanelHandle, TABPANEL_NUMERICKNOB, &temp);
有意思的是,Visual Basic 和Delphi 对控件属性的引用中用了3 个“.”,这3 个“.”对应于LabWindows/CVI 的3 个函数语句,三种语言之间,属性获得或设置都是从容器到实例的逐次递进引用,逻辑上是相同的,只是表现方式不同,其逻辑关系如图1-9 所示。
(a) Visual Basic 或Delphi (b)LabWindows/CVI
图1-9 属性获得或设置逻辑关系
与Visual Basic 和Delphi 的差别也可以很清晰地发现,LabWindows/CVI 的编码量巨大,函数量众多,而且大量的编码是用在属性设置上的,为了克服编码量大、函数多程序员不容易记,书写容易出现错误的缺点,LabWindows/CVI 引入函数面板(FuncTIon Panel),即我们常用的.fp 文件,使得对于属性的编程只需要用函数面板输入几个参数就可以实现大部分功能,简单、易操作。
在编辑状态,双击面板中的Tab 控件,弹出Edit Tab 对话框,如图1-10 所示。在Control Apperarance 框中,Tabs Fit Mode 用来设置单行、多行显示等,默认为单行显示,Tab LocaTIon 用来设置Tab 标签的位置,默认放置在控件的左上角。在Tab 框中,可以设置每一个页面的标签、常量名等,可以进行添加、剪切、复制、粘贴,页面的前后移动,对页面标签添加图标以及设置每个页面控件的焦点转移次序Tab Order 等操作。
图1-10 Edit Tab 对话框
1.4.2 Tab 控件程序设计
(1)面板设计
编写一个Tab 控件属性设置程序,能够响应页面(标签页)内的事件、设置标签、计算Tab 页面数目,改变标签的显示风格,并且在标签上可以设置显示图标。在页面中,有两个Command Button 控件,拥有相同的常量值和回调函数名,在调用时,需要通过标题字符来区分所要响应的事件。面板设计如图1-11 所示,面板中主要控件属性设置如表1-11 所示。
图1-11 Tab 控件面板
表1-11 控件属性设置表
(2)程序源代码
//头文件声明
#include 《formaTIo.h》
#include 《cvirte.h》
#include 《userint.h》
#include “Tab 控件.h”
//Tab 页面句柄声明
static int TabPanelHandle;
//Tab 页面索引声明
static int index = 0;
static int panelHandle; //主函数
int main (int argc, char *argv[])
{
if (InitCVIRTE (0, argv, 0) == 0)
return –1; /* out of memory */ if ((panelHandle = LoadPanel (0, “Tab 控件.uir”, PANEL)) 《 0)
return –1;
DisplayPanel (panelHandle);
RunUserInterface ();
DiscardPanel (panelHandle);
return 0;
} //面板回调函数
int CVICALLBACK PanelCB (int panel, int event, void *callbackData,
int eventData1, int eventData2)
{
switch (event)
{
case EVENT_CLOSE:
QuitUserInterface (0);
break;
}
return 0;
}
//事件响应页面
int CVICALLBACK valset (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int temp;
switch (event)
{
case EVENT_COMMIT:
// 获得Tab 页面句柄
GetPanelHandleFromTabPage (panelHandle, PANEL_TAB, 0, &TabPanelHandle);
// 获得Tab 页面中控件值
GetCtrlVal (TabPanelHandle, TABPANEL_NUMERICKNOB, &temp);
// 设置Tab 页面中控件值
SetCtrlVal (TabPanelHandle, TABPANEL_NUMERICTHERM, temp);
break;
}
return 0;
}
//页面中的Command Button 按钮
int CVICALLBACK OkCallback (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int area;
char s[20];
char label[20];
int count;
switch (event)
{
case EVENT_COMMIT:
// 获得控件属性
GetCtrlAttribute (panel, control, ATTR_LABEL_TEXT, label);
// 获得当前激活页面索引
GetActiveTabPage (panelHandle, PANEL_TAB, &index);
// 通过索引获得当前页面句柄
GetPanelHandleFromTabPage (panelHandle, PANEL_TAB, index, &TabPanelHandle);
// 如果当前页面中的按钮为“设置”时,执行以下操作
if (CompareStrings (label, 0, “ 设置”, 0, 1) == 0)
{
// 获得页面中指定值
GetCtrlVal (TabPanelHandle, TABPANEL_2_STRING, s);
// 设置当前页面中标签为指定值
SetTabPageAttribute (panelHandle, PANEL_TAB, index, ATTR_LABEL_TEXT, s);
}
// 如果当前页面中的按钮为“获取”时,执行以下操作
if (CompareStrings (label, 0, “ 获取”, 0, 1) == 0)
{
// 获得Tab 控件中页面数目
GetNumTabPages (panelHandle, PANEL_TAB, &count);
// 显示页面数目
SetCtrlVal (TabPanelHandle, TABPANEL_3_NUMERIC, count);
}
break;
}
return 0;
}
//显示风格回调函数
int CVICALLBACK style (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int kind;
switch (event)
{
case EVENT_COMMIT:
// 获得控件值
GetCtrlVal (panel, control, &kind);
// 设置Tabp 控件风格
SetCtrlAttribute (panelHandle, PANEL_TAB, ATTR_TABS_LOCATION, kind);
break;
}
return 0;
}
3:程序注释
① GetPanelHandleFromTabPage 函数获得Tab 页面句柄。函数原型为:
int GetPanelHandleFromTabPage (int Panel_Handle, int Control_ID, int Index, int
*Tab_Panel_Handle);
Panel_Handle:Tab 控件所在面板句柄。
Control_ID:Tab 控件ID。
Index:Tab 页面(标签页)索引,第一个页面为0,依次递增。注意,此值需要用户设定而非系统返回值。
*Tab_Panel_Handle :返回Tab 页面索引指定的页面句柄。
② GetActiveTabPage 函数获得当前激活的Tab 页面索引。函数原型为:int GetActiveTabPage (int Panel_Handle, int Control_ID, int *Active_Index);
*Active_Index :当前激活页面索引值,从0 开始计,–1 表示没有活动页面。
③ CompareStrings 函数
字符串比较函数。将字符串1 中从某一位置开始所组成的子串与字符串2 中从某一位置开始所组成的子串进行比较。函数原型为:
int CompareStrings (char String_1[], int String_1_Index, char String_2[], int String_2_Index, int
Case_Sensitive);
String_1[] :字符串1。
String_1_Index :子串1 起始位置。
String_2[] :字符串2。
String_2_Index :子串2 起始位置。
Case_Sensitive:是否区分大小写。
返回值:0 表示两子串相等,–1 表示子串1 小于子串2,1 表示子串1 大于子串2。
④ valset 回调函数
在valset 回调函数中编写了GetCtrlVal (TabPanelHandle, TABPANEL_NUMERICKNOB, &temp) 语句,值得注意的是,GetCtrlVal 函数Panel Handle 参数并非在程序前面声明的panelHandle, 而是采用了GetPanelHandleFromTabPage 函数的返回Tab 页面句柄TabPanelHandle,也可以利用控件回调函数传回的函数句柄panel 。Control ID 参数可以写成TABPANEL_NUMERICKNOB ,也可以利用控件回调函数传回的控件常量control 参数。此外,为了使数值设置与数值显示看起来同步,可以将event 事件中的EVENT_COMMIT 改写为EVENT_VAL_CHANGED ,修改后的回调函数如下:
int CVICALLBACK valset (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int temp;
switch (event)
{
case EVENT_VAL_CHANGED:
// 获得Tab 页面句柄
GetPanelHandleFromTabPage (panelHandle, PANEL_TAB, 0, &TabPanelHandle);
// 获得Tab 页面中控件值
GetCtrlVal (panel, control, &temp);
// 设置Tab 页面中控件值
SetCtrlVal (panel, TABPANEL_NUMERICTHERM, temp);
break;
}
return 0;
}
⑤ 具有相同常量名和回调函数的Command Button 按钮事件响应方式设计在面板设计中,Command Button 按钮在标签设置和Tab 数目页面出现了两次,分别为“设置”和“获取”按钮,其回调函数名称和常量名完全一致,只是标题不同,这是由于Tab 控件本身就是多面板的集合,类似于一个容器控件,Tab 页面以子面板的方式出现,造成了可以重命名不同控件的问题,虽然不利于控件事件的区分,但却能减少回调函数的数量,利于代码的优化。面板和控件在程序头文件中定义如下:
#define PANEL 1
#define PANEL_TAB 2
#define TABPANEL_NUMERICTHERM 2
#define TABPANEL_NUMERICKNOB 3
#define TABPANEL_2_STRING 2
#define TABPANEL_2_OKBUTTON 3
#define TABPANEL_3_NUMERIC 2
#define TABPANEL_3_OKBUTTON 3
#define TABPANEL_4_RING 2
从以上代码可以看出,虽然两个控件在界面设计阶段具有相同的常量名,在由于所在的Tab 页面不同,系统生成的常量名并不相同,分别为TABPANEL_2_OKBUTTON 和TABPANEL_3_OKBUTTON ,但常量值是相同的且均为3,在相同回调函数下,只能通过控件的不同标题来区分所响应的事件。
(4)运行效果图
点击工具栏中的Debug Project 按钮,程序开始运行,其效果如图1-12 所示。
图1-12 运行效果图
技术专区
- 关于酒精传感器的介绍
- LabWindows/CVI虚拟仪器 Tab 控件
- 现代频谱仪测量微弱信号的黑科技
- 工业互联6大场景TSN+OPC UA测试床联合发布 落地工业互联网“最后
- 使用树莓派测定颗粒物的技巧分享