最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

CodeBlock下的人機交互界面設計

2021-09-02 17:53 作者:liaojiaohong  | 我要投稿


?

人機交互界面指的是計算機系統(tǒng)與用戶之間的接口。通過該接口,一方面,計算機系統(tǒng)向用戶輸出系統(tǒng)的運行狀態(tài)、運行控制和運行結果等方面信息;另一方面,用戶根據(jù)輸出信息向系統(tǒng)輸入相應的指令和數(shù)據(jù)等信息。

?

3.4.1 控制臺窗口和屏幕緩沖區(qū)

控制臺窗口是個二維平面空間,其坐標系統(tǒng)的原點(0, 0)設在窗口左上角,即窗口第一行第一列字符單元的位置。橫軸(X軸)的正向沿原點向右,與窗口的第一行重合,每刻度為一個字符寬度;縱軸(Y軸)的正向沿原點向下,與窗口的第一列重合,每刻度為一個字符高度。窗口中每個字符單元對應一個二維坐標。比如,第5行第32列字符單元的坐標為(31, 4)。如圖3.8所示。

圖3.8 控制臺窗口和屏幕緩沖區(qū)關系示意圖

?

屏幕緩沖區(qū)是個二維數(shù)組,邏輯上可看作一個二維平面空間。數(shù)組第一個元素的下標[0][0]對應此平面空間坐標系統(tǒng)的原點(0, 0),數(shù)組第1維的下標對應坐標系統(tǒng)的縱坐標(Y坐標),第2維下標對應坐標系統(tǒng)的橫坐標(X坐標)。屏幕緩沖區(qū)存放著M行N列字符單元的信息,M和N的大小由系統(tǒng)設置,并可以進行修改。

每個字符單元信息用一個CHAR_INFO結構類型的數(shù)據(jù)來表示,結構成員Char存放字符的碼值(Unicode碼或ASCII碼,取決于系統(tǒng)所采用的字符集),結構成員Attributes存放字符的屬性(字符顯示所用的前景色和背景色)。

操作系統(tǒng)以一定的頻率從屏幕緩沖區(qū)讀取字符單元信息,并顯示在控制臺窗口中。應用程序的輸出信息實際上輸出到了屏幕緩沖區(qū),由此改變了控制臺窗口所顯示的內(nèi)容。初始狀態(tài)下,屏幕緩沖區(qū)坐標系統(tǒng)與控制臺窗口坐標系統(tǒng)重合,窗口中第m行第n列字符的碼值和顏色值存放在屏幕緩沖區(qū)二維數(shù)組中下標為[m-1][n-1]的元素中。利用控制臺函數(shù)可以改變這兩個坐標系統(tǒng)的對應關系,實現(xiàn)特殊的顯示效果。圖3.8表示了控制臺窗口和屏幕緩沖區(qū)的相互關系。

一個控制臺可擁有多個屏幕緩沖區(qū),但只有處于激活狀態(tài)的屏幕緩沖區(qū)內(nèi)容顯示在控制臺窗口中。操作系統(tǒng)在為進程創(chuàng)建控制臺的同時會創(chuàng)建一個屏幕緩沖區(qū)。

進程可調(diào)用函數(shù)CreateConsoleScreenBuffer為其控制臺創(chuàng)建另外的屏幕緩沖區(qū)。

調(diào)用函數(shù)SetConsoleActiveScreenBuffer可以將某個已有的屏幕緩沖區(qū)置為激活狀態(tài),使其內(nèi)容顯示在屏幕窗口中。

不管是否處于激活狀態(tài),屏幕緩沖區(qū)都可以通過句柄來進行讀寫操作,只不過激活狀態(tài)下屏幕緩沖區(qū)的內(nèi)容可以看到,非激活狀態(tài)下看不到而已。

屏幕緩沖區(qū)相關的多個屬性可以獨立進行設置。激活的屏幕緩沖區(qū)屬性值的變化能在控制臺窗口中產(chǎn)生奇妙的外觀效果。屏幕緩沖區(qū)相關的屬性包括:

l?屏幕緩沖區(qū)大小,以字符行和列為單位;

l?文本屬性(文本信息顯示的前景色和背景色);

l?窗口大小和定位(控制臺屏幕緩沖區(qū)在控制臺窗口中顯示時所處的矩形區(qū)域);

l?光標位置、外觀和是否可見;

l?輸出模式(控制字符的輸出處理和行末換行處理)。

屏幕緩沖區(qū)在創(chuàng)建時,它所包含的字符內(nèi)容初始化為空格,光標設為可見并定位在緩沖區(qū)原點(0, 0),而窗口的原點(左上角)與緩沖區(qū)原點置為重合。控制臺屏幕緩沖區(qū)的大小、窗口的大小、文本屬性和光標的外觀取決于用戶或系統(tǒng)的缺省設置。

