|
4.4 打字练习
4.4.1 打字练习设计方法
一般的打字练习软件包含几大功能:中文打字练习、数字打字练习、英文打字练习、中文打字测速、英文打字测速等。如金山公司推出的金山打字通软件,是一款功能齐全、数据丰富、界面友好的、集打字练习和测试于一体的打字软件。打字练习可以循序渐进突破盲打障碍,短时间运指如飞,完全摆脱枯燥学习。
利用LabWindows/CVI 开发一款打字练习游戏,设计两个字母从屏幕上方自由下落,当按键与字母相同时该字母消失并重新生成新的下落字母,每成功击键一次,得分就加一分,通过速度调节可适当加速或降低下落速度,时间设置为60s, 当一次游戏结束时,给出一分钟内打字速度说明。读者通过游戏的编程可体会键盘类游戏设计方法,同时也能提高打字速度。游戏字母范围设置为a~z,亦可设置其他字符如0~9 和A~Z,满足实际键盘练习要求。
打字窗口背景用LED 控件,并配以底色,下落字符采用Text Message 控件,设置背景透明,与窗口背景能很好融合。下落字符通过伪随机数产生,下落的Left 属性是通过随机数确定的。
对于键盘类游戏设计,通常都会在回调函数响应EVENT_KEYPRESS 事件,也只有利用该事件才能获得击键字符。一般来说,用户按键事件,支持面板和绝大多数控件。当面板或控件处于激活状态时,通过回调函数接收键盘事件。在面板或控件回调函数中,eventData1 表示按键ASCII 码,eventData2 为指向存储ASCII 码值的指针。LabWindows/CVI 约定,modifier (修饰)键为Shift 和Alt 键,menu modifier 键为Ctrl 键。
大部分虚拟键在winuser.h 头文件中定义,并以VK_开头。LabWindows/CVI 对其进行了重新定义,如表4-7 所示。
表4-7 虚拟键表
常量名
| 常量值
| 说明
| VAL_FWD_DELETE_VKEY
| 0x0100
| Del 键
| VAL_BACKSPACE_VKEY
| 0x0200
| 退格键
| VAL_ESC_VKEY
| 0x0300
| ESC 键
| VAL_TAB_VKEY
| 0x0400
| Tab 键
| VAL_ENTER_VKEY
| 0x0500
| 回车键
| VAL_UP_ARROW_VKEY
| 0x0600
| ↑键
| VAL_DOWN_ARROW_VKEY
| 0x0700
| ↓键
| VAL_LEFT_ARROW_VKEY
| 0x0800
| ←键
| VAL_RIGHT_ARROW_VKEY
| 0x0900
| →键
| VAL_INSERT_VKEY
| 0x0A00
| Insert 键
| VAL_HOME_VKEY
| 0x0B00
| Home 键
| VAL_END_VKEY
| 0x0C00
| End 键
| VAL_PAGE_UP_VKEY
| 0x0D00
| Page Up 键
| VAL_PAGE_DOWN_VKEY
| 0x0E00
| Page Down 键
| VAL_F1_VKEY
| 0x0F00
| F1 键
| VAL_F2_VKEY
| 0x1000
| F2 键
| VAL_F3_VKEY
| 0x1100
| F3 键
| VAL_F4_VKEY
| 0x1200
| F4 键
| VAL_F5_VKEY
| 0x1300
| F5 键
| VAL_F6_VKEY
| 0x1400
| F6 键
| VAL_F7_VKEY
| 0x1500
| F7 键
| VAL_F8_VKEY
| 0x1600
| F8 键
| VAL_F9_VKEY
| 0x1700
| F9 键
| VAL_F10_VKEY
| 0x1800
| F10 键
| VAL_F11_VKEY
| 0x1900
| F11 键
| VAL_F12_VKEY
| 0x1A00
| F12 键
| 通常情况下,ASCII 码不能与虚拟键同时使用,但可与modifier 键组合使用。如:VAL_MENUKEY_MODIFIER | 'A'表示同时按下Ctrl + A 键。LabWindows/CVI 约定,键值可用4 字节整型量表示为0x00MMVVAA ,其中,MM 为modifier 键,VV 为虚拟键,AA 为ASCII 码键。
4.4.2 打字练习程序设计
(1)面板设计
编写一个打字练习程序,在面板中点击开始按钮进行一分钟计时,在此时间内测试打字的速度,测试结束时显示结果并提示是否继续练习。点击暂停按钮,暂停计时。程序运行时可根据实际情况对字母下落速度进行适当调整。屏幕最多可出现两个字母同时下落。两个定时器分别用于产生字符下落效果和速度测试计时。选中下落字符Text Message 控件,通过菜单Arrange→Control ZPanel Order→Move to Front 使其置于背景控件之上。面板设计如图4-11 所示,面板中主要控件属性设置如表4-8 所示。
图4-11 打字练习面板
表4-8 控件属性设置表
常量名
| 控件类型
| 控件的主要属性
| PANEL
| Panel
| 标题:打字练习回调函数:PanelCB
| TEXTMSG
| Text Message
| 默认值:a
| TEXTMSG_1
| Text Message
| 默认值:a
| TIMER
| Timer
| 标题:timer1 回调函数:timer1 Interval:1.000
| TIMER_2
| Timer
| 标题:timer2 回调函数:timer2 Interval:1.000
| NUMERIC_SCORE
| Numeric
| 标题:得分
| NUMERIC_SPEED
| Numeric
| 标题:速度默认值:10
| NUMERICTHERM_TIME
| Numeric Thermometer
| 标题:时间
| BACKGROUND
| LED
| ( 背景)
| CMD_START
| Command Button
| 标题:开始回调函数:start
| CMD_PAUSE
| Command Button
| 标题:暂停回调函数:pause
|
(2)程序源代码
//头文件声明
#include <formatio.h>
#include "toolbox.h"
#include <cvirte.h>
#include <userint.h>
#include "打字练习.h"
static int panelHandle;
//声明得分为全局静态变量
static int score;
//声明自定义初始化函数
int InitChar (int choice);
//主函数
int main (int argc, char *argv[])
{
if (InitCVIRTE (0, argv, 0) == 0)
return –1;
if ((panelHandle = LoadPanel (0, " 打字练习.uir", PANEL)) < 0)
return –1;
//初始化要显示的下落字符
InitChar (0);
InitChar (1);
//设置下落字符背景透明
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG, ATTR_TEXT_BGCOLOR, VAL_TRANSPARENT);
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG_1, ATTR_TEXT_BGCOLOR, VAL_TRANSPARENT);
DisplayPanel (panelHandle);
RunUserInterface ();
DiscardPanel (panelHandle);
return 0;
}
//面板回调函数
int CVICALLBACK PanleCB (int panel, int event, void *callbackData,
int eventData1, int eventData2)
{
char str[2];
switch (event)
{
//响应键盘事件
case EVENT_KEYPRESS:
str[0] = '\0';
GetCtrlVal (panelHandle, PANEL_TEXTMSG, str);
// 判断按键是否与下落字符一致
if (eventData1 == str[0])
{
InitChar (0);
// 加分
score ++;
SetCtrlVal (panelHandle, PANEL_NUMERIC_SCORE, score);
break;
}
str[0] = '\0';
GetCtrlVal (panelHandle, PANEL_TEXTMSG_1, str);
if (eventData1 == str[0])
{
InitChar (1);
score ++;
SetCtrlVal (panelHandle, PANEL_NUMERIC_SCORE, score);
break;
}
break;
case EVENT_CLOSE:
QuitUserInterface (0);
break;
}
return 0;
}
//定时器1——产生字符下落效果
int CVICALLBACK timer1 (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int Cheight;
int speed;
int top;
int top1;
switch (event)
{
case EVENT_TIMER_TICK:
// 获得下载字符Top 属性
GetCtrlAttribute (panelHandle, PANEL_TEXTMSG, ATTR_TOP, &top);
GetCtrlAttribute (panelHandle, PANEL_TEXTMSG_1, ATTR_TOP, &top1);
// 获得速度值
GetCtrlVal (panelHandle, PANEL_NUMERIC_SPEED, &speed);
// 设置字符下落速度
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG, ATTR_TOP, top + speed);
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG_1, ATTR_TOP, top1 + speed);
GetCtrlAttribute (panelHandle, PANEL_BACKGROUND, ATTR_HEIGHT, &Cheight);
// 当下落字符淡出屏幕,重新从顶端下落
if (top + speed> Cheight)
{
InitChar (0);
}
if (top1 + speed> Cheight)
{
InitChar (1);
}
break;
}
return 0;
}
//定时器2——计时
int CVICALLBACK timer2 (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
static int i = 0;
int sel;
char str[100];
char strscore[10];
switch (event)
{
case EVENT_TIMER_TICK:
// 设置显示时间
SetCtrlVal (panelHandle, PANEL_NUMERICTHERM_TIME, i);
// 当时间大于等于60s 时,重新开始计时
if (i >= 60)
{
SetCtrlAttribute (panelHandle, PANEL_TIMER, ATTR_ENABLED, 0);
SetCtrlAttribute (panelHandle, PANEL_TIMER_2, ATTR_ENABLED, 0);
Fmt (strscore, "%s<%i", score);
// 计算每分钟打字速度
str[0] = '\0';
strcat (str, " 您一分钟的打字速度是:");
strcat (str, strscore);
strcat (str, " 个字!");
// 弹出提示对话框
sel = GenericMessagePopup (" 提示", str, " 继续", "退出", 0, 0, 0, 0, VAL_GENERIC_
POPUP_BTN1, VAL_GENERIC_POPUP_BTN1, VAL_GENERIC_POPUP_BTN2);
if (sel == 1)
{
// 选择继续按钮
start (panelHandle, PANEL_CMD_START, EVENT_COMMIT, 0, 0, 0);
SetCtrlVal (panelHandle, PANEL_NUMERIC_SCORE, 0);
score = 0;
}
else
{
// 选择退出按钮
PanleCB (panelHandle, EVENT_CLOSE, 0, 0, 0);
}
i = 0;
}
i ++;
break;
}
return 0;
}
//开始按钮
int CVICALLBACK start (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_COMMIT:
// 设置下落字符可见
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG, ATTR_VISIBLE, 1);
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG_1, ATTR_VISIBLE, 1);
// 启动定时器
SetCtrlAttribute (panelHandle, PANEL_TIMER, ATTR_ENABLED, 1);
SetCtrlAttribute (panelHandle, PANEL_TIMER_2, ATTR_ENABLED, 1);
break;
}
return 0;
}
//暂停按钮
int CVICALLBACK pause (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_COMMIT:
SetCtrlAttribute (panelHandle, PANEL_TIMER, ATTR_ENABLED, 0);
SetCtrlAttribute (panelHandle, PANEL_TIMER_2, ATTR_ENABLED, 0);
break;
}
return 0;
}
//随机产生字符
int InitChar (int choice)
{
int CTop;
int CWidth;
char str[2] = {0x00, 0x00};
GetCtrlAttribute (panelHandle, PANEL_BACKGROUND, ATTR_WIDTH, &CWidth);
GetCtrlAttribute (panelHandle, PANEL_BACKGROUND, ATTR_TOP, &CTop);
//产生随机字符
SetRandomSeed (0);
str[0] = Random (97, 123);
//设置两个下落字符的Left 属性
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG + choice, ATTR_LEFT, CWidth * (int)(Random (97, 123)
– 97) / 27);
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG + choice, ATTR_TOP, CTop);
//choice 为0 时,设置控件PANEL_TEXTMSG ,为1 时,设置控件PANEL_TEXTMSG_1
SetCtrlVal (panelHandle, PANEL_TEXTMSG + choice, str);
return 0;
}
(3) 程序注释
① InitChar 自定义函数随机产生下落字符。函数原型为:
int InitChar (int choice);
choice :选择参数,0 为设置PANEL_TEXTMSG 控件,1 为设置PANEL_TEXTMSG_1 控件。
设置两个下落字符属性,将Text Message 控件Tab Order 依次相连,通过设置choice 参数来确定控件。
在LabWindows/CVI 中,函数的定义都是相互平行、相互独立的,也就是说在函数定义时,函数体内不能包含另一个函数的定义,即函数不能嵌套定义,但可以嵌套调用。如:
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG+choice, ATTR_LEFT, CWidth * (int)(Random (97, 123)–97)/27);
嵌套函数是指在某些情况下,将某一函数作为另一函数的参数使用,这一函数就是嵌套
函数。本例中,Random 函数就是嵌套函数。此外,如果不采用PANEL_TEXTMSG + choice 形式,可以将InitChar 函数进行改写:
int InitChar (int choice)
{
int CTop;
int CWidth;
char str[2] = {0x00, 0x00};
GetCtrlAttribute (panelHandle, PANEL_BACKGROUND, ATTR_WIDTH, &CWidth);
GetCtrlAttribute (panelHandle, PANEL_BACKGROUND, ATTR_TOP, &CTop);
//产生随机字符
SetRandomSeed (0);
str[0] = Random (97, 123);
switch (choice)
{
case 0:
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG, ATTR_LEFT, CWidth * (int)(Random
(97, 123) – 97) / 27);
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG, ATTR_TOP, CTop);
SetCtrlVal (panelHandle, PANEL_TEXTMSG, str);
break;
case 1:
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG_1, ATTR_LEFT, CWidth * (int)
(Random (97, 123) – 97) / 27);
SetCtrlAttribute (panelHandle, PANEL_TEXTMSG_1, ATTR_TOP, CTop);
SetCtrlVal (panelHandle, PANEL_TEXTMSG_1, str);
break;
}
return 0;
}
② 面板EVENT_KEYPRESS 事件说明
在面板的EVENT_KEYPRESS 事件中,每个if 语句体最后都加入了break ,主要是为了在执行完成一次击键事件后能够退出,防止出现落下两个相同字母后击一次键而全部消除的错误。
③ GenericMessagePopup 函数
显示一个用户自定义提示信息和按钮的对话框。可设置提示信息和三个Command Button 标题内容。函数原型为:
int GenericMessagePopup (char Title[], char Message[], char Button_Label_1[], char Button_Label_ 2[], char Button_Label_3[], char Response_Buffer[], int Max_Response_Length, int Button_Alignment, int Active_Control, int Enter_Button, int Escape_Button); Title[] :标题字符串。Message[]:提示信息。如果需要显示多行信息,可用“\n”换行。Button_Label_1[] :按钮1 标题。Button_Label_2[] :按钮2 标题,输入0 隐藏该按钮。Button_Label_3[] :按钮3 标题,输入0 隐藏该按钮。
Response_Buffer[] :用户输入字符缓冲区,为用户输入数据长度加上1 个NULL 字符。输入0 隐藏该按钮。
Max_Response_Length :最大允许输入字符长度。Button_Alignment :输入0 表示按钮排列在对话框底部,输入1 表示排列在对话框右侧。Active_Control : 处于激活状态的按钮或输入框,可以取值包括VAL_GENERIC_POPUP_BTN1 、
VAL_GENERIC_POPUP_BTN2 、VAL_GENERIC_POPUP_BTN3 、VAL_GENERIC_POPUP_
INPUT_STRING 。
Enter_Button: 当按下回车键时响应的按钮。不响应该按键,可输入VAL_GENERIC_POPUP_
NO_CTRL 。
Escape_Button:当按下ESC 键时响应的按钮。不响应该按键,可输入VAL_GENERIC_POPUP_ NO_CTRL 。
返回值:返回按下的按钮值,1 表示VAL_GENERIC_POPUP_BTN1,2 表示VAL_GENERIC_
POPUP_BTN2,3 表示VAL_GENERIC_POPUP_BTN3 。
(4)运行效果图
点击工具栏中的Debug Project 按钮,程序开始运行,其效果如图4-12 所示。
图4-12 运行效果图
相关阅读
《虚拟仪器技术,将“软件就是仪器”进行到底!》 |
|