收藏 分销(赏)

uC-GUI-窗体管理及消息处理机制分析.doc

上传人:仙人****88 文档编号:12006488 上传时间:2025-08-26 格式:DOC 页数:17 大小:105KB 下载积分:10 金币
下载 相关 举报
uC-GUI-窗体管理及消息处理机制分析.doc_第1页
第1页 / 共17页
uC-GUI-窗体管理及消息处理机制分析.doc_第2页
第2页 / 共17页


点击查看更多>>
资源描述
UCGUI 窗体管理及消息处理机制分析 1 各种基本消息介绍及处理流程 2 UCGUI 中一些基本的消息如下: 2 WM_CREATE---窗体创建消息 2 WM_SHOW-----显示窗体消息 2 WM_SET_ENABLE---设置窗体不能使用消息 2 WM_PAINT ----窗体重画消息 3 WM_TOUCH 3 WM_KEY 3 WM_SET_FOCUS 3 WM_NOTIFY_PARENT 4 WM_DELETE 4 WIDGET_HandleActive() 4 WM_DefaultProc() 5 WM_TOUCH 消息的处理流程如下: 14 GUI_PID_Load 14 GUI_PID_GetState 14 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 结构。 typedef struct WM_OBJ_struct WM_Obj; struct WM_OBJ_struct { GUI_RECT Rect; /* 窗体矩形区域 */ GUI_RECT InvalidRect; /* 窗体无效矩形区域 */ WM_CALLBACK* cb; /* 窗体消息回调函数 */ WM_HWIN hNextLin; /* 窗体下一个窗体句柄*/ WM_HWIN hParent; /* 父窗体句柄*/ WM_HWIN hFirstChild; /* 第一子窗体句柄*/ WM_HWIN hNext; /* 下一个兄弟窗体句柄 */ U16 Status; /* 窗体当前状态 */ }; 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 消息中处理[片段如下]: 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\n in a message box", "Caption/Title", GUI_MESSAGEBOX_CF_MOVEABLE); } if (Id == GUI_ID_CANCEL) { /* Cancel Button */ 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,此时证明一次点击事件完成。 void WM_NotifyParent(WM_HWIN hWin, int Notification){ WM_MESSAGE Msg; Msg.MsgId = WM_NOTIFY_PARENT; Msg.Data.v = Notification; WM_SendToParent(hWin, &Msg); } 这个函数相当简单, 其主要还是 WM_SendToParent 这个函数的调用, 这个函 数再调用 void WM_SendMessage(WM_HWIN hWin, 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 系统的体积 大小,并不是所有的情况都要有这种支持,当然我们希望在下一版本可以有多个 对话框的直接支持。 创建对话框: void MainTask(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 消息的处理流程: 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; } 这个 LOOP 类似我们非常熟悉的 WIN 下面的消息 LOOP, 其原理是一致的. GUI_CreateDialogBox 负责创建对话框的所有子窗体,特别注意它其中一个参数 传入是 Dialog.c 中定义的_cbDialog,这个函数什么也没做,基本上是转而调用 _cb,后面我们会提到关于它的修改。_cb 是对话框的用户定义窗口消息处理函 数,这里面有一个判断,就是_cb 非空时,才进行消息 LOOP, _cb 在 Dialog.c 中 的定义为:[static WM_CALLBACK* _cb;] _cb 是一个全局变量,我们程序中创 建对话框与弹出消息框时两次调用了 GUI_ExecDialogBox,后一次的_cb 将会把 前面的值冲,它是用户自定义的窗口消息处理函数。在 while 中有判断, 那么可见_cb 是在 GUI_Exec 之中是有使用的,对话框的 FrameWin 子窗体消息流转调如下面的所示,窗 口消息处理函数是在 WM_SendMessage 中通过函数指针的调用中, 注意[]内部的就是真正被调用来处 理消息的函数: GUI_Exec-->GUI_Exec1-->WM_Exec-->WM_Exec1-->WM_HandlePID-->WM_SendMes sage-->(*pWin->cb)(pMsg)[_FRAMEWIN_Callback]-->_OnTouch()-->(*cb)(pMs g)[_cbDialog]--> *_cb)(pMsg)[_MESSAGEBOX_cbCallback] WM_HandlePID()------专门处理类似 MOUSE 的滑动操作外设消息的函数. WM_SendMessage()----基层的发送消息的函数,即调用相对应的窗体的消息 回调函数来处理消息. 现在讲到了窗体消息 LOOP,在窗体系统中最根本一点的就是对外部输入消息 的处理,窗体就是靠消息驱动的,其处理代码如下: 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个词准确的意思应该是统计/测试的意思,这里是调用用户 的统计测试滑动操作外设的一个接口,用户可以通过WM_SetpfPollPID()函 数来设置自己用于统计/测试滑动操作外设的具体函数。 处理滑动操作外设 WM_TOUCH消息,真正的处理是在函数WM_HandlePID()中 处理的,在后面滑动外设消息处理流程时有详细说明,在新版中更细分此消 息为WM_PID_STATE_CHANGED/ WM_MOUSEOVER/ WM_TOUCH三种消息,其实在WIN 下面类似消息的处理更为复杂,有移动/滚动/单击DOWN及UP[左右键]/双击 [左右键]等七八种MOUSE消息,而且这些消息又分为窗体体客户区与标题区 的差别,标题区的都会在消息上加上NC的前辍,如WM_NCLBUTTONUP标题区单 击弹起消息。从这里我们也可以看到UCGUI中非常简化的处理,简单得不能 再简单了,的确是一个微型的GUI图形支持系统。 按键式外设消息处理,GUI_PollKeyMsg()函数在发现有新的按键消息生时会 调用WM_OnKey()将消息发送到当前焦点窗体处理,如果一直处于按键按下状 态时则会将前按钮的虚拟码存在一全部变量中,以供GUI_GetKey()调用来返 回当前按下键值。UCGUI中有一个外部的键盘接口,外界通过 GUI_StoreKeyMsg()发送键盘消息给UCGUI以驱动键盘,在我的模拟器当中就 是将LCD模拟显示屏窗口的所有键盘消息通过GUI_StoreKeyMsg()传送到 UCGUI中以驱动键盘消息处理,关于键盘消息的处理UCGUI中也是来一个处理 一个,没有任何缓冲处理,如果某些按钮消息处理用时过长,就会造成其后 的一些按键消息丢失。 static int _Key; //记载当前按键,GUI_GetKey时返回此值 static int _KeyMsgCnt; //当前键盘消息数量 static struct { int Key; //键盘虚拟码… int PressedCnt; //按键次数… } _KeyMsg; 上面是键盘消息结构,UCGUI中以一个全局的_KeyMsg键盘变量记载当前最新 键盘消息,当前按键值用_Key,每产生按下键时用GUI_StoreKey更新一次此 值,UCGUI中没有按键弹起消息的处理。 检测是否有无效窗体,如果有无效窗体,则向该无效窗体发送重画消息,有 一个全局变量WM NumInvalidWindows用于记载当前无效窗体的数目,在函 数_DrawNext()中每次重画一个无效窗体,查找无效窗体时是通过遍历查找 的方法,先前说过窗体基本结构中有一个成员hNextLin记载下一个窗体,就 在是此处用于遍历所有窗体,找出无效的窗体,发送WM_PAINT消息给窗体。 注意这里每次画一个窗体的原因就是为了不影响窗体的消息处理,如果在此 处用时太多,会严重影响消息处理的反应速度。 了解了 UCGUI 中消息处理的具体流程,那么再来分析这个先前提到的问题:无 论是消息框还是对话框哪一个先被关掉, 都会掉用 GUI_EndDialog,将_cb 被清 为零,也就意味着消息 LOOP 到此结束了,所以后面另外一个未被关掉的当然不 会再有任何响应了! void GUI_EndDialog(WM_HWIN hWin, int r) { _cb = NULL; _r = r; //通知 WM_Exec 等消息 LOOP 返回… WM_DeleteWindow(hWin);//free 该窗体结构占用的内存… } 现在我们可以得出一个结论:UCGUI 中对话框的设计只支持单窗口的消息处 理,如果要多窗口的支持,可以如同示例中一样,启用多任务支持,不然在单任 务下一个 MainTask 中只能支持一个独立窗体,但是如果我们只是为了要弹出一 个消息框而启动一个任务, 这未免太不实际。 了解 UCGUI 后初步修改路分析如下: 消息传送-----经过详细的分析,认识到在消息处理中创建一个对话框窗 体后,必须建立一个消息 LOOP 处理,来向 UCGUI 中的窗口捕捉并传送外 设的输入消息,消息的处理实质上是通过 WM_SendMessage 函数来调用相 应的窗口的消息回调函数。 消息 LOOP----如果创建多个对话框窗体,则会进入一个新的消息 LOOP 处理层而挂起原来的消息 LOOP 处理,要避免这种情况发生必须将消息 LOOP 移到 MainTask 之外,并在创建完所有对话框之后执行消息 LOOP 处 理。 消息分发-----用一个数组将所有创建对话框的自定义消息回调函数存 放起来,然后在对话框消息分布处(_cbDialog 函数处)对应分发各个对 话框的消息,要注意和解决的问题是,必须根据消息所对应用窗体来正 确分布。 删除窗体-----在清除独立窗体时,必须将此对话框对应的用户自定义的 窗体消息回调函数清零,并清除该窗体与其它窗体的数据关系及其占用 资源,使其退出消息处理。 四、对 UCGUI 源码做出部分修改以实现多独立窗口支持. 在第三节当中,我们通过进一步的分析源码,大致找到了解决问题的办法, 但那只是理论上的指导,实际上的修改其实还会带来其它的很多问题,因为在 UCGUI 体系中,对其源码作出改动,一定都会影响到其它的地方,现在我们就实 际的源码修改说明几点要注意的问题: 实际上这个问题有两种决办法,第一种改动很小,只须进行很小的几处改动, 是在之后想到的方法,最先采用的是第二种方法,对 Dialog.c 进行多处比较大 的改动,比较复杂,但是之所以还在此处列出,其原因是为了让大家更加清楚的 了解 UCGUI 中的外设输入消息处理机制,只有在了解了第二种方法的基础上才能 更好的理解第一种方法,否则对于第一种方法不能理解透。下面分别描述这两种 修改办法: 第一种办法: 将 GUI_ExecDialogBox 中的消息 LOOP 提出到 MainTask 这个 UCGUI 应用 当中,放在所有的对话框创建之后进行;并将在它当中增加一个创建对 话框个数的变量用于统计当前已经创建的对话框数量,在 Dialog.c 当中 有一个现成没作什么用的全局变量 static int _r;可以改做此用. 将 GUI_ExecDialogBox 中建对话框时中调用 GUI_CreateDialogBox 所传 的窗体消息回调函数改成用户自定义指定的,而非 Dialog.c 中中默认的 _cbDialog,这个函数的作用就是调用用户指定的对话框窗体消息回调函 数,所以可以在创建对话框中直接传用户指定的消息回调函数。 在 GUI_EndDialog 当中相应的将当前对话框个数减少,每关闭一个对话 框减一. 在 Dialog.c 增加一个函数用于返回当前已经创建的对话框个数 GUI_ExecDialogNum(),用于判断是否应该继续进行消息 LOOP 处理。 须要修改的几个函数修改成如下所示: int GUI_ExecDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget, int NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent, int x0, int y0) { GUI_CreateDialogBox(paWidget, NumWidgets, cb, hParent, x0, y0); return ++_r; } void GUI_EndDialog(WM_HWIN hWin, int r) { _cb = NULL; _r--; WM_DeleteWindow(hWin); } int GUI_ExecDialogNum() { return _r; } void MainTask(void) { GUI_Init(); WM_SetDesktopColor(GUI_RED); WM_SetCreateFlags(WM_CF_MEMDEV); GUI_ExecDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), &_cbCallback, 0, 0, 0); while(GUI_ExecDialogNum()) { if (!GUI_Exec()) GUI_X_ExecIdle(); } } 第二种办法: 1、将原来的_cb 修改成一个结构为 new_cb 的结构数组,首设定最多可创建 10 个对话框窗体: typedef struct win_cb{ WM_CALLBACK* _cb; //用户自定义消息回调函数.. WM_HWIN hwin; //_cb 消息函数对应的对话框窗口.. WM_HWIN hclient; //_cb 消息函数对应的对话框 FrameWin 窗口客户区... }new_cb,*lpnew_cb; //在_cb 数组中当前可用元素位置. static int dialog_pos = 0; //最多可创建对话框窗体数目,其实可以改成支持无数个,但这里作简单处理 static int MAX_DIALOG = 10; //检查是否还有独立窗体存在, 以决定是否退出消息 LOOP... int checkHasDialog(); //获取当前可用于存放对话框的位置索引, 创建新对话框时调用. int getDialogIndex(lpnew_cb lp_cb); //对话框窗口数组,创建对话框后, 将其相关信息记载到该数组当中时,其成员 //
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服