為獲取控制臺屏幕緩沖區(qū)各種相關屬性的當前值,可分別調(diào)用函數(shù):

GetConsoleScreenBufferInfo;

GetConsoleCursorInfo;

GetConsoleMode。

屏幕緩沖區(qū)光標信息用CONSOLE_CURSOR_INFO結構類型的數(shù)據(jù)表示,成員bVisible表示光標是否可見,成員dwSize表示光標外觀大小,取值范圍為1~100。光標可見時,dwSize的取值從100變?yōu)?,光標外觀大小從充滿整個字符單元變?yōu)槌霈F(xiàn)在單元底部的一條水平線。調(diào)用函數(shù)GetConsoleCursorInfo和SetConsoleCursorInfo分別可以獲得和設置光標屬性值。

由高級控制臺I/O函數(shù)(如getchar,putchar,printf,scanf等)輸出的字符將輸出在光標當前位置,同時光標移動到下一個字符輸出位置。

調(diào)用函數(shù):

GetConsoleScreenBufferInfo和SetConsoleCursorPosition,

分別可以獲得和設置光標在屏幕緩沖區(qū)坐標系統(tǒng)中的當前位置,由此可以控制高級I/O函數(shù)輸出或回顯字符的位置。

字符屬性分為兩類:顏色屬性和DBCS(Double-Byte Character Set,雙字節(jié)字符集)屬性。表3.14中的符號常量在wincon.h頭文件中進行定義。

表3.14 字符屬性符號常量表

屬性

含義

FOREGROUND_BLUE

文本顏色包含藍色

FOREGROUND_GREEN

文本顏色包含綠色

FOREGROUND_RED

文本顏色包含紅色

FOREGROUND_INTENSITY

文本顏色加亮

BACKGROUND_BLUE

背景含藍色

BACKGROUND_GREEN

背景含綠色

BACKGROUND_RED

背景含紅色

BACKGROUND_INTENSITY

背景加亮

COMMON_LVB_LEADING_BYTE

首字節(jié)

COMMON_LVB_TRAILING_BYTE

末字節(jié)

COMMON_LVB_GRID_HORIZONTAL

首行

COMMON_LVB_GRID_LVERTICAL

左列

COMMON_LVB_GRID_RVERTICAL

右列

COMMON_LVB_REVERSE_VIDEO

翻轉前景及背景屬性

COMMON_LVB_UNDERSCORE

下劃線

?

前綴為FOREGROUND的常量值指定文本顏色(文本的前景色)。前綴為BACKGROUND的常量值指定用于填充字符單元背景的顏色。其他常量值用于DBCS屬性。

應用程序可以將前景色和背景色常量值組合起來,獲得不同顏色。例如,下面顏色組合的效果為藍色背景上的亮青色文本。

FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_BLUE

配色問題可以由實驗的輸出試驗確定!

如果不指定背景顏色值,那么背景為黑色,而不指定前景顏色值,文本為黑色。例如,下面顏色組合將產(chǎn)生白色背景上的黑色文本效果。

BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED

每個屏幕緩沖區(qū)字符單元儲存了在“畫”該單元的文本(前景)和背景時所使用的顏色屬性值。應用程序可以分別設置每個字符單元的顏色值,并將顏色值存儲在每個單元CHAR_INFO結構類型數(shù)據(jù)的Attributes成員中。

?

3.4.2 在屏幕上指定位置輸出信息

有多種方法在屏幕指定位置輸出帶屬性的字符串信息,這里介紹其中四種基本方法。

(1) 用標準輸出函數(shù)(putchar, printf, puts等)輸出字符串信息;

//設置光標位置

SetConsoleCursorPosition(output_handle, new_pos);

//輸出字符串string

printf(%s, string);

?

//在字符串輸出位置填充指定的文本屬性

FillConsoleOutputAttribute(output_handle, new_attributes, strlen(string), new_pos, NULL);

其中output_handle是屏幕緩沖區(qū)句柄,new_pos為COORD類型的變量,存放指定的光標位置坐標,new_attributes為WORD類型的變量,存放指定的文本屬性值,string是字符數(shù)組,存放被輸出的字符串。

?

(2) 用函數(shù)WriteConsole輸出字符串信息;

//設置光標位置

SetConsoleCursorPosition?(output_handle, new_pos);

//設置文本屬性

SetConsoleTextAttribute(output_handle, new_attributes);

//輸出字符串

WriteConsole(output_handle, string, strlen(string), NULL, NULL);

