uCGUI中的窗口管理机制以及解决独立窗口并存的方法
- 格式:docx
- 大小:139.68 KB
- 文档页数:11
修改UCGUI源代码以支持多个独立窗体的说明发布时间: 2008-11-17 来源: 来自网络作者:匿名浏览: 2868问题的提出:关于对话框对话框处理程序中,ok按钮按下后想出现一个消息框。
但直接加在程序中好像不行,该怎么办?[独立窗体]------指该窗体的父窗体不是用户创建窗体,在UCGUI中其父窗体为句柄为1.一,消息处理的流程.ucgui是采用的消息驱动的. 它专门有对外的一套采集消息的接口, 我在模似器中, 就是通过LCD窗口的MOUSE消息,将MOUSE移动, 点击消息传入到这个接口中, 以驱动UCGUI中的事件的..UCGUI中的消息驱动, 其实与WINDOWS的是类似的, 几种基本的消息与WINDOWS是一样的, 但UCGUI的更简单, 消息更少, 没有WINDOWS那么多的消息种类. 在WINDOWS中, 我们最简单处理按钮事件的是在WM_COMMAND消息中, 通过按钮的标志ID来处理不同的按钮, 所以, 我们的按钮的标志ID都是不同的. 要不然无*区别开的.要处理点击OK这个按钮的事件, UCGUI的处理方*有些不同, 是在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_EndDialog(hWin, 0);}if (Id == GUI_ID_CANCEL) { /* Cancel Button */GUI_EndDialog(hWin, 1);}break;}break;ucgui中的消息非常的少, 只有差不多不到二十种, 其实这对于嵌入式系统来说, 已经完全足够了, 用户可以自定义消息, WM_NOTIFY_PARENT这个消息是由你窗体传送过来的, 是由函数WM_NotifyParent(hObj, Notification)实现的.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这个函数的调用, 这个函数又调用voidWM_SendMessage(WM_HWIN hWin, WM_MESSAGE* pMsg), 这个函数是最基本的一个消息处理函数, 它的第一个参数指定了接受这个要处理的消息的句柄, 第二个指定了是什么消息.这个函数的主要作是, 是调用相就窗口的消息处理函数来处理消息.比如说, 在这里,我们这个消息的处理过程是这样的, 首先, 你左键点击OK按钮, 那么在GUI_Exec()这个窗体消息LOOP处理当中, 首先将此消息(WM_TOUCH)传给OK按钮(注意这也是一个窗体), 然后, OK按钮的消息处理函数将此消息再以WM_NOTIFY_PARENT传送到你窗体---对话框当中, 这样, 对话框就可以处理到点击OK按扭这个事件了...其实这个消息处理的流程与WINDOWS也是类似的. 原理一样..理解了这个过程, 那么, 我们就可以这个消息中处理很多东西了, 只要是在对话框中的子控件, 一般的消息都可以如此处理.二,发现存在的问题----两个独立的窗口并立时, 当关闭其中一个, 另外一个不再有响应.在如下代码中处理弹出消息框, 点击对话框的OK后弹出消息框, 会出现当按对话框的Cancel关闭对话框后, 弹出的消息框就没有任何响应的情况. 或者是关闭掉弹出的消息框, 对话框就没有任何响应的情形. 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);GUI_EndDialog(hWin, 0);}if (Id == GUI_ID_CANCEL) {GUI_EndDialog(hWin, 1);}break;}经分析, 粗步的原因是, 调用MainTask的主线程已经退出了, 这个主线程是在模拟器中开启的, 它的主线程函数是Thread(), Thread函数里调用main, main再调用MainTask; 所以主线程退出后, 消息框再也没有任何反应了.这是从模拟器的角度来分析, 现在我们分析一下, 为什么MainTask的主线程会这么早退出呢? 深入UCGUI的源码, 可以知道, 对话框的消息处理函数是在如下的函数中调用的.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();//出现HardFaultException()中断的原因}return _r;}在如上的代码中我们可以看到, 当UCGUI中有一个独立的窗体退出后, _cb会被清为0, 此时退出GUI窗口LOOP. 即结束了UCGUI窗口消息处理.其实, GUI_MessageBox弹出的消息框其实也是一种对话框, 在这里我们就可以发觉UCGUI的窗口支持系统还不是非常的完备, 我们期待的结果是, 点击对话框的OK, 弹出消息框, GUI_EndDialog关闭掉对话框,消息框继续有反应, 如果没有在OK按钮中调用GUI_EndDialog关闭对话框, 则对话框与消息框都要能够正常反应.关于这个问题的解决, 我想还要进一步了解UCGUI窗口处理的细节.三.寻找问题的解决办法.在我们发现这个问题, 我们已经粗步分析了, 问题不是出在我们编写程序上, 而是UCGUI的内部, 那么要解决这个问题, 我们就要进一步了解UCGUI的窗口体系.其实换一句话说, 在嵌入式应用中, 窗口的强大直接决定到GUI系统的体积大小, 并不是所有的情况都要有这种支持, 也不一定说是UCGUI的BUG, 当然我们希望在下一版本不再有这个问题.下面是详细分析:1. 对话框void MainTask(void) {GUI_Init();WM_SetDesktopColor(GUI_RED); /* Automacally update desktop window */WM_SetCreateFlags(WM_CF_MEMDEV); /* Use memory devices on all windows to **oid flicker */GUI_ExecDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), &_cbCallback, 0, 0, 0);}上面是我们创建对话框的程序, 是我们编写的代码, 具体看GUI_ExecDialogBox()这个函数代码如下, 它主要有以下两个作用:[1].创建对话框中的所有子窗体.[2].进入消息LOOP, 收集并转发消息到对应窗体进行处理. 消息如WM_TOUCH及WM_KEY, 这些消息被分发到对话框中各子窗体中去处理,父窗体(对话框)之所以能够处理其上子控件的消息,全都是因为子控件在传递消息, 父窗体中对子窗体的消息进行处理, 也可以不处理, 我们编程者要处理时, 则要清楚哪些消息是可以处理的, 不然无*编写程序, 所以子窗体中的消息一直都在往父窗体中发送, 与父窗体有无处理无关系.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;}2. 消息框其实消息框本身就是一个只含静态文框及OK按钮的对话框, 在对话框的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_EndDialog(hWin, 0);}if (Id == GUI_ID_CANCEL) { /* Cancel Button */GUI_EndDialog(hWin, 1);}break;}break;在关闭对话框之前弹出一个消息框, 当我们都不点击消息框与对话框的OK按钮来结束时, 他们都有反应也可以前后台切换, 不存在什么问题. 问题就出在关闭其中一个后, 其它的任何一个也无反应.这个原因, 在上面有过过粗步的分析, 但没有进入到UCGUI的实现机制内, 现在我们来看一下._cb = cb;while (_cb) {if (!GUI_Exec())GUI_X_ExecIdle();}这个LOOP, 其实它就是类似我们非常熟悉的WIN下面的消息LOOP, 其原理是一致的. _cb是什么东西呢? 这里我们可以看到, 它其实就是对话框的窗口消息处理函数, 这里面有一个判断, 就是_cb非空时, 才进行消息LOOP, _cb在Dialog.c中的定义如下:static WM_CALLBACK* _cb;_cb是一个全局变量! 我们程序中创建对话框与弹出消息框时两次调用了GUI_ExecDialogBox, 后一次的_cb将会把前面的值冲掉,我们程序中, 可以知道它是消息框的窗口消息处理函数地址. 由此可知道谁在后面调用, 则另外一个对话框的窗口消息处理函数根本就是无用的, 它的消息其实是被后来创建的对话框消息处理函数所处理的.在while中有判断, 那么证明_cb是在GUI_Exec所产生的调用中有使用的.进一步看, 可以看到, 调用的其实是如下面的箭头方向所指, 一步一步深入的, 其中最明显的一点就是, 窗口消息处理函数是在WM_SendMessage中通过函数指针的调用中, 注意[]内部的就是真正被调用来处理消息的函数, 它是将消息一步一步的传到真正要处理此消息的窗体中, 在这里其实就是后面弹出的消息框窗体.GUI_Exec--->GUI_Exec1-->WM_Exec--->WM_Exec1-->WM_HandlePID-->WM_SendMessage-->(*pWin->cb)(pMsg)[_FRAMEWIN_Callback]-->_OnTouch()--->(*cb)(pMsg)[_cbDialog]--(*_cb)(pMsg)[_MESSA GEBOX_cbCallback]WM_HandlePID()----------专门处理MOUSE消息的函数.WM_SendMessage()--------基层的分派消息的函数, 决定谁应该被调来来处理消息.这里面有些人会提出一个问题, 为什么消息框与对话框的消息都在后来创建的消息处理函数里面处理而不会引发什么问题呢?是的, 表面上看去, 消息框与对话框都好好的, 没什么异常, 但其实如果你在对话框中进行过消息的处理,那么你就可以很清楚的看到这些消息是从来不会发生的, 即使你在它的消息处理函数里下断点它也不会有任何中断.至于其它的一些常用的消息的处理, 对话框与消息没有什么区别, 最重要的消息就是重画, 重画的时候, 比如消息框与对话框有重叠的话, 当进行窗口前后切换将其中一个切换到前面来时, 会产生重画消息. 这样就会重新计算一次该画到屏慕上的东西, 不光要发重画消息到各窗口, 还会将整个屏慕的东西都重新计算一次, 并画出来. 这一点还是很复杂的, 须要进一步学习.是GUI图形的一个难点和重点. 以后再详细分析.进行到这里, 我们就可以比较明白的知道, 在点击OK后, 无论是消息框还是对话框, 哪一个先被关掉, 都会调用下面的GUI_EndDialog. 这里, _cb被清为零, 也就意味着消息LOOP到此结束了. 所以后面另外一个未被关掉的当然不会再有任何响应了.void GUI_EndDialog(WM_HWIN hWin, int r) {_cb = NULL;_r = r;WM_DeleteWindow(hWin);}原因我们分析出来了. 现在我们要解决这个问题, 那还差一大步, 还有很多问题要进一步弄清, 为什么UCGUI是这样的机制.要如何解决. 将会在下一步仔细学习分析.楼主再仔细读一下我的分析, 结合实际自己也来分析一下, 问题虽然小, 但其实隐藏着的问题不小. 你的所言其实还是没有理解清楚问题, 因为你所说的HIDE对话框. 其实没有点着问题实际, 我们现在所讲的是UCGUI对于多个独立窗体的消息处理支持. UCGUI不完善.对这个目前没有支持. 你要深入到这一点来理解问题.再仔细看下我的分析. 有什么问题再提, 结合实际, 进行源码调试, 对于问题会有更多的了解....四. 进一步分析找到解决办*.点击对话框的OK按钮后, 其实这个时候, 由于消息框是对话框后弹出来的,那么这个消息框的消息处理函数_MESSAGEBOX_cbCallback则成为_cb的值了, 那么以后对话框与消息框的消息处理都是在消息框的_MESSAGEBOX_cbCallback里面处理的, 如果你是在点击对话框的OK按扭之后弹出消息框.如下: 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);GUI_EndDialog(hWin, 0);}if (Id == GUI_ID_CANCEL) {GUI_EndDialog(hWin, 1);}那么, 程序执行到弹出消息框之前, 消息WM_NOTIFY_PARENT 都是由对话框的消息处理函数_cbCallback处理的, 在弹出消息框之后, 如我们在(二)上面的分析, 就知道以后的消息都是由消息框的_MESSAGEBOX_cbCallback来处理的, 此时, 在消息LOOP中马上又会发现上一次的MOUSE消息, 因为这个点击OK产生的消息还在, 并没有产生新的消息, [说来其实UCGUI中的消息是没有什么队列的, 是接收一个处理一个, 即时处理那种, 这样就简单了很多,] 那么又一次进行WM_NOTIFY_PARENT的GUI_ID_OK按钮的处理, 只不过这个处理是在消息框的_MESSAGEBOX_cbCallback函数中进行的, 在这个函数里面默认的对于GUI_ID_OK的处理就是调用GUI_EndDialog(hWin, 1); 一旦调用这个函数, 那么产生的结果就如同先前分析的,会导致WM_DeleteWindow的调用, 而将_cb的值清为空, 并清除hWin 及其所有了窗体. 从而结束了LOOP消息处理.所以, 我们可以看到, 在点击对话框的OK后, 弹出消息框, 之后即使我们不调用清除对话框的函数, 什么也不做. 也会导到消息LOOP结束, 退出所有窗口消息的处理.其实, 须要进一步说明的是, 在UCGUI中, 似乎在设计时只是支持单窗口的消息处理, 如果要多窗口的支持, 可以如同示例中一样, 启用多任务支持, 不然, 在单任务下, 一个MainTask中只能支持一个独立窗体, 但是, 显然我们如果只是为了要弹出一个消息框而启动一个任务, 这未免太不实际.解决的办*, 还是有的, 那就是了解UCGUI, 自己来修改. 可以如下做-------[如上创建一对话框及消息框, 对话框与消息框即为如下所讲的外部独立窗体]1. 经过详细的分析, 认识到在消息处理中, 创建一个独立窗体, 则其窗口消息处理函数就会成为整个UCGUI中的消息的外部接收口, 用户所能处理到所有消息均在此处接收到.2. 如果在后来在创建独立窗体, 则后来的窗口的消息处理函数代替了先前创建的窗口的消息处理. 那么, 如果不使它替代, 而是用一个数组将所有消息处理函数都存放起来, 然后在内部将消息往外传的地方(即外部独立窗口消息函数被调用处)修改成调用所有非空的外部独立窗口消息处量函数(而不是单个).这样就可以达到每个独立窗口消息处理函数都被调用, 各自独立运行. 但要注意和解决的问题是, 消息该如何分配到各独立窗口.3.在清除独立窗体时, 只是将此独立窗体对应的消息处理函数清零, 并清除该窗体及其的所有子窗体. 使其不再处理消息.那么. 我们在点击OK关闭对话框, 也就是清除对话框并让其退出消息接收. 对于弹出的消息框, 我们希望它作为一个新的独立窗体而进入窗体消息接收与处理. 就达到了我们的目的.这是理论上的分析. 我将把实际修改后的东东也发上来, 做一示例.五. 对UCGUI源码做出部分修改以实现多独立窗口支持.[2005-6-30 2:30]在第三节当中, 我们通过进一步的分析源码, 大致找到了解决问题的办*, 但那只是理论上的指导, 实际上的修改其实还会带来其它的很多问题, 因为在UCGUI体系中, 对其源码作出改动, 一定都会影响到其它的地方, 现在我们就实际的源码修改说明几点要注意的问题:1、将原来的_cb一个变量, 修改成一个结构为new_cb的结构数组,有10个元素,/static WM_CALLBACK* _cb;typedef struct win_cb{WM_CALLBACK* _cb;WM_HWIN hwin; /_cb消息函数对应的窗口..WM_HWIN hclient;/_cb消息函数对应的窗体窗户区...}new_cb,*lpnew_cb;static int dialog_pos = 0;/在数组中当前可用作存窗体消息函数的元素.static int MAX_DIALOG = 10;/最多可支持打开的独立窗口数, 其实可以改成支持无数个,但这里作简单处理int checkHasDialog(); /检查是否还有独立窗体存在, 以决定是否退出消息LOOP...int getDialogIndex(lpnew_cb lp_cb);/获取当前可用于存放独立窗体的位置索引, 创建新独立窗体时调用. static new_cb _cb[10]; /独立窗口数组创建一个新的独立窗体, 将此窗体加入到独立窗体数组当/中时, 必须注意几个问题,在如下的新旧代码对比中说明:/新修改后的创建对话框的函数...int GUI_ExecDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget,int NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent,int x0, int y0){/ _cb = cb;dialog_pos = getDialogIndex(_cb);if(dialog_pos != -1) _cb[dialog_pos]._cb = cb;else return _r;GUI_CreateDialogBox(paWidget, NumWidgets, _cbDialog, hParent, x0, y0);/ while (_cb) {/* 2005-6-30 0:20:16while (checkHasDialog()) {if (!GUI_Exec())GUI_X_ExecIdle();}*/return _r;}/旧的创建对话框的函数...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;}WM_HWIN GUI_CreateDialogBox(const GUI_WIDGET_CREATE_INFO* paWidget, int NumWidgets, WM_CALLBACK* cb, WM_HWIN hParent,int x0, int y0){WM_HWIN hDialog = paWidget->pfCreateIndirect(paWidget, hParent, x0, y0, cb); /* Create parent window */WM_HWIN hDialogClient = WM_GetClientWindow(hDialog);_cb[dialog_pos].hwin = hDialog;_cb[dialog_pos ].hclient = hDialogClient; /加到GUI_CreateDialogBox中的,其余不变...WIDGET_OrState(hDialog, paWidget->Flags);........}[1]、getDialogIndex(_cb),创建新的独立窗体前,首先就要在独立窗体数组中查找空位置,如果独立窗体已达最大数,则不可再加入独立窗体,这里我做的是简单处理,没有用到动态内存分配,将独立窗体数组扩大,主要是因为是演示,读者自己可以试度支持无限独立窗体。