uCGUI多对话框弹出问题解析

再次解析UCGUI390多对话框问题

 

作者:UCGUI

邮箱:UCGUI@163.com

主页:http://www.UCGUI.com

版本:v1.0.0.0

时间:2006/12/01

 

版本

修改说明

时间

v1.0.0.0

ü 完成多对话框支持的原理解析,以及模态对话框架的原理。

ü 指出某些情况下UCGUI多对话框支持的问题及解决办法。

 

2006/12/01

 

 

摘要:说明UCGUI390版多对话框支持的原理以及模态对话框的支持原理,并指出在某些情况下使用多对话框时的导致UCGUI出错的问题.

 

目录

 

一. UCGUI中的基本设计策略----微型.

二. 为什么UCGUI的消息处理机制会导致多对话框的问题.

三. UCGUI324与UCGUI390版本在处理多对话框上的几点不同之处.

四. 如何改进UCGUI390, 以使其支持在任意控件的点击消息当中弹出对话框.


问题:

caseWM_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("Message","caption",GUI_MESSAGEBOX_CF_MOVEABLE);

       }

       if (Id == GUI_ID_LISTBOX0 ){             

           pObj=LISTBOX_H2P(hListBox);

           LISTBOX_GetItemText(hListBox,1,ListItemName,20);     GUI_MessageBox("item is:",ListItemName,GUI_MESSAGEBOX_CF_MOVEABLE);

//改成下面这句正确..

//MESSAGEBOX_Create(ListItemName,"itemis:",GUI_MESSAGEBOX_CF_MOVEABLE);

       }

             

在如上代码当中, 在点击ListBox时,执行GUI_MessageBox()会导致弹出N多对话框,以及"GUITASK.c: GUI_Lock() _EntranceCntoverflow "的错误提示. 但是点击按钮时, 执行GUI_MessageBox()没有问题, 这是什么原因??

 

论题分析:

 

深入UCGUI的消息处理机制当中,详细介绍一下为什么在UCGUI390版本中,仅有Button控件的点击支持弹出对话框,其它的控件均会导致出错呢?

 

UCGUI324版本当中, 我们详细的剖析了为什么在点击Button消息中弹出对话框会导致出错, 详细的分析可见"UCGUI窗体管理及消息处理机制分析.pdf"(可到www.ucgui.com/bbs当中下载).

 

[原创]有关UCGUI的几篇技术文章下载

http://www.ucgui.com/bbs/dispbbs.asp?boardID=1&ID=131&page=1

 

这里不再重复的讲先前讲过的东西了, 下面我们将大略介绍如下几点:

 


一. UCGUI中的基本设计策略----微型.

 

与其它一些同样是嵌入式的GUI图形系统相比, UCGUI很明显的一大区别是:微型; 这微型的特征主要体现在两点:节省空间, 包括代码空间与程序运行占用空间.

 

-----代码空间占用少, 是指其对于同样的功能, 其实现简单,结构简单,设计简单,复杂的功能放弃;

 

-----程序运行占用空间少, 是指支持GUI运行时, 所消耗的变量空间小, 这对于本来RAM空间就紧张的小型嵌入式应用, 是非常的重要的. 程序运行消息消耗的空间小, 主要有两个方面:

 

[1].消息处理不用队列,而是采取两个变量, 一个变量记载当前消息,一个变量记载先前消息,当前消息从外部输入驱动中获取更新, 处理消息时与先前消息进行比较, 处理完消息再将当前消息更新到先前消息变量中.这样处理起来,比起采用消息队列, 就省了不知道多少空间了,因为在启用MOSUE类设备时,移动消息是非常多的.不采用消息队列, 也导致了UGGUI中的所有的消息处理都是同步的, 但这对于UCGUI来说也不是什么严重的问题, 并不是必须的功能; 没有消息队列, 则每个消息的处理不能占用大量的时间, 否则严重影响消息处理的灵敏度,造成消息的丢失.但是这些问题都是可以接受的, 因来带来的空间效率带有帮助了.

 

[2].单一运行绪:UCGUI运行的时候, 仅有一条执行路径, 也就是单一运行绪.这与MINIGUI多线程版是有很大的差别的,熟悉MINIGUI的朋友都知道MINIGUI分了三个版本, 多进程版/多线程版/单一版.多线程版分如下线程:

 

-----用户程序是一个线程.

-----桌面程序是一个线程.

-----事集收集是一个线程.

