前段時間在做一個窗口項目,這個項目菜單項過多,在管理起來比較麻煩。想做一個高效移植又方便的一個切換機制。后來在網上多方查找這方面資料,但是感覺比較少。后來自己整理出了這個結構,希望對后來朋友有所幫助。
本結構不只局限與按鍵操作,同時也支持觸摸切換,也可以兩者共存同時對窗口進行操作。在按鍵操作過程中我想很多朋友都會遇到窗口焦點問題,內存不足問題等等。這個機制可以盡可能的解決這些問題。
首先先說下這個機制的主要思想。在感官上我們看到是這樣子:開一個窗口會覆蓋前一個窗口,再開新的窗口就會重復覆蓋上一個窗口。而在退出窗口時就是一層一層恢復到前一級的窗口。以此反復。但是在嵌入式系統操作時用這種方式的話,就有點太耗內存了,菜單深度越多那么資源浪費就越多。那要怎么辦呢?當然如果我們在進入下一個窗口時銷毀上一個窗口就可以有效的解決這個問題。那么退出本級窗口該怎么辦呢? 其實退出本級窗口和進入新窗口的性質是一樣的,只不過這個新窗口是本窗口的前一級而非后一級。
我們的窗口每級每級是怎么連接在一起的呢?這里我采用的樹的結構,一個大家很熟悉的模型樹——二叉樹。為什么用樹呢,大家應該都清楚用樹有幾大好處: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工程進行了更新: