免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
UCGUI窗體管理及消息處理機(jī)制分析
2010-01-14 17:13

UCGUI窗體管理及消息處理機(jī)制分析

UCGUI窗體管理及消息處理機(jī)制分析

----多對(duì)話框/模態(tài)窗體/透明窗體支持分析

作者:ucgui

日期: 2005-09-08[v1.0.0.0 2005-06-30完成]

來源: http://www.ucgui.com

版本: v1.0.0.1

版本
修改說明
時(shí)間

v1.0.0.0
ü    實(shí)現(xiàn)UCGUI中多對(duì)話框支持。
2005-06-30

v1.0.0.1
ü    增加UCGUI中各種基本消息介紹 。

ü    增加窗體消息LOOP機(jī)制介紹。

ü    增加對(duì)話框結(jié)構(gòu)說明及其消息LOOP處理移到M ainTask函數(shù)中由用戶處理的原因剖析,并詳細(xì)分析了對(duì)話框中按鈕的點(diǎn)擊消息傳送到用戶自定義對(duì)話框回調(diào)函數(shù)中處理的傳遞流程。

ü    增加外部輸入設(shè)備消息處理機(jī)制介紹。如滑動(dòng)操作外設(shè)MOUSE及觸摸屏輸入消息(WM_TOUCH)的處理機(jī)制,及按鍵式操作外設(shè)消息(WM_KEY)處理機(jī)制。

ü    增加一種更簡單的多對(duì)話框支持的方法及說明。

ü    增加模態(tài)對(duì)話框?qū)崿F(xiàn)原理分析。

ü    增加透明窗體實(shí)現(xiàn)原理分析。
2005-09-08




問題的提出:

        [求助]關(guān)于對(duì)話框處理程序中,想在OK按鈕按下后想彈出一個(gè)消息框,該怎么做?直接加在程序中好像不行,如何讓消息框彈出后成為模態(tài)窗體呢?請版主幫幫忙。

        [解析]在UCGUI中,對(duì)話框只支持單個(gè)對(duì)話框窗體,不支持多個(gè)獨(dú)立的對(duì)話框,現(xiàn)在我們從其源碼來分析一下它為什么支持單個(gè)對(duì)話框窗體以及如何改進(jìn)它以支持多個(gè)獨(dú)立對(duì)話框,要講解這個(gè)問題我們必須首先理解UCGUI中的窗體消息LOOP,沒有消息LOOP窗體就是死水一潭,不能接受任何外界的輸入,只是一個(gè)畫在那里的圖畫而已。

        [聲明]本文中提到的源碼均為UCGUI3.24版源碼,新版UCGUI源碼會(huì)有改動(dòng),請下載本文示例代碼來結(jié)合閱讀本文。

摘要: 本文主要介紹了UCGUI中的對(duì)話框的消息處理機(jī)制,并指出在現(xiàn)有UCGUI上如何增加多窗體支持,并在分析解決問題時(shí)著重介紹了其輸入設(shè)備消息WM_TOUTCH及WM_KEY兩類消息處理方法,并同時(shí)初步指出一種在UCGUI中實(shí)現(xiàn)模態(tài)對(duì)話框以及透明窗體的原理說明,不還有窗體重畫消息WM_PAINT消息處理原理。

一、各種基本消息介紹及處理流程----對(duì)話框內(nèi)部消息流轉(zhuǎn)及外部消息LOOP分析.

UCGUI是采用的消息驅(qū)動(dòng)的,它專門有對(duì)外的一套收集消息的接口, 我在模似器中, 就是通過LCD模擬顯示屏窗口的MOUSE消息,將MOUSE消息傳入到這個(gè)接口中, 以驅(qū)動(dòng)UCGUI中的窗體的。

UCGUI中的消息驅(qū)動(dòng)其實(shí)與WINDOWS的是類似的,幾種基本的消息與WINDOWS是一樣的,但UCGUI的更簡單且消息更少,對(duì)于一些消息的處理得也很簡化,沒有WINDOWS那么多的消息種類及復(fù)雜處理。在WINDOWS中,如我們處理按鈕控件的點(diǎn)擊事件的是在WM_COMMAND消息中,通過按鈕的標(biāo)志ID來區(qū)分不同的按鈕,所以按鈕標(biāo)志ID必須不同的,否則無法區(qū)別開(除非不在父窗體的WM_COMMAND消息中處理)。

UCGUI中一些基本的消息如下:

        WM_CREATE---窗體創(chuàng)建消息,每創(chuàng)建一個(gè)窗體完后都會(huì)向該窗體發(fā)送此消息,如WM_CreateWindowAsChild創(chuàng)建完窗體均會(huì)發(fā)一此消息,但在UCGUI中對(duì)于此消息的很少處理,如果用戶想在對(duì)話框之后做些初始化操作或是創(chuàng)建其它子窗體的動(dòng)作,可以處理此消息,不過對(duì)話框一般有專門的初始化消息WM_INIT_DIALOG,它是在創(chuàng)建對(duì)話框后發(fā)送的。

        WM_SHOW-----顯示窗體消息,此消息在UCGUI中各控件窗體內(nèi)均未作處理,如果你通過消息發(fā)送函數(shù)來發(fā)送這類沒有在UCGUI中各窗體中處理的消息,是沒有有什么響應(yīng)的,不要感到奇怪。要顯示窗體一般是通過WM_ShowWindow()函數(shù)實(shí)現(xiàn)的,這個(gè)函數(shù)做的也就是改變窗體顯示標(biāo)志[WM_SF_ISVIS],并使窗體矩形區(qū)域無效[WM_InvalidateWindow()]以產(chǎn)生重畫消息。

        WM_SET_ENABLE---設(shè)置窗體不能使用消息,UCGUI中有一種復(fù)選框?yàn)椴豢筛淖兊?,但是這個(gè)功能也不完全,如果你對(duì)著UCGUI中的按鈕使用WM_DisableWindow()來設(shè)置其無效,按鈕照樣還是可以使用,不過要改進(jìn)這些小毛病還是很容易的,這里只是提醒大家UCGUI中很多沒有實(shí)現(xiàn)的小地方,不要到時(shí)候使用時(shí)感到很奇怪,感覺到奇怪時(shí)最好去看看源碼,看看源碼中是否實(shí)現(xiàn)了此功能,不要郁悶。

        WM_PAINT ----窗體重畫消息,當(dāng)窗體所在區(qū)域全部或是部分區(qū)域無效時(shí),系統(tǒng)將會(huì)發(fā)出該重畫消息,將無效區(qū)域重畫,但UCGUI中的處理比較簡單,都是將窗體全部區(qū)域重畫;如果用戶自己想在窗體上畫上一些信息,一般都在在該消息當(dāng)中畫,UCGUI中的各種提供的系統(tǒng)控件都必須在其系統(tǒng)的提供的消息回調(diào)函數(shù)中處理此消息來畫出控件。當(dāng)由外部輸入操作引起無效窗體區(qū)域產(chǎn)生時(shí),系統(tǒng)都會(huì)在消息處理中發(fā)送該消息到窗體消息回調(diào)函數(shù)中,以重畫此窗體,在下面講解消息循環(huán)機(jī)制時(shí)將會(huì)著重講解到該消息的產(chǎn)生。

[透明窗體]---經(jīng)常有朋友想知道在UCGUI中如何實(shí)現(xiàn)透明窗體,透明窗

窗體顯示在前臺(tái)時(shí),可以看到部分位于其窗體后的內(nèi)容,即透過窗體可     以看到窗體背后的圖象。在UCGUI中有關(guān)于透明窗體的設(shè)置選項(xiàng),可是

沒有實(shí)現(xiàn)此功能,其實(shí)要實(shí)現(xiàn)原理如下:第一透明窗體及其所有子窗體

都必須透明處理;第二是對(duì)于所有有透明屬性的窗體,在繪圖時(shí)必須使

用透明填充功能的矩形填充函數(shù),主要是修改窗體的WM_PAINT消息中畫

窗體時(shí)的矩形填充函數(shù)為透明的矩形填充;第三透明的矩形填充函數(shù)的

實(shí)現(xiàn),通常情況下的矩形填充是以當(dāng)前前景色來填充,那么關(guān)鍵就是實(shí)

現(xiàn)畫點(diǎn)函數(shù)的透明填充,要使一個(gè)透明,可以取當(dāng)前顯存中存點(diǎn)的點(diǎn)的

RGB顏色,然后再與當(dāng)前要畫的顏色按照一個(gè)比例進(jìn)行混合得一個(gè)新的

RGB值,再將此值畫以屏幕上就可能實(shí)現(xiàn)透明填充的效果。

        WM_TOUCH----處理類似MOUSE的滑動(dòng)操作方式的輸入外設(shè)的消息,如觸摸屏一般都是將其消息從硬件接收到后轉(zhuǎn)化為該消息形式發(fā)送出去,該消息中必須包含消息在屏幕中的發(fā)生位置坐標(biāo)及輸入設(shè)備狀態(tài)(按下狀態(tài)或彈起狀態(tài)),此消息在任務(wù)消息循環(huán)中循環(huán)處理,一旦產(chǎn)生就會(huì)發(fā)送給當(dāng)前焦點(diǎn)窗體,在后面將詳細(xì)講解該消息的處理機(jī)制。

        WM_KEY------處理類似KEY的按鍵式操作的輸入外設(shè)的消息,消息中必須包含按鍵的按下或彈起狀態(tài),此消息也是在任務(wù)消息循環(huán)中循環(huán)處理,一旦產(chǎn)生就會(huì)發(fā)送給當(dāng)前焦點(diǎn)窗體,講解消息LOOP時(shí)再詳細(xì)介紹。

        WM_SET_FOCUS----講到剛才上面的兩個(gè)消息時(shí),就反復(fù)提到了當(dāng)前焦點(diǎn)窗體的概念,所有外部輸入設(shè)備消息都是發(fā)送給當(dāng)前焦點(diǎn)窗體的,用戶可以通過此消息來設(shè)定當(dāng)前的焦點(diǎn)窗體。外部輸入操作也會(huì)改變當(dāng)前焦點(diǎn)窗體,如點(diǎn)擊某窗體時(shí)會(huì)在該窗體的WM_TOUCH消息處理中設(shè)置該窗體本身為當(dāng)前焦點(diǎn)窗體;當(dāng)在對(duì)話框中按鍵TAB鍵時(shí),同樣也可以將焦點(diǎn)在對(duì)話框上各控件間切換,這是在對(duì)話框的WM_KEY消息中處理實(shí)現(xiàn)的[了解一下WM_SetFocusOnNextChild()函數(shù)],是根據(jù)創(chuàng)建對(duì)話框時(shí)指定的資源定義數(shù)組中的順序來切換的,并沒有WIN下面指定的TabIndex這樣一個(gè)值來指定次序的值。

        WM_NOTIFY_PARENT---這個(gè)消息將子窗體的外設(shè)輸入的消息傳送到它的父窗體,因?yàn)橐话愕那闆r下消息都是在父窗體中統(tǒng)一處理的,如對(duì)話框中的按鈕點(diǎn)擊事件,一般都是在用戶自定義的窗體消息處理函數(shù)中處理,所以就必須要子窗體將獲取的輸入外設(shè)的消息傳送給父窗體,這樣才能在父窗體中進(jìn)行子窗體的點(diǎn)擊事件消息的處理,這個(gè)消息的機(jī)制類似WIN下面的WM_COMMAND消息,處理該消息時(shí)通過控件ID來區(qū)別不同的控件,通過消息中的通知碼來區(qū)別控件被操作的各種狀態(tài),具體這個(gè)消息的詳細(xì)說明請參見后面的分析。

        WM_DELETE---要?jiǎng)h除窗體時(shí)發(fā)送的消息,主要清除窗體數(shù)據(jù)結(jié)構(gòu)所占用內(nèi)存,此消息主要由WM_DeleteWindow()函數(shù)發(fā)送了,如點(diǎn)擊OK按鈕關(guān)閉對(duì)話框時(shí),最終會(huì)調(diào)用此函數(shù)來刪除窗體,不過UCGUI中沒有最大化最小化關(guān)閉等系統(tǒng)功能按鈕。最基礎(chǔ)窗體結(jié)構(gòu)注解如下,在該結(jié)構(gòu)中有兩個(gè)很重要的成員,hNextLin是記載窗體的下一個(gè)窗體,這個(gè)成員用于遍歷所有已經(jīng)創(chuàng)建的窗體;hNext是記載窗體下一個(gè)兄弟窗體,這個(gè)成員用于遍歷每個(gè)窗體對(duì)應(yīng)的子窗體;這個(gè)結(jié)構(gòu)是最基礎(chǔ),一般的控件在這個(gè)結(jié)構(gòu)之上還會(huì)有一些擴(kuò)展的結(jié)構(gòu),如按鈕對(duì)應(yīng)有BUTTON_Obj結(jié)構(gòu)。

typedef struct WM_OBJ_struct WM_Obj;

struct WM_OBJ_struct {

GUI_RECT Rect;        /* 窗體矩形區(qū)域 */

GUI_RECT InvalidRect; /* 窗體無效矩形區(qū)域 */

WM_CALLBACK* cb;      /* 窗體消息回調(diào)函數(shù) */

WM_HWIN hNextLin;     /* 窗體下一個(gè)窗體句柄*/

WM_HWIN hParent;       /* 父窗體句柄*/

WM_HWIN hFirstChild;   /* 第一子窗體句柄*/

WM_HWIN hNext;         /* 下一個(gè)兄弟窗體句柄 */

U16 Status;           /* 窗體當(dāng)前狀態(tài) */

};

      WIDGET_HandleActive()—基礎(chǔ)控件共通消息處理,在大部分的UCGUI控件中都會(huì)在消息回調(diào)函數(shù)的頭部進(jìn)行這個(gè)調(diào)用,如果處理了消息后,就直接退出消息回調(diào)函數(shù)的調(diào)用。這個(gè)函數(shù)中處理如下消息:

2       WM_GET_ID[返窗體控件標(biāo)志ID].

2       WM_SET_FOCUS[設(shè)置當(dāng)前窗體為焦點(diǎn)窗體,設(shè)置完后還必須向該窗體的父窗體發(fā)送一個(gè)WM_NOTIFY_CHILD_HAS_FOCUS消息讓其父窗體更新它記載的當(dāng)前焦點(diǎn)子窗體].

2       WM_GET_HAS_FOCUS[獲取當(dāng)前窗體是否為焦點(diǎn)窗體].

2       WM_SET_ENABLE[設(shè)置窗體為不可用窗體] .

2       WM_GET_ACCEPT_FOCUS[獲取當(dāng)前窗體是否可設(shè)置為焦點(diǎn)窗體].

2       WM_GET_INSIDE_RECT[返回窗體內(nèi)框矩形,如按鈕有3D效果時(shí)會(huì)有效果邊框?qū)挾龋瑑?nèi)框矩形就是窗體矩形被邊框剪裁后的矩形].

      WM_DefaultProc()----窗體默認(rèn)消息處理函數(shù),UCGUI中提供一些基礎(chǔ)的控件,這些控件有些共通的消息均在此處理,如下:

2       WM_GETCLIENTRECT[獲取窗體矩形區(qū)域,相對(duì)于矩形自身]

2        WM_GETORG[獲取窗體矩形左上角坐標(biāo)].

2       WM_GET_INSIDE_RECT[獲取窗體矩形區(qū)域,相對(duì)屏幕].

2       WM_GET_CLIENT_WINDOW[獲取窗體客戶區(qū)子窗體句柄,如對(duì)話框的中的子窗體FrameWin即為此種窗體].

2       WM_KEY[銨鍵消息處理,通知父窗體子窗體的按鍵消息,有些控件自己要處理這個(gè)消息,如Edit控件處理完此消息后就沒有再調(diào)用WM_DefaultProc(),從而沒有將WM_KEY消息通父窗體;如Button控件,根本沒有對(duì)此消息進(jìn)行處理,直接是通過默認(rèn)處理發(fā)給了父窗體處理;有些控件如Checkbox自己處理該消息,同時(shí)也調(diào)用默認(rèn)消息處量將此消息通知父窗本,此種消息源窗體為子控件,目標(biāo)窗體為父窗體。如此處理WM_KEY消息完全是UCGUI中如此做,在WIN中并沒有這樣做].

2       WM_GET_BKCOLOR[獲取窗體背景色,在此未實(shí)現(xiàn),返回0xfffffff值,但 FrameWin窗體實(shí)現(xiàn)了此消息處理].

在UCGUI的對(duì)話框的窗口消息處理函數(shù)中OK按鈕的點(diǎn)擊事件, UCGUI的處理方法與WIN下面是不同, 它在WM_NOTIFY_PARENT消息中處理[片段如下]:

    case WM_NOTIFY_PARENT:

      Id    = WM_GetId(pMsg->hWinSrc);    /* Id of widget */

      NCode = pMsg->Data.v;               /* Notification code */

      switch (NCode) {

        case WM_NOTIFICATION_RELEASED:    /* React only if released */

          if (Id == GUI_ID_OK) {          /* OK Button */

             GUI_MessageBox("This text is shown\nin a message box",

                   "Caption/Title", GUI_MESSAGEBOX_CF_MOVEABLE);

          }

          if (Id == GUI_ID_CANCEL) {      /* Cancel Button */

            GUI_EndDialog(hWin, 1);

          }

          break;

      }break;

UCGUI中的消息種類不多, 只有差不多不到二十種,但對(duì)于嵌入式系統(tǒng)來說已經(jīng)完全足夠了,用戶可以自定義消息(從WM_USER起)。 WM_NOTIFY_PARENT這個(gè)消息是由子窗體傳送給父窗體的, 由消息的名字也可以看出這一點(diǎn),OK按鈕也是一個(gè)窗體,當(dāng)MOUSE點(diǎn)擊在它上面時(shí),UCGUI首先會(huì)傳遞一個(gè)WM_TOUCH消息到OK按鈕的窗口消息處理函數(shù),OK按鈕是一個(gè)系統(tǒng)提供的控件,系統(tǒng)已經(jīng)提供了一個(gè)默認(rèn)的消息的窗口消息處理函數(shù),這個(gè)函數(shù)會(huì)處理大部分的默認(rèn)窗口消息并隨后將此消息轉(zhuǎn)發(fā)給父窗體,即WM_NOTIFY_PARENT消息,它是由函數(shù)WM_NotifyParent(hObj, Notification)實(shí)現(xiàn)的.

