?
12.2??Qt/Embedded開發(fā)入門
12.2.1??Qt/Embedded介紹
1.架構
Qt/Embedded以原始Qt為基礎,并做了許多出色的調整以適用于嵌入式環(huán)境。Qt/Embedded通過Qt?API與Linux?I/O設施直接交互,成為嵌入式Linux端口。同Qt/X11相比,Qt/Embedded很省內存,因為它不需要一個X服務器或是Xlib庫,它在底層拋棄了X?lib,采用framebuffer(幀緩沖)作為底層圖形接口。同時,將外部輸入設備抽象為keyboard和mouse輸入事件。Qt/Embedde的應用程序可以直接寫內核緩沖幀,這避免開發(fā)者使用繁瑣的Xlib/Server系統。圖12.1所示比較了Qt/Embedded與Qt/X11的架構區(qū)別。
使用單一的API進行跨平臺的編程可以有很多好處。提供嵌入式設備和桌面計算機環(huán)境下應用的公司可以培訓開發(fā)人員使用同一套工具開發(fā)包,這有利于開發(fā)人員之間共享開發(fā)經驗與知識,也使得管理人員在分配開發(fā)人員到項目中的時候增加靈活性。更進一步來說,針對某個平臺而開發(fā)的應用和組件也可以銷售到Qt支持的其他平臺上,從而以低廉的成本擴大產品的市場。
(1)窗口系統。
一個Qt/Embedded窗口系統包含了一個或多個進程,其中的一個進程可作為服務器。該服務進程會分配客戶顯示區(qū)域,以及產生鼠標和鍵盤事件。該服務進程還能夠提供輸入方法和一個用戶接口給運行起來的客戶應用程序。該服務進程其實就是一個有某些額外權限的客戶進程。任何程序都可以在命令行上加上“-qws”的選項來把它作為一個服務器運行。
客戶與服務器之間的通信使用共享內存的方法實現,通信量應該保持最小,例如客戶進程直接訪問幀緩沖來完成全部的繪制操作,而不會通過服務器,客戶程序需要負責繪制它們自己的標題欄和其他式樣。這就是Qt/Embedded庫內部層次分明的處理過程。客戶可以使用QCOP通道交換消息。服務進程簡單的廣播QCOP消息給所有監(jiān)聽指定通道的應用進程,接著應用進程可以把一個插槽連接到一個負責接收的信號上,從而對消息做出響應。消息的傳遞通常伴隨著二進制數據的傳輸,這是通過一個QDataStream類的序列化過程來實現的,有關這個類的描述,請讀者參考相關資料。
QProcess類提供了另外一種異步的進程間通信機制。它用于啟動一個外部的程序并且通過寫一個標準的輸入和讀取外部程序的標準輸出和錯誤碼來和它們通信。
(2)字體
Qt/Embedded支持4種不同的字體格式:True?Type字體(TTF),Postscript?Type1字體,位圖發(fā)布字體(BDF)和Qt的預呈現(Pre-rendered)字體(QPF)。Qt還可以通過增加Qfont-
Factory的子類來支持其他字體,也可以支持以插件方式出現的反別名字體。
每個TTF或者TYPE1類型的字體首次在圖形或者文本方式的環(huán)境下被使用時,這些字體的字形都會以指定的大小被預先呈現出來,呈現的結果會被緩沖。根據給定的字體尺寸(例如10或12點陣)預先呈現TTF或者TYPE1類型的字體文件并把結果以QPF的格式保存起來,這樣可以節(jié)省內存和CPU的處理時間。QPF文件包含了一些必要的字體,這些字體可以通過makeqpf工具取得,或者通過運行程序時加上“-savefonts”選項獲取。如果應用程序中使用到的字體都是QPF格式,那么Qt/Embedded將被重新配置,并排除對TTF和TYPE1類型的字體的編譯,這樣就可以減少Qt/Embedded的庫的大小和存儲字體的空間。例如一個10點陣大小的包含所有ASCII字符的QPF字體文件的大小為1300字節(jié),這個文件可以直接從物理存儲格式映射成為內存存儲格式。
Qt/Embedded的字體通常包括Unicode字體的一部分子集,ASCII和Latin-1。一個完整的16點陣的Unicode字體的存儲空間通常超過1MB,我們應盡可能存儲一個字體的子集,而不是存儲所有的字,例如在一個應用中,僅僅需要以Cappuccino字體、粗體的方式顯示產品的名稱,但是卻有一個包含了全部字形的字體文件。
(3)輸入設備及輸入法。
Qt/Embedded?3.0支持幾種鼠標協議:BusMouse、IntelliMouse,Microsoft和MouseMan.Qt/
Embedded還支持NECVr41XX和iPAQ的觸摸屏。通過從QWSMouseHandler或者Qcalibra-
tedMouseHandler派生子類,開發(fā)人員可以讓Qt/Embedded支持更多的客戶指示設備。
Qt/Embedded支持標準的101鍵盤和Vr41XX按鍵,通過子類化QWSKeyboardHandler可以讓Qt/Embedded支持更多的客戶鍵盤和其他的非指示設備。
對于非拉丁語系字符(例如阿拉伯、中文、希伯來和日語)的輸入法,需要把它寫成過濾器的方式,并改變鍵盤的輸入。輸入法的作者應該對全部的Qt?API的使用有完整的認識。在一個無鍵盤的設備上,輸入法成了惟一的輸入字符的手段。Qtopia提供了4種輸入方法:筆跡識別器、圖形化的標準鍵盤、Unicode鍵盤和基于字典方式提取的鍵盤。
(4)屏幕加速
通過子類化QScreen和QgfxRaster可以實現硬件加速,從而為屏幕操作帶來好處。Troll-
tech提供了Mach64和Voodoo3視頻卡的硬件加速的驅動例子,同時可以按照協議編寫其他的驅動程序。
?
2.Qt的開發(fā)環(huán)境
Qt/Embedded的開發(fā)環(huán)境可以取代那些我們熟知的UNIX和Windows開發(fā)工具。它提供了幾個跨平臺的工具使得開發(fā)變得迅速和方便,尤其是它的圖形設計器。UNIX下的開發(fā)者可以在PC機或者工作站使用虛擬緩沖幀,從而可以模仿一個和嵌入式設備的顯示終端大小,像素相同的顯示環(huán)境。
嵌入式設備的應用可以在安裝了一個跨平臺開發(fā)工具鏈的不同的平臺上編譯。最通常的做法是在一個UNIX系統上安裝跨平臺的帶有l(wèi)ibc庫的GNU?C++編譯器和二進制工具。在開發(fā)的許多階段,一個可替代的做法是使用Qt的桌面版本,例如通過Qt/X11或是Qt/Windows來進行開發(fā)。這樣開發(fā)人員就可以使用他們熟悉的開發(fā)環(huán)境,例如微軟公司的Visual?C++或者Borland?C++。在UNIX操作系統下,許多環(huán)境也是可用的,例如Kdevelop,它也支持交互式開發(fā)。
如果Qt/Embedded的應用是在UNIX平臺下開發(fā)的話,那么它就可以在開發(fā)的機器上以一個獨立的控制臺或者虛擬緩沖幀的方式來運行,對于后者來說,其實是有一個X11的應用程序虛擬了一個緩沖幀。通過指定顯示設備的寬度、高度和顏色深度,虛擬出來的緩沖幀將和物理的顯示設備在每個像素上保持一致。這樣每次調試應用時開發(fā)人員就不用總是刷新嵌入式設備的Flash存儲空間,從而加速了應用的編譯、鏈接和運行周期。運行Qt的虛擬緩沖幀工具的方法是在Linux的圖形模式下運行以下命令:
qvfb?(回車)
當Qt嵌入式的應用程序要把顯示結果輸出到虛擬緩沖幀時,我們在命令行運行這個程序,并在程序名后加上-qws的選項。例如:$>?hello–qws。
3.Qt的支撐工具
Qt包含了許多支持嵌入式系統開發(fā)的工具,有兩個最實用的工具是qmake和Qt?designer(圖形設計器)。
n qmake是一個為編譯Qt/Embedded庫和應用而提供的Makefile生成器。它能夠根據一個工程文件(.pro)產生不同平臺下的Makefile文件。qmake支持跨平臺開發(fā)和影子生成,影子生成是指當工程的源代碼共享給網絡上的多臺機器時,每臺機器編譯鏈接這個工程的代碼將在不同的子路徑下完成,這樣就不會覆蓋別人的編譯鏈接生成的文件。qmake還易于在不同的配置之間切換。
n Qt圖形設計器可以使開發(fā)者可視化地設計對話框而不需編寫代碼。使用Qt圖形設計器的布局管理可以生成能平滑改變尺寸的對話框。
qmake和Qt圖形設計器是完全集成在一起的。
12.2.2??Qt/Embedded信號和插槽機制
1.機制概述
信號和插槽機制是Qt的核心機制,要精通Qt編程就必須對信號和插槽有所了解。信號和插槽是一種高級接口,應用于對象之間的通信,它是Qt的核心特性,也是Qt區(qū)別于其他工具包的重要地方。信號和插槽是Qt自行定義的一種通信機制,它獨立于標準的C/C++語言,因此要正確地處理信號和插槽,必須借助一個稱為moc(Meta?Object?Compiler)的Qt工具,該工具是一個C++預處理程序,它為高層次的事件處理自動生成所需要的附加代碼。
所謂圖形用戶接口的應用就是要對用戶的動作做出響應。例如,當用戶單擊了一個菜單項或是工具欄的按鈕時,應用程序會執(zhí)行某些代碼。大部分情況下,是希望不同類型的對象之間能夠進行通信。程序員必須把事件和相關代碼聯系起來,這樣才能對事件做出響應。以前的工具開發(fā)包使用的事件響應機制是易崩潰的,不夠健壯的,同時也不是面向對象的。
以前,當使用回調函數機制把某段響應代碼和一個按鈕的動作相關聯時,通常把那段響應代碼寫成一個函數,然后把這個函數的地址指針傳給按鈕,當那個按鈕被單擊時,這個函數就會被執(zhí)行。對于這種方式,以前的開發(fā)包不能夠確?;卣{函數被執(zhí)行時所傳遞進來的函數參數就是正確的類型,因此容易造成進程崩潰。另外一個問題是,回調這種方式緊緊地綁定了圖形用戶接口的功能元素,因而很難進行獨立的開發(fā)。
信號與插槽機制是不同的。它是一種強有力的對象間通信機制,完全可以取代原始的回調和消息映射機制。在Qt中信號和插槽取代了上述這些凌亂的函數指針,使得用戶編寫這些通信程序更為簡潔明了。信號和插槽能攜帶任意數量和任意類型的參數,它們是類型完全安全的,因此不會像回調函數那樣產生core?dumps。
圖12.2??對象間信號與插槽的關系
所有從QObject或其子類(例如Qwidget)派生的類都能夠包含信號和插槽。當對象改變狀態(tài)時,信號就由該對象發(fā)射(emit)出去了,這就是對象所要做的全部工作,它不知道另一端是誰在接收這個信號。這就是真正的信息封裝,它確保對象被當作一個真正的軟件組件來使用。插槽用于接收信號,但它們是普通的對象成員函數。一個插槽并不知道是否有任何信號與自己相連接。而且,對象并不了解具體的通信機制。
用戶可以將很多信號與單個插槽進行連接,也可以將單個信號與很多插槽進行連接,甚至將一個信號與另外一個信號相連接也是可能的,這時無論第一個信號什么時候發(fā)射,系統都將立刻發(fā)射第二個信號。總之,信號與插槽構造了一個強大的部件編程機制。
?
2.信號與插槽實現實例
(1)信號。
當某個信號對其客戶或所有者內部狀態(tài)發(fā)生改變時,信號就被一個對象發(fā)射。只有定義了這個信號的類及其派生類才能夠發(fā)射這個信號。當一個信號被發(fā)射時,與其相關聯的插槽將被立刻執(zhí)行,就像一個正常的函數調用一樣。信號-插槽機制完全獨立于任何GUI事件循環(huán)。只有當所有的槽返回以后發(fā)射函數(emit)才返回。如果存在多個槽與某個信號相關聯,那么,當這個信號被發(fā)射時,這些槽將會一個接一個地執(zhí)行,但是它們執(zhí)行的順序將會是隨機的、不確定的,用戶不能人為地指定哪個先執(zhí)行、哪個后執(zhí)行。
Qt的signals關鍵字指出進入了信號聲明區(qū),隨后即可聲明自己的信號。例如,下面定義了3個信號:
signals:
void?mySignal();
void?mySignal(int?x);
void?mySignalParam(int?x,int?y);
在上面的定義中,signals是Qt的關鍵字,而非C/C++的。接下來的一行void?mySignal()定義了信號mySignal,這個信號沒有攜帶參數;接下來的一行void?mySignal(int?x)定義了重名信號mySignal,但是它攜帶一個整形參數,這有點類似于C++中的虛函數。從形式上講信號的聲明與普通的C++函數是一樣的,但是信號卻沒有函數體定義。另外,信號的返回類型都是void。信號由moc自動產生,它們不應該在.cpp文件中實現。
(2)插槽。
插槽是普通的C++成員函數,可以被正常調用,它們惟一的特殊性就是很多信號可以與其相關聯。當與其關聯的信號被發(fā)射時,這個插槽就會被調用。插槽可以有參數,但插槽的參數不能有缺省值。
插槽是普通的成員函數,因此與其他的函數一樣,它們也有存取權限。插槽的存取權限決定了誰能夠與其相關聯。同普通的C++成員函數一樣,插槽函數也分為3種類型,即public?slots、private?slots和protected?slots。
n public?slots:在這個區(qū)內聲明的槽意味著任何對象都可將信號與之相連接。這對于組件編程非常有用,用戶可以創(chuàng)建彼此互不了解的對象,將它們的信號與槽進行連接以便信息能夠正確地傳遞。
n protected?slots:在這個區(qū)內聲明的槽意味著當前類及其子類可以將信號與之相連接。這適用于那些槽,它們是類實現的一部分,但是其界面接口卻面向外部。
n private?slots:在這個區(qū)內聲明的槽意味著只有類自己可以將信號與之相連接。這適用于聯系非常緊密的類。
插槽也能夠被聲明為虛函數,這也是非常有用的。插槽的聲明也是在頭文件中進行的。例如,下面聲明了3個插槽:
public?slots:
void?mySlot();
void?mySlot(int?x);
void?mySignalParam(int?x,int?y);
(3)信號與插槽關聯。
通過調用QObject對象的connect()函數可以將某個對象的信號與另外一個對象的插槽函數或信號相關聯,當發(fā)射者發(fā)射信號時,接收者的槽函數或信號將被調用。
該函數的定義如下所示:
bool?QObject::connect?(const?QObject?*?sender,?const?char?*?signal,const?QObject?*?receiver,?const?char?*?member)?[static]
這個函數的作用就是將發(fā)射者sender對象中的信號signal與接收者receiver中的member插槽函數聯系起來。當指定信號signal時必須使用Qt的宏SIGNAL(),當指定插槽函數時必須使用宏SLOT()。如果發(fā)射者與接收者屬于同一個對象的話,那么在connect()調用中接收者參數可以省略。
n 信號與插槽相關聯。
下例定義了兩個對象:標簽對象label和滾動條對象scroll,并將valueChanged()信號與標簽對象的setNum()插槽函數相關聯,另外信號還攜帶了一個整型參數,這樣標簽總是顯示滾動條所處位置的值。
QLabel?*label?=?new?QLabel;
QScrollBar?*scroll?=?new?QScrollBar;
QObject::connect(scroll,?SIGNAL(valueChanged(int)),label,?SLOT(setNum(int)));
n 信號與信號相關聯。
在下面的構造函數中,MyWidget創(chuàng)建了一個私有的按鈕aButton,按鈕的單擊事件產生的信號clicked()與另外一個信號aSignal()進行關聯。這樣,當信號clicked()被發(fā)射時,信號aSignal()也接著被發(fā)射。如下所示:
class?MyWidget?:?public?QWidget
{
public:
MyWidget();
...
signals:
void?aSignal();
...
private:
...
QPushButton?*aButton;
};
MyWidget::MyWidget()
{
????aButton?=?new?QPushButton(this);
????connect(aButton,?SIGNAL(clicked()),?SIGNAL(aSignal()));
}
(4)解除信號與插槽關聯。
當信號與槽沒有必要繼續(xù)保持關聯時,用戶可以使用disconnect()函數來斷開連接。其定義如下所示:
bool?QObject::disconnect?(const?QObject?*?sender,?const?char?*?signal,const?Object?*?receiver,?const?char?*?member)?[static]
這個函數斷開發(fā)射者中的信號與接收者中的槽函數之間的關聯。
有3種情況必須使用disconnect()函數。
n 斷開與某個對象相關聯的任何對象。
當用戶在某個對象中定義了一個或者多個信號,這些信號與另外若干個對象中的槽相關聯,如果想要切斷這些關聯的話,就可以利用這個方法,非常簡潔。如下所示:
disconnect(myObject,?0,?0,?0)
或者
myObject->disconnect()
n 斷開與某個特定信號的任何關聯。
這種情況是非常常見的,其典型用法如下所示:
disconnect(myObject,?SIGNAL(mySignal()),?0,?0)
或者
myObject->disconnect(SIGNAL(mySignal()))
n 斷開兩個對象之間的關聯。
這也是非常常用的情況,如下所示:
disconnect(myObject,?0,?myReceiver,?0)
或者
myObject->disconnect(myReceiver)
注意 |
在disconnect()函數中0可以用作一個通配符,分別表示任何信號、任何接收對象、接收對象中的任何槽函數。但是發(fā)射者sender不能為0,其他3個參數的值可以等于0。 |
?
12.2.3??搭建Qt/Embedded開發(fā)環(huán)境
一般來說,用Qt/Embedded開發(fā)的應用程序最終會發(fā)布到安裝有嵌入式Linux操作系統的小型設備上,所以使用裝有Linux操作系統的PC機或者工作站來完成Qt/Embedded開發(fā)當然是最理想的環(huán)境,此外Qt/Embedded也可以安裝在UNIX或Windows系統上。這里就以在Linux操作系統中安裝為例進行介紹。
這里需要有3個軟件安裝包:tmake工具安裝包、Qt/Embedded安裝包和Qt的X11版的安裝包。
n tmake1.11或更高版本:生成Qt/Embedded應用工程的Makefile文件。
n Qt/Embedded:Qt/Embedded安裝包。
n Qt?2.3.2?for?X11:Qt的X11版的安裝包,產生X11開發(fā)環(huán)境所需要的兩個工具。
注意 |
這些軟件安裝包都有許多不同的版本,由于版本的不同會導致這些軟件在使用時可能引起的沖突,為此必須依照一定的安裝原則,Qt/Embedded安裝包的版本必須比Qt?for?X11的安裝包的版本新,這是因為Qt?for?X11的安裝包中的兩個工具uic和designer產生的源文件會和Qt/Embedded的庫一起被編譯鏈接,因此要本著“向前兼容”的原則,Qt?for?X11?的版本應比Qt/Embedded的版本舊。 |
1.安裝tmake
用戶使用普通的解壓縮即可,注意要將路徑添加到全局變量中去,如下所示:
tar?zxvf?tmake-1.11.tar.gz
export?TMAKEDIR=$PWD/tmake-1.11
export?TMAKEPATH=$TMAKEDIR/lib/qws/linux-x86-g++
export?PATH=$TMAKEDIR/bin:$PATH
2.安裝Qt/Embedded?2.3.7
這里使用常見的解壓命令及安裝命令即可,要注意這里的路徑與不同的系統有關,讀者要根據實際情況進行修改。另外,這里的configure命令帶有參數“-qconfig?–qvfb?–depths?4816,32”分別為指定Qt嵌入式開發(fā)包生成虛擬緩沖幀工具qvfb,并支持4、8、16、32?位的顯示顏色深度。另外讀者也可以在configure的參數中添加“-system”、“-jpeg”或“gif”命令,使Qt/Embedded平臺能支持jpeg、gif格式的圖形。
Qt/Embedded開發(fā)包有5種編譯范圍的選項,使用這些選項可控制Qt生成的庫文件的大小。如命令make?sub-src指定按精簡方式編譯開發(fā)包,也就是說有些Qt類未被編譯。其他編譯選項的具體用法可通過“./configure–help”命令查看。精簡方式的安裝步驟如下所示:
tar?zxvf?qt-embedded-2.3.7.tar.gz
cd?qt-2.3.7
export?QTDIR=$PWD
export?QTEDIR=$QTDIR
export?PATH=$QTDIR/bin:$PATH
export?LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
./configure?-qconfig?local-qvfb?-depths?4,8,16,32
make?sub-src
3.安裝Qt/X11?2.3.2
與上一步類似,用戶也可以在configure后添加一定的參數,如“-no-opengl”或“-no-xfs”,可以鍵入命令“./configure?–help”來獲得一些幫助信息。
tar?xfz?qt-x11-2.3.2.tar.gz
cd?qt-2.3.2
export?QTDIR=$PWD
export?PATH=$QTDIR/bin:$PATH
export?LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
./configure?-no-opengl
make
make?-C?tools/qvfb
mv?tools/qvfb/qvfb?bin
cp?bin/uic?$QTEDIR/bin
12.2.4??Qt/Embedded窗口部件
Qt提供了一整套的窗口部件。它們組合起來可用于創(chuàng)建用戶界面的可視元素。按鈕、菜單、滾動條、消息框和應用程序窗口都是窗口部件的實例。因為所有的窗口部件既是控件又是容器,因此Qt的窗口部件不能任意地分為控件和容器。通過子類化已存在的Qt部件或少數時候必要的全新創(chuàng)建,自定義的窗口部件能很容易地創(chuàng)建出來。
窗口部件是QWidget或其子類的實例,用戶自定義的窗口通過子類化得到,如圖12.3所示。
圖12.3??源自QWidget的類層次結構
一個窗口部件可包含任意數量的子部件。子部件在父部件的區(qū)域內顯示。沒有父部件的部件是頂級部件(比如一個窗口),通常在桌面的任務欄上有它們的入口。Qt不在窗口部件上施加任何限制。任何部件都可以是頂級部件,任何部件都可以是其他部件的子部件。通過自動或手動(如果你喜歡)使用布局管理器可以設定子部件在父部件區(qū)域中的位置。如果父部件被停用、隱藏或刪除,則同樣的動作會應用于它的所有子部件。
1.Hello窗口實例
下面是一個顯示“Hello?Qt/Embedded!”的程序的完整代碼:
#include?<qapplication.h>
#include?<qlabel.h>
int?main(int?argc,?char?**argv)
{
?????QApplication?app(argc,?argv);
?????QLabel?*hello=new?QLabel?
("<font?color=blue>Hello""<i>Qt?Embedded!</i></font>",0);
?????app.setMainWidget(hello);
?????hello->show();
?????return?app.exec();
}
2.常見通用窗口組合
Qt中還有一些常見的通用窗口,它們使用了Windows風格顯示,圖12.5、12.6、12.7、12.8分別描述了常見的一些通用窗口的組合使用。圖12.4是該Hello窗口的運行效果圖:
圖12.4 Hello窗口運行效果圖 ???????? 圖12.5 使用QHBox排列一個標簽和一個按鈕
圖12.6 使用了QButtonGroup的兩個單選框和兩個復選框????? 圖12.7 QGroupBox組合圖示
?
圖12.8使用了QGroupBox進行排列的日期類QDateTimeEdit、一個行編輯框類QLine-
Edit、一個文本編輯類QTextEdit和一個組合框類QComboBox。
圖12.9是以QGrid排列的一個QDial、一個QProgressBar、一個QSpinBox、一個QScrollBar、一個QLCDNumber和一個QSlider。
圖12.10是以QGrid排列的一個QIconView、一個QListView、一個QListBox和一個QTable。
???圖12.8 QGrid組合圖示1 ???????圖12.9 QGrid組合圖示2 ???????圖12.10??鐘表部件圖示
?
3.自定義窗口
開發(fā)者可以通過子類化QWidget或它的一個子類創(chuàng)建他們自己的部件或對話框。為了舉例說明子類化,下面提供了數字鐘部件的完整代碼。
鐘表部件是一個能顯示當前時間并自動更新的LCD。一個冒號分隔符隨秒數的流逝而閃爍,如圖12.10所示。
Clock從QLCDNumber部件繼承了LCD功能。它有一個典型部件類所擁有的典型構造函數,帶有可選的parent和name參數(如果設置了name參數,測試和調試會更容易)。系統有規(guī)律地調用從QObject繼承的timerEvent()函數。
它在clock.h中定義如下所示:
#include?<qlcdnumber.h>
class?Clock:public?QLCDNumber
{
public:
?????Clock(QWidget?*parent=0,const?char?*name=0);
protected:
?????void?timerEvent(QTimerEvent?*event);
private:
?????void?showTime();
?????bool?showingColon;
};
構造函數showTime()是用當前時間初始化鐘表,并且告訴系統每1000ms調用一次timerEvent()來刷新LCD的顯示。在showTime()中,通過調用QLCDNumber::display()來顯示當前時間。每次調用showTime()來讓冒號閃爍時,冒號就被空白代替。
clock.cpp的源碼如下所示:
#include?<qdatetime.h>
#include?"clock.h"
Clock::Clock(QWidget?*parent,const?char?*name)
:QLCDNumber(parent,name),showingColon(true)
{
?????showTime();
?????startTimer(1000);
}
void?Clock::timerEvent(QTimerEvent?*)
{
?????showTime();
}
void?Clock::showTime()
{
?????QString?timer=QTime::currentTime().toString().left(5);
?????if?(!showingColon)
?????{
?????time[2]='?';
?????}
?????display(time);
?????showingColon=!showingColon;
}
文件clock.h和clock.cpp完整地聲明并實現了Clock部件。
#include?<qapplication.h>
#include?"clock.h"
int?main(int?argc,char?**argv)
{
????QApplication?app(argc,argv);
????Clock?*clock=new?Clock;
????app.setMainWidget(clock);
????clock->show();
????return?app.exec();
}
?
12.2.5??Qt/Embedded圖形界面編程
Qt提供了所有可能的類和函數來創(chuàng)建GUI程序。Qt既可用來創(chuàng)建“主窗口”式的程序,即一個有菜單欄,工具欄和狀態(tài)欄作為環(huán)繞的中心區(qū)域;也可以用來創(chuàng)建“對話框”式的程序,使用按鈕和必要的選項卡來呈現選項與信息。Qt支持SDI(單文檔界面)和MDI(多文檔界面)。Qt還支持拖動、放下和剪貼板。工具欄可以在工具欄區(qū)域內移動,拖拽到其他區(qū)域或者作為工具托盤浮動起來。這個功能是內建的,不需要額外的代碼,但程序員在需要時可以約束工具欄的行為。
使用Qt可以大大簡化編程。例如,如果一個菜單項、一個工具欄按鈕和一個快捷鍵都完成同樣的動作,那么這個動作只需要一份代碼。
Qt還提供消息框和一系列標準對話框,使得程序向用戶提問和讓用戶選擇文件、文件夾、字體以及顏色變得更加簡單。為了呈現一個消息框或一個標準對話框,只需要用一個使用方便的Qt靜態(tài)函數的一行的語句。
1.主窗口類
QMainWindow類提供了一個典型應用程序的主窗口框架。
一個主窗口包含了一組標準窗體的集合。主窗口的頂部包含一個菜單欄,它的下方放置著一個工具欄,工具欄可以移動到其他的停靠區(qū)域。主窗口允許停靠的位置有頂部、左邊、右邊和底部。工具欄可以被拖放到一個??康奈恢?,從而形成一個浮動的工具面板。主窗口的下方,也就是在底部的??课恢孟路接幸粋€狀態(tài)欄。主窗口的中間區(qū)域可以包含其他的窗體。提示工具和“這是什么”幫助按鈕以旁述的方式闡述了用戶接口的使用方法。
對于小屏幕的設備,使用Qt圖形設計器定義的標準的QWidget模板比使用主窗口類更好一些。典型的模板包含有菜單欄、工具欄,可能沒有狀態(tài)欄(在必要的情況下,可以用任務欄,標題欄來顯示狀態(tài))。
例如,一個文本編輯器可以把QTextEdit作為中心部件:
QTextEdit?*editor?=?new?QTextEdit(mainWindow);
mainWindow->setCentralWidget(editor);
2.菜單類
彈出式菜單QPopupMenu類以垂直列表的方式顯示菜單項,它可以是單個的(例如上下文相關菜單),可以以菜單欄的方式出現,或者是別的彈出式菜單的子菜單出現。
每個菜單項可以有一個圖標、一個復選框和一個加速器(快捷鍵),菜單項通常對應一個動作(例如存盤),分隔器通常顯示成一條豎線,它用于把一組相關聯的動作菜單分離成組。
下面是一個建立包含有New、Open和Exit菜單項的文件菜單的例子。
QPopupMenu?*fileMenu?=?new?QPopupMenu(this);
fileMenu->insertItem("&New",?this,?SLOT(newFile()),?CTRL+Key_N);
fileMenu->insertItem("&Open...",?this,?SLOT(open()),?CTRL+Key_O);
fileMenu->insertSeparator();
fileMenu->insertItem("&Exit",?qApp,?SLOT(quit()),?CTRL+Key_Q);
當一個菜單項被選中,和它相關的插槽將被執(zhí)行。加速器(快捷鍵)很少在一個沒有鍵盤輸入的設備上使用,Qt/Embedded的典型配置并未包含對加速器的支持。上面出現的代碼“&New”意思是在桌面機器上以“New”的方式顯示出來,但是在嵌入式設備中,它只會顯示為“New”。
QMenuBar類實現了一個菜單欄,它會自動地設置幾何尺寸并在它的父窗體的頂部顯示出來,如果父窗體的寬度不夠寬以至不能顯示一個完整的菜單欄,那么菜單欄將會分為多行顯示出來。Qt內置的布局管理能夠自動調整菜單欄。
Qt的菜單系統是非常靈活的,菜單項可以被動態(tài)使能、失效、添加或者刪除。通過子類化QCustomMenuItem,用戶可以建立客戶化外觀和功能的菜單項。
3.工具欄
工具欄可以被移動到中心區(qū)域的頂部、底部、左邊或右邊。任何工具欄都可以拖拽到工具欄區(qū)域的外邊,作為獨立的浮動工具托盤。
QToolButton類實現了具有一個圖標,一個3D框架和一個可選標簽的工具欄。切換型工具欄按鈕具有可以打開或關閉某些特征的功能。其他的則會執(zhí)行一個命令。可以為活動、關閉、開啟等模式,打開或關閉等狀態(tài)提供不同的圖標。如果只提供一個圖標,Qt能根據可視化線索自動地辨別狀態(tài),例如將禁用的按鈕變灰,工具欄按鈕也能觸發(fā)彈出式菜單。
QToolButton通常在QToolBar內并排出現。一個程序可含有任意數量的工具欄并且用戶可以自由地移動它們。工具欄可以包括幾乎所有部件,例如QComboBox和QSpinBox。
?
4.旁述
現在的應用主要使用旁述的方式去解釋用戶接口的用法。Qt提供了兩種旁述的方式,即“提示欄”和“這是什么”幫助按鈕。
n “提示欄”是小的,通常是黃色的矩形,當光標在窗體的某些位置游動時,它就會自動地出現。它主要用于解釋工具欄按鈕,特別是那些缺少文字標簽說明的工具欄按鈕的用途。下面就是如何設置一個“存盤”按鈕的提示代碼。
QToolTip::add(saveButton,"Save");
當提示字符出現之后,還可以在狀態(tài)欄顯示更詳細的文字說明。
對于一些沒有鼠標的設備(例如那些使用觸點輸入的設備),就不會出現鼠標的光標在窗體上進行游動,這樣就不能激活提示欄。對于這些設備也許就需要使用“這是什么”幫助按鈕,或者使用一種狀態(tài)來表示輸入設備正在進行游動,例如用按下或者握住的狀態(tài)來表示現在正在進行游動。
n “這是什么”幫助按鈕和提示欄有些相似,只不過前者是要用戶單擊它才會顯示旁述。在小屏幕設備上,要想單擊“這是什么”幫助按鈕,具體的方法是,在靠近應用的X窗口的關閉按鈕“x”附近你會看到一個“?”符號的小按鈕,這個按鈕就是“這是什么”的幫助按鈕。一般來說,“這是什么”幫助按鈕按下后要顯示的提示信息應該比提示欄要多一些。下面是設置一個存盤按鈕的“這是什么”文本提示信息的方法:
QWhatsThis::add(saveButton,?"Save?the?current?file.");
QToolTip和QWhatsThis類提供了可以通過重新實現來獲取更多特殊化行為的虛函數,比如根據鼠標在部件的位置來顯示不同的文本。
5.動作
應用程序通常提供幾種不同的方式來執(zhí)行特定的動作。比如,許多應用程序通過菜單(Flie->Save)、工具欄(像一個軟盤的按鈕)和快捷鍵(Ctrl+S)來提供“Save”動作。QAction類封裝了“動作”這個概念。它允許程序員在某個地方定義一個動作。
下面的代碼實現了一個“Save”菜單項、一個“Save”工具欄按鈕和一個“Save”快捷鍵,并且均有旁述幫助:
QAction?*saveAct?=?new?QAction("Save",?saveIcon,?"&Save",CTRL+Key_S,?this);
connect(saveAct,SIGNAL(activated()),?this,?SLOT(save()));
saveAct->setWhatsThis("Saves?the?current?file.");
saveAct->addTo(fileMenu);
saveAct->addTo(toolbar);
為了避免重復,使用QAction可保證菜單項的狀態(tài)與工具欄保持同步,而工具提示能在需要的時候顯示。禁用一個動作會禁用相應的菜單項和工具欄按鈕。類似地,當用戶單擊切換型按鈕時,相應的菜單項會因此被選中或不選。
12.2.6??Qt/Embedded對話框設計
Qt/Embedded對話框的設計比較復雜,要使用布局管理自動地設置窗體與別的窗體之間相對的尺寸和位置,這樣可以確保對話框能夠最好地利用屏幕上的可用空間,接著還要使用Qt圖形設計器可視化設計工具建立對話框。下面就詳細講解具體的步驟。
1.布局
Qt的布局管理用于組織管理一個父窗體區(qū)域內的子窗體。它的特點是可以自動設置子窗體的位置和大小,并可確定出一個頂級窗體的最小和缺省的尺寸,當窗體的字體或內容變化后,它可以重置一個窗體的布局。
使用布局管理,開發(fā)者可以編寫獨立于屏幕大小和方向之外的程序,從而不需要浪費代碼空間和重復編寫代碼。對于一些國際化的應用程序,使用布局管理,可以確保按鈕和標簽在不同的語言環(huán)境下有足夠的空間顯示文本,不會造成部分文字被剪掉。
布局管理提供部分用戶接口組件,例如輸入法和任務欄變得更容易。我們可以通過一個例子說明這一點,當Qtopia的用戶輸入文字時,輸入法會占用一定的文字空間,應用程序這時也會根據可用屏幕尺寸的變化調整自己。
Qtopia的布局管理示例如圖12.11所示。
?
(1)內建布局管理器
Qt提供了3種用于布局管理的類:QHBoxLayout、QVBox-
Layout和QGridLayout。
n QHBoxLayout?布局管理把窗體按照水平方向從左至右排成一行。
n QVBoxLayout布局管理把窗體按照垂直方向從上至下排成一列。
n QGridLayout布局管理以網格的方式來排列窗體,一個窗體可以占據多個網格。
它們的示例如圖12.12所示。
在多數情況下,Qt的布局管理器為其管理的部件挑選一個最適合的尺寸以便窗口能夠平滑地縮放。如果其缺省值不合適,開發(fā)者可以使用以下機制微調布局:
n 設置一個最小尺寸、一個最大尺寸,或者為一些子部件設置固定的大小。
圖12.12??3種布局管理類示意圖
n 設置一些延伸項目或間隔項目,延伸或間隔項目會填充空余的布局空間。
n 改變子部件的尺寸策略。通過調用QWidget::setSizePolicy(),程序員可以仔細調整子部件的縮放行為。子部件可以設置為擴展、收縮、保持原大小等狀態(tài)。
n 改變子部件的建議大小。QWidget::sizeHint()和QWidget::minimumSizeHint()會根據內容返回部件的首選尺寸和最小首選尺寸。內建部件提供了合適的重新實現。
n 設置延伸因子。延伸因子規(guī)定了子部件的相應增量,比如,2/3的可用空間分配給部件A而1/3分配給B。
(2)布局嵌套。
布局可以嵌套任意層。圖12.13顯示了一個對話框的兩種大小。
圖12.13??一個對話框的兩種大小
這個對話框使用了3種布局:一個QVBoxLayout組合了按鈕,一個QHBoxLayout組合了國家列表和那組按鈕,一個QVBoxLayout組合了“Select?a?country”標簽和剩下的部件。一個延伸項目用來維護Cancel和Help按鈕間的距離。
下面的代碼創(chuàng)建了對話框部件和布局:
QVBoxLayout?*buttonBox?=?new?QVBoxLayout(6);
buttonBox->addWidget(new?QPushButton("OK",?this));
buttonBox->addWidget(new?QPushButton("Cancel",?this));
buttonBox->addStretch(1);
buttonBox->addWidget(new?QPushButton("Help",?this));
QListBox?*countryList?=?new?QListBox(this);
countryList->insertItem("Canada");
/*...*/
countryList->insertItem("United?States?of?America");
QHBoxLayout?*middleBox?=?new?QHBoxLayout(11);
middleBox->addWidget(countyList);
middleBox->addLayout(buttonBox);
QVBoxLayout?*topLevelBox?=?new?QVBoxLayout(this,6,11);
topLevelBox->addWidget(new?QLabel("Select?a?country",?this));
topLevelBox->addLayout(middleBox);
可以看到,Qt讓布局變得非常容易。
(3)自定義布局。
通過子類化QLayout,開發(fā)者可以定義自己的布局管理器。和Qt一起提供的customlayout樣例展示了3個自定義布局管理器:BorderLayout、CardLayout和SimpleFlow,程序員可以使用并修改它們。
Qt還包括QSplitter,是一個最終用戶可以操縱的分離器。某些情況下,QSplitter可能比布局管理器更為可取。
為了完全控制,重新實現每個子部件的QWidget::resizeEvent()并調用QWidget::setGeometry(),就可以在一個部件中手動地實現布局。
2.Qt/Embedded圖形設計器
Qt圖形設計器是一個具有可視化用戶接口的設計工具。Qt的應用程序可以完全用源代碼來編寫,或者使用Qt圖形設計器來加速開發(fā)工作。啟動Qt圖形設計器的方法是:
cd?qt-2.3.2/bin
./designer
這樣就可以啟動一個圖形化的設計界面,如圖12.14所示。
圖12.14??Qt圖形設計器界面
?
開發(fā)者單擊工具欄上的代表不同功能的子窗體/組件的按鈕,然后把它拖放到一個表單(Form)上,這樣就可以把一個子窗體/組件放到表單上了。開發(fā)者可以使用屬性對話框來設置子窗體的屬性,精確地設置子窗體的位置和尺寸大小是沒必要的。開發(fā)者可以選擇一組窗體,然后對它們進行排列。例如,我們選定了一些按鈕窗體,然后使用“水平排列(lay?out?horizontally)”選項對它們進行一個接一個地水平排列。這樣做不僅使得設計工作變得更快,而且完成后的窗體將能夠按照屬性設置的比例填充窗口的可用范圍。
使用Qt圖形設計器進行圖形用戶接口的設計可以消除應用的編譯、鏈接和運行時間,同時使修改圖形用戶接口的設計變得更容易。Qt圖形設計器的預覽功能使開發(fā)者能夠在開發(fā)階段看到各種樣式的圖形用戶界面,也包括客戶樣式的用戶界面。通過Qt集成功能強大的數據庫類,Qt圖形設計器還可提供生動的數據庫數據瀏覽和編輯操作。
開發(fā)者可以建立同時包含有對話框和主窗口的應用,其中主窗口可以放置菜單、工具欄、旁述幫助等子窗口部件。Qt圖形設計器提供了幾種表單模板,如果窗體會被多個不同的應用反復使用,那么開發(fā)者也可建立自己的表單模板以確保窗體的一致性。
Qt圖形設計器使用向導來幫助人們更快、更方便地建立包含有工具欄、菜單和數據庫等方面的應用。程序員可以建立自己的客戶窗體,并把它集成到Qt圖形設計器中。
Qt圖形設計器設計的圖形界面以擴展名為“ui”的文件進行保存,這個文件有良好的可讀性,這個文件可被uic(Qt提供的用戶接口編譯工具)編譯成為C++的頭文件和源文件。qmake工具在它為工程生成的Makefile文件中自動包含了uic生成頭文件和源文件的規(guī)則。
另一種可選的做法是在應用程序運行期間載入ui文件,然后把它轉變?yōu)榫邆湓热抗δ艿谋韱巍_@樣開發(fā)者就可以在程序運行期間動態(tài)地修改應用的界面,而不需重新編譯應用,另一方面,也使得應用的文件尺寸減小了。
3.建立對話框
Qt為許多通用的任務提供了現成的包含了實用的靜態(tài)函數的對話框類,主要有以下幾種。
n QMessageBox類:是一個用于向用戶提供信息或是讓用戶進行一些簡單選擇(例如“yes”或“no”)的對話框類,如圖12.15所示。
n QProgressDialog類:包含了一個進度欄和一個“Cancel”按鈕,如圖12.16所示。
n QWizard類:提供了一個向導對話框的框架,如圖12.17所示。
圖12.15??QMessageBox類對話框???圖12.16??QProgressDialog類對話框????圖12.17??QWizard類對話框
另外,Qt提供的對話框還包括QColorDialog、QFileDialog、QFontDialog和QPrintDialog。這些類通常適用于桌面應用,一般不會在Qt/Embedded中編譯使用它們。