1引言
大多數(shù)嵌入式系統(tǒng),僅提供幾個按鍵和像素點較少的LCD,同時處理器運算能力有限(如8/16位單片機),不宜運行商用的GUI圖形庫(如uC/GUI、miniGUI、QT等),但仍然得為用戶提供GUI功能。一個具有代表的硬件平臺如下,提供6個輸入按鍵:上移、下移、左移、右移、確定和取消;有一LCD,不限制物理尺寸與像素點數(shù)。如工業(yè)儀器需要設置參數(shù)一樣,GUI需要支持用戶輸入,本文為滿足這種需要設計圖形控件。
2圖形控件基類
面向?qū)ο笫?a href="http://www.nxhydt.com/tags/C++/" target="_blank">C++和JAVA之類的高級語言話題,在這里為什么需要用C語言來實現(xiàn)呢,原因在于嵌入式環(huán)境下往往不支持C++和JAVA語言,況且嵌入式工程師對C語言十分親切,減少了學習成本。圖形構件因為其關聯(lián)性比較強(繼承),往往將數(shù)據(jù)和操作組織在一起(封裝),同時為高效實現(xiàn)經(jīng)常將異構的控件統(tǒng)一操作(多態(tài)),基于面向?qū)ο髞韺崿F(xiàn)是順其自然的。[1]
下面結(jié)合圖形控件基類的設計探討C語言實現(xiàn)面向?qū)ο蟮脑怼?/p>
圖1圖形控件基類的設計
當我們用C語言將上述控件用結(jié)構體來組織時,實際上就完成了封裝。這時,對一個對象的操作僅需要調(diào)用它的方法,如例1所示。
例1 設定義如下圖形控件基類
typedef struct _graphic_ctrl {……} GRAPHIC_CTRL;
定義一個圖形控件對象:GRAPHIC_CTRL stGCtrl;
可以操作這個對象的方法:gctrl_Create(&stGCtrl,NULL),這個方法根據(jù)對象的CtrlSort自動選擇對應的派生類的創(chuàng)建函數(shù)。
如果另外需要定義一個類,如文本輸入框,它除了繼承基類外,還有自己的屬性和方法,C語言的實現(xiàn)是將基類包含在子類的結(jié)構體中,例2說明了繼承的實現(xiàn)。
例2文本輸入類繼承基類
typedef struct _text_edit
{
GRAPHIC_CTRL stGCtrl;
char *p_chBuf;?
……? ? /* Other attributes of TextEditor */
} TEXT_EDIT;
定義一個文本輸入框?qū)ο螅篢EXT_EDIT stTextEdit;它可以操作父類的方法,如gctrl_Create(&stTextEdit,NULL)。
一個按鈕和一個文本輸入框的回車響應動作是不同的,如何實現(xiàn)控件這種需求呢?改變它調(diào)用方法的實現(xiàn)細節(jié),這就是多態(tài)的特性。
例3 重載RespBtn方法實現(xiàn)多態(tài)
void EnterBtn(void);??
void EnterTextEdit(void);
GRAPHIC_CTRL stBtn;
TEXT_EDIT stTextEdit;
ConstructBtn(&stBtn, EnterBtn);
ConstructTextEdit(&stTextEdit, EnterTextEdit);
現(xiàn)在,終于可以展現(xiàn)一下面向?qū)ο髱淼膬?yōu)勢了。設系統(tǒng)響應用戶的“回車”按鍵,軟件可以不考慮控件對象的類別而統(tǒng)一調(diào)用RespBtn ()方法,這就實現(xiàn)了軟件開發(fā)的精髓之一:高度抽象把復雜的事情簡單化,例4描述了這種統(tǒng)一操作。
例4 統(tǒng)一響應控件的回車操作
gctrl_RespBtn((GRAPHIC_CTRL *)&stBtn);
gctrl_RespBtn((GRAPHIC_CTRL *)&stTextEdit);
有了上述C語言實現(xiàn)面向?qū)ο蟮幕A后,我們再來考慮設計圖形控件就變得容易多了。
3按鈕
如右圖4個按鈕:
按鈕是簡單的控件,它直接繼承GRAPHIC_CTRL父類就能實現(xiàn)。當執(zhí)行Create()方法時,先將字符串打印在區(qū)域中間,后畫出一個矩形;Destroy()方法是空操作,不需要實現(xiàn);執(zhí)行Active()方法時,將字符串和矩形區(qū)域反白顯示“上一頁”;Inactive()方法同Create()方法一樣,打印字符串和繪制矩形;RespBtn()方法中,它響應“確定”按鍵的具體動作,針對上/下/左/右方向按鍵它向調(diào)用者(如窗口)返回切換控件的消息;擴展的Enter()方法實現(xiàn)不同按鍵的“確定”動作響應,從而實現(xiàn)多態(tài)技術。
圖2按鍵類的設計
4下拉菜單
Create()方法創(chuàng)建的下拉選擇框如右圖,
除繪制矩形框和框內(nèi)字符串外,還需要打印框外的文本(定義為STATIC_TEXT),因而在類的屬性中需要添加STATIC_TEXT的指針。Active()方法表現(xiàn)的效果是“MU識別 MAC”,和按鈕激活的動作相似;Inactive()方法與Create()方法相同,Refresh()方法與Create()方法也相同。
在響應按鍵消息時就有區(qū)別了。如果“確定”鍵已經(jīng)被按下,那么控件將呈現(xiàn)圖3左邊的選擇項,這時需要計算是否有空間向下繪制選擇項(可能會超過LCD的下端),如果沒有空間將向上繪制選擇項。“上/下按鍵”可以操作選擇項,圖3右顯示了“下按鍵”操作的結(jié)果,因此也需要一個記錄上下鍵位置的數(shù)據(jù)。再次按下“確定”鍵完成選擇操作“MU識別 APPID”,如,而“取消”鍵隨時可以退出操作過程。
圖3下拉選擇項
如果“確定”鍵沒有被按下,“上/下/左/右按鍵”的響應簡單為向調(diào)用者(如窗口)返回切換控件的消息。
很明顯,響應按鍵操作需要一個狀態(tài)機,因此類的屬性中將添加BtnStat。
控件的Destroy()方法就變得繁忙了,它有三件事要干:首先向控件返回被選擇項的索引,如果是“確定”鍵將返回有效下標,如果是“取消”鍵將返回無效下標;其次清除選擇項繪制的整個區(qū)域;最后得把被選擇項區(qū)域破壞的其他控件恢復。
恢復被破壞的控件將是一個值得商榷的話題,理想的方法是先將被破壞的區(qū)域保存起來再予以恢復,但這種方法需要操作像素VRAM區(qū),我們將采用這種方法。它的硬件實現(xiàn)原理是LCD的控件器一般都支持READ_LCD_DATA()操作,當指定特定區(qū)域的位置和大小后,就可以讀取該區(qū)域的像素數(shù)據(jù)并予以保存;軟件上需要實現(xiàn)類似“壓棧/彈?!钡牟僮鳎瑮5纳疃纫蕾囉谛枰4鎂RAM的遞歸次數(shù)。有了軟硬件的支持后,先把即將被破壞的區(qū)域予以壓棧保存,當需要恢復該區(qū)域時,調(diào)用彈棧函數(shù)恢復該區(qū)域。
總結(jié)下拉選擇框的類定義如圖4所示:
圖4下拉選擇框類的設計
5文本輸入框
右圖顯示了一個文本輸入框的實例:,
除去矩形框前后的靜態(tài)文本顯示外,它和按鈕的顯示是很相似的。
文本輸入框需要一緩沖區(qū)來存儲用戶的輸入,此外響應“確定”按鍵時調(diào)用軟鍵盤,當軟鍵盤退出時拷貝輸入的字符串,它還需要限定輸入字符串的個數(shù),檢查輸入值的有效性,當輸入非法時需要提示正確的數(shù)值范圍。它的類定義如圖5所示。
圖5文本輸入框類的定義
6 軟鍵盤
圖6顯示了一個軟鍵盤的實例,它分為四部分:標題欄、輸入欄、操作集和選項集。標題欄顯示輸入對象的名稱;輸入欄顯示當前已經(jīng)輸入的文本集,光標可以在輸入字符的任意位置;操作集從左到右是:光標左移、光標右移、退格、下一頁和回車,光標左移和光標右移控制光標在輸入欄中進行移動,退格可以刪除光標前的字符,下一頁就是顯示下一頁選項集(如果有分頁),回車退出軟鍵盤并返回所輸入的字符串,選項集是用戶能進行選擇操作的字符集。
圖6軟鍵盤實例
當軟鍵盤Create()時,它從調(diào)用者的對象中獲取標題欄字符串,再根據(jù)選項集的數(shù)目計算頁數(shù)與行數(shù);Destroy()方法與下拉選擇框相同,除清除自己外還需要通知刷新窗口內(nèi)的所有控件。RespBtn()方法依賴不同的按鍵操作:上/下按鍵修改行索引,左/右按鍵修改列索引,當索引被修改時,先注銷前一個選項(表現(xiàn)為失焦),后激活當前選項(表現(xiàn)為獲焦)。當前激活項是選項集時,“確定”按鍵將該選項的字符添加到輸入欄中;當前激活項是操作集時,“確定”按鍵執(zhí)行對應的操作。“取消”按鍵可以隨時退出軟鍵盤。
軟鍵盤類的定義如圖7所示:
圖7軟鍵盤類的定義
7 圖形控件的使用
上述設計了4種常用的圖形控件,現(xiàn)在講解調(diào)用它們的一般方法。
首先需要為該圖形對象申明并開辟內(nèi)存,如:
static BTN s_stBC91MExit =
{
.stGCtrl =
{
.chID = BC91M_EXIT,
.chItemNum = 1,
.chSelIx = 0,
.nAddrX = CHAR_LEN * 33,
.nAddrY = BTN_CTRL_Y,
.nSizeW = BTN_CTRL_W,
.nSizeH = CHAR_WIDE,
.pp_chBtnStr = &a_strBC91MBtnStr[3],??
},
};
其次需要調(diào)用方法初始化該圖形對象,如下所示:
gctrl_ConsBtn(&s_stBC91MExit, BC91MBtnEnter);
創(chuàng)建窗口時也會調(diào)用控件的創(chuàng)建函數(shù):
gctrl_Create(&s_stBC91MExit, NULL);
當用戶按鍵選中該按鈕時,gctrl_Active(&s_stBC91MExit, NULL)會被調(diào)用,以便于激活該對象;同樣,gctrl_Inactive(&s_stBC91MExit, NULL)用于注銷該對象;如果本按鈕是激活對象,那么任何用戶按鍵消息將會傳遞給它,即調(diào)用方法gctrl_RespBtn(&s_stBC91MExit,
&uRtn);最后當窗口退出時會調(diào)用按鈕的銷毀方法,gctrl_Destroy(&s_stBC91MExit, NULL)。
7 結(jié)束語
本文設計的輕量級嵌入式圖形控件已經(jīng)在某工業(yè)控制產(chǎn)品中穩(wěn)定使用多年,該產(chǎn)品選用TRULY公司320x240像素的LCD。采用面向?qū)ο蟮脑O計,使軟件系統(tǒng)容易開發(fā);簡單化的設計使系統(tǒng)異常穩(wěn)定;另外占用資源很少,這是商業(yè)GUI無法比擬的。需要了解LCD硬件連接與驅(qū)動以及窗口系統(tǒng)的實現(xiàn),可以參見姊妹篇論文《一種輕量級嵌入式GUI設計》。
參 考 文 獻
[1] Steve McConnell. Code Complete. Second Edition. 金戈等譯。電子工業(yè)出版社,2006.3
評論
查看更多