uCGUI 按键窗口切换机制
前段时间在做一个窗口项目,这个项目菜单项过多,在管理起来比较麻烦。想做一个高效移植又方便的一个切换机制。后来在网上多方查找这方面资料,但是感觉比较少。后来自己整理出了这个结构,希望对后来朋友有所帮助。
本结构不只局限与按键操作,同时也支持触摸切换,也可以两者共存同时对窗口进行操作。在按键操作过程中我想很多朋友都会遇到窗口焦点问题,内存不足问题等等。这个机制可以尽可能的解决这些问题。
首先先说下这个机制的主要思想。在感官上我们看到是这样子:开一个窗口会覆盖前一个窗口,再开新的窗口就会重复覆盖上一个窗口。而在退出窗口时就是一层一层恢复到前一级的窗口。以此反复。但是在嵌入式系统操作时用这种方式的话,就有点太耗内存了,菜单深度越多那么资源浪费就越多。那要怎么办呢?当然如果我们在进入下一个窗口时销毁上一个窗口就可以有效的解决这个问题。那么退出本级窗口该怎么办呢? 其实退出本级窗口和进入新窗口的性质是一样的,只不过这个新窗口是本窗口的前一级而非后一级。
我们的窗口每级每级是怎么连接在一起的呢?这里我采用的树的结构,一个大家很熟悉的模型树——二叉树。为什么用树呢,大家应该都清楚用树有几大好处:1)维护方便,在您的菜单中增加一个窗口或改变一个窗口位置应该是长有的事吧。那么在修改时我们也只需修改树中结点的位置就可以了,而不用改了位置又要去考虑改窗口名顺序等连带操作。当然这样也可以避免很多出错可能;2)合理使用资源,有多少窗口用开多少窗口的结点,而无须预留充足空间以被扩展。当添加新窗口只需在树中追加节点。
下面能过一个图型来地架构进行解说,如下图
首先先说下窗口的规则,我是以元素的右孩子为子菜单,左孩子为水平菜单。
这是我建立的一个简单事例,主窗口是一个码表盘。主窗口的下一层是一个列表窗口,列表窗口的内容是他的下层窗口的名子。“MenuTest”下层有四个菜单,前三个是列表窗口,最后一个是对话框窗口。 “MenuTest0”又有7个子菜单,前三个为列表窗口,第四个无窗口,第五个是对话框窗口,第六个是数字轴窗口,第七个是一个电字时钟。有人一定会问:只能在最右一排下建立子窗口吗。当然不是,我只是为了方便都在最右排建立子集,你也可以在MenuTest1或MenuTest2下建立,根据你的窗口需要对组合。完成这个树的建立,只通过了很少代码就完成了。完成这个功能只用了两步,1)建立元素,2)将元素链接到一起。 如果我有窗口想一直都在最顶层或有时显示有时不显示,不能和树结构整合到一起怎么办!其实在工程中有WIN_WindowsNumpad一个按键板,他就是一个不依赖在树中的独立窗口。是一个从始至终都会显示的窗口。这些窗口只要独立创建于树外就可以了,他的显示或不显示由你程序操作。
窗口实体就是建立的元素,下面对元素的内容进行详细的介绍:
树中每一个结点的内容如下:
typedef struct BiTreeElem{
DATA_TYPE *data; //数据元素
struct BiTreeElem *Lift; //窗口标题
struct BiTreeElem *Right; //窗口标题
}ELEMENT;
数据DATA_TYPE元素所指向的内容如下:
typedef const struct {
const char *title; //窗口标题
WIN_INFO_TYPE *win; //窗口信息
FunCBType *enter; //窗口进入回调函数
FunCBType *exit; //窗口退出回调函数
}MENU_INFO_TYPE;
通过注释消息可以看出每个元素的作用。这里需要说的FunCBType类型,这两个是回调函数,本窗口进入时运行*enter所指向的指针函数,退出时执行*exit所指向的数据。如果本窗口不需要执行这两个或其中的一个,可以让其指向0。
WIN_INFO_TYPE *win是窗口的相关消息,包括三个元素,内容如下:
typedef const struct{
void *hWin; //窗口句柄
FunCBType *create; //窗口建立
FunCBType *destroy; //窗口回收
}WIN_INFO_TYPE;
*create指向特定窗口建立的回调函数,*destroy指向特定窗口销毁的回调函数,*hWin指向本窗口的句柄(备用)。
这里大家会发现为什么在结构体前加const ,这是因为嵌入式的资源都有限,这样只会占用ROM空间。还有个好处,就是不会被改变,增加可靠性。
好了,我们所用到的内容就是这三个结构体了。
1) ELEMENT元素:以经在WIN_Tree.c文件中对其进行了封装。所有的树操作可以完全通过文件内API来实现,这样可以大大降低在树操作时可能出现的故障率。
2) WIN_INFO_TYPE元素:我们所用到的所有窗口的界面。这里需要提的就是,在操作中有很多窗口是类型是相同的界面,只建立一个就可以了,比如像列表窗口,符合窗口的重复利用性。如果窗口是特有功能和界面的就必须单独建立了。
3) MENU_INFO_TYPE 元素:一个结点必须有一个这个元素。
本结构的主要文件
WIN.c WIN.h |
结构初始化,树建立等等。 |
WIN_Tree.c WIN_Tree.h |
树相关API。 |
WIN_Windows.c WIN_Windows.h |
所有的窗口元素。窗口的建立,销毁,接口等操作。如果建立新窗口或是独立的窗口,般命名规则:WIN_WindowsXxxxx.c |
WIN_Context.c WIN_Context.h |
窗口调度相关操作,包括窗口进入,退出等。 |
WIN_OS.c WIN_OS.h |
如果您的GUI运行在OS平台,可对其进入修改,现工程已经集成了uCosII,uCosIII,win32。 根据你的OS加载对应的头文件就可以了。 uCosII加载"ucos_ii.h"; uCosIII加载"os.h "; win32及其它平台可以 宏定义#define _WIN32_即可。 |
WIN_API.c WIN_API.h WINAPI_Type.h |
所有外部的接口文件,包括获取外部的数据或外部操作显示屏的数据刷新等。 WIN_API.h对外调用接口:就是在文件如果要用到窗口操作,只加载这个接口头文件就可以了。 WINAPI_Type.h 内部调用文件。 |
使用本操作接口,如果添加一个新的窗口,主要的工作就是写一个新窗口的建立,销毁,接口这三个函数。并在WINAPP_xxx.c中将其连接到树中。
接口细节:
WIN_API 接口部分相对比较难理解,这里需要进行讲解下。这里所有的接口分为两类,1)对外接口,2)对内接口。
对外接口:就是显示函数更新等操作,可被外部调用的函数。在这里所有的本地函数最好都在同一个任务中进行,这样可以减少其它任务栈的使用量,以及避免多任务互斥引起的问题。如果才可以保证函数只在本任务中被调用呢,有个方法就是外部任务在调用本地函数时,把其函数地址及函数参数放入缓冲区,在本地任务中对缓冲区进行事实扫描就可以了。这样可以很好的解决。实现这些方法有很多种,这里我所用的方法是一个相对复杂但是对模块划分及资源高效性考虑的一个方法。把所有接口函数划分为三大类,1)一个整形参数的函数,2)两个整形参数的函数,3)一个字符串函数的函数。大部分的接口函数可以划分成这三个函数。外部调用时把这三个函数装入FIFO中,在任务中实时的对FIFO进行获取并执行。
对内接口:就是外部任务所提供的一些函数,比如说LCD要显示的一些参数等操作。主要可以分为两类:一类是GET,一类是SET。这里我是把这两个类型定义成一个结构体数组中WINAPI_INF_TYPE,在接口函数中只要定义一个常量,内容为“对内接口”一个GET一个SET。当不赋值时,对内接口为空。这样最大的好处就是,不用在你的程序中重复去调用“对内接口”函数的头文件。所有的接口都在WIN_API.c中,这样也增加了您程序的移植性。
以下为工程代码:
http://pan.baidu.com/share/link?shareid=2249960431&uk=118334538
可以用vs2008打开。
视频http://www.tudou.com/programs/view/D-gK-_SLjUA/#
2014-11-24工程进行了更新: