uCGUI 窗体管理及消息处理机制分析.docx
- 文档编号:16169512
- 上传时间:2023-07-11
- 格式:DOCX
- 页数:28
- 大小:36.10KB
uCGUI 窗体管理及消息处理机制分析.docx
《uCGUI 窗体管理及消息处理机制分析.docx》由会员分享,可在线阅读,更多相关《uCGUI 窗体管理及消息处理机制分析.docx(28页珍藏版)》请在冰点文库上搜索。
uCGUI窗体管理及消息处理机制分析
UCGUI窗体管理及消息处理机制分析1
各种基本消息介绍及处理流程2
UCGUI中一些基本的消息如下:
2
WM_CREATE---窗体创建消息2
WM_SHOW-----显示窗体消息2
WM_SET_ENABLE---设置窗体不能使用消息2
WM_PAINT----窗体重画消息3
WM_TOUCH3
WM_KEY3
WM_SET_FOCUS3
WM_NOTIFY_PARENT4
WM_DELETE4
WIDGET_HandleActive()4
WM_DefaultProc()5
WM_TOUCH消息的处理流程如下:
14
GUI_PID_Load14
GUI_PID_GetState14
UCGUI窗体管理及消息处理机制分析
----多对话框/模态窗体/透明窗体支持分析
问题的提出:
[求助]关于对话框处理程序中,想在OK按钮按下后想弹出一个消息框,该怎么做?
直接加在程序中好像不行,如何让消息框弹出后成为模态窗体呢?
请版主帮帮忙。
[解析]在UCGUI中,对话框只支持单个对话框窗体,不支持多个独立的对话框,现在我们从其源码来分析一下它为什么支持单个对话框窗体以及如何改进它以支持多个独立对话框,要讲解这个问题我们必须首先理解UCGUI中的窗体消息LOOP,没有消息LOOP窗体就是死水一潭,不能接受任何外界的输入,只是一个画在那里的图画而已。
[声明]本文中提到的源码均为UCGUI3.24版源码,新版UCGUI源码会有改动,请下载本文示例代码来结合阅读本文。
摘要:
本文主要介绍了UCGUI中的对话框的消息处理机制,并指出在现有UCGUI上如何增加多窗体支持,并在分析解决问题时着重介绍了其输入设备消息WM_TOUTCH及WM_KEY两类消息处理方法,并同时初步指出一种在UCGUI中实现模
UCGUI窗体管理及消息处理机制分析
态对话框以及透明窗体的原理说明,不还有窗体重画消息WM_PAINT消息处理原理。
各种基本消息介绍及处理流程
对话框内部消息流转及外部消息LOOP分析.
UCGUI是采用的消息驱动的,它专门有对外的一套收集消息的接口,在模拟器中,就是通过LCD模拟显示屏窗口的MOUSE消息,将MOUSE消息传入到这个接口中,以驱动UCGUI中的窗体的。
UCGUI中的消息驱动其实与WINDOWS的是类似的,几种基本的消息与WINDOWS是一样的,但UCGUI的更简单且消息更少,对于一些消息的处理得也很简化,没有WINDOWS那么多的消息种类及复杂处理。
在WINDOWS中,如我们处理按钮控件的点击事件的是在WM_COMMAND消息中,通过按钮的标志ID来区分不同的按钮,所以按钮标志ID必须不同的,否则无法区别开(除非不在父窗体的WM_COMMAND消息中处理)。
UCGUI中一些基本的消息如下:
WM_CREATE---窗体创建消息
每创建一个窗体完后都会向该窗体发送此消息,如WM_CreateWindowAsChild创建完窗体均会发一此消息,但在UCGUI中对于此消息的很少处理,如果用户想在对话框之后做些初始化操作或是创建其它子窗体的动作,可以处理此消息,不过对话框一般有专门的初始化消息WM_INIT_DIALOG,它是在创建对话框后发送的。
WM_SHOW-----显示窗体消息
此消息在UCGUI中各控件窗体内均未作处理,如果你通过消息发送函数来发送这类没有在UCGUI中各窗体中处理的消息,是没有有什么响应的,不要感到奇怪。
要显示窗体一般是通过WM_ShowWindow()函数实现的,这个函数做的也就是改变窗体显示标志[WM_SF_ISVIS],并使窗体矩形区域无效[WM_InvalidateWindow()]以产生重画消息。
WM_SET_ENABLE---设置窗体不能使用消息
UCGUI中有一种复选框为不可改变的,但是这个功能也不完全,如果你对着UCGUI中的按钮使用WM_DisableWindow()来设置其无效,按钮照样还是可以使用,不过要改进这些小毛病还是很容易的,这里只是提醒大家UCGUI中很多没有实现的小地方,不要到时候使用时感到很奇怪,感觉到奇怪时最好去看看源码,看看源码中是否实现了此功能,不要郁闷。
WM_PAINT----窗体重画消息
当窗体所在区域全部或是部分区域无效时,系统将会发出该重画消息,将无效区域重画,但UCGUI中的处理比较简单,都是将窗体全部区域重画;如果用户自己想在窗体上画上一些信息,一般都在在该消息当中画,UCGUI中的各种提供的系统控件都必须在其系统的提供的消息回调函数中处理此消息来画出控件。
当由外部输入操作引起无效窗体区域产生时,系统都会在消息处理中发送该消息到窗体消息回调函数中,以重画此窗体,在下面讲解消息循环机制时将会
着重讲解到该消息的产生。
[透明窗体]---经常有朋友想知道在UCGUI中如何实现透明窗体,透明窗窗体显示在前台时,可以看到部分位于其窗体后的内容,即透过窗体可以看到窗体背后的图象。
在UCGUI中有关于透明窗体的设置选项,可是没有实现此功能,其实要实现原理如下:
第一透明窗体及其所有子窗体都必须透明处理;第二是对于所有有透明属性的窗体,在绘图时必须使用透明填充功能的矩形填充函数,主要是修改窗体的WM_PAINT消息中画窗体时的矩形填充函数为透明的矩形填充;第三透明的矩形填充函数的实现,通常情况下的矩形填充是以当前前景色来填充,那么关键就是实现画点函数的透明填充,要使一个透明,可以取当前显存中存点的点的RGB颜色,然后再与当前要画的颜色按照一个比例进行混合得一个新的RGB值,再将此值画以屏幕上就可能实现透明填充的效果。
WM_TOUCH
----处理类似MOUSE的滑动操作方式的输入外设的消息,如触摸屏一般都是将其消息从硬件接收到后转化为该消息形式发送出去,该消息中必须包含消息在屏幕中的发生位置坐标及输入设备状态(按下状态或弹起状态),此消息在任务消息循环中循环处理,一旦产生就会发送给当前焦点窗体,在后面将详细讲解该消息的处理机制。
WM_KEY
------处理类似KEY的按键式操作的输入外设的消息,消息中必须包含按键的按下或弹起状态,此消息也是在任务消息循环中循环处理,一旦产生就会发送给当前焦点窗体,讲解消息LOOP时再详细介绍。
WM_SET_FOCUS
----讲到刚才上面的两个消息时,就反复提到了当前焦点窗体的概念,所有外部输入设备消息都是发送给当前焦点窗体的,用户可以通过此消息来设定当前的焦点窗体。
外部输入操作也会改变当前焦点窗体,如点击某窗体时会在该窗体的WM_TOUCH消息处理中设置该窗体本身为当前焦点窗体;当在对话框中按键TAB键时,同样也可以将焦点在对话框上各控件间切换,这是在对话框的WM_KEY消息中处理实现的[了解一下WM_SetFocusOnNextChild()函数],是根据创建对话框时指定的资源定义数组中的顺序来切换的,并没有WIN下面指定的TabIndex这样一个值来指定次序的值。
WM_NOTIFY_PARENT
---这个消息将子窗体的外设输入的消息传送到它的父窗体,因为一般的情况下消息都是在父窗体中统一处理的,如对话框中的按钮点击事件,一般都是在用户自定义的窗体消息处理函数中处理,所以就必须要子窗体将获取的输入外设的消息传送给父窗体,这样才能在父窗体中进行子窗体的点击事件消息的处理,这个消息的机制类似WIN下面的WM_COMMAND消息,处理该消息时通过控件ID来区别不同的控件,通过消息中的通知码来区别控件被操作的各种状态,具体这个消息的详细说明请参见后面的分析。
WM_DELETE
---要删除窗体时发送的消息,主要清除窗体数据结构所占用内存,此消息主要由WM_DeleteWindow()函数发送了,如点击OK按钮关闭对话框时,最终会调用此函数来删除窗体,不过UCGUI中没有最大化最小化关闭等系统功能按钮。
最基础窗体结构注解如下,在该结构中有
两个很重要的成员,hNextLin是记载窗体的下一个窗体,这个成员用于遍历所有已经创建的窗体;hNext是记载窗体下一个兄弟窗体,这个成员用于遍历每个窗体对应的子窗体;这个结构是最基础,一般的控件在这个结构之上还会有一些扩展的结构,如按钮对应有BUTTON_Obj结构。
typedefstructWM_OBJ_structWM_Obj;
structWM_OBJ_struct{
GUI_RECTRect;
/*窗体矩形区域*/
GUI_RECTInvalidRect;
/*窗体无效矩形区域*/
WM_CALLBACK*cb;
/*窗体消息回调函数*/
WM_HWINhNextLin;
/*窗体下一个窗体句柄*/
WM_HWINhParent;/*父窗体句柄*/
WM_HWINhFirstChild;/*第一子窗体句柄*/
WM_HWINhNext;/*下一个兄弟窗体句柄*/
U16Status;/*窗体当前状态*/
};
WIDGET_HandleActive()
—基础控件共通消息处理,在大部分的UCGUI控件中都会在消息回调函数的头部进行这个调用,如果处理了消息后,就直接退出消息回调函数的调用。
这个函数中处理如下消息:
—WM_GET_ID[返窗体控件标志ID].
—WM_SET_FOCUS[设置当前窗体为焦点窗体,设置完后还必须向该窗体的父窗体发送一个WM_NOTIFY_CHILD_HAS_FOCUS消息让其父窗体更新它记载的当前焦点子窗体].
—WM_GET_HAS_FOCUS[获取当前窗体是否为焦点窗体].
—WM_SET_ENABLE[设置窗体为不可用窗体].
—WM_GET_ACCEPT_FOCUS[获取当前窗体是否可设置为焦点窗体].
—WM_GET_INSIDE_RECT[返回窗体内框矩形,如按钮有3D效果时会有效果边框宽度,内框矩形就是窗体矩形被边框剪裁后的矩形].
WM_DefaultProc()
----窗体默认消息处理函数,UCGUI中提供一些基础的控件,这些控件有些共通的消息均在此处理,如下:
—WM_GETCLIENTRECT[获取窗体矩形区域,相对于矩形自身]
—WM_GETORG[获取窗体矩形左上角坐标].
—WM_GET_INSIDE_RECT[获取窗体矩形区域,相对屏幕].
—WM_GET_CLIENT_WINDOW[获取窗体客户区子窗体句柄,如对话框的中的子窗体FrameWin即为此种窗体].
—WM_KEY[铵键消息处理,通知父窗体子窗体的按键消息,有些控件自己要处理这个消息,如Edit控件处理完此消息后就没有再调用WM_DefaultProc(),从而没有将WM_KEY消息通父窗体;如Button控件,根本没有对此消息进行处理,直接是通过默认处理发给了父窗体处理;有些控件如Checkbox自己处理该消息,同时也调用默认消息处量将此消息通知父窗本,此种消息源窗体为子控件,目标窗体为父窗体。
如此处理WM_KEY消息完全是
UCGUI中如此做,在WIN中并没有这样做].
—WM_GET_BKCOLOR[获取窗体背景色,在此未实现,返回0xfffffff
值,但FrameWin窗体实现了此消息处理].
在UCGUI的对话框的窗口消息处理函数中OK按钮的点击事件,UCGUI的处理方法与WIN下面是不同,它在WM_NOTIFY_PARENT消息中处理[片段如下]:
caseWM_NOTIFY_PARENT:
Id=WM_GetId(pMsg->hWinSrc);/*Idofwidget*/
NCode=pMsg->Data.v;/*Notificationcode*/
switch(NCode){
caseWM_NOTIFICATION_RELEASED:
/*Reactonlyifreleased*/
if(Id==GUI_ID_OK)
{
/*OKButton*/
GUI_MessageBox("Thistextisshown\ninamessagebox",
"Caption/Title",GUI_MESSAGEBOX_CF_MOVEABLE);
}
if(Id==GUI_ID_CANCEL){/*CancelButton*/
GUI_EndDialog(hWin,1);
}
break;
}break;
UCGUI中的消息种类不多,只有差不多不到二十种,但对于嵌入式系统来说已经完全足够了,用户可以自定义消息(从WM_USER起)。
WM_NOTIFY_PARENT这个消息是由子窗体传送给父窗体的,由消息的名字也可以看出这一点,OK按钮也是一个窗体,当MOUSE点击在它上面时,UCGUI首先会传递一个WM_TOUCH消息到OK按钮的窗口消息处理函数,OK按钮是一个系统提供的控件,系统已经提供了一个默认的消息的窗口消息处理函数,这个函数会处理大部分的默认窗口消息并随后将此消息转发给父窗体,即WM_NOTIFY_PARENT消息,它是由函数WM_NotifyParent(hObj,Notification)实现的.
WM_TOUCH消息在按钮的消息处理函数_BUTTON_Callback中的_OnTouch函数中处理,在处理过程完后会调用WM_NotifyParent向按钮的父窗体发WM_NOTIFY_PARENT消息告诉对话框回调函数按钮被点击了,这个过程再说详细一点是这样的:
点击OK按钮.
产生按钮WM_TOUCH消息.
UCGUI中的消息LOOP调用按钮默认的按钮窗口消息处理函数
_BUTTON_Callback.
_OnTouch默认处理按钮点击并发送给父窗体WM_NOTIFY_PARENT消息,这里要注意MOUSE点击后,有三种情况:
第一种是点击后在按钮范围内弹出MOUSE,这种情况下,会送的消息中还有一个通知码就是WM_NOTIFICATION_RELEASED;
第二种情况是点击拖到按钮范围外弹起MOUSE,此时通知码是WM_NOTIFICATION_MOVED_OUT;第三种情况是点击后一直未弹起MOUSE的过程中消息通知码为WM_NOTIFICATION_CLICKED;在这个函数中还会处理设置按钮点击后MOUSE至未弹起前的按下状态,这样在按钮下一次画出时就会以按下的状态显示出来.
默认的对话框窗体消息处理函数_FRAMEWIN_Callback收到WM_NOTIFY_PARENT消息并最终传送该消息到用户自己定义的对话框消息处理函数,这里要注意的一点是,其实对话框对话框主要是由一个FrameWin子窗体构成的,这个子窗体大小为对话框指定的大小,对话框上的其它控件是都是FrameWin的子窗体,由_FRAMEWIN_Callback传送的消息首先是传送到对话框的默认窗体消息回调函数_cbDialog,然后再经它传送到用户自定义的窗体回调函数当中。
用户在自己的对话框消息处理函数中处理WM_NOTIFY_PARENT消息,即按钮的点击消息,该消息参数中含有按钮的ID及操作状态,如果通知码是WM_NOTIFICATION_RELEASED,此时证明一次点击事件完成。
voidWM_NotifyParent(WM_HWINhWin,intNotification){WM_MESSAGEMsg;
Msg.MsgId=WM_NOTIFY_PARENT;
Msg.Data.v=Notification;WM_SendToParent(hWin,&Msg);
}
这个函数相当简单,其主要还是WM_SendToParent这个函数的调用,这个函数再调用voidWM_SendMessage(WM_HWINhWin,WM_MESSAGE*pMsg),这个函数是最基本的一个消息发送处理函数,它的第一个参数指定了接受这个要处理的消息的句柄,第二个指定了是什么消息。
这个函数的主要作用是调用相应窗口的消息处理函数来处理消息,如果你有消息要发送给指定的窗体处理,那么也可以使用这个函数。
在上面,我们刚刚分析了在对话框内部消息处理的流转,其中分析了我们在自己指定的对话框消息处理函数当中是如何可以获得按钮的点击消息并进行处理的,现在我们就再来分析一下对话框外面的消息接收:
首先是来了解一下GUI_ExecDialogBox函数,这个函数有几个参数:
第一个是对话框的资源定义数组,这个数组定义了对话框的组成子窗体,其中数组第一个成员必须是FrameWin窗体,数组每一个成员记载了创建子窗体所用函数/子窗体Caption/子窗体标志ID/子窗体的位置及宽高/创建窗体时样式标志/额外传送的参数.
第二个参数是上述的数组的大小.
第三个参数是用户指定的对话框窗体消息回调函数指针.
第四个参数是对话框的父窗体,默认为0.
第五、六参数指定对话框的左上角屏幕位置.
GUI_ExecDialogBox
主要完成如下几件事:
根据传进来的对话框资源定义数组创建对话框及对话框中的子窗体.
根据传进来的窗口消息处理函数,记载到一全局变量保存,当这个全局变量中记载的函数指针为非空时,执行消息LOOP,消息LOOP中会将当前的MOUSE及KEY消息发送给当前焦点窗体.当对话框关闭时,记载对话窗体消息回调函数的全局变量会被清为0,此时消息LOOP就会退出,对话框结束.
二、发现存在的问题-----点击OK后无论先关闭消息框还是对话框,另一个不再响应.
点击对话框的OK后弹出消息框,会出现当按下对话框的Cancel关闭对话框后,弹出的消息框就没有任何响
应的情况.或者是关闭掉弹出的消息框,对话框就没有任何响应的情形:
从外部粗步分析的原因是调用MainTask的线程已经退出了,这个线程是在模拟器中开启的专门用于运行GUI任务的线程,它的线程函数是Thread,Thread函数里调用main,main中再调用MainTask,所以该线程退出后也就代表UCGUI任务已经结束了。
这是从模拟器的角度来分析,现在我们分析一下为什么MainTask的调用线程会这么早退出呢?
由我们第一节中关于GUI_ExecDialogBox所做的几件中可以分析到,当UCGUI中有一个独立的窗体退出后_cb
会被清为0,此时退出GUI窗口LOOP.即结束了UCGUI窗口消息处理。
其实,GUI_MessageBox弹出的消息框其实也是一种对话框,这最终调用的还是GUI_ExecDialogBox,开始我们就分析过,进入这个函数后,会有一个全局变量记录当前对话框窗体的消息处理函数指针,但是目前的问题如
下:
已经建立了两个这样的对话框窗体,这样一个全局变量来记载当前对话框的窗体消息处理函数指针显然不够,而且先前打开的对话框的的用户指定的窗体消息回调函数已经不再被调用了,此时第一个对话框的由子窗体回传到父窗体的消息均会传到第二次打开的对话框的用户指定的窗体消息回调函数中第二次弹出消息框再次进入GUI_ExecDialogBox中的while循环后,先前的对话框中的while循环就被挂起了,直至第二次的GUI_ExecDialogBox中的while循环退出,无论关闭消息框还是对话框,都会导致知退出第二次消息LOOP。
第二次消息LOOP退出后返回点为弹出消息框后的下一句,直至返回到第一个对
话框的while循环后退出GUI_ExecDialogBox.
但我们期待的结果是,点击对话框的OK弹出消息框,关闭掉对话框或是消息框,其它的都要对话框继续有反应,下面我们就来分析一下如何达到这个目标,看看要做些什么具体的改动:
三、UCGUI中的消息LOOP处理分析-----寻找问题的解决办法.在我们发现这个问题,我们已经粗步分析了,问题不是出在我们编写程序上,而上UCGUI的内部,那么要解决这个问题,我们就要进一步了解UCGUI的窗口体系。
其实换一句话说,在嵌入式应用中,窗口的强大直接决定到GUI系统的体积大小,并不是所有的情况都要有这种支持,当然我们希望在下一版本可以有多个对话框的直接支持。
创建对话框:
voidMainTask(void)
{
GUI_Init();
WM_SetDesktopColor(GUI_RED);
WM_SetCreateFlags(WM_CF_MEMDEV);
GUI_ExecDialogBox(_aDialogCreate,GUI_COUNTOF(_aDialogCreate),
&_cbCallback,0,0,0);
}
上面是我们创建对话框的程序,是我们编写的代码,GUI_ExecDialogBox()这个函数的作用我们已经分析过了,它所做的事用一句话来说就是创建对话框并进入窗体消息LOOP处理,下面将详细分析一下LOOP消息的处理流程:
intGUI_ExecDialogBox(constGUI_WIDGET_CREATE_INFO*paWidget,
intNumWidgets,WM_CALLBACK*cb,WM_HWINhParent,intx0,inty0)
{
_cb=cb;
GUI_CreateDialogBox(paWidget,NumWidgets,_cbDialog,hParent,x0,y0);
while(_cb){
if(!
GUI_Exec())GUI_X_ExecIdle();
}
return_r;
}
这个LOOP类似我们非常熟悉的WIN下面的消息LOOP,其原理是一致的.
GUI_CreateDialogBox负责创建对话框的所有子窗体,特别注意它其中一个参数传入是Dialog.c中定义的_cbDialog,这个函数什么也没做,基本上是转而调用
_cb,后面我们会提到关于它的修改。
_cb是对话框的用户定义窗口消息处理函数,这里面有一个判断,就是_cb非空时,才进行消息LOOP,_cb在Dialog.c中的定义为:
[staticWM_CALLBACK*_cb;]_cb是一个全局变量,我们程序中创建对话框与弹出消息框时两次调用了GUI_ExecDialogBox,后一次的_cb将会把前面的值冲,它是用户自定义的窗口消息处理函数。
在while中有判断,那么可见_cb是在
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- uCGUI 窗体管理及消息处理机制分析 窗体 管理 消息 处理 机制 分析