新手必學(xué) Qt類簡(jiǎn)介之QWidget類
一、詳細(xì)描述
QWidget類是所有用戶界面對(duì)象的基類。通俗的來講,Qt基本上所有的UI類都是由QWidget繼承出來的,而QWidget繼承于QObject,大家可以查閱Qt source 即可發(fā)現(xiàn)一些微妙的寫法,如這篇文章有詳細(xì)介紹:Qt 庫對(duì)象數(shù)據(jù)的聲明和使用
窗口層次
窗口部件是用戶界面的一個(gè)原子:它從窗口系統(tǒng)接收鼠標(biāo)、鍵盤和其它事件,并且在屏幕上繪制自己的表現(xiàn)。每一個(gè)窗口部件都是矩形,并且它們按Z軸順序排列的。一個(gè)窗口部件可以被它的父窗口部件或者它前面的窗口部件蓋住一部分。
QDialog是最普通的***窗口。不被嵌入到一個(gè)父窗口部件的窗口部件被叫做***窗口部件。通常情況下,***窗口部件是有框架和標(biāo)題欄的窗口(盡管如果使用了一定的窗口部件標(biāo)記,創(chuàng)建***窗口部件時(shí)也可能沒有這些裝飾。)在Qt中,QMainWindow和和不同的QDialog的子類是最普通的***窗口。一個(gè)沒有父窗口部件的窗口部件一直是***窗口部件。非***窗口部件是子窗口部件。它們是它們的父窗口部件中的子窗口。你通常不能在視覺角度從它們的父窗口部件中辨別一個(gè)子窗口部件。在Qt中的絕大多數(shù)其它窗口部件僅僅作為子窗口部件才是有用的。(當(dāng)然把一個(gè)按鈕作為或者叫做***窗口部件也是可能的,但絕大多數(shù)人喜歡把他們的按鈕放到其它按鈕當(dāng)中,比如 QDialog。)
QWidget有很多成員函數(shù),但是它們中的一些有少量的直接功能:例如,QWidget有一個(gè)字體屬性,但是它自己從來不用。有很多繼承它的子類提供了實(shí)際的功能,比如QPushButton、QListBox和QTabDialog等等。
每一個(gè)窗口部件構(gòu)造函數(shù)接受一個(gè)或兩個(gè)標(biāo)準(zhǔn)參數(shù):
1. QWidget *parent = 0是新窗口部件的父窗口部件。如果為0(默認(rèn)),新的窗口部件將是一個(gè)***窗口部件。如果不是,它將會(huì)使parent的一個(gè)孩子,并且被parent的幾何形狀所強(qiáng)迫(除非你指定WType_TopLevel作為窗口部件標(biāo)記)。
2. WFlags f = 0(在可用的情況下)設(shè)置窗口部件標(biāo)記,默認(rèn)設(shè)置對(duì)于幾乎所有窗口部件都是適用的,但是,舉例來說,一個(gè)沒有窗口系統(tǒng)框架的***窗口部件,你必須使用特定的標(biāo)記。
二、Top-Level 屬性以及孩子構(gòu)件
A widget without a parent widget is always an independent window (top-level widget). For these widgets,一個(gè)沒有父構(gòu)件的構(gòu)件通常是top-level 屬性的窗口。對(duì)于這類構(gòu)件,setWindowTitle() 和 setWindowIcon() 都是有效的。
三、QWidget事件簡(jiǎn)介
基本事件
paintEvent() – 只要窗口部件需要被重繪就被調(diào)用。每個(gè)要顯示輸出的窗口部件必須實(shí)現(xiàn)它并且不在paintEvent()之外在屏幕上繪制是明智的。
resizeEvent() – 當(dāng)窗口部件被重新定義大小時(shí)被調(diào)用。
mousePressEvent() – 當(dāng)鼠標(biāo)鍵被按下時(shí)被調(diào)用。有六個(gè)鼠標(biāo)相關(guān)事件,但是鼠標(biāo)按下和鼠標(biāo)釋放事件是到目前為止最重要的。當(dāng)鼠標(biāo)在窗口部件內(nèi)或者當(dāng)它使用grabMouse()來捕獲鼠標(biāo)時(shí),它接收鼠標(biāo)按下事件。
mouseReleaseEvent() – 當(dāng)鼠標(biāo)鍵被釋放時(shí)被調(diào)用。當(dāng)窗口部件已經(jīng)接收相應(yīng)的鼠標(biāo)按下事件時(shí),它接收鼠標(biāo)釋放事件。這也就是說如果用戶在你的窗口部件內(nèi)按下鼠標(biāo),然后拖著鼠標(biāo)到其它某個(gè)地方,然后釋放,你的窗口部件接收這個(gè)釋放事件。這里有一個(gè)例外:如果出現(xiàn)在彈出菜單中,當(dāng)鼠標(biāo)鍵被按下時(shí),這個(gè)彈出菜單立即會(huì)偷掉這個(gè)鼠標(biāo)事件。
mouseDoubleClickEvent() – 和它看起來也許不太一樣。如果用戶雙擊,窗口部件接收一個(gè)鼠標(biāo)按下事件(如果他們沒有拿牢鼠標(biāo),也許會(huì)出現(xiàn)一個(gè)或兩個(gè)鼠標(biāo)移動(dòng)事件)、一個(gè)鼠標(biāo)釋放事件并且最終是這個(gè)事件。直到你看到第二次點(diǎn)擊是否到來之前,不能從一個(gè)雙擊中辨別一個(gè)點(diǎn)擊。(這是為什么絕大多數(shù)圖形用戶界面圖書建議雙擊是單擊的一個(gè)擴(kuò)展,而不是一個(gè)不同行為的觸發(fā)的一個(gè)原因。)
如果你的窗口部件僅僅包含子窗口部件,你也許不需要實(shí)現(xiàn)任何一個(gè)事件處理器。如果你想檢測(cè)在子窗口部件中的鼠標(biāo)點(diǎn)擊,請(qǐng)?jiān)诟复翱诓考膍ousePressEvent()中調(diào)用子窗口部件的hasMouse()函數(shù)。
接收鍵盤的窗口部件需要重新實(shí)現(xiàn)一些更多的事件處理器:
keyPressEvent() – 只要鍵被按下和當(dāng)鍵已經(jīng)被按下足夠長(zhǎng)的時(shí)間可以自動(dòng)重復(fù)了就被調(diào)用。注意如果Tab和Shift+Tab鍵被用在焦點(diǎn)變換機(jī)制中,它們僅僅被傳遞給窗口部件。為了強(qiáng)迫那些鍵被你的窗口部件處理,你必須重新實(shí)現(xiàn)QWidget::event()。
focusInEvent() – 當(dāng)窗口部件獲得鍵盤焦點(diǎn)(假設(shè)你已經(jīng)調(diào)用setFocusPolicy())時(shí)被調(diào)用。寫得好的窗口部件意味著它們能按照一種清晰但謹(jǐn)慎的方式來獲得鍵盤焦點(diǎn)。
focusOutEvent() – 當(dāng)窗口部件失去鍵盤焦點(diǎn)時(shí)被調(diào)用。
一些窗口部件也許需要實(shí)現(xiàn)一些不太普通的事件處理器:
mouseMoveEvent() – 只要當(dāng)鼠標(biāo)鍵被按下時(shí)鼠標(biāo)移動(dòng)就會(huì)被調(diào)用。舉例來說,對(duì)于拖動(dòng),這個(gè)很有用。如果你調(diào)用setMouseTracking(TRUE),盡管沒有鼠標(biāo)鍵被按下,你也會(huì)獲得鼠標(biāo)移動(dòng)事件。(注意這個(gè)使用鼠標(biāo)跟蹤的應(yīng)用程序在低下的X連接下不是很有用。)(也可以參考拖放信息。)
keyReleaseEvent() – 只要鍵被釋放和當(dāng)如果這個(gè)鍵是自動(dòng)重復(fù)的并且被按下一段時(shí)間時(shí)就被調(diào)用。在這種情況下窗口部件接收一個(gè)鍵釋放事件并且對(duì)于每一個(gè)重復(fù)立即有一個(gè)鍵按下事件。注意如果Tab和Shift+Tab鍵被用在焦點(diǎn)變換機(jī)制中,它們僅僅被傳遞給窗口部件。為了強(qiáng)迫那些鍵被你的窗口部件處理,你必須重新實(shí)現(xiàn)QWidget::event()。
wheelEvent() — 當(dāng)窗口部件擁有焦點(diǎn)時(shí),只要用戶轉(zhuǎn)動(dòng)鼠標(biāo)滾輪就被調(diào)用。
enterEvent() – 當(dāng)鼠標(biāo)進(jìn)入這個(gè)窗口部件屏幕空間時(shí)被調(diào)用。(這不包括被這個(gè)窗口部件的子窗口部件所擁有的屏幕空間。)
leaveEvent() – 當(dāng)鼠標(biāo)離開這個(gè)窗口部件的屏幕空間時(shí)被調(diào)用。
moveEvent() – 當(dāng)窗口部件相對(duì)于它的父窗口部件已經(jīng)被移動(dòng)時(shí)被調(diào)用。
closeEvent() – 當(dāng)用戶關(guān)閉窗口部件時(shí)(或這當(dāng)close()被調(diào)用時(shí))被調(diào)用。
這里還有一些不太明顯的事件。它們?cè)趒event.h中被列出并且你需要重新實(shí)現(xiàn)event()來處理它們。event()的默認(rèn)實(shí)現(xiàn)處理Tab和Shift+Tab(移動(dòng)鍵盤焦點(diǎn))并且其它絕大多數(shù)事件給上面提到的一個(gè)或更多的特定處理器。
#p#
四、Window flag標(biāo)識(shí)
關(guān)于QWidget 的flag 的介紹: enum Qt::WindowType flags Qt::WindowFlags 這兩個(gè)參數(shù)參閱官方文檔。
Qt 的 WindowFlags 有很多,實(shí)際使用時(shí),若不關(guān)心窗口層次的話,大可不比太關(guān)心這個(gè)。比如說在Window 上做應(yīng)用開發(fā),大可只關(guān)注:Qt::Dialog,Qt::Tool,Qt::Window 即可。但若是做嵌入式開發(fā)就得好好看看這個(gè)屬性,整理好這部分屬性,有利于窗口管理。
五、著重介紹幾個(gè)重要成員函數(shù)
- bool QWidget::close () [slot]
關(guān)閉這個(gè)窗口部件。如果窗口部件被關(guān)閉,返回真,否則返回假。首先它發(fā)送給這個(gè)窗口部件一個(gè)QCloseEvent。如果它接收這個(gè)關(guān)閉事件,它就被隱藏了。QWidget::closeEvent()的默認(rèn)實(shí)現(xiàn)是接收這個(gè)關(guān)閉事件。當(dāng)***一個(gè)可視的***窗口部件被關(guān)閉,QApplication::lastWindowClosed()信號(hào)被發(fā)射。
注意窗口的enum Qt::WidgetAttribute 屬性,窗口默認(rèn)屬性是Qt::WA_MacOpaqueSizeGrip,設(shè)置上這個(gè)屬性意味著窗口調(diào)用close()只銷毀了UI想關(guān),QWidget內(nèi)還有很多內(nèi)存空間沒有釋放,需調(diào)用delete 銷毀QWidget 。若需要在調(diào)用close時(shí)一并銷毀窗口可以給窗口設(shè)置Qt::WA_DeleteOnClose屬性。
- bool QWidget::event ( QEvent * e ) [虛 保護(hù)]
這是主事件處理器,它處理事件e。你可以在子類中被重新實(shí)現(xiàn)整個(gè)函數(shù),但是我們建議你使用一個(gè)特定的事件處理器來替代它。
主事件首先把事件傳遞給所有已經(jīng)被安裝的事件過濾器。如果沒有過濾器中途截取這個(gè)事件,它調(diào)用一個(gè)特定的事件處理器。
鍵按下和釋放事件被處理得和其它事件不同。event()檢查Tab和Shift+Tab并且試圖適當(dāng)?shù)匾苿?dòng)焦點(diǎn)。如果沒有窗口部件被焦點(diǎn)移入(或者鍵按下不是Tab或Shift+Tab),event()調(diào)用keyPressEvent()。
如果它能夠把一個(gè)事件傳遞給沒個(gè)東西,這個(gè)函數(shù)就返回真,否則如果沒有任何東西想要這個(gè)事件,返回假。
也可以參考closeEvent()、focusInEvent()、focusOutEvent()、enterEvent()、keyPressEvent()、keyReleaseEvent()、leaveEvent()、mouseDoubleClickEvent()、mouseMoveEvent()、mousePressEvent()、 mouseReleaseEvent()、moveEvent()、paintEvent()、resizeEvent()、QObject::event()和QObject::timerEvent()。
- void QWidget::paintEvent ( QPaintEvent * ) [虛 保護(hù)]
這個(gè)事件處理器可以在子類中被重新實(shí)現(xiàn)來接收繪制事件。
繪制事件就是重新繪制這個(gè)窗口部件的所有部分的一個(gè)請(qǐng)求。它可以是repaint()或update()的結(jié)果,或者因?yàn)檫@個(gè)窗口部件原來被變暗并且現(xiàn)在已經(jīng)不再被覆蓋了,或者其它很多原因。
很多窗口部件在當(dāng)它們被請(qǐng)求時(shí),它們很簡(jiǎn)單地重新繪制整個(gè)界面,但是一些比較慢的窗口部件需要通過僅僅繪制被請(qǐng)求的區(qū)域QPaintEvent::region()進(jìn)行優(yōu)化。這個(gè)速度優(yōu)化不會(huì)改變結(jié)果,在事件處理過程中,繪制僅僅發(fā)生在被改變的區(qū)域中。例如,QListView和QCanvas就是這樣做的。
Qt也試圖通過把多個(gè)繪制事件合并為一個(gè)來加快繪制速度。當(dāng)update()被調(diào)用幾次或者窗口系統(tǒng)發(fā)送幾次繪制事件,Qt把它們合并為一個(gè)比較大區(qū)域(請(qǐng)參考QRegion::unite())的一個(gè)事件中。repaint()不允許這樣優(yōu)化,所以只要可能我們建議使用update()。
當(dāng)繪制事件發(fā)生,更新區(qū)域通常被擦除,所以你正在這個(gè)窗口部件的背景上繪制。這里有一些例外并且QPaintEvent::erased()告訴你這個(gè)窗口部件是否被擦除。
背景可以通過使用setBackgroundMode()、setPaletteBackgroundColor()或setBackgroundPixmap()來設(shè)置。setBackgroundMode()的文檔詳細(xì)描述了背景,我們建議你去讀一下。
- void QWidget::raise () [槽]
把這個(gè)窗口部件升高到它的父窗口部件的棧的頂部。
如果在屏幕上有與這個(gè)窗口部件重疊的兄弟,這個(gè)窗口部件將在它后來的這些兄弟之前變的可視了。
也可以參考lower()和stackUnder()。
- void QWidget::repaint ( int x, int y, int w, int h, bool erase = TRUE ) [槽]
通過立即調(diào)用paintEvent()來直接重新繪制窗口部件,除非更新是失效的或者窗口部件被隱藏。
如果erase為真,Qt在paintEvent()調(diào)用之前擦除區(qū)域(x,y,w,h)。
如果w是負(fù)數(shù),它被width()-x替換,并且如果h是負(fù)數(shù),它被height()-y替換。
如果你需要立即重新繪制,我們建議使用repaint(),比如在動(dòng)畫期間。在絕大多數(shù)情況下,update()更好,因?yàn)樗试SQt來優(yōu)化速度并且防止閃爍。
警告:如果你在一個(gè)函數(shù)中調(diào)用repaint(),而它自己又被paintEvent()調(diào)用,你也許會(huì)看到無線循環(huán)。update()函數(shù)從來不會(huì)產(chǎn)生循環(huán)。
也可以參考update()、paintEvent()、updatesEnabled和erase()。
小結(jié):QWidget有很多成員函數(shù),但是它們中的一些有少量的直接功能:例如,QWidget有一個(gè)字體屬性,但是它自己從來不用。有很多繼承它的子類提供了實(shí)際的功能,比如QPushButton、QListBox和QTabDialog等等。