WM_TOUCH消息在按鈕的消息處理函數(shù)_BUTTON_Callback中的_OnTouch函數(shù)中處理,在處理過程完后會(huì)調(diào)用WM_NotifyParent向按鈕的父窗體發(fā)WM_NOTIFY_PARENT消息告訴對(duì)話框回調(diào)函數(shù)按鈕被點(diǎn)擊了,這個(gè)過程再說詳細(xì)一點(diǎn)是這樣的:

        點(diǎn)擊OK按鈕.

        產(chǎn)生按鈕WM_TOUCH消息.

        UCGUI中的消息LOOP調(diào)用按鈕默認(rèn)的按鈕窗口消息處理函數(shù)_BUTTON_Callback.

        _OnTouch默認(rèn)處理按鈕點(diǎn)擊并發(fā)送給父窗體WM_NOTIFY_PARENT消息,這里要注意MOUSE點(diǎn)擊后,有三種情況:第一種是點(diǎn)擊后在按鈕范圍內(nèi)彈出MOUSE,這種情況下,會(huì)送的消息中還有一個(gè)通知碼就是WM_NOTIFICATION_RELEASED;第二種情況是點(diǎn)擊拖到按鈕范圍外彈起MOUSE,此時(shí)通知碼是WM_NOTIFICATION_MOVED_OUT;第三種情況是點(diǎn)擊后一直未彈起MOUSE的過程中消息通知碼為WM_NOTIFICATION_CLICKED;在這個(gè)函數(shù)中還會(huì)處理設(shè)置按鈕點(diǎn)擊后MOUSE至未彈起前的按下狀態(tài),這樣在按鈕下一次畫出時(shí)就會(huì)以按下的狀態(tài)顯示出來.

        默認(rèn)的對(duì)話框窗體消息處理函數(shù)_FRAMEWIN_Callback收到WM_NOTIFY_PARENT消息并最終傳送該消息到用戶自己定義的對(duì)話框消息處理函數(shù),這里要注意的一點(diǎn)是,其實(shí)對(duì)話框?qū)υ捒蛑饕怯梢粋€(gè)FrameWin子窗體構(gòu)成的,這個(gè)子窗體大小為對(duì)話框指定的大小,對(duì)話框上的其它控件是都是FrameWin的子窗體,由_FRAMEWIN_Callback傳送的消息首先是傳送到對(duì)話框的默認(rèn)窗體消息回調(diào)函數(shù)_cbDialog,然后再經(jīng)它傳送到用戶自定義的窗體回調(diào)函數(shù)當(dāng)中。

        用戶在自己的對(duì)話框消息處理函數(shù)中處理WM_NOTIFY_PARENT消息,即按鈕的點(diǎn)擊消息,該消息參數(shù)中含有按鈕的ID及操作狀態(tài),如果通知碼是WM_NOTIFICATION_RELEASED,此時(shí)證明一次點(diǎn)擊事件完成。

void WM_NotifyParent(WM_HWIN hWin, int Notification){

WM_MESSAGE Msg;

Msg.MsgId   = WM_NOTIFY_PARENT;

Msg.Data.v = Notification;

WM_SendToParent(hWin, &Msg);

}

這個(gè)函數(shù)相當(dāng)簡單, 其主要還是WM_SendToParent這個(gè)函數(shù)的調(diào)用, 這個(gè)函數(shù)再調(diào)用void WM_SendMessage(WM_HWIN hWin, WM_MESSAGE* pMsg), 這個(gè)函數(shù)是最基本的一個(gè)消息發(fā)送處理函數(shù), 它的第一個(gè)參數(shù)指定了接受這個(gè)要處理的消息的句柄, 第二個(gè)指定了是什么消息。這個(gè)函數(shù)的主要作用是調(diào)用相應(yīng)窗口的消息處理函數(shù)來處理消息,如果你有消息要發(fā)送給指定的窗體處理,那么也可以使用這個(gè)函數(shù)。

在上面, 我們剛剛分析了在對(duì)話框內(nèi)部消息處理的流轉(zhuǎn),其中分析了我們在自己指定的對(duì)話框消息處理函數(shù)當(dāng)中是如何可以獲得按鈕的點(diǎn)擊消息并進(jìn)行處理的,現(xiàn)在我們就再來分析一下對(duì)話框外面的消息接收:首先是來了解一下GUI_ExecDialogBox函數(shù),這個(gè)函數(shù)有幾個(gè)參數(shù):

        第一個(gè)是對(duì)話框的資源定義數(shù)組,這個(gè)數(shù)組定義了對(duì)話框的組成子窗體,其中數(shù)組第一個(gè)成員必須是FrameWin窗體,數(shù)組每一個(gè)成員記載了創(chuàng)建子窗體所用函數(shù)/子窗體Caption/子窗體標(biāo)志ID/子窗體的位置及寬高/創(chuàng)建窗體時(shí)樣式標(biāo)志/額外傳送的參數(shù).

        第二個(gè)參數(shù)是上述的數(shù)組的大小.

        第三個(gè)參數(shù)是用戶指定的對(duì)話框窗體消息回調(diào)函數(shù)指針.

        第四個(gè)參數(shù)是對(duì)話框的父窗體,默認(rèn)為0.

        第五、六參數(shù)指定對(duì)話框的左上角屏幕位置.

GUI_ExecDialogBox主要完成如下幾件事:

        根據(jù)傳進(jìn)來的對(duì)話框資源定義數(shù)組創(chuàng)建對(duì)話框及對(duì)話框中的子窗體.

        根據(jù)傳進(jìn)來的窗口消息處理函數(shù),記載到一全局變量保存,當(dāng)這個(gè)全局變量中記載的函數(shù)指針為非空時(shí),執(zhí)行消息LOOP,消息LOOP中會(huì)將當(dāng)前的MOUSE及KEY消息發(fā)送給當(dāng)前焦點(diǎn)窗體.

        當(dāng)對(duì)話框關(guān)閉時(shí),記載對(duì)話窗體消息回調(diào)函數(shù)的全局變量會(huì)被清為0,此時(shí)消息LOOP就會(huì)退出,對(duì)話框結(jié)束.

二、發(fā)現(xiàn)存在的問題-----點(diǎn)擊OK后無論先關(guān)閉消息框還是對(duì)話框,另一個(gè)不再響應(yīng).

