gui回调函数调用和消息响应的区别

事件处理程序和回调之间的区别


┅般来说“回调”是在检测过程的控制之下。因此您告诉GUI管理器“ myaction按下此按钮时调用”,GUI管理器在按下按钮时调用操作

另一方面,倳件处理程序在一步操作中被删除GUI管理器配置为将消息发送到事件处理程序。您告诉事件管理器按钮按下由myaction程序处理当按下按钮时,GUI管理器将消息放在事件处理程序的队列中然后继续GUI管理。事件处理程序从队列中获取消息看到它是按钮按下,启动myaction程序然后继续处悝下一个事件。通常myaction程序将作为独立的线程运行甚至是单独的进程。

虽然“事件处理程序”模式更复杂但它更强大,并且在操作失败時不太可能挂起它还可以提供响应更快的GUI。

回调是您作为参数传递给另一个过程的过程接收参数的过程可以调用它,或者共享它以便系统中的其他一些过程可以调用它。

事件处理程序是事件发生时调用的过程它可以是一个回调。

事件处理程序是一种回调只要事件發生就会调用它。该术语通常用于用户界面其中事件是移动鼠标,点击某些内容等等

回调(来自维基百科):“作为参数传递给其他玳码的可执行代码”。
事件处理程序(同样来自维基百科):“异步回调子程序处理程序中收到的输入”。

这恰好是我一直理解的方式:事件处理程序是一种非常特殊的回调类型

这个问题很老但我发现这个链接非常有趣。我希望遇到这个问题的其他任何人都能从这个链接中获得一些东西

事件 - 想想服务器(员工)和客户(老板)。一个员工可以有很多老板员工在完成任务时提出事件,并且老板可能决萣是否听取员工事件员工是出版商,老板是订户

回调 - 老板专门要求员工完成任务,在完成任务结束时Boss希望得到通知。员工将确保在任务完成时他只通知所要求的老板,而不是所有老板如果部分工作完成,员工将不会通知Boss只有在完成所有任务后才会这样做。只有┅位老板要求提供信息员工只将答复发布给一位老板。

另一个方面是事件描述了过去发生的事情而事件发生时经常使用回调。

当事件發生时您会被告知发生了某些事情。使用回调时系统会要求您参与某些操作。

库或框架可能会发出事件让您知道发生了什么。框架為您提供了可以插入代码(可能作为回调)的点以便您可以积极参与流程。

问题的一部分是事件回调是指技术的mcehanism以及更抽象的过程。

詹姆斯安德森的回答是最详细的扩大他的答案; 回调是指任何代码,它作为参数传递给方法打算稍后异步调用。但是回调并没有定义洳何实现回调本身的过程。这是回调分类开始的地方传统上,回调过程看起来如下:

  • 开发人员定义一个回调并将其传递给库定义的函数(一个知道如何处理回调以便调用进程或检测进程可以调用它),例如在节点中
  • 在运行时,检测过程接收回调的直接位置(因为库定義的函数为你做了)并调用它这意味着两件事:
    • 库的开发人员总是需要知道如何处理不同的检测过程,因为每个可能有不同的回调方式
    • 洳果需要多次调用回调它可能会对检测过程负责。例如如果检测过程是GUI过程,那么您希望GUI线程以尽可能少的任务运行以获得流畅的GUI體验。

因此需要实现回调机制这解决了这两个问题:

  • 一个外部进程,它定义库函数的并发点以注册回调并为检测进程通知何时需要调鼡这些已注册的回调。
    • 这意味着这两个流程的开发人员现在可以彼此独立工作而无需真正了解彼此的方式。
    • 这个外部过程被称为事件循環(在节点中例如)。术语“事件”只是通过检测过程通知事件循环的过程并且已注册的回调被称为事件处理程序。
  • 外部进程(或事件循环)将事件排队并执行它们从而减轻检测过程的负担,现在可以恢复到最佳状态

因此,对于多次发生的事件事件循环或事件处悝程序成为实现回调的方式,而原始回调仍然是一次性事件的首选因为您没有真正执行检测过程并且不需要事件只有一个事件循环,因為它效率不高

  • 上面的节点js代码是一次性事件(因为与客户端的服务器的连接是一次性事件,而可以有许多响应被实现为事件处理程序)並且是简单回调的示例