變量的含義同上。

?

(3) 用函數(shù)WriteConsoleOutputCharacter輸出字符串信息;

//在指定位置填充與所輸出字符串等長的文本屬性值

FillConsoleOutputAttribute(output_handle, new_attributes, strlen(string), new_pos, NULL);

//在該指定位置輸出字符串

WriteConsoleOutputCharacter(output_handle, string, strlen(string), new_pos, NULL);

?

(4) 用函數(shù)WriteConsoleOutput輸出字符串信息;

CHAR_INFO * lpBuffer;

COORD?pos = {0, 0};

COORD?size = { strlen(string), 1};

SMALL_RECT area?=?{new_pos.X, new_pos.Y, new_pos.X+strlen(string)-1, new_pos.Y};

lpBuffer = (CHAR_INFO *)malloc(size.X * size.Y * sizeof(CHAR_INFO));

for(i=0; i<strlen(string); i++) {

????lpBuffer->Char.AsciiChar?= string[i];

????lpBuffer->Attributes?= new_attributes;

}

WriteConsoleOutput(output_handle, lpBuffer, size, pos, &area);

free(&area);

?

演示并解釋例3.1 menu_ex3_1

?

這種方法使用起來相對復雜一些?;舅枷胧菍⑤敵鲂畔斪饕粋€矩形字符信息塊,設置矩形塊的大小size,矩形塊在窗口中的輸出位置area,將矩形塊內(nèi)字符信息存放在一個動態(tài)存儲緩沖區(qū)lpBuffer內(nèi),字符信息包含了字符的碼值和顏色屬性,最后調(diào)用函數(shù)WriteConsoleOutput將lpBuffer中字符塊信息寫到控制臺屏幕緩沖區(qū)指定位置。

?

3.4.3 彈出窗口的設計

屏幕窗口是個有限的信息顯示區(qū)域。在文本字符界面下,控制臺窗口的大小通常設為80個字符的寬度和25行字符的高度,即每屏可以顯示2000個字符。

彈出窗口設計的基本思路是:

1、確定輸出信息的屏幕位置和大??;

2、將新窗口彈出后所要覆蓋的屏幕區(qū)域字符信息讀入到一塊內(nèi)存緩沖區(qū);

3、在新窗口內(nèi)輸出信息,模擬“彈出”效果。

4、彈出窗口內(nèi)的操作完成后,把保存在內(nèi)存緩沖區(qū)的字符信息寫到其原來所在的屏幕位置,彈出窗口消失,屏幕恢復為窗口彈出之前的外觀。

?

演示并解釋例3.1 menu_ex3_2

?

多層彈出窗口自學

按照這一思路,可以實現(xiàn)多層彈出窗口。窗口的多層彈出和逐層關閉,給屏幕信息的維護帶來了復雜性。為了便于處理,我們用鏈表來模擬堆棧,實現(xiàn)彈出窗口的棧式管理。

彈出窗口棧式管理用到以下結構類型。

????typedef struct layer_node {

????????char LayerNo; ????????????//彈出窗口層數(shù)

????????SMALL_RECT rcArea; ????//彈出窗口區(qū)域坐標

????????CHAR_INFO *pContent; ??//彈出窗口區(qū)域字符單元原信息存儲緩沖區(qū)

????????char *pScrAtt; ???????????//彈出窗口區(qū)域字符單元原屬性值存儲緩沖區(qū)

????????struct layer_node *next;?????//下一結點的地址

????} LAYER_NODE;

利用這種結構類型的數(shù)據(jù)可以模擬出如圖3.10所示的堆棧,對彈出窗口信息進行管理。

圖3.10 彈出窗口信息堆棧

?

LAYER_NODE結構的5個成員分別表示了彈出窗口相關信息。LayerNo表示當前彈出窗口的層數(shù);rcArea表示當前彈出窗口矩形區(qū)域的位置和大小;pContent指向的動態(tài)存儲區(qū)存放被彈出窗口所覆蓋區(qū)域的原字符單元信息,用于當前彈出窗口關閉后恢復原屏幕窗口信息;pScrAtt指向的動態(tài)存儲區(qū)存放內(nèi)容的用途與輸入處理相關,將在下面的輸入處理中進行介紹;next存放下層彈出窗口相關信息的地址。

堆棧棧頂由LAYER_NODE結構指針TopLayer來指示,初值為NULL。系統(tǒng)界面初始化完成之后,屏幕窗口看作第一層彈出窗口,窗口相關信息用LAYER_NODE結構類型的動態(tài)存儲區(qū)存放后入棧,結構指針TopLayer指向棧頂;以后每彈出一層窗口,就執(zhí)行一次入棧操作。關閉彈出窗口時,用TopLayer指向的LAYER_NODE結構類型數(shù)據(jù)恢復被覆蓋的屏幕區(qū)域,將TopLayer指向下一層結點,釋放用過的動態(tài)存儲區(qū),完成出棧操作。