點(diǎn)擊對(duì)話框的OK后彈出消息框, 會(huì)出現(xiàn)當(dāng)按下對(duì)話框的Cancel關(guān)閉對(duì)話框后, 彈出的消息框就沒有任何響應(yīng)的情況. 或者是關(guān)閉掉彈出的消息框, 對(duì)話框就沒有任何響應(yīng)的情形:從外部粗步分析的原因是調(diào)用MainTask的線程已經(jīng)退出了, 這個(gè)線程是在模擬器中開啟的專門用于運(yùn)行GUI任務(wù)的線程,它的線程函數(shù)是Thread, Thread函數(shù)里調(diào)用main,main中再調(diào)用MainTask,所以該線程退出后也就代表UCGUI任務(wù)已經(jīng)結(jié)束了。這是從模擬器的角度來分析, 現(xiàn)在我們分析一下為什么MainTask的調(diào)用線程會(huì)這么早退出呢?

由我們第一節(jié)中關(guān)于GUI_ExecDialogBox所做的幾件中可以分析到, 當(dāng)UCGUI中有一個(gè)獨(dú)立的窗體退出后_cb會(huì)被清為0, 此時(shí)退出GUI窗口LOOP. 即結(jié)束了UCGUI窗口消息處理。

其實(shí), GUI_MessageBox彈出的消息框其實(shí)也是一種對(duì)話框, 這最終調(diào)用的還是GUI_ExecDialogBox,開始我們就分析過,進(jìn)入這個(gè)函數(shù)后,會(huì)有一個(gè)全局變量記錄當(dāng)前對(duì)話框窗體的消息處理函數(shù)指針,但是目前的問題如下:

        已經(jīng)建立了兩個(gè)這樣的對(duì)話框窗體,這樣一個(gè)全局變量來記載當(dāng)前對(duì)話框的窗體消息處理函數(shù)指針顯然不夠,而且先前打開的對(duì)話框的的用戶指定的窗體消息回調(diào)函數(shù)已經(jīng)不再被調(diào)用了,此時(shí)第一個(gè)對(duì)話框的由子窗體回傳到父窗體的消息均會(huì)傳到第二次打開的對(duì)話框的用戶指定的窗體消息回調(diào)函數(shù)中.

        第二次彈出消息框再次進(jìn)入GUI_ExecDialogBox中的 while循環(huán)后,先前的對(duì)話框中的while循環(huán)就被掛起了,直至第二次的GUI_ExecDialogBox中的 while循環(huán)退出,無論關(guān)閉消息框還是對(duì)話框,都會(huì)導(dǎo)致知退出第二次消息LOOP。第二次消息LOOP退出后返回點(diǎn)為彈出消息框后的下一句,直至返回到第一個(gè)對(duì)話框的while循環(huán)后退出GUI_ExecDialogBox.

但我們期待的結(jié)果是,點(diǎn)擊對(duì)話框的OK彈出消息框, 關(guān)閉掉對(duì)話框或是消息框,其它的都要對(duì)話框繼續(xù)有反應(yīng),下面我們就來分析一下如何達(dá)到這個(gè)目標(biāo),看看要做些什么具體的改動(dòng):

三、UCGUI中的消息LOOP處理分析-----尋找問題的解決辦法.

在我們發(fā)現(xiàn)這個(gè)問題, 我們已經(jīng)粗步分析了,問題不是出在我們編寫程序上, 而上UCGUI的內(nèi)部,那么要解決這個(gè)問題, 我們就要進(jìn)一步了解UCGUI的窗口體系。其實(shí)換一句話說,在嵌入式應(yīng)用中,窗口的強(qiáng)大直接決定到GUI系統(tǒng)的體積大小,并不是所有的情況都要有這種支持,當(dāng)然我們希望在下一版本可以有多個(gè)對(duì)話框的直接支持。

創(chuàng)建對(duì)話框:

void MainTask(void)

{

GUI_Init();

WM_SetDesktopColor(GUI_RED);

WM_SetCreateFlags(WM_CF_MEMDEV);

GUI_ExecDialogBox(_aDialogCreate,GUI_COUNTOF(_aDialogCreate), &_cbCallback, 0, 0, 0);

}

   上面是我們創(chuàng)建對(duì)話框的程序,是我們編寫的代碼, GUI_ExecDialogBox()這個(gè)函數(shù)的作用我們已經(jīng)分析過了,它所做的事用一句話來說就是創(chuàng)建對(duì)話框并進(jìn)入窗體消息LOOP處理,下面將詳細(xì)分析一下LOOP消息的處理流程:

int GUI_ExecDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget,

                     int NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent,

                     int x0, int y0)