底层机制类似,但语义不同回调和事件处理程序都是异步调用的。

gui回调函数调用通常从调用程序例程显式传递鉯请求一些信息一段时间后返回信息,作为参数传递回被调用者的回调此时,调用例程完成其业务通常,回调是一个闭包 - 在语法例程中语法化通常是未命名的(匿名)。它可能看起来有点像下面的javascript:

因此callee(someLibrary.getMeSomething)被赋予一个匿名gui回调函数调用一段时间后,这个函数被returnData調用回调就像是单个接收器的单次事件。

事件处理程序也被“回调”但通常它们会在很长一段时间内用于多个事件,例如鼠标点击網络事件等。此外多个对象可能对同一事件感兴趣。出于这些原因您通常会“订阅”或“注册”设置代码中的事件(如对象初始化),事件处理程序通常是命名方法通常,每个事件类型都被标识为常量或字符串

所以在Python中它可能看起来像:

我喜欢这些答案之间的差异。

我将从中得出结论从术语的角度来看,事件和回调是可以互换的它们在特定的编程语言或框架中的含义有所不同,因为任何平台都傾向于选择自己喜欢的术语

回调是作为参数传递给另一个函数(方法)的函数(方法)。接收参数的函数(方法)可以调用它或者共享它,这样系统中的其他一些函数(方法)就可以调用它

事件处理程序是事件发生时调用的函数(方法)。它可以是一个回调

}

UCGUI窗体管理及消息处理机制分析

----多對话框/模态窗体/透明窗体支持分析

实现UCGUI中多对话框支持

ainTask函数中由用户处理的原因剖析,并详细分析了对话框中按钮的点击消息传送到用戶自定义对话框gui回调函数调用中处理的传递流程

增加外部输入设备消息处理机制介绍。如滑动操作外设MOUSE及触摸屏输入消息(WM_TOUCH)的处理机制忣按键式操作外设消息(WM_KEY)处理机制。

ü    增加一种更简单的多对话框支持的方法及说明

[求助]关于对话框处理程序中,想在OK按钮按下后想弹出┅个消息框该怎么做?直接加在程序中好像不行如何让消息框弹出后成为模态窗体呢?请版主帮帮忙

[解析]UCGUI中,对话框只支持单个對话框窗体不支持多个独立的对话框,现在我们从其源码来分析一下它为什么支持单个对话框窗体以及如何改进它以支持多个独立对话框要讲解这个问题我们必须首先理解UCGUI中的窗体消息LOOP,没有消息LOOP窗体就是死水一潭不能接受任何外界的输入,只是一个画在那里的图画而巳。

本文主要介绍了UCGUI中的对话框的消息处理机制,并指出在现有UCGUI上如何增加多窗体支持,并在分析解决问题时着重介绍了其输入设备消息WM_TOUTCHWM_KEY两類消息处理方法,并同时初步指出一种在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中很多没有实现的小地方,不要到时候使用时感到很奇怪感觉到奇怪时最好去看看源码,看看源码中是否实现了此功能不要郁闷。

----窗体重画消息当窗体所在区域全部或是部分区域无效时,系统将会发出该重画消息将无效区域重画,但UCGUI中的处理比较简单都是将窗体全部区域重画;如果用户自己想在窗体上画上一些信息,┅般都在在该消息当中画,UCGUI中的各种提供的系统控件都必须在其系统的提供的消息gui回调函数调用中处理此消息来画出控件当由外部输入操莋引起无效窗体区域产生时,系统都会在消息处理中发送该消息到窗体消息gui回调函数调用中以重画此窗体,在下面讲解消息循环机制时將会着重讲解到该消息的产生

[透明窗体]---经常有朋友想知道在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结构

WIDGET_HandleActive()—基础控件共通消息处理,在大部分的UCGUI控件中都会在消息gui回调函数调用的头部进行这个调用如果处理了消息后,就直接退出消息gui回调函数调用的调用这个函数中处理如下消息:

WM_KEY[铵键消息处理,通知父窗体子窗体的按键消息,有些控件自己要处理这个消息洳Edit控件处理完此消息后就没有再调用WM_DefaultProc(),从而没有将WM_KEY消息通父窗体;如Button控件根本没有对此消息进行处理,直接是通过默认处理发给了父窗體处理;有些控件如Checkbox自己处理该消息同时也调用默认消息处量将此消息通知父窗本,此种消息源窗体为子控件目标窗体为父窗体。如此处理WM_KEY消息完全是UCGUI中如此做在WIN中并没有这样做].

UCGUI的对话框的窗口消息处理函数中OK按钮的点击事件,

UCGUI中的消息种类不多, 只有差不多不到二十種,但对于嵌入式系统来说已经完全足够了,用户可以自定义消息(WM_USER) 由消息的名字也可以看出这一点,OK按钮也是一个窗体当MOUSE点击在它仩面时,UCGUI首先会传递一个WM_TOUCH消息到OK按钮的窗口消息处理函数OK按钮是一个系统提供的控件,系统已经提供了一个默认的消息的窗口消息处理函数这个函数会处理大部分的默认窗口消息并随后将此消息转发给父窗体,即WM_NOTIFY_PARENT消息它是由函数WM_NotifyParent(hObj,

WM_TOUCH消息在按钮的消息处理函数_BUTTON_Callback中的_OnTouch函数中處理,在处理过程完后会调用WM_NotifyParent向按钮的父窗体发WM_NOTIFY_PARENT消息告诉对话框gui回调函数调用按钮被点击了这个过程再说详细一点是这样的:

_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传送的消息首先是传送到对话框的默认窗体消息gui回調函数调用_cbDialog,然后再经它传送到用户自定义的窗体gui回调函数调用当中

它的第一个参数指定了接受这个要处理的消息的句柄, 第二个指定了是什么消息。这个函数的主要作用是调用相应窗口的消息处理函数来处理消息如果你有消息要发送给指定的窗体处理,那么也可以使用这個函数

我们刚刚分析了在对话框内部消息处理的流转,其中分析了我们在自己指定的对话框消息处理函数当中是如何可以获得按钮的点擊消息并进行处理的现在我们就再来分析一下对话框外面的消息接收:首先是来了解一下GUI_ExecDialogBox函数,这个函数有几个参数:

第一个是对话框嘚资源定义数组这个数组定义了对话框的组成子窗体,其中数组第一个成员必须是FrameWin窗体数组每一个成员记载了创建子窗体所用函数/子窗体Caption/子窗体标志ID/子窗体的位置及宽高/创建窗体时样式标志/额外传送的参数.

根据传进来的窗口消息处理函数,记载到一全局变量保存当这個全局变量中记载的函数指针为非空时,执行消息LOOP消息LOOP中会将当前的MOUSEKEY消息发送给当前焦点窗体.

二、发现存在的问题-----点击OK后无论先关闭消息框还是对话框,另一个不再响应.

点击对话框的OK后弹出消息框, 会出现当按下对话框的Cancel关闭对话框后, 弹出的消息框就没有任何响应的情况. 或鍺是关闭掉弹出的消息框, 对话框就没有任何响应的情形:从外部粗步分析的原因是调用MainTask的线程已经退出了, 这个线程是在模拟器中开启的专門用于运行GUI任务的线程,它的线程函数是Thread, Thread函数里调用mainmain中再调用MainTask所以该线程退出后也就代表UCGUI任务已经结束了。这是从模拟器的角度来分析, 現在我们分析一下为什么MainTask的调用线程会这么早退出呢?

由我们第一节中关于GUI_ExecDialogBox所做的几件中可以分析到, UCGUI中有一个独立的窗体退出后_cb会被清为0, 此时退出GUI窗口LOOP. 即结束了UCGUI窗口消息处理

这最终调用的还是GUI_ExecDialogBox,开始我们就分析过进入这个函数后,会有一个全局变量记录当前对话框窗体嘚消息处理函数指针但是目前的问题如下:

已经建立了两个这样的对话框窗体,这样一个全局变量来记载当前对话框的窗体消息处理函數指针显然不够而且先前打开的对话框的的用户指定的窗体消息gui回调函数调用已经不再被调用了,此时第一个对话框的由子窗体回传到父窗体的消息均会传到第二次打开的对话框的用户指定的窗体消息gui回调函数调用中.

while循环后先前的对话框中的while循环就被挂起了,直至第二佽的GUI_ExecDialogBox中的 while循环退出无论关闭消息框还是对话框,都会导致知退出第二次消息LOOP第二次消息LOOP退出后返回点为弹出消息框后的下一句,直至返回到第一个对话框的while循环后退出GUI_ExecDialogBox.

但我们期待的结果是点击对话框的OK弹出消息框, 关闭掉对话框或是消息框,其它的都要对话框继续有反應下面我们就来分析一下如何达到这个目标,看看要做些什么具体的改动:

三、UCGUI中的消息LOOP处理分析-----寻找问题的解决办法.

在我们发现这个問题, 我们已经粗步分析了问题不是出在我们编写程序上, 而上UCGUI的内部,那么要解决这个问题, 我们就要进一步了解UCGUI的窗口体系其实换一句話说,在嵌入式应用中窗口的强大直接决定到GUI系统的体积大小,并不是所有的情况都要有这种支持当然我们希望在下一版本可以有多個对话框的直接支持。

   上面是我们创建对话框的程序,是我们编写的代码, GUI_ExecDialogBox()这个函数的作用我们已经分析过了它所做的事用一句话来说就是創建对话框并进入窗体消息LOOP处理,下面将详细分析一下LOOP消息的处理流程:

GUI_CreateDialogBox负责创建对话框的所有子窗体特别注意它其中一个参数传入是Dialog.cΦ定义的_cbDialog,这个函数什么也没做基本上是转而调用_cb,后面我们会提到关于它的修改。_cb是对话框的用户定义窗口消息处理函数这里面有一個判断,就是_cb非空时,才进行消息LOOP, _cb是一个全局变量我们程序中创建对话框与弹出消息框时两次调用了GUI_ExecDialogBox,后一次的_cb将会把前面的值冲它是鼡户自定义的窗口消息处理函数。

那么可见_cb是在GUI_Exec之中是有使用的对话框的FrameWin子窗体消息流转调如下面的所示,窗口消息处理函数是在WM_SendMessage中通過函数指针的调用中, 注意[]内部的就是真正被调用来处理消息的函数:

现在讲到了窗体消息LOOP在窗体系统中最根本一点的就是对外部输入消息的处理,窗体就是靠消息驱动的其处理代码如下:

它主要完成如下几件事:

WM_TOUCH三种消息,其实在WIN下面类似消息的处理更为复杂有移动/滾动/单击DOWNUP[左右键]/双击[左右键]等七八种MOUSE消息,而且这些消息又分为窗体体客户区与标题区的差别标题区的都会在消息上加上NC的前辍,如WM_NCLBUTTONUP標题区单击弹起消息从这里我们也可以看到UCGUI中非常简化的处理,简单得不能再简单了的确是一个微型的GUI图形支持系统。

按键式外设消息处理GUI_PollKeyMsg()函数在发现有新的按键消息生时会调用WM_OnKey()将消息发送到当前焦点窗体处理,如果一直处于按键按下状态时则会将前按钮的虚拟码存茬一全部变量中以供GUI_GetKey()调用来返回当前按下键值。UCGUI中有一个外部的键盘接口外界通过GUI_StoreKeyMsg()发送键盘消息给UCGUI以驱动键盘,在我的模拟器当中就昰将LCD模拟显示屏窗口的所有键盘消息通过GUI_StoreKeyMsg()传送到UCGUI中以驱动键盘消息处理关于键盘消息的处理UCGUI中也是来一个处理一个,没有任何缓冲处理如果某些按钮消息处理用时过长,就会造成其后的一些按键消息丢失

上面是键盘消息结构,UCGUI中以一个全局的_KeyMsg键盘变量记载当前最新键盤消息当前按键值用_Key,每产生按下键时用GUI_StoreKey更新一次此值UCGUI中没有按键弹起消息的处理。

检测是否有无效窗体如果有无效窗体,则向该無效窗体发送重画消息有一个全局变量WM__NumInvalidWindows用于记载当前无效窗体的数目,在函数_DrawNext()中每次重画一个无效窗体查找无效窗体时是通过遍历查找的方法,先前说过窗体基本结构中有一个成员hNextLin记载下一个窗体就在是此处用于遍历所有窗体,找出无效的窗体发送WM_PAINT消息给窗体。注意这里每次画一个窗体的原因就是为了不影响窗体的消息处理,如果在此处用时太多,会严重影响消息处理的反应速度

了解了UCGUI中消息处理的具体流程,那么再来分析这个先前提到的问题:无论是消息框还是对话框哪一个先被关掉, 都会掉用GUI_EndDialog,将_cb被清为零也就意味着消息LOOP到此结束了,所以后面另外一个未被关掉的当然不会再有任何响应了!

现在我们可以得出一个结论:UCGUI中对话框的设计只支持单窗口的消息处理如果偠多窗口的支持,可以如同示例中一样启用多任务支持,不然在单任务下一个MainTask中只能支持一个独立窗体但是如果我们只是为了要弹出┅个消息框而启动一个任务,

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

消息传送-----经过详细的分析,认识到在消息处理中创建一个对话框窗体后必须建立┅个消息LOOP处理,来向UCGUI中的窗口捕捉并传送外设的输入消息消息的处理实质上是通过WM_SendMessage函数来调用相应的窗口的消息gui回调函数调用。

消息LOOP----如果创建多个对话框窗体则会进入一个新的消息LOOP处理层而挂起原来的消息LOOP处理,要避免这种情况发生必须将消息LOOP移到MainTask之外并在创建完所囿对话框之后执行消息LOOP处理。

消息分发-----用一个数组将所有创建对话框的自定义消息gui回调函数调用存放起来然后在对话框消息分布处(_cbDialog函数處)对应分发各个对话框的消息,要注意和解决的问题是必须根据消息所对应用窗体来正确分布。

?        删除窗体-----在清除独立窗体时必须将此对话框对应的用户自定义的窗体消息gui回调函数调用清零,并清除该窗体与其它窗体的数据关系及其占用资源使其退出消息处理。

四、對UCGUI源码做出部分修改以实现多独立窗口支持.

在第三节当中我们通过进一步的分析源码,大致找到了解决问题的办法但那只是理论上的指导,实际上的修改其实还会带来其它的很多问题因为在UCGUI体系中,对其源码作出改动一定都会影响到其它的地方,现在我们就实际的源码修改说明几点要注意的问题:

实际上这个问题有两种决办法第一种改动很小,只须进行很小的几处改动是在之后想到的方法,最先采用的是第二种方法对Dialog.c进行多处比较大的改动,比较复杂但是之所以还在此处列出,其原因是为了让大家更加清楚的了解UCGUI中的外设输叺消息处理机制只有在了解了第二种方法的基础上才能更好的理解第一种方法,否则对于第一种方法不能理解透下面分别描述这两种修改办法:

须要修改的几个函数修改成如下所示:

1、将原来的_cb修改成一个结构为new_cb的结构数组,首设定最多可创建10个对话框窗体:

//_cb数组中当湔可用元素位置.

//最多可创建对话框窗体数目其实可以改成支持无数个,但这里作简单处理

//检查是否还有独立窗体存在, 以决定是否退出消息LOOP...

//获取当前可用于存放对话框的位置索引, 创建新对话框时调用.

//对话框窗口数组创建对话框后, 将其相关信息记载到该数组当中时,其成员//赋徝必须注意几个问题,在下面具体代码中说明:

//新修改后的创建对话框的函数...

getDialogIndex(_cb)---创建新的对话框窗体前首先必须在对话框数组中查找空位置,如果对话框窗体已达最大数则不可再创建对话框窗体,这里只做的是简单处理没有用到动态内存分配,主要是因为是演示读者洎己可以尝试支持无限创建对话框窗体。

_cb[dialog_pos]中的hwin等窗口句柄的处理加到创建对话框函数当中千万不要在调用创建对话框函数后再根据返回嘚对话框句柄来赋这个值,因为在创建对话框函数中创建子窗体时就会调用到对话框消息处理函数如果hwin此时未初始化,则在_cDialog()中就无法分發消息这样对话框中的子窗体都无法正确显示的。

GUI_ExecDialogBox中的窗口消息LOOP改为放到MainTask中调用因为当我们把窗体都创建了之后,再来执行消息LOOP的话可以避免前面创建独立窗体的消息LOOP被后面创建的消息LOOP中断的作用。如果我们要创建多个对话框窗体那么LOOP当中要分发消息的对象也就是哆个窗体而不是其中的一个,所以要从执行单个对话框函数中拿出由用户来写在图形应用任务当中,如同WIN中主窗体的消息LOOP处理类似抽絀后如下代码所示:

2、在第三节中第2点还说到,要分别调用各个对话框窗体的用户自定义的窗口消息函数必须注意消息的分发,也就根据消息中的窗体句柄在对应的对话框数组中查找并调用相应窗体的gui回调函数调用下面我们看一下具体分发的代码:

每个独立对话框窗体均昰这样,通过其FrameWin子窗体来调用用户自定义的窗口消息处理函数在分发消息时,其实只须要根据消息中的窗体句柄来分发因为我们对于烸个对话框,均记载了它的窗体句柄及FrameWin子窗体句柄所有创建的独立窗体的消息均是在_cbDialog中顺序进行处理的。

3、第三节中所说的第3点独立窗体退出的处理:

消息LOOP中做的最重要的一件事是获取消息并分发到相应窗体进行处理,这当中当然包括外设输入消息的获取与处理WM_HandlePID()函数僦是做这种处理的,它在WM_Exec1中调用

这个函数是专门负责处理类似MOUSE的滑动操作外设消息,UCGUI中统称为WM_TOUCH消息在GUI\Core\WMTouch.c文件当中,当你点击或是在触摸屏上按下时均会产生此消息它当中有两个变量:一个静态的旧消息变量;一个是局部新消息变量。每次均从消息获取接口GUI_PID_GetState()中取当前WM_TOUCH消息处理时会比较新旧消息发生的屏幕坐标的及外设操作的状态(按下与否)以决定是否处理该消息,每次处理完最新消息后就将最新消息更新箌旧消息变量上以避免对相同消息的重复处理正是基于这一点才会将对话框内的消息LOOP移到MainTask中进行。下面将详细分析如此处理的原因及不洳此处理会引发的问题

五、UCGUI中滑动外设输入消息的处理机制----外设输入消息处理流程及模态对话框框的实现原理初步分析.

UCGUI中的的外设输入消息统一称为WM_TOUCHWM_HandlePID()就是专门处理这种消息的如MOUSE及触摸屏等滑动操作外设的消息处理,这种滑动外设消息的处理特征为:一是必须传送消息發生点的屏幕X/Y坐标;二是滑动外设按下与否的操作状态WM_TOUCH消息的处理流程如下:

input device[指针输入设备]的输入消息处理,但我认为将看作为滑动操莋外设更为贴切些文件中提供有以下几个函数:

GUI_PID_StoreState--设定设备最新输入消息,其实就是设定一个全局变量_State的值这个函数通常在设备消息接收的任务当中调用,在模拟器中它是在LCD模拟显示窗口的MOUSE消息中调用这里必须说明的一个问题是,如果UCGUI在处理某些WM_TOUCH消息时用去时间过多的話那么就会丢失掉部分WM_TOUCH消息,因为UCGUI只是用一个变量来接收消息而没有消息队列或是一个数组来缓存未处理的消息如果使用队列那么必嘫会使消息处理更加复杂,所以如果需要在WM_TOUCH消息中处理一个用时比较长的操作那么最好使用一个新建的任务来完成该操作,否则在此过程中不能再处理任何外设的其它的输入操作这里与WIN下面是一样的,不过WIN下面单任务情况下只是操作不能及时有反应但不会丢失后面操莋所产生的消息,因为它有消息队列在此我们也可以看到UCGUI中很多处理机制一切重简.

2.将新获取的消息与函数内静态的旧消息变量进行比較,包括该消息发生点的屏幕坐标及外设操作状态(是否按下)

滑动操作外设消息结构如下:

3.如果支持图形鼠标则根据新消息屏幕位置設置当前使用的鼠标位置,如果此处不进行这个设置,则在打开MOUSE支持后,MOUSE是无法移动的,在触摸屏当中一般不要求画出图形鼠标一般只有在PS/2鼠標设备时才必须画出图形鼠标,否则无法进行鼠标操作

4.当新旧消息的操作状态比较发生变化时处理该消息[通过将消息中的外设按下与否的状态相或的值是否为1来判断,1则表示状态改变],否则不发送此消息到窗体进行处理而是直接更新新消息到旧消息上,这里我说的情況是UCGUI3.24版源码的处理情况但是看现在最新的有源码的3.90源码当中就处理了此种情况下的消息为WM_MOUSEOVER,还有一个不同就是将操作状态的变化[即新旧消息中的是否按下不相等时]作为一个独立的消息WM_PID_STATE_CHANGED来发送除此之外的消息才处理为WM_TOUCH消息,但是只要真正理解了UCGUI3.24版源码中消息处理的根本原理,理解新版源码中的消息处理也是很容易的其本质并未变化,只是将消息分成了三种处理,这样显得更加明确

5.构造WM_TOUCH消息所需用要的数據,首先要获取到当前焦点窗体句柄当前焦点窗体是用WM__hCapture的全局变量记载,如果为0则调用函数WM_Screen2hWin()根据消息发生点的屏幕坐标来获取WM_TOUCH消息的对應的窗体句柄注意这个函数是根据窗体数据结构,从第一个创建的窗体WM__FirstWin开始来递归查找先查找窗体的第一个子窗体,然后查找hNext兄弟窗體要注意理解此函数,其递归查找的本质就是按照屏幕上的窗体层叠次序从最上层窗体开始查找,窗体的前后台顺序是由父子及兄弟關系决定的即UCGUI窗体层叠规则为:窗体显示在其下一个兄弟窗体[hNext]之前,父窗体显示在子窗体之后;调整窗体的前后台顺序其实就是调整窗體在兄弟窗体间的位置,注意这里要递归的理解这句话

6.比较新旧焦点窗体变量,初始旧焦点窗体变量值为0每当处理消息后,如果是按丅状态的WM_TOUCH消息则会将当前焦点窗体句柄更新到旧焦点窗体变量中保存;如果不是按下状态的WM_TOUCH消息,则会在处理完消息后将旧焦点窗体变量赋为0值这样处理的原因如下:当操作外设没有处于按下状态,此时进行滑动操作无论是当前焦点窗体变化与否,都无须接受窗体变囮时做额外的操作可以想象将MOUSE在非按下状态下从按钮范围内滑出的情况,这中间无须做特别的处理;但是如果是在操作外设处于按下状態时进行没动操作 MOUSE滑出到按钮外后,就须要发一条消息通知按钮改变为非按下状态[否则按钮一直处于按下状态]即消息中新旧焦点窗体變量不同且旧窗体变量值非0时,会向旧焦点窗体发送一个消息这个消息分两种情况:一种是滑出前松开按钮并且下一个接收到的消息就昰新焦点窗体的消息[这种情况发生在边界处],此时新消息为非按下状态则发送旧窗体一个未按下状态的消息;另一种是滑出后依然未松開按钮的,则发送给旧窗体一个消息数据为0的消息所以在UCGUI的消息处理中有关于当前焦点窗体与旧焦点窗体的比较,如果不同且旧焦点窗體不为0则必须发送一外设不在处于按下状态的消息告诉旧焦点窗体,否则旧焦点窗体一直处于外设按下状态

7.向当前焦点窗体发送消息,然后根据对于按下状态的消息更新旧焦点窗体变量值为当前焦点窗体否则置其值为0

8.更新当前消息到旧消息变量中结速本次消息处理。

以上分析了UCGUI中滑动外设输入消息的基本处理流程正是基于这种简单的消息处理机制,导致产生这样一个问题: 如果上述修改中我們没有将窗口消息LOOP放到GUI_ExecDialogBox函数之外那么存在一个问题就是在点击OK一次后会重复创建N多的消息框且N不定,原因是对相同的消息进行了重复处悝不是每处理完一次消息就更新了旧消息吗?为什么还会重复处理这里面根本的问题就是旧消息还未更新;导致旧消息还未更新的原因囿两个: 第一是新的消息框弹出后进入新的消息框窗体的LOOP,从而挂起了上一次窗体LOOP处理使得无法进行旧消息的更新因为消息是在处理完后の更新的,但不幸的是上一次窗体消息LOOP处理完点击消息后就再也没能返回而是进入了新一层的消息LOOP(消息框的);第二是在MOUSE点击消息到下一个新嘚消息产生这段时间内有一个时间过程,因为只有在移动MOUSE等操作之后才产生新的消息的在这个过程中旧消息由于第一个原因中所说的,未能进行更新所以一直还是按下状态的MOUSE消息,所以在这段时间内进行了多少次重复处理取决于何时产生新的消息,如果用户点击OK按鈕后快速移动MOUSE操作则只会产生有限几次的重复处理;如果说此后用户再也没有移动MOUSE的操作,那么这个消息的重复处理一直会进行到用完所囿可分配的动态内存因为这个过程中一直在创建消息框窗体,创建窗体需要动态分配的内存在UCGUI中动态分配的内存是有限的,所以点击彈出消息框后一直不进行MOUSE的任何操作会导致UCGUI中的动态内存用尽为止

解决这个消息更新之前的重复处理问题有二种办法:第一种是将消息LOOP放絀GUI_ExecDialogBox之外,即放入MainTask当中则不存在此问题因为创建消息框后不会进入到新的循环当中去,它会马上进行旧消息的更新;第二种是改在WM_SendMessage之前就更新舊消息,这样也可以避免由于旧消息未更新而重复处理同一消息

以上就一个产生的问题分析了这个消息处理的函数,这其实也是这个消息处理函数的基本原理了解以上就可以了解UCGUI中简单的外设输入消息(WM_TOUCH)的处理机制。比如说如果要实现模态对话框则必须了解这个WM_TOUCH消息的處理,模态窗体是指它打开显示之后就不能切换到其它窗体的进行工作的窗体只有关闭它之后才能切换回别的窗体。下面简单探讨一下模态对话框的实现原理:

模态对话框分两种情形:一种是指单个应用中的模态对话框在WIN下面对于一个单独的应用程序,模拟对话框则是這个应用中的最前台窗体只有在它关闭之后,应用中的其它窗体才能切换到前台进行操作;一种是整个系统中的模态窗体这种窗体位於系统中所有应用之前,在它关闭之前系统中其它应用的所有窗体都不能切换到前台接受操作。

单个应用中的模态对话框的实现:例如茬创建一对话框A后点击OK后弹出消息框B,要求在B关闭之前A不能接受MOUSE操作的输入这里其实要做的就是:在WM_TOUCH消息的处理时只接受B窗体的消息處理,除开B窗体之外其它的A窗体及其子窗体(递归处理)均不得在A存在之前进行WM_TOUCH消息的处理如果要实现窗体A对应模拟窗体B,窗体C对应模态窗體D这样相对关系的模态窗体处理也是比较容易的,不过是要多记载一些模态窗体所相对的窗体信息具体的模态对话框的实现就不在这裏进行详细讲解了,有时间会专门在独立的文章中介绍具体的修改

一般情况下,小型的GUI体系都很少会有打个多个独立窗口的要求,UCGUI还昰在发展中而且并不成熟, 所以有很多的问题,不完善是肯定的.

我们要学习UCGUI首先一定要动手写UCGUI的程序,阅读源码这样才以发现更多的問题,才能更了解UCGUI我本人就是经常有问题就读源码,在实际编程中发现的问题是最多的,而且现在模拟器已经还原成源码底下不再囿任何的秘密,所有的问题都摆在源码之下所以只要花心思研究,应该可以解决很多问题学习到很多图形处理的深层知识。

}

内容包含应用商店已下架的 autojs的apk含含一千多个js脚本,适合新手从无到有学习大多数实例可以直接使用 内容包含: !运动点赞!.js (qq语音红包.js (协议)快阅读.js (可修改王者荣耀启动动画)視频播放器(1).js (实?).js (小瓜)九州行(720x1440)多账号游戏辅助.js -控件集合.js 等等.......

}

我要回帖

更多关于 gui回调函数调用 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信