?

3.4.4 鍵盤和鼠標輸入信息的獲取

ReadConsoleInput函數(shù)原型為:

BOOL WINAPI ReadConsoleInput(HANDLE hConsoleInput, PINPUT_RECORD

lpBuffer, DWORD nLength, LPDWORD lpNumberOfEventsRead);

功能:用來從控制臺輸入緩沖區(qū)讀取輸入數(shù)據(jù),并將讀出數(shù)據(jù)從輸入緩沖區(qū)刪除掉。

函數(shù)ReadConsoleInput被調(diào)用時,如果緩沖區(qū)中沒有輸入數(shù)據(jù),函數(shù)將等待下去,直到讀到至少一條記錄后返回。讀到的記錄存放在參數(shù)lpBuffer所指向的內(nèi)存單元。

記錄用INPUT_RECORD結構類型的數(shù)據(jù)來表示;

成員EventType表示事件的類型;

成員Event是聯(lián)合類型,存放事件的具體內(nèi)容。

編程時,需要對EventType的值為KEY_EVENT(鍵盤輸入)或MOUSE_EVENT(鼠標輸入)的兩類事件進行處理。

EventType的值為KEY_EVENT時,Event的聯(lián)合成員KeyEvent存放了按鍵相關信息。KeyEvent是KEY_EVENT_RECORD結構類型,其成員bKeyDown表明鍵是被按下(TRUE)還是被釋放(FALSE),成員wRepeatCount表明按鍵重復的次數(shù),成員wVirtualKeyCode存放按鍵的虛擬鍵碼,成員wVirtualScanCode存放按鍵的虛擬掃描碼,成員uChar存放按鍵的ASCII碼或Unicode碼(取決于系統(tǒng)所采用的字符集),成員dwControlKeyState表示有哪些控制鍵被同時按下。我們每按一次鍵會產(chǎn)生兩個事件記錄,一個記錄表示鍵被按下,另一條記錄表示鍵被釋放。常用鍵的各種碼值參見附錄。

EventType的值為MOUSE_EVENT時,Event的聯(lián)合成員MouseEvent存放了鼠標操作相關信息。MouseEvent是MOUSE_EVENT_RECORD結構類型,其成員dwMousePosition存放了鼠標操作時的坐標位置,表明鼠標處于窗口中的某行和某列的字符單元位置上,成員dwButtonState表明鼠標哪些按鈕被按下,取值可為下面符號常量之一或多個符號常量的組合值:

FROM_LEFT_1ST_BUTTON_PRESSED值為1,表示按下了鼠標最左邊按鈕;

RIGHTMOST_BUTTON_PRESSED值為2,表示按下了鼠標最右邊按鈕;

FROM_LEFT_2ND_BUTTON_PRESSED值為4,表示按下了鼠標左起第二個按鈕;

FROM_LEFT_3RD_BUTTON_PRESSED值為8,表示按下了鼠標左起第三個按鈕;

FROM_LEFT_4TH_BUTTON_PRESSED值為16,表示按下了鼠標左起第四個按鈕。

成員dwControlKeyState表示在鼠標事件發(fā)生時有哪些控制鍵被同時按下,成員dwEventFlags表示鼠標事件的具體類型,取值為以下符號常量:

MOUSE_MOVED值為1,表示鼠標移動事件;

DOUBLE_CLICK值為2,表示鼠標雙擊事件;

MOUSE_WHEELED值為4,表示鼠標滾輪滾動事件。

?

3.4.5 輸入處理(略)

?(1) 鍵盤輸入處理

按照表3.15進行處理,對其他按鍵不予響應。

表3.15 主界面下的鍵盤輸入處理設計

按鍵

系統(tǒng)響應

F1

執(zhí)行幫助菜單下的幫助主題子菜單對應功能模塊

Alt+X

執(zhí)行文件菜單下的退出系統(tǒng)子菜單對應功能模塊

Alt+F

清除當前選中菜單項標記,標記文件菜單項并彈出文件菜單的子菜單

Alt+M

清除當前選中菜單項標記,標記數(shù)據(jù)維護菜單項并彈出數(shù)據(jù)維護菜單的子菜單

Alt+Q

清除當前選中菜單項標記,標記數(shù)據(jù)查詢菜單項并彈出數(shù)據(jù)查詢菜單的子菜單

Alt+S