{

_cb = cb;

GUI_CreateDialogBox(paWidget, NumWidgets, _cbDialog,hParent, x0, y0);

while(_cb){

    if (!GUI_Exec())

      GUI_X_ExecIdle();

}

return _r;

}

   這個(gè)LOOP類似我們非常熟悉的WIN下面的消息LOOP, 其原理是一致的. GUI_CreateDialogBox負(fù)責(zé)創(chuàng)建對(duì)話框的所有子窗體,特別注意它其中一個(gè)參數(shù)傳入是Dialog.c中定義的_cbDialog,這個(gè)函數(shù)什么也沒做,基本上是轉(zhuǎn)而調(diào)用_cb,后面我們會(huì)提到關(guān)于它的修改。_cb是對(duì)話框的用戶定義窗口消息處理函數(shù),這里面有一個(gè)判斷,就是_cb非空時(shí),才進(jìn)行消息LOOP, _cb在Dialog.c中的定義為:[static WM_CALLBACK* _cb;] _cb是一個(gè)全局變量,我們程序中創(chuàng)建對(duì)話框與彈出消息框時(shí)兩次調(diào)用了GUI_ExecDialogBox,后一次的_cb將會(huì)把前面的值沖,它是用戶自定義的窗口消息處理函數(shù)。

   在while中有判斷, 那么可見_cb是在GUI_Exec之中是有使用的,對(duì)話框的FrameWin子窗體消息流轉(zhuǎn)調(diào)如下面的所示,窗口消息處理函數(shù)是在WM_SendMessage中通過函數(shù)指針的調(diào)用中, 注意[]內(nèi)部的就是真正被調(diào)用來處理消息的函數(shù):

GUI_Exec-->GUI_Exec1-->WM_Exec-->WM_Exec1-->WM_HandlePID-->WM_SendMessage-->(*pWin->cb)(pMsg)[_FRAMEWIN_Callback]-->_OnTouch()-->(*cb)(pMsg)[_cbDialog]--> *_cb)(pMsg)[_MESSAGEBOX_cbCallback]

        WM_HandlePID()------專門處理類似MOUSE的滑動(dòng)操作外設(shè)消息的函數(shù).

        WM_SendMessage()----基層的發(fā)送消息的函數(shù),即調(diào)用相對(duì)應(yīng)的窗體的消息回調(diào)函數(shù)來處理消息.

現(xiàn)在講到了窗體消息LOOP,在窗體系統(tǒng)中最根本一點(diǎn)的就是對(duì)外部輸入消息的處理,窗體就是靠消息驅(qū)動(dòng)的,其處理代碼如下:

int WM_Exec1(void){

if(WM_pfPollPID){/* Poll PID if necessary */

    WM_pfPollPID();

}

if(WM_pfHandlePID){

    if (WM_pfHandlePID())

      return 1;             /* We have done something ... */

}

if(GUI_PollKeyMsg()){

    return 1;               /* We have done something ... */

}

if(WM_IsActive && WM__NumInvalidWindows) {

    WM_LOCK();

    _DrawNext();

    WM_UNLOCK();

    return 1;               /* We have done something ... */

}

return 0;                  /* There was nothing to do ... */

}

它主要完成如下幾件事:

        Poll PID中Poll個(gè)詞準(zhǔn)確的意思應(yīng)該是統(tǒng)計(jì)/測試的意思,這里是調(diào)用用戶的統(tǒng)計(jì)測試滑動(dòng)操作外設(shè)的一個(gè)接口,用戶可以通過WM_SetpfPollPID()函數(shù)來設(shè)置自己用于統(tǒng)計(jì)/測試滑動(dòng)操作外設(shè)的具體函數(shù)。

        處理滑動(dòng)操作外設(shè) WM_TOUCH消息,真正的處理是在函數(shù)WM_HandlePID()中處理的,在后面滑動(dòng)外設(shè)消息處理流程時(shí)有詳細(xì)說明,在新版中更細(xì)分此消息為WM_PID_STATE_CHANGED/ WM_MOUSEOVER/ WM_TOUCH三種消息,其實(shí)在WIN下面類似消息的處理更為復(fù)雜,有移動(dòng)/滾動(dòng)/單擊DOWN及UP[左右鍵]/雙擊[左右鍵]等七八種MOUSE消息,而且這些消息又分為窗體體客戶區(qū)與標(biāo)題區(qū)的差別,標(biāo)題區(qū)的都會(huì)在消息上加上NC的前輟,如WM_NCLBUTTONUP標(biāo)題區(qū)單擊彈起消息。從這里我們也可以看到UCGUI中非常簡化的處理,簡單得不能再簡單了,的確是一個(gè)微型的GUI圖形支持系統(tǒng)。

        按鍵式外設(shè)消息處理,GUI_PollKeyMsg()函數(shù)在發(fā)現(xiàn)有新的按鍵消息生時(shí)會(huì)調(diào)用WM_OnKey()將消息發(fā)送到當(dāng)前焦點(diǎn)窗體處理,如果一直處于按鍵按下狀態(tài)時(shí)則會(huì)將前按鈕的虛擬碼存在一全部變量中,以供GUI_GetKey()調(diào)用來返回當(dāng)前按下鍵值。UCGUI中有一個(gè)外部的鍵盤接口,外界通過GUI_StoreKeyMsg()發(fā)送鍵盤消息給UCGUI以驅(qū)動(dòng)鍵盤,在我的模擬器當(dāng)中就是將LCD模擬顯示屏窗口的所有鍵盤消息通過GUI_StoreKeyMsg()傳送到UCGUI中以驅(qū)動(dòng)鍵盤消息處理,關(guān)于鍵盤消息的處理UCGUI中也是來一個(gè)處理一個(gè),沒有任何緩沖處理,如果某些按鈕消息處理用時(shí)過長,就會(huì)造成其后的一些按鍵消息丟失。