-----时间Timer是一个线程.

-----创建窗口也可以单独启用一个线程.

 

分了线程,当然有些好处, 如计时器/消息接收不至被消息处理阻塞; 但是UCGUI中没有这些复杂的机制,从而少了很多的线程同步以及与其它系统的相关性(便于移植).而且在资源占用了又少了一层, 节省了线程资源.

 

[3].剪切处理, 窗口系统的剪切处理有两个要素, 一是时间效率;二是空间效率.UCGUI中采取的是空间策略, 每进行一次图形绘制,都必须处理剪切; 在时间效率优先的系统当中, 会计算过后就保存每个窗口的剪切矩形, 如果是在这个窗口上绘图, 就无须每一次绘图操作都进行剪切处理, 只须要判断是否处于剪切矩形之内即可, 当然这个判断也是很讲究策略的, 因为剪切矩形是一个系列,如果是绘制一个矩形, 这个矩形被分布在几个剪切矩形当中, 这个时候也能采取两种办法:一是逐点判断之后画点;二是再进行一次剪切计算,剪切出可以绘制的矩形.这两个方法其实也体现了空间与时间的问题, 不过第二种方法在时间上很优, 在空间上也仅是占用一时, 所以是比较优的. 但是如果要计算过每个窗体的剪切矩形后, 要保存起来, 供窗体上再次作画时用,这个空间占用则是比较大的.

 

 

二. 为什么UCGUI的消息处理机制会导致多对话框的问题.

 

同上面关于UCGUI中的消息机制的描述, 具体来看一看为什么会导致多话框支持上的问题.

 

[1].UCGUI中创建对话框后, 会形成一个独立的消息LOOP, 独自的进行消息处理. 我们已经说过, UCGUI中的所有消息处理都是同步的,而且是单一执行绪的. 所以这样一来, 明显就带来的一个问题, 试想如下一个点击消息中弹出窗体的流程:

 

----1.获取外部驱动的消息,更新了当前消息变量, 设当前消息为CurrentMsg, 先前旧消息为PrevMsg, 比较新旧消息,如单击发生的坐标及按下与否的状态.

----2.如果是外部驱动产生的新消息(如单击消息), 处理单击消息, .

----3.进入单击消息处理,发送单击消息给对应的当前窗体, 根据当前的点击坐标取得单击发生位置的窗体句柄.

----4.对应的消息窗体处理消息, 弹出对话框.

----5.更新消息,CurrentMsg=PrevMsg, 再转入第1步, 检测消息.

 

这是一个整体的消息处理完成的过程, 这个过程是一直LOOP, 反复检测的.想想如果是新弹出一个对话框, 又进入了新的消息LOOP, 那么可以肯定的是, 在新的消息LOOP处理当中, 由于上一次消息LOOP的第5步[更新消息]没有完成, 则新消息LOOP中当中就会反复处理这个没有更新的消息了. 然且这个过程是一个嵌套的过程, 一层的消息LOOP引发下一层的消息LOOP, 但都无法完成在短时间内最后一步的消息更新.

这里我说短时间内, 是指的新的消息还没有到来的时候, 因为新的消息一到来, 就终止了不断处理旧消息弹出对话框的过程.

 

[2].UCGUI中详细的消息处理流程:

 

在最新版的UCGUI390版, 消息的处理主要分成四个部分.

 

----模态窗口消息分发, 检测当前窗体是否与模态窗体有关, 即是否是模态窗体本身或者其子孙窗体, 用WM__IsInModalArea()检测;这一步控制了消息的分布, 实现模态窗体的功能就是这一句代码了.

 

----弹起与按下状态变化的消息分发(WM_PID_STATE_CHANGED消息), 这个消息的分布在按下MOUSE或者弹起MOUSE的时刻发生.

 

----只要是按下状态, 以及按下与弹起状态变化的消息分发(WM_TOUCH), 这个消息在MOUSE按下或者弹起, 以及一直处于按下时发送, 比如你按下一个BUTTON, 一直不放, 当移动到另一个BUTTON时, 会看到焦点发生了切换到了新的BUTTON上, 就是这个消息导致的.另外这个消息会在消息发生的新旧窗体交界时, 会最后向原来的旧窗体发最后一个WM_TOUCH消息(状态为弹起), 发送这个消息为了让原来的旧窗体改变自己的状态(如取消息焦点框等), 正如上面你按住MOUSE滑动新的BUTTON, 则原来的BUTTON的焦点状态取消, 不再高亮显示, 就是这个最后的消息发送导致的.

 

