文章目录
用LABwindows/CVI创建简单学生管理器
软件介绍
labwindows/cvi 2017是一款集成式的开发环境,集源代码编辑、32位ANSI C编译、联结、调试以及标准ANSI C库等为一体,为用户提供简单的拖放式用户界面编辑器以及自动代码生成器,是编程人员的好帮手。广泛应用于军工、电讯、工业、航空航天等领域。
环境安装
采用的是LABWindows/CVI2017 环境,具体安装方法见下面链接: https://editor.csdn.net/md/?articleId=104800261.
学生管理器说明
在本节中,我们要做的例子很简单,是创建一个如下图所示的应用程序。通过简单的界面用链表实现学生数据的添加、删除和显示。
相关链表的知识,请到这个链接学习https://editor.csdn.net/md/?articleId=104807198.
创建工程文件
运行LabWindows/CVI,单击菜单File-New-Project(*.prj)…,将会出现新建工程的选项对话框。在对话框中可以选择是否同时新建一个工作空间,是否保持跟上个工程同样的配置。
创建用户界面文件
单击菜单File-New-User Interface(*.uir)…,将会弹出用户界面编辑器。用户界面编辑器是一个利用交互式拖放控件的方法来设计图形用户界面的编辑器。对于一个新建的用户界面文件,编辑器将自动产生一个空面板,如下图所示。
双击面板 ,弹出属性编辑框。在属性编辑框中可以修改程序的标题、窗口位置、窗口按钮/菜单、窗口风格等特性。
初始状态下,面板属性对话框的Callback Function(回调函数)留空。此处可以填写回调函数的名称(此示例填写“MainCallBack”),以方便后面添加窗口关闭、窗口激活或者失活的响应代码。
在用户界面中添加控件
在该窗口中Untitled Panel的灰色区域中单击鼠标右键,便会弹出一个如图 所示的控件选择的快捷菜单。
从该菜单中选择所需的控件,并摆放到面板的适当位置(也可通过Create 菜单项来实现上述操作)。每个控件通过一系列属性设置对话框来设定,可以通过设置控件的属性来改变其外观、设置、热键和标签等。
在此,我们选择添加两个Numeric菜单下的Numeric控件,添加一个Text菜单下的String控件,添加Command Button菜单下的52Square Command Button控件。添加完毕后,双击控件(或者右击-Edit Control)修改控件的相关属性。在按键中添加回调函数MainPanelBtnCallback,以方便后面生成回调函数。如图所示
学生姓名采用String控件,设置如下
学生学号采用整形控件,设置如下:
五个按键,设置类似,如下图:
分别是BTN_Add,BTN_Change,BTN_insert,BTN_Delete,BTN_Show
当然,为了美观,可以在Quick Edit Window中修改按钮等控件的背景颜色,在Label Appearance中修改按钮中文字的颜色。上图中OK按钮即普通按钮修改颜色之后的效果。
生成框架源代码
在用户界面编辑窗口中,选择菜单Code-Generate-All Code,此时将会弹出一个提示存储.uir文件的对话框,在此我们存为1.uir(CVI将会生成同名的.h文件。不建议大家像示例一样起无意义的命名)。保存后CVI会弹出生成代码的对话框,一般情况下直接点击OK,框架源代码会自动保存在跟.uir同名的C文件中。
至此,我们点击菜单Run-Debug Project之后,程序UI已经可以运行了。运行结果如下图所示。当然了,因为尚未编写按钮的回调函数的响应代码,此时点击任何按钮都不会执行对应的操作。
修改界面字体
由于系统自带的字体放大后不清晰 ,为了美观就需要对其进行修改。
在option中点font,选择相应字体修改,如图所示。
生成的头文件内容
生成框架代码之后,我们应该可以在uir同名的h文件中看到CVI为我们生成了如下代码,包含了CVI框架运行、CVI用户界面运行所需要的基本函数的声明与定义。show.h是界面文件show.uir的头文件库,包含了界面中的控件的定义与声明:
/**************************************************************************/
/* LabWindows/CVI User Interface Resource (UIR) Include File */
/* */
/* WARNING: Do not add to, delete from, or otherwise modify the contents */
/* of this include file. */
/**************************************************************************/
#include <userint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Panels and Controls: */
#define PANEL 1 /* callback function: MainCallBack */
#define PANEL_STRING_Name 2 /* control type: string, callback function: (none) */
#define PANEL_BTN_Change 3 /* control type: command, callback function: MainPanelBtnCallback */
#define PANEL_BTN_Show 4 /* control type: command, callback function: MainPanelBtnCallback */
#define PANEL_BTN_insert 5 /* control type: command, callback function: MainPanelBtnCallback */
#define PANEL_BTN_Delete 6 /* control type: command, callback function: MainPanelBtnCallback */
#define PANEL_BTN_Add 7 /* control type: command, callback function: MainPanelBtnCallback */
#define PANEL_NUM_Student_Insert 8 /* control type: numeric, callback function: (none) */
#define PANEL_NUM_Student 9 /* control type: numeric, callback function: (none) */
/* Control Arrays: */
/* (no control arrays in the resource file) */
/* Menu Bars, Menus, and Menu Items: */
/* (no menu bars in the resource file) */
/* Callback Prototypes: */
int CVICALLBACK MainCallBack(int panel, int event, void *callbackData, int eventData1, int eventData2);
int CVICALLBACK MainPanelBtnCallback(int panel, int control, int event, void *callbackData, int eventData1, int eventData2);
#ifdef __cplusplus
}
#endif
添加的文件
#include <ansi_c.h>
#include <cvirte.h> //CVI运行时库(CVI Run-Time Engine)
#include <userint.h>//CVI用户界面库(CVI User Interface)
#include <stdlib.h> //包含了C、C++语言的最常用的系统函数
#include "show.h"
添加结构体
static int panelHandle;//为面板的全局句柄,当对面板或者面板上的控件进行操作时经常用到
#define LEN sizeof(struct Student)//宏定义节点长度得命名
#define TYPE struct Student//宏定义结构体变量命名
struct Student //定义一个学生类型结构体,包括学号,分数
{
char Name[20];
int Number;
TYPE *Next;//next是指针变量,指向结构体变量
};
TYPE *Head = NULL; //定义头指针地址,全局变量
TYPE *PTmp = NULL; //定义新开辟结点变量,全局变量
以下就是五个按键对应的功能函数
动态链表函数
TYPE* Creat(void) //定义函数,此函数返回一个指向链表头的指针
{
char Name[20] = {0}; //定义保存姓名的数组
int Number = 0; //定义保存姓名的整形
GetCtrlAttribute(panelHandle, PANEL_STRING_Name, ATTR_CTRL_VAL, Name); //得到PANEL_STRING_Name字符框中字符串给Name
GetCtrlAttribute(panelHandle, PANEL_NUM_Student, ATTR_CTRL_VAL, &Number); //得到PANEL_SPANEL_NUM_Student整形框中字符串给Number
if((strlen(Name) < 1) || (Number < 1)) //根据输入的数据长度来判断学生数据是否输入有误
{
MessagePopup("提示", "请重新输入..."); //显示对话消息对话框
}
else
{
PTmp = (struct Student *)malloc(sizeof(struct Student));//开辟新的结点空间
memset(PTmp, 0, sizeof(struct Student)); // 将空间清零
PTmp->Number = Number; //结点中保存学号,姓名
strcat(PTmp->Name, Name);
struct Student *PMov = Head; //每次都返回头指针
if(Head == NULL)
{
Head = PTmp; //把第一个结点给头指针
}
else
{
while(PMov->Next) //循环从头到尾找指针域
{
PMov = PMov->Next;
}
PMov->Next = PTmp; //找到指针域后更新
}
printf("新添加了1个学生:%s 学号:%d号\n", PTmp->Name, PTmp->Number); //显示添加信息
}
return (Head); //返回首地址
}
删除结点函数
void delet(TYPE* head) //定义结点删除函数
{
TYPE* p = head, * in; //定义两边指针
int i = 0; //定义记录值
char Name[20] = {0}; //定义姓名数组
GetCtrlAttribute(panelHandle, PANEL_STRING_Name, ATTR_CTRL_VAL, Name);//得到PANEL_STRING_Name字符框中字符串给Name
while ((strcmp(p->Name, Name) !=0) && p != NULL) //找到对应姓名并且不为空的结点
{
in = p; //找到左边的
p = p->Next; //找到右边的
i++;
}
if (p != NULL)
{
printf("删除的学生为:%s 学号:%d号\n", p->Name, p->Number); //显示删除信息
in->Next = p->Next; //将左右链接
free(p); //释放中间结点
}
else {
MessagePopup("提示", "学生节点不存在..."); //显示对话消息对话框
}
}
链表显示函数
void print(TYPE * head) //定义将链表输出函数
{
TYPE * p; //定义指针
p = head; //使p指向第一个结点
int count = 0; //记录多少哥结点数
if(Head == NULL) //判断是否为空
{
MessagePopup("提示", "表中不包含任何学生..."); //显示对话消息对话框
}
else
if(head!=NULL) //输出第一个结点后的信息
do {
printf("第%d个学生:%s 学号:%d号\n", ++count, p->Name, p->Number);//显示结点信息
p = p->Next; //指向下个结点
} while (p != NULL);
}
链表的插入
void insert(TYPE* head) { //定义链表的插入函数
TYPE* p = head, * in; //定义首尾指针
int i = 0; //记录值
int n; //定义插入的地方
char Name[20] = {0}; //定义姓名数组 ,学号
int Number = 0;
GetCtrlAttribute(panelHandle, PANEL_NUM_Student_Insert, ATTR_CTRL_VAL, &n); ////得到PANEL_NUM_Student_Insert整形中数据给n
while (i < (n-1) && p != NULL) {
p = p->Next;
i++; //找到相应结点
}
if (p != NULL) {
in = (TYPE*)malloc(sizeof(TYPE)); //开辟新的空间
memset(in, 0, sizeof(struct Student)); //清零
GetCtrlAttribute(panelHandle, PANEL_STRING_Name, ATTR_CTRL_VAL, Name); //得到PANEL_STRING_Name字符中数据给Name
GetCtrlAttribute(panelHandle, PANEL_NUM_Student, ATTR_CTRL_VAL, &Number);//得到PANEL_NUM_Student整形中数据给Number
in->Number = Number;
strcat(in->Name, Name); //复制信息给in->Name
printf("插入学生:%s 学号:%d号\n", in->Name, in->Number); //显示插入的信息
in->Next = p->Next; //填充in节点的指针域,也就是说把in的指针域指向p的下一个节点
p->Next = in; //填充p节点的指针域,把p的指针域重新指向in
}
else {
MessagePopup("提示", "表中不包含任何学生...");//显示对话消息对话框
}
}
链表的修改
void change(TYPE* head) //修改指定位置的结点的信息函数
{
TYPE* p = head; //传入首地址
int i = 0; //记录值
char Name[20] = {0}; //定义姓名数组 ,学号
int Number = 0;
GetCtrlAttribute(panelHandle, PANEL_STRING_Name, ATTR_CTRL_VAL, Name); ////得到PANEL_STRING_Name字符中数据给Name
while ((strcmp(p->Name, Name) !=0) && p != NULL) { //找到对应姓名并且不为空的结点
p = p->Next;
i++;
} //找到相应的位置结点
if (p != NULL) {
GetCtrlAttribute(panelHandle, PANEL_STRING_Name, ATTR_CTRL_VAL, Name); //得到PANEL_STRING_Name字符中数据给Name
GetCtrlAttribute(panelHandle, PANEL_NUM_Student, ATTR_CTRL_VAL, &Number);//得到PANEL_NUM_Student整形中数据给Number
p->Number = Number; //保存学号
printf("修改的学生:%s 学号:%d号\n", p->Name, p->Number); //显示修改的信息
}
else
MessagePopup("提示", "表中不包含该学生"); //显示对话消息对话框
}
主函数
main函数是用户程序运行时首先被执行的函数。main函数实现的功能分别是CVI运行引擎初始化(InitCVIRTE函数)、加载面板(LoadPanel函数)、显示面板(DisplayPanel函数)、开始消息队列循环(RunUserInterface函数)。其中RunUserInterface会不断的检查面板的消息或者操作系统发送的消息,永远不会返回,除非用户主动调用QuitUserInterface函数中止消息循环。所以,正常运行状态下,main函数中的DiscardPanel永远不会被执行,main函数永远不会退出,直到程序被关闭。
int main (int argc, char *argv[])
{
if (InitCVIRTE (0, argv, 0) == 0)
return -1; /* out of memory */
if ((panelHandle = LoadPanel (0, "show.uir", PANEL)) < 0)
return -1;
DisplayPanel (panelHandle);
RunUserInterface ();
DiscardPanel (panelHandle);
return 0;
}
回调函数一
MainCallBack是我们在面板属性编辑框中填写的Callback Function的名字,负责执行主程序面板消息的响应代码。在switch语句中,我们可以看到,CVI已经事先为我们生成好了三个面板最常用的事件:EVENT_GOT_FOCUS、EVENT_LOST_FOCUS以及EVENT_CLOSE。分别在对应的case中添加代码,即可在窗口被激活、窗口失活、以及点击窗口右上角“关闭”按钮时执行的代码。callbackData表示消息发出时所附带的数据。可是eventData1以及eventData2却容易让初学者云里雾里。其实我们只需要选中任意case语句中的事件(此处可以选择EVENT_GOT_FOCUS),右击选择Go To Defination之后即可发现,CVI User Interface的事件定义全在这里。对eventData1以及eventData2的说明,也在定义的注释中有所说明。
在此,为了让用户点击程序右上角的关闭按钮时,程序可以顺利关闭,在case EVENT_CLOSE语句后面添加QuitUserInterface函数:
int CVICALLBACK MainCallBack (int panel, int event, void *callbackData,
int eventData1, int eventData2)
{
switch (event)
{
case EVENT_GOT_FOCUS:
break;
case EVENT_LOST_FOCUS:
break;
case EVENT_CLOSE:
QuitUserInterface (0);
break;
}
return 0;
}
回调函数二
这个函数响应五个键的功能。
int CVICALLBACK MainPanelBtnCallback (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_COMMIT:
switch(control)
{
char Name[20] = {0};
int Number = 0;
case PANEL_BTN_Add: //增加
Creat();
break;
case PANEL_BTN_Delete: //删除
delet(Head);
break;
case PANEL_BTN_Show: //显示
print (Head);
break;
case PANEL_BTN_insert: //插入
insert (Head);
break;
case PANEL_BTN_Change: //更改
change (Head);
break;
}
break;
}
return 0;
}
运行调式
点击菜单Run-Debug Project(或点击工具栏的绿色三角形按钮)即可直接编译并运行当前工程,同时也可以在工程所在目录下生成exe文件。
右击工程名并选择Build(或点击菜单Build-Create Debuggable Executable)即可编译并生成exe文件。
在CVI代码编辑窗口中单击代码前空白部分或者按F9按键,则可以在对应的C语言语句处设置断点。
在程序中断时,单击菜单Window即可控制开启或关闭Memory(内存)、Variables(变量)、Watch(监控)窗口,通过查看或者修改对应的参数的值来方便的调试程序。
感谢语
多谢赵老师的讲解和代码,本人在老师继续上,根据之前链表学习内容,加以应用。