static int _Key;         //記載當(dāng)前按鍵,GUI_GetKey時(shí)返回此值  

static int _KeyMsgCnt;   //當(dāng)前鍵盤消息數(shù)量

static struct {

int Key;               //鍵盤虛擬碼…

int PressedCnt;        //按鍵次數(shù)…

} _KeyMsg;

上面是鍵盤消息結(jié)構(gòu),UCGUI中以一個(gè)全局的_KeyMsg鍵盤變量記載當(dāng)前最新鍵盤消息,當(dāng)前按鍵值用_Key,每產(chǎn)生按下鍵時(shí)用GUI_StoreKey更新一次此值,UCGUI中沒有按鍵彈起消息的處理。

        檢測是否有無效窗體,如果有無效窗體,則向該無效窗體發(fā)送重畫消息,有一個(gè)全局變量WM__NumInvalidWindows用于記載當(dāng)前無效窗體的數(shù)目,在函數(shù)_DrawNext()中每次重畫一個(gè)無效窗體,查找無效窗體時(shí)是通過遍歷查找的方法,先前說過窗體基本結(jié)構(gòu)中有一個(gè)成員hNextLin記載下一個(gè)窗體,就在是此處用于遍歷所有窗體,找出無效的窗體,發(fā)送WM_PAINT消息給窗體。注意這里每次畫一個(gè)窗體的原因就是為了不影響窗體的消息處理,如果在此處用時(shí)太多,會(huì)嚴(yán)重影響消息處理的反應(yīng)速度。

了解了UCGUI中消息處理的具體流程,那么再來分析這個(gè)先前提到的問題:無論是消息框還是對(duì)話框哪一個(gè)先被關(guān)掉, 都會(huì)掉用GUI_EndDialog,將_cb被清為零,也就意味著消息LOOP到此結(jié)束了,所以后面另外一個(gè)未被關(guān)掉的當(dāng)然不會(huì)再有任何響應(yīng)了!

void GUI_EndDialog(WM_HWIN hWin, int r) {

_cb = NULL;

_r = r;                //通知WM_Exec等消息LOOP返回…

WM_DeleteWindow(hWin);//free該窗體結(jié)構(gòu)占用的內(nèi)存…

}

現(xiàn)在我們可以得出一個(gè)結(jié)論:UCGUI中對(duì)話框的設(shè)計(jì)只支持單窗口的消息處理,如果要多窗口的支持,可以如同示例中一樣,啟用多任務(wù)支持,不然在單任務(wù)下一個(gè)MainTask中只能支持一個(gè)獨(dú)立窗體,但是如果我們只是為了要彈出一個(gè)消息框而啟動(dòng)一個(gè)任務(wù), 這未免太不實(shí)際。

了解UCGUI后初步修改路分析如下:

        消息傳送-----經(jīng)過詳細(xì)的分析,認(rèn)識(shí)到在消息處理中創(chuàng)建一個(gè)對(duì)話框窗體后,必須建立一個(gè)消息LOOP處理,來向UCGUI中的窗口捕捉并傳送外設(shè)的輸入消息,消息的處理實(shí)質(zhì)上是通過WM_SendMessage函數(shù)來調(diào)用相應(yīng)的窗口的消息回調(diào)函數(shù)。

        消息LOOP----如果創(chuàng)建多個(gè)對(duì)話框窗體,則會(huì)進(jìn)入一個(gè)新的消息LOOP處理層而掛起原來的消息LOOP處理,要避免這種情況發(fā)生必須將消息LOOP移到MainTask之外,并在創(chuàng)建完所有對(duì)話框之后執(zhí)行消息LOOP處理。

        消息分發(fā)-----用一個(gè)數(shù)組將所有創(chuàng)建對(duì)話框的自定義消息回調(diào)函數(shù)存放起來,然后在對(duì)話框消息分布處(_cbDialog函數(shù)處)對(duì)應(yīng)分發(fā)各個(gè)對(duì)話框的消息,要注意和解決的問題是,必須根據(jù)消息所對(duì)應(yīng)用窗體來正確分布。

        刪除窗體-----在清除獨(dú)立窗體時(shí),必須將此對(duì)話框?qū)?yīng)的用戶自定義的窗體消息回調(diào)函數(shù)清零,并清除該窗體與其它窗體的數(shù)據(jù)關(guān)系及其占用資源,使其退出消息處理。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
GUI的種類及uC/GUI的架構(gòu)
關(guān)于ucGUI對(duì)個(gè)多個(gè)對(duì)話框切換的問題
Ucos-II+ucGUI390+ARM2410+LCD+觸摸屏基本整合成功
uCGUI 學(xué)習(xí)筆記
ClientToScreen 和ScreenToClient 用法
MFC程序的消息處理順序 (zz)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服