清除當前選中菜單項標記,標記數(shù)據(jù)統(tǒng)計菜單項并彈出數(shù)據(jù)統(tǒng)計菜單的子菜單

Alt+H

清除當前選中菜單項標記,標記幫助菜單項并彈出幫助菜單的子菜單

向左←

清除當前選中菜單項標記,標記左側菜單項

向右→

清除當前選中菜單項標記,標記右側菜單項

向下↓

彈出當前菜單項的子菜單

f或F

清除當前選中菜單項標記,標記文件菜單項并彈出文件菜單的子菜單

m或M

清除當前選中菜單項標記,標記數(shù)據(jù)維護菜單項并彈出數(shù)據(jù)維護菜單的子菜單

q或Q

清除當前選中菜單項標記,標記數(shù)據(jù)查詢菜單項并彈出數(shù)據(jù)查詢菜單的子菜單

s或S

清除當前選中菜單項標記,標記數(shù)據(jù)統(tǒng)計菜單項并彈出數(shù)據(jù)統(tǒng)計菜單的子菜單

h或H

清除當前選中菜單項標記,標記幫助菜單項并彈出幫助菜單的子菜單

回車

彈出當前菜單項的子菜單

?

鍵盤輸入處理時,要考慮輸入處理的優(yōu)先級,應先響應快捷鍵,再響應組合鍵,最后響應單鍵。以Alt組合鍵為例,判斷組合鍵的方法為:

ReadConsoleInput(hIn, &inRec, 1, &res);????//從輸入緩沖區(qū)讀取一條輸入記錄

