第四讲键盘事件汇总.docx
- 文档编号:10857337
- 上传时间:2023-05-28
- 格式:DOCX
- 页数:20
- 大小:800.76KB
第四讲键盘事件汇总.docx
《第四讲键盘事件汇总.docx》由会员分享,可在线阅读,更多相关《第四讲键盘事件汇总.docx(20页珍藏版)》请在冰点文库上搜索。
第四讲键盘事件汇总
第四讲:
键盘事件
【事件驱动】
所谓的事件驱动其实就是当满足什么样的条件下发生另外一件事情。
比如
当你按下按钮时就会执行相应的代码一样。
由于控制台程序在同一个时刻只能
够执行一个任务,这就造成了在处理事件的效率上太过于低下,通常现代的程
序都是面向多任务的,也就是说同一个时间段可以完成好几个功能,那么如何
才能够做到这一点呢?
就依赖于事件驱动机制。
理解事件驱动就必须要先理解事件,所谓的事件(Event)其实就是触发某
样事情发生的条件,例如键盘按下、单击按钮、双击图标等等。
这些称之为事
件。
而当这些事件发生之后,就会触发另外一件事情。
例如当我们点击“XX
搜索”按钮时就会跳转到搜索的结果界面一样。
当然,采用事件驱动的直接原因就是最大化利用 CPU,由于控制台程序的
限制,我们不能够完成多任务的操作(早期的 DOS 操作系统是单用户单任务的,
即同一个时刻只能够完成一个任务,效率很慢)故此我们需要事件驱动这样的
机制为我们提高效率,为我们最大化利用 CPU 资源。
简而言之,事件驱动就是一个用于提高程序效率的机制。
如果上述还没有
让你明白过来,你可以看看XX百科上的解释,如下:
所谓事件驱动,简单地说就是你点什么按钮(即产生什么事件),电脑执行什么
操作(即调用什么函数).当然事件不仅限于用户的操作. 事件驱动的核心自然
是事件。
从事件角度说,事件驱动程序的基本结构是由一个事件收集器、一个
事件发送器和一个事件处理器组成。
事件收集器专门负责收集所有事件,包括
来自用户的(如鼠标、键盘事件等)、来自硬件的(如时钟事件等)和来自软件
的(如操作系统、应用程序本身等)。
事件发送器负责将收集器收集到的事件分
发到目标对象中。
事件处理器做具体的事件响应工作,它往往要到实现阶段才
完全确定,因而需要运用虚函数机制(函数名往往取为类似于 HandleMsg 的一
个名字)。
对于框架的使用者来说,他们唯一能够看到的是事件处理器。
这也是
他们所关心的内容。
【程序查询方式】
通常检测事件有两种方法,一种称为程序查询法,一种称之为程序中断法。
由于在控制台中我们无法直接使用系统中断(系统中断只有汇编语言可以直接
使用),因此我们无法在高级语言中使用程序中断法检测指令。
故此只能够使用
程序查询方式进行事件的检测。
所谓的程序查询方式实际上是利用一个死循环不停地对事件进行查询,如
果事件发生则执行相应的代码,当代码执行结束之后继续开始下一轮的查询,
由于 CPU 的计算速度相当快,你不用担心会有延时的问题。
这样一轮一轮查询
的方式,我们称之为程序查询方式,也叫轮询法。
程序查询法的流程图如下:
【键盘按下事件】
所谓的键盘事件,是指无论在何时只要键盘按键被按下,即可被程序所截
获,并执行相应的功能代码。
故此负责监听键盘事件的 API 函数会一直处于工
作状态,在 MFC 中我们可以通过死循环进行操作,当然也可以通过我们即将
接触到的多线程编程进行操作。
在这里我们介绍如何利用死循环监听键盘。
【DWORD 类型】
在 MFC 中,通常会见到 DWORD 类型,所谓的 DWORD 类型就是 Double
Word 类型,即双字节类型,MFC 中将 unsigned long 类型使用 typedef 关键字重
新制定为 DWROD 类型。
MFC 框架中的声明如下:
typedef unsigned long DWORD;
故此,DWROD 类型的本质其实是一个无符号的长整型数。
在 MFC 中我
们可以使用 DWORD 类型表示任何双字节的数据。
这里笔者将不再重复如何使
用 DWORD 类型的变量的方法。
事件取值
相关描述
FOCUS_EVENT
摄像头事件,控制对摄像头的操作
KEY_EVENT
键盘事件,控制对键盘的操作
MENU_EVENT
菜单事件,控制对 UI 中菜单的操作
MOUSE_EVENT
鼠标事件,控制对鼠标的操作
WINDOW_BUFFER_SIZE_EVENT
窗体缓冲事件
【INPUT_RECORD 结构体】
INPUT_RECORD 结构体是用于描述输入设备事件的结构体,声明如下:
typedef struct _INPUT_RECORD {
WORD EventType;// 事件类型
union {
KEY_EVENT_RECORDKeyEvent;//键盘事件
MOUSE_EVENT_RECORDMouseEvent;//鼠标事件
WINDOW_BUFFER_SIZE_RECORDWindowBufferSizeEvent; //窗口缓冲
事件
MENU_EVENT_RECORDMenuEvent;//菜单事件
FOCUS_EVENT_RECORDFocusEvent;//摄像头事件
} Event;
} INPUT_RECORD;
当然上述的结构体不像一般的结构体,它有一些复杂,但是也不影响我们
的使用,在此要多说一句的是 union 一词,union 也是 C/C++语言中的关键字,
用于描述“共用体”类型,所谓的共用体实际上是指在进行某些算法编程的时
候,需要使几种不同类型的变量存放到同一段内存单元中。
也就是使用覆盖技
术,几个变量互相覆盖。
这种几个不同的变量共同占用一段内存的结构,在 C
语言中,被称作“共用体”类型结构,简称共用体。
也被称之为“联合体”。
它
是早期的 C 语言过渡到 C++中的产物。
当然这里我们只需要了解一下就行,并
不需要做太多的深究。
【事件类型】
INPUT_RECORD 结构体中的 EnventType 成员用于说明事件的类型,通常
取值如下:
小提示:
EventType 有 5 种事件,对应五个 RECORD。
所以当要使用的是键盘事件时,应
该先判断 EventType 是否为 KeyEvent,然后使用 KEY_EVENT_RECORD,判断现
在的键盘是什么情况。
其他事件也是一样的(一般只使用键盘和鼠标事件)。
键
盘事件通常有字符事件和按键事件。
之所以称为事件,当它们被按下时,事件
被激发。
【KEY_EVENT_RECORD 结构体】
KEY_EVENT_RECORD 用于记录键盘事件的情况,当有键盘事件发生时,
我们就可以使用该结构获取此时键盘的情况。
KEY_EVENT_RECORD 结构体
声明如下;
typedef struct _KEY_EVENT_RECORD
{
BOOL bKeyDown;// TRUE 表示键按下,FALSE 表示键释放
WORD wRepeatCount;// 按键次数
WORD wVirtualKeyCode;// 虚拟键代码
WORD wVirtualScanCode;// 虚拟键扫描码
union {
WCHAR UnicodeChar;// 宽字符
CHAR AsciiChar;// ASCII 字符
} uChar;// 字符
DWORD dwControlKeyState; // 控制键状态
}
KEY_EVENT_RECORD;
【虚拟键码】
接下来为大家介绍一下虚拟键码的概念,由于键盘上的一些特殊键,例如
ctrl、alt、shift、enter 等键不能够直接被控制台程序所识别,故此在 MFC 中引
入了虚拟键码的概念。
虚拟指的是假定存在于思想中而不是现实世界中的一些事物,也只有熟练
使用 DOS 组合语言编写应用程式的程式写作者才有可能指出,为什么对
Windows 键盘处理如此基本的键码是虚拟的而不是真实的。
对於早期的程序员来说,真实的键码由实际键盘硬体产生。
在 Windows 文
件中将这些键码称为「扫描码(scancodes)」。
在 IBM 相容机种上,扫描码 16
是 Q 键,17 是 W 键,18 是 E、19 是 R,20 是 T,21 是 Y 等等。
这时您会发
现,扫描码是依据键盘的实际布局的。
Windows 开发者认为这些代码过於与设
备相关了,於是他们试图通过定义所谓的虚拟键码,以便经由与装置无关的方
十进制
十六进
制
WINUSER.H 识别字
IBM 相容键盘
8
8
VK_BACK
Backspace
9
9
VK_TAB
Tab
12
0C
VK_CLEAR
Lock 关闭时的数字键盘
5
13
0D
VK_RETURN
Enter
16
10
VK_SHIFT
Shift
17
11
VK_CONTROL
Ctrl
18
12
VK_MENU
Alt
19
13
VK_PAUSE
Pause
20
14
VK_CAPITAL
Caps Lock
27
1B
VK_ESCAPE
Esc
32
20
VK_SPACE
Spacebar
33
21
VK_PRIOR
Page Up
34
22
VK_NEXT
Page Down
35
23
VK_END
End
36
24
VK_HOME
Home
37
25
VK_LEFT
左箭头
38
26
VK_UP
上箭头
39
27
VK_RIGHT
右箭头
40
28
VK_DOWN
下箭头
41
29
VK_SELECT
42
2A
VK_PRINT
43
2B
VK_EXECUTE
44
2C
VK_SNAPSHOT
Print Screen
式处理键盘。
其中一些虚拟键码不能在 IBM 相容机种上产生,但可能会在其他
制造商生产的键盘中找到,或者在未来的键盘上找到。
提出虚拟键盘码的好处就在于可以兼容不同布局的键盘。
(不要忘记即使是
笔记本电脑也有小键盘和大键盘之分的哦!
)
常用的虚拟键盘码如下:
45
2D
VK_INSERT
Insert
46
2E
VK_DELETE
Delete
47
2F
VK_HELP
91
5B
VK_LWIN
左 Windows 键
92
5C
VK_RWIN
右 Windows 键
106
6A
VK_MULTIPLY
数字键盘上的*
107
6B
VK_ADD
数字键盘上的+
108
6C
VK_SEPARATOR
109
6D
VK_SUBTRACT
数字键盘上的-
110
6E
VK_DECIMAL
数字键盘上的.
111
6F
VK_DIVIDE
数字键盘上的/
上述是在 MFC 中常用的一些虚拟键码,更多的大家可以到网上进行查询。
使用虚拟键码读者需要注意以下的几个要点:
1、Windows 下的程序通常不需要监视 shift、ctrl 或 alt 键的状态
2、键盘上的字母和数字使用其 ASCII 码直接表示,而不采用虚拟键码
3、Windows 操作系统中使用 VK_LWIN 以及 VK_RWIN 两个键打开“开始菜
单”或者是启动“工作管理员模式”,故此需要谨慎使用这两个虚拟键码。
4、对于键盘上的功能键(F1 – F12)一般不进行处理,当然虚拟键码表中提供
了 24 个功能键的编码。
【键盘事件的操作步骤】
键盘事件的操作步骤如下:
while
(1)
{
监听键盘;
if(有键盘事件发生)
{
if(键盘上有按键被按下)
{
执行被触发的功能代码;
}
}
}
【监听键盘事件】
在 MFC 框架中通常使用 ReadConsoleInput 函数对键盘事件进行侦听,并通
过形参将侦听到的结果保存在 INPUT_RECORD 结构体中。
函数声明如下:
BOOL ReadConsoleInput(
HANDLEhConsoleInput,// 输入设备句柄
PINPUT_RECORDlpBuffer,// 返回数据记录
DWORDnLength,// 要读取的记录数
LPDWORDlpNumberOfEventsRead// 返回已读取的记录数
);
【判断是否有键盘事件发生】
判断是否有键盘事件发生通常使用 INPUT_RECORD 结构体中的
EnventType 进行判断,如果该成员的值与 KEY_ENVENT(键盘事件)等价时,
则证明发生了键盘事件,即:
if(rec.EventType = KEY_EVENT)//如果捕捉到了键盘事件
{
cout<<"发生了键盘事件!
"< } 【判断是否有键被按下】 那么如何判断键盘上是否有键被按下呢? 这很简单,我们需要通过判断 INPUT_RECORD 结构体中的成员 Event 中的 KeyEvent 中的成员 bKeyDown(即 Bool KeyDown)是否为 true 即可。 即: if(rec.Event.KeyEvent.bKeyDown == true)//如果此时有键按下 { cout<<”有键被按下! ”< } 【判断键盘按下的键值】 那么我们怎么才能够知道到底是哪个键被按下呢? 此时我们可以通过 KeyEvent 成员中的 uChar 成员获取该键的 ASCII 码或者是 Unicode 编码,进行 判断。 获取该键的 ASCII 码的代码为: char c = rec.Event.KeyEvent.uChar.AsciiChar;//获取该键的 ASCII 码 获取该键的 Unicode 编码的代码为: WCHAR ch = rec.Event.KeyEvent.uChar.UnicodeChar; 通常我们使用 ASCII 码判断该键。 [例] 键盘事件监听小例子 #include #include using namespace std; void main() { HANDLE in = GetStdHandle(STD_INPUT_HANDLE);//获取键盘句柄 INPUT_RECORD rec;//保存键盘事件结果的结构体 DWORD recCount;//已读取的记录数 while (1)//始终循环监听 { ReadConsoleInput(in, &rec, 1, &recCount);//侦听键盘事件 if(rec.EventType = KEY_EVENT)//如果捕捉到了键盘事件 { if(rec.Event.KeyEvent.bKeyDown == true)//如果此时有键按下 { char c = rec.Event.KeyEvent.uChar.AsciiChar;//获取该键 的 ASCII 码 putchar(c);//打印该键 } } } } 运行效果如下: 当然,光看图是不行的,还需要读者自己去运行代码体会代码中的含义。 【键盘事件综合案例】 此处笔者提供一个综合案例供大家练习和参考! 该案例是一个简单的文本 编辑器,可以模拟打字、按 backspace 退格删除、空格、按 esc 退出等功能。 #include #include #include using namespace std; void main() { HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);//获取控制台输出句柄 HANDLE in = GetStdHandle(STD_INPUT_HANDLE);//获取控制台输出句柄 DWORD dwRes,dwState = 0; COORD crHome = {0,0},crPos; INPUT_RECORD keyRec; CONSOLE_SCREEN_BUFFER_INFO bInfo; cout<<"欢迎您使用基于控制台的文本编辑器! "< cout<<"请按任意键继续! "< getch(); system("cls"); while (1) { ReadConsoleInput(in, &keyRec, 1, &dwRes);//读取屏幕输入 if(keyRec.EventType == KEY_EVENT)//若存在键盘事件 { if (keyRec.Event.KeyEvent.bKeyDown)//当有按键按下时 { dwState = keyRec.Event.KeyEvent.dwControlKeyState;// 获取键盘状态 GetConsoleScreenBufferInfo(out,&bInfo);//获取控制台 信息 SetConsoleCursorPosition(out,crHome);//设置控制台 SetConsoleCursorPosition(out, bInfo.dwCursorPosition);//还原光标位置 switch(keyRec.Event.KeyEvent.wVirtualKeyCode)//判断按 下的键值 { case VK_SPACE: cout<<" ";break; case VK_RETURN: cout<<"\n";break; case VK_BACK: { GetConsoleScreenBufferInfo(out, &bInfo); crPos = bInfo.dwCursorPosition; if (crPos.X! =0) { crPos.X -= 1; } else { crPos.Y -= 1; crPos.X = 60; } SetConsoleCursorPosition(out,crPos); cout<<" "; SetConsoleCursorPosition(out,crPos); }break; case VK_ESCAPE: CloseHandle(out);// 关闭标准输出设备句柄 CloseHandle(in);// 关闭标准输入设备句柄 return; default: break; } char ch = keyRec.Event.KeyEvent.uChar.AsciiChar; if (ch>0x20 && ch<0x7e) { putchar(ch); } } } } }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第四 键盘 事件 汇总
![提示](https://static.bingdoc.com/images/bang_tan.gif)