----移动的消息分发(WM_MOUSEOVER),这个消息是在打开了GUI_SUPPORT_MOUSE宏配置的时候才分发, 这个消息是很大量的, 只要MOUSE移动都会产生.

 

三. UCGUI324与UCGUI390版本在处理多对话框上的几点不同之处.

 

新旧版本主要有如下几点不一样:

 

[1].增中了模态窗体的支持, 这个在第二点中已经描述过了.

[2].增加了WM_MOUSEOVER消息.

[3].增加了WM_PID_STATE_CHANGED消息.

 

但是如果仅仅这个地方一点小变化, 也不足以让UCGUI390支持在按钮点击消息中弹出对话框, 还有如下几个变化:

 

[1].创建对话框的变化.

具体的问题, 请用户自己查看代码.

 

-----UCGUI324版中, 在创建对话框时, 用一个全局的回调函数_cbDialog(), 作为每个对话框的回调函数, 用一个全局变量_cb来保存用户对话框自己指定的回调函数. 然后在_cbDialog()函数中再调用用户对话框自己的回调函数(存在_cb变量中). 这显然是有问题的, 如果创建了新的对话框, 则这些全局变量就被新的赋值取代了, 就调用不到用户原来对话框指定的回调函数了.

 

-----UCGUI390版中, 就不再用什么全局变量了, 直接用用户指定的回调函数, 就不存在创建新的对话框后, 用户原来对话框指定的回调函数无法调用到的问题了.

 

[2].BUTTON按钮的点击消息处理的变化.

 

-----主要的差别在于, 新版当中加上了Button是否BUTTON_STATE_PRESSED状态的判断, 即在处理按下BUTTON弹起后, 要向父窗体发送出WM_NOTIFICATION_RELEASED消息前, 调用WIDGET_AndState(hObj, BUTTON_STATE_PRESSED);来取消Button的按下状态, 就是这一点, 就使得即使有未更新的消息再次处理, 也不会导致再次给父窗体发送Button弹起的消息, 从而避免了反复陷入弹出对话框, 消息处理中消息一直无法更新的问题.

 

[3].除了Button控件之外, 其它控件在单击消息中弹出对话框, 会出问题.

 

-----比较上面Button的处理, 问题就相当容易理解了, 因为其它控件没有关于BUTTON_STATE_PRESSED这种类似状态的判断, 所以导致了未更新的消息反复的处理, 在短时间内弹出一堆的对话框出来, 并且由于反复的消息LOOP嵌套, 导致加锁的没有解锁, 从而报出"GUITASK.c: GUI_Lock() _EntranceCnt overflow "的错误提示.

 

四. 如何改进UCGUI390, 以使其支持在任意控件的点击消息当中弹出对话框.

 

[1].弹出对话框时, 如果没有什么特别的问题, 可以使用模态对话框, UCGUI390中支持模态对话框, 由于是模态对话框, 那么先前未更新的旧窗体的消息, 就无法再进行处理了, 所以不会陷入反复的弹出对话框中去.

 

创建模态对话框:

{

    WM_HWIN hWinDlg =MESSAGEBOX_Create("   YouWin!!!   ", "Win",GUI_MESSAGEBOX_CF_MOVEABLE);

    WM_MakeModal(hWinDlg);

    GUI_ExecCreatedDialog(hWinDlg);

}

 

[2].如果是不采用弹出模态对话框, 我想另外的办法就是, 改进每个控件,对于MOUSE弹起消息转发父窗体时, 加上类似BUTTON的控制, 加上一个状态的识别,就可以了, 但这要改动几乎每个控件的_OnTouch()处理.

 

[3].前面说过, 正是因为弹出的对话框进入了新的消息LOOP, 导致原来的对话框的消息LOOP无法再执行下去, 无法更新旧消息变量. 那么我们就可以采取不让新建的对话框开启新的消息LOOP, 这很容易, 打开对话框时如下:

 

MESSAGEBOX_Create(ListItemName,"sel item is", GUI_MESSAGEBOX_CF_MOVEABLE);

   

这里仅创建对话框, 而不执行 GUI_ExecCreatedDialog(hWin), 就不会开启新的消息LOOP; 那消息LOOP放到哪里去呢?直接放到MainTask()当中去:

 

    while(1) {GUI_Exec();};                                                  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值