if (inRec.EventType == KEY_EVENT && inRec.Event.KeyEvent.bKeyDown) {

//輸入事件類別為KEY_EVENT,且事件由鍵被按下所觸發(fā)

????vkc = inRec.Event.KeyEvent.wVirtualKeyCode; ???//提取虛擬鍵碼

????asc = inRec.Event.KeyEvent.uChar.AsciiChar; ????//提取ASCII碼

????if (inRec.Event.KeyEvent.dwControlKeyState?????//如果左或右Alt鍵被按下

??????& (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) {

????????…… ?//進一步判斷與Alt組合的另一個鍵,并做響應處理

????}

????…… ?//非Alt組合鍵的響應處理

}

常用控制鍵所對應的符號常量在wincon.h頭文件中進行了定義,如表3.16所示。

表3.16 控制鍵對應符號常量表

鍵名

符號常量

鍵名

符號常量

右Alt

RIGHT_ALT_PRESSED

1

左Alt

LEFT_ALT_PRESSED

2

右Ctrl

RIGHT_CTRL_PRESSED

4

左Ctrl

LEFT_CTRL_PRESSED

8

左右Shift

SHIFT_PRESSED

16

數(shù)字鎖定

NUMLOCK_ON

32

屏幕鎖定

SCROLLLOCK_ON

64

大寫鎖定

CAPSLOCK_ON

128

?

(2) 鼠標輸入處理

熱區(qū)本是網(wǎng)頁設計中用到的一個概念,是指網(wǎng)頁上建有鏈接的區(qū)域。我們將熱區(qū)這個概念借用到程序設計中人機交互界面的設計上來,用來指界面上需要對鼠標事件(包括鼠標移動、滾輪轉動、雙擊和任意一到多個鍵的按下)產(chǎn)生反應的區(qū)域。

基于這一思想,可用一個字符(8個二進制位)來表示窗口中某個字符單元的屬性。如圖3.12所示,這8個二進制位分為三段:0~1比特為A1,2~5比特為A2,6~7比特為A3。A1的取值范圍為0~3,用來表示字符單元的“高度”,即該字符單元上彈出窗口的層數(shù),0表示字符單元處于系統(tǒng)主界面層,沒有彈出窗口覆蓋該字符單元,這樣彈出窗口的層數(shù)最多可到3層;A2取值范圍為0~15,用來表示字符單元的熱區(qū)編號,0表示字符單元不屬于熱區(qū),這樣同一層上的熱區(qū)最多可為15個;A3取值范圍為0~3,在A2取值不為0時用來表示字符單元的熱區(qū)類型,0代表按鈕類型,1代表輸入框類型,2代表下拉選框類型,3保留備用。不同類型的熱區(qū)被鼠標擊中時,系統(tǒng)可以分別進行處理。本課程設計中,字符單元的屬性用8個二進制位來存放,剛好夠用。在其他應用程序開發(fā)中,如果彈出菜單超過3層,或某層窗口中熱區(qū)超過15個,或熱區(qū)類型超過4類,可以考慮用16個二進制位來存放字符單元屬性。

7

6

5

4

3

2

1

0

?

?

?

?

?

?

?

?

A3

A2

A1

圖3.12 字符單元屬性的表示

?

前面在介紹彈出窗口設計時,彈出窗口的棧式管理用到以下結構類型:

????typedef struct layer_node {

????????char LayerNo; ????????????//彈出窗口層數(shù)

????????SMALL_RECT rcArea; ????//彈出窗口區(qū)域坐標

????????CHAR_INFO *pContent; ??//彈出窗口區(qū)域字符單元原信息存儲緩沖區(qū)

????????char *pScrAtt; ???????????//彈出窗口區(qū)域字符單元原屬性值存儲緩沖區(qū)

????????struct layer_node *next;?????//下一結點的地址

????} LAYER_NODE;

其中,結構成員pScrAtt用來指向一個動態(tài)存儲區(qū),該存儲區(qū)存放被彈出窗口所覆蓋字符單元的原先屬性值,在彈出窗口關閉時用于恢復窗口彈出前屏幕字符單元的屬性。

系統(tǒng)界面初始化完成之后,屏幕顯示系統(tǒng)的主界面(如圖3.9所示)。用一個字符數(shù)組ScrAtt存放屏幕上所有字符單元的屬性值,字符單元的坐標X和Y與存放其屬性值的數(shù)組元素下標n的關系為:

n =?Y × 屏幕緩沖區(qū)的寬度 + X

主界面中只有5個主菜單項顯示區(qū)域為熱區(qū),依次編號1~5,熱區(qū)類型為按鈕型。此后,如果有窗口彈出(彈出菜單也是彈出窗口),則將窗口所覆蓋區(qū)域字符單元的信息和屬性分別保存起來,執(zhí)行彈出窗口信息入棧操作,然后在彈出窗口區(qū)域輸出提示信息,將彈出窗口字符單元的屬性值寫入數(shù)組ScrAtt對應元素以設置熱區(qū)。鼠標輸入處理時,取鼠標所在字符單元的屬性值,根據(jù)字符單元的層數(shù)、熱區(qū)編號和熱區(qū)類型,結合鼠標事件類型做出相應處理。當彈出窗口關閉時,用所保存的彈出窗口區(qū)域字符單元原先的屬性值修改數(shù)組ScrAtt對應元素值,恢復窗口彈出前屏幕字符單元的屬性,最后執(zhí)行彈出窗口信息出棧操作。

?

3.4.6 菜單操作與系統(tǒng)功能函數(shù)的調(diào)用

按照概要設計,系統(tǒng)功能分為五個模塊,各模塊所包含的子模塊共有22個,分別用22個函數(shù)實現(xiàn)相應功能。其中,數(shù)據(jù)加載函數(shù)和界面初始化函數(shù)只在系統(tǒng)啟動時執(zhí)行一次,以后不再執(zhí)行。其余20個函數(shù)可以通過菜單操作或快捷鍵執(zhí)行相應功能。

例3.3?在圖3.9所示的主界面下,實現(xiàn)菜單操作與系統(tǒng)功能函數(shù)的調(diào)用。本例中給出了函數(shù)SysRun和函數(shù)ExeFunction的定義,分別用于菜單操作和系統(tǒng)功能函數(shù)的調(diào)用。例子中調(diào)用了例3.1和例3.2中的函數(shù),而其余函數(shù)的定義沒有給出。

#include "dorm.h"

?

void SysRun(?)

{

????INPUT_RECORD inRec;

????DWORD res;

????COORD pos = {0, 0};

????BOOL bRet = TRUE;

????int i, loc, num;

????int cNo, cAtt; ?????//cNo:字符單元層號, cAtt:字符單元屬性

????char vkc, asc; ?????//vkc:虛擬鍵代碼, asc:字符的ASCII碼值

?

????while (bRet) { ?// 循環(huán)

????????ReadConsoleInput(hIn, &inRec, 1, &res);

????????if (inRec.EventType == MOUSE_EVENT) {

????????????pos = inRec.Event.MouseEvent.dwMousePosition;?/*?pos 是坐標 */

????????????cNo = ScrAtt[pos.Y * ScrCol + pos.X] & 3;

????????????cAtt = ScrAtt[pos.Y * ScrCol + pos.X] >> 2;

????????????if (cNo == 0) {

????????????????if (cAtt > 0 && cAtt != SelMenu && TopLayer->LayerNo > 0) {

????????????????????PopOff();

????????????????????SelSMenu = 0;

????????????????????PopMenu(cAtt);

????????????????}

????????????}

????????????else if (cAtt > 0) {

????????????????TagSMenu(cAtt);

????????????}

?

????????????if (inRec.Event.MouseEvent.dwButtonState

????????????????== FROM_LEFT_1ST_BUTTON_PRESSED) {

????????????????if (cNo == 0) {

????????????????????if (cAtt > 0) {

????????????????????????PopMenu(cAtt);

????????????????????}

????????????????????else if (TopLayer->LayerNo > 0) {

????????????????????????PopOff();

????????????????????????SelSMenu = 0;

????????????????????}

????????????????}

????????????????else {

????????????????????if (cAtt > 0) {

????????????????????????PopOff();

????????????????????????SelSMenu = 0;

????????????????????????bRet = ExeFunction(SelMenu, cAtt);

????????????????????}

????????????????}

????????????}

????????????else if (inRec.Event.MouseEvent.dwButtonState

?????????????????????== RIGHTMOST_BUTTON_PRESSED) {

????????????????if (cNo == 0) {

????????????????????PopOff();

????????????????????SelSMenu = 0;

????????????????}

????????????}

????????}

????????else if (inRec.EventType == KEY_EVENT

?????????????????&& inRec.Event.KeyEvent.bKeyDown) {

????????????vkc = inRec.Event.KeyEvent.wVirtualKeyCode;

????????????asc = inRec.Event.KeyEvent.uChar.AsciiChar;

????????????//系統(tǒng)快捷鍵的處理

????????????if (vkc == 112) { ???//F1鍵

????????????????if (TopLayer->LayerNo != 0) {

????????????????????PopOff();

????????????????????SelSMenu = 0;

????????????????}

????????????????bRet = ExeFunction(5, 1); ?//F1幫助主題

????????????}

????????????else if (inRec.Event.KeyEvent.dwControlKeyState

?????????????????????& (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) {

????????????????switch (vkc) { ?//組合鍵Alt+字母

????????????????????case 88: if (TopLayer->LayerNo != 0) {

?????????????????????????????????PopOff();

?????????????????????????????????SelSMenu = 0;

?????????????????????????????}

?????????????????????????????bRet = ExeFunction(1,4); break; ?//Alt+X 退出

????????????????????case 70: PopMenu(1); break; ?//Alt+F

????????????????????case 77: PopMenu(2); break; ?//Alt+M

????????????????????case 81: PopMenu(3); break; ?//Alt+Q

????????????????????case 83: PopMenu(4); break; ?//Alt+S

????????????????????case 72: PopMenu(5); break; ?//Alt+H

????????????????}

????????????}

????????????else if (asc == 0) { ?//方向鍵的處理

????????????????if (TopLayer->LayerNo == 0) { ?//未彈出子菜單時

????????????????????switch (vkc) { ?//方向鍵(左、右、下)的處理

????????????????????????case 37: SelMenu--;

?????????????????????????????????if (SelMenu == 0) {

?????????????????????????????????????SelMenu = 5;

?????????????????????????????????}

?????????????????????????????????TagMenu(SelMenu); break;

????????????????????????case 39: SelMenu++;

?????????????????????????????????if (SelMenu == 6) {

?????????????????????????????????????SelMenu = 1;

?????????????????????????????????}

?????????????????????????????????TagMenu(SelMenu); break;

????????????????????????case 40: PopMenu(SelMenu);

?????????????????????????????????TagSMenu(1); break;

????????????????????}

????????????????}

????????????????else { ?//已彈出子菜單時

????????????????????for (loc=0,i=1; i<SelMenu; i++) {

????????????????????????loc += cSMenu[i-1];

????????????????????} ?//找到子菜單第一項在子菜單數(shù)組中的位置(下標)

????????????????????switch (vkc) { ?//方向鍵(左、右、上、下)的處理

????????????????????????case 37: SelMenu--;

?????????????????????????????????if (SelMenu < 1) {

?????????????????????????????????????SelMenu = 5;

?????????????????????????????????}

?????????????????????????????????TagMenu(SelMenu); PopOff();

?????????????????????????????????PopMenu(SelMenu); TagSMenu(1);

?????????????????????????????????break;

????????????????????????case 38: num = SelSMenu - 1;

?????????????????????????????????if (num < 1) {

?????????????????????????????????????num = cSMenu[SelMenu-1];

?????????????????????????????????}

?????????????????????????????????if (strlen(SMenu[loc+num-1]) == 0) {

?????????????????????????????????????num--;

?????????????????????????????????}

?????????????????????????????????TagSMenu(num); break;

????????????????????????case 39: SelMenu++;

?????????????????????????????????if (SelMenu > 5) {

?????????????????????????????????????SelMenu = 1;

?????????????????????????????????}

?????????????????????????????????TagMenu(SelMenu); PopOff();

?????????????????????????????????PopMenu(SelMenu); TagSMenu(1);

?????????????????????????????????break;

????????????????????????case 40: num = SelSMenu + 1;

?????????????????????????????????if (num > cSMenu[SelMenu-1]) {

?????????????????????????????????????num = 1;

?????????????????????????????????}

?????????????????????????????????if (strlen(SMenu[loc+num-1]) == 0) {

?????????????????????????????????????num++;

?????????????????????????????????}

?????????????????????????????????TagSMenu(num); break;

????????????????????}

????????????????}

????????????}

????????????else if ((asc-vkc == 0) || (asc-vkc == 32)){ ?//按下普通鍵

????????????????if (TopLayer->LayerNo == 0) { ?//未彈出子菜單時

????????????????????switch (vkc) {

????????????????????????case 70: PopMenu(1); break; ?//f或F

????????????????????????case 77: PopMenu(2); break; ?//m或M

????????????????????????case 81: PopMenu(3); break; ?//q或Q

????????????????????????case 83: PopMenu(4); break; ?//s或S

????????????????????????case 72: PopMenu(5); break; ?//h或H

????????????????????????case 13: PopMenu(SelMenu); ??//回車

?????????????????????????????????TagSMenu(1); break;

????????????????????}

????????????????}

????????????????else { ?//已彈出子菜單時的鍵盤輸入處理

????????????????????if (vkc == 27) { ?//按下ESC鍵時, 關閉子菜單

????????????????????????PopOff();

????????????????????????SelSMenu = 0;

????????????????????}

????????????????????else if(vkc == 13) { //|| vkc == 32

????????????????????????num = SelSMenu;

????????????????????????PopOff();

????????????????????????SelSMenu = 0;

????????????????????????bRet = ExeFunction(SelMenu, num);

????????????????????}

????????????????????else {

????????????????????????for (loc=0,i=1; i<SelMenu; i++) {

????????????????????????????loc += cSMenu[i-1];

????????????????????????}

????????????????????????for (i=loc; i<loc+cSMenu[SelMenu-1]; i++) {

????????????????????????????if (strlen(SMenu[i])>0 && vkc==SMenu[i][1]) {

????????????????????????????????PopOff();

????????????????????????????????SelSMenu = 0;

????????????????????????????????bRet = ExeFunction(SelMenu, i-loc+1);

????????????????????????????}

????????????????????????}

????????????????????}

????????????????}

????????????}

????????}

????}

}

?

BOOL ExeFunction(int m, int s)

{

????BOOL bRet = TRUE;

????BOOL (*pFunction[cSMenu[0]+cSMenu[1]+cSMenu[2]+cSMenu[3]+cSMenu[4]])(void);

????int i, loc;

?

????//pFunction

????pFunction[0] = DataSave;

????pFunction[1] = DataBackup;

????pFunction[2] = DataRestore;

????pFunction[3] = SySexit;

????pFunction[4] = MaintainSex;

????pFunction[5] = MaintainType;

????pFunction[6] = NULL;

????pFunction[7] = MaintainDorm;

????pFunction[8] = MaintainStu;

????pFunction[9] = MaintainCharge;

????pFunction[10] = QuerySex;

????pFunction[11] = QueryType;

????pFunction[12] = NULL;

????pFunction[13] = QueryDorm;

????pFunction[14] = QueryStu;

????pFunction[15] = QueryCharge;

????pFunction[16] = StatIn;

????pFunction[17] = StatType;

????pFunction[18] = StatCharge;

????pFunction[19] = StatUncharge;

????pFunction[20] = HelpTopic;

????pFunction[21] = NULL;

????pFunction[22] = AboutDorm;

?

????for (i=1,loc=0; i<m; i++) {

????????loc += cSMenu[i-1];

????}

????loc += s - 1;

?

????if (pFunction[loc] != NULL) {

????????bRet = (*pFunction[loc])();

????}

?

????return bRet;

}

?

演示并解釋例3.1 menu_ex3_2


CodeBlock下的人機交互界面設計的評論 (共 條)

分享到微博請遵守國家法律
启东市| 新乡县| 永宁县| 小金县| 宾川县| 杭锦后旗| 洱源县| 连云港市| 阳东县| 正镶白旗| 青阳县| 那坡县| 大宁县| 浙江省| 固阳县| 鄄城县| 新密市| 原阳县| 天柱县| 修文县| 贞丰县| 平果县| 韶关市| 福鼎市| 鄂州市| 嵊泗县| 九龙县| 会泽县| 西峡县| 梅河口市| 理塘县| 蒲江县| 灌阳县| 余干县| 蒙自县| 玉山县| 巢湖市| 从化市| 扎鲁特旗| 渑池县| 呼图壁县|