一、QApplication僻肖、QGuiApplication、QCoreApplication簡介
1卢鹦、繼承關(guān)系見下圖臀脏,其中左側(cè)為頂級父類
QObject<-QCoreApplication<-QGuiApplication<-QApplication
2、一個程序中只能有一個QCoreApplication及其子類的對象冀自。
3揉稚、QCoreApplication:主要提供無GUI程序的事件循環(huán)。
4熬粗、QGuiApplication:用于管理GUI程序的控制流和主要設(shè)置搀玖。
5、QApplication:該類專門為QGuiApplication提供基于QWidget的程序所需的一些功能驻呐,主要用于處理部件的初始化灌诅、最終化。主要職責(zé)如下:
使用用戶的桌面設(shè)置初始化應(yīng)用程序暴氏。
執(zhí)行事件處理,也就是說該類能從底層系統(tǒng)接收并分發(fā)事件绣张。比如答渔,使用QCoreApplication::sendEvent()或QCoreApplication::postEvent()函數(shù)分發(fā)自定義事件。
解析常用命令行參數(shù)并設(shè)置其內(nèi)部狀態(tài)侥涵。
定義了應(yīng)用程序的界面外觀沼撕,可使用QApplication::setStyle()進行更改。
?指定應(yīng)程程序如何分配顏色芜飘。
?使用QCoreApplication::translate()函數(shù)對字符串進行轉(zhuǎn)換务豺。
通過QApplication::desktop()函數(shù)處理桌面,通過QCoreApplication::clipboard()函數(shù)處理剪貼板嗦明。
管理應(yīng)用程序的鼠標(biāo)光標(biāo)笼沥。比如使用QGuiApplication::setOverrideCuresor()函數(shù)設(shè)置光標(biāo)等。
二娶牌、Qt對事件的描述及分類
1奔浅、事件:是由程序內(nèi)部或外部產(chǎn)生的事情或某種動作的通稱。比如用戶按下鍵盤或鼠標(biāo)诗良,就會產(chǎn)生一個鍵盤事件或鼠標(biāo)事件(這是由程序外部產(chǎn)生的事件)汹桦;再如,當(dāng)窗口第一次顯示時鉴裹,會產(chǎn)生一個繪制事件舞骆,以通知窗口需要重新繪制其自身钥弯,從而使該窗口可見(這是由程序內(nèi)部產(chǎn)生的事件)。
2督禽、事件和信號:
?他們兩個是不同的概念脆霎,不要弄混淆。信號是由對象產(chǎn)生的赂蠢,而事件則不一定是由對象產(chǎn)生的(比如由鼠標(biāo)產(chǎn)生的事件)绪穆,事件通常來自底層的窗口系統(tǒng),但也可以手動發(fā)送自定義的事件虱岂,可見信號和事件的來源是不同的玖院。
事件既可以同步使用,也可以異步使用(取決于調(diào)用sendEvent()還是postEvents())第岖,而使用信號和槽總是同步的难菌。事件的另一個好處是可以被過濾。
3蔑滓、Qt中使用QEvent及其子類來描述事件(其繼承關(guān)系見下圖)郊酒,比如QMouseEvent類用于描述與鼠標(biāo)相關(guān)的事件,該類保存了與鼠標(biāo)相關(guān)的大量信息键袱,比如是哪一個鍵激發(fā)了該事件燎窘、產(chǎn)生該事件時鼠標(biāo)的位置等。
4蹄咖、事件的分類: ①褐健、方式一:根據(jù)事件的來源和傳遞方式,事件可分為以下三大類
?自發(fā)事件:這是由窗口系統(tǒng)生成的澜汤,這些事件置于系統(tǒng)隊列中蚜迅,并由事件循環(huán)一個接一個地處理。
?發(fā)布的事件(Posted events):該類事件由Qt或應(yīng)用程序生成俊抵,這些事件由Qt排隊谁不,并由事件循環(huán)處理。
?發(fā)送的事件(Sent events):該類事件由Qt或應(yīng)用程序生成徽诲,這些事件直接發(fā)送到目標(biāo)對象刹帕,不經(jīng)過事件循環(huán)處理。
②谎替、方式二:事件被細(xì)分為很多種類型(有一百多種)轩拨,每一種類型使用QEvent類中的枚舉常量進行表示,比如QMouseEvent管理的鼠標(biāo)事件有鼠標(biāo)雙擊院喜、移動亡蓉、按下等類型,這些類型分別使用QEvent::Type枚舉類型中的枚舉常量MouseButtonDblClick喷舀、MouseMove砍濒、MouseButtonPress表示淋肾。所有的類型分類請查閱幫助文檔“中希可使用函數(shù)Type QEvent::type() const;獲取事件的類型
5樊卓、使用Qt編程,幾乎不需考慮事件杠河,因為當(dāng)產(chǎn)生某種事件時碌尔,Qt窗口部件都會發(fā)射一個相應(yīng)的信號(即Qt會把事件轉(zhuǎn)換為一個對應(yīng)的信號),比如按鈕被按下時券敌,會產(chǎn)生一個MouseButtonPress事件唾戚,Qt會處理這一事件,并且會發(fā)射一個clicked()單擊信號待诅,程序員可以直接處理clicked()信號叹坦,而不必處理底層的事件。
6卑雁、對于事件募书,我們不需要知道Qt是怎樣把事件轉(zhuǎn)換為QEvent或其子類類型的對象的,程序員只需要知道怎樣傳遞和處理這些事件即可测蹲。比如對于按下鼠標(biāo)事件莹捡,不需要知道Qt是怎樣把該事件轉(zhuǎn)換為QMouseEvent類型的對象的,只需要知道怎樣傳遞和處理該事件即可扣甲。
QEvent QDropEvent QCloseEvent QPaintEvent QInputEvent QResizeEvent QTimerEvent QContextMenuEvent QKeyEvent QMouseEvent QWheelEvent QDragMoveEvent QDragEnterEvent QActionEvent QChildEvent QDragLeaveEvent QEnterEvent QExposeEvent QFileOpenEvent QFocusEvent QGraphicsSceneEvent QHelpEvent QHideEvent QIconDragEvent QInputMethodEvent QInputMethodQueryEvent QMoveEvent QScrollEvent QPlatformSurfaceEvent QScrollPrepareEvent QShortcutEvent QShowEvent QStateMachine::SignalEvent QStateMachine::WrappedEvent QStatusTipEvent QWhatsThisClickedEvent QWindowStateChangeEvent QDynamicPropertyEvent QGestureEvent QHoverEvent QNativeGestureEvent QTabletEvent QTouchEvent QGraphicsSceneWheelEvent
……
三篮赢、事件的傳遞(或分發(fā))及處理
1、事件傳遞步驟(見下圖):
①文捶、基本規(guī)則:若事件未被目標(biāo)對象處理荷逞,則把事件傳遞給其父對象處理媒咳,若父對象仍未處理粹排,則再傳遞給父對象的父對象處理,重復(fù)這個過程涩澡,直至這個事件被處理或到達頂級對象為止顽耳。注意:事件是在對象間傳遞的,這里是指對象的父子關(guān)系妙同,而不是指類的父子關(guān)系射富。
②、在Qt中有一個事件循環(huán)粥帚,該循環(huán)負(fù)責(zé)從可能產(chǎn)生事件的地方捕獲各種事件胰耗,并把這些事件轉(zhuǎn)換為帶有事件信息的對象,然后由Qt的事件處理流程分發(fā)給需要處理事件的對象來處理事件芒涡。
③柴灯、通過調(diào)用QCoreApplication::exec()函數(shù)啟動事件主循環(huán)卖漫,主循環(huán)從事件隊列中獲取事件,然后創(chuàng)建一個合適的QEvent對象或其子類類型的對象來表示該事件赠群,在此步驟中羊始,事件循環(huán)首先處理所有發(fā)布的事件,直到隊列為空查描,然后處理自發(fā)的事件突委,最后處理在自發(fā)事件期間產(chǎn)生的已發(fā)布事件。注意:發(fā)送的事件不由事件循環(huán)處理冬三,該類事件會被直接傳遞給對象匀油。
④、然后长豁,Qt會調(diào)用QCoreApplication::notify()函數(shù)對事件進行傳遞(或分發(fā))
⑤缤削、最后,QObject對象調(diào)用QObject::event()函數(shù)接收事件趟妥,
event()函數(shù)是一個虛函數(shù)脱柱,是QObject對象處理事件的入口,
在QObject的子類中通常會重寫event()函數(shù)酸舍,比如QWidget類就重寫了event()函數(shù)帅韧。
?event()函數(shù)與事件處理函數(shù)的關(guān)系:event()函數(shù)負(fù)責(zé)把事件傳遞給目標(biāo)對象并調(diào)用對應(yīng)的事件處理函數(shù)處理事件,比如調(diào)用QWidget::keyPressEvent()函數(shù)處理鍵盤按下事件等啃勉,注意忽舟,QObject中的event()函數(shù)并不能實現(xiàn)該功能,這是通過QObject的子類中重寫的event()函數(shù)實現(xiàn)的淮阐,比如調(diào)用QWidget::keyPressEvent()函數(shù)叮阅,就是由在QWidget類中重寫的event()函數(shù)來完成的。
?event()函數(shù)對大多數(shù)事件都調(diào)用了默認(rèn)的處理函數(shù)泣特,但并不能包括全部的事件浩姥,因此,有時我們需要重寫該函數(shù)状您,這也是我們處理Qt事件的方式之一勒叠。
注意:event()函數(shù)不會處理事件,他只是根據(jù)事件的類型進行事件的傳遞(或分發(fā))膏孟,若返回true則表示這個事件被接受并進行了處理眯分,否則事件未被處理,需進行進一步傳遞或丟棄柒桑。
事件1 事件2 事件3->exec()->notify()->QObject::event()->keyPressEvent()->mousePressEvent()
2弊决、事件的處理
①、任何QObject的子類都可以處理事件魁淳,QEvent及其子類雖然可以描述一個事件飘诗,但并不對事件進行處理傅联。
②、事件處理函數(shù)
事件是由類的成員函數(shù)(通常為虛函數(shù))處理的疚察,通常把處理事件的成員函數(shù)稱為事件處理函數(shù)蒸走。由此可見,處理事件需要兩個要求:處理該事件的對象和該對象中用于處理該事件的事件處理函數(shù)貌嫡。
Qt中對絕大多數(shù)常用類型的事件提供了默認(rèn)的事件處理函數(shù)比驻,并作了默認(rèn)處理,這些事件處理函數(shù)位于QObject的子類中岛抄,比如QWidget::mousePressEvent()函數(shù)用于處理鼠標(biāo)按下事件别惦,QWidget::keyPressEvent()用于處理鍵盤按下事件等。對這些默認(rèn)事件處理函數(shù)的調(diào)用是通過QObject::event()虛函數(shù)進行的夫椭,因此若默認(rèn)的事件處理函數(shù)不能滿足用戶要求(比如對Tab鍵的處理)掸掸,則可以通過重新實現(xiàn)event()函數(shù)來達到目的。下面為QWidget類的部分源代碼(qwidget.cpp)
bool QWidget::event(QEvent *event){
switch (event->type()) {
//調(diào)用QWidget類的默認(rèn)事件處理函數(shù)
case QEvent::MouseButtonPress:
mousePressEvent((QMouseEvent*)event); break;
case QEvent::MouseButtonRelease:
mouseReleaseEvent((QMouseEvent*)event); break;?
} }
3蹭秋、由事件傳遞過程可見扰付,Qt可以使用以下5種方式來處理事件
①、重新實現(xiàn)各部件內(nèi)部默認(rèn)的事件處理函數(shù)仁讨,比如重新實現(xiàn)paintEvent()羽莺、mousePressEvent()等事件處理函數(shù),這是最常用洞豁、最簡單的方式
②盐固、重新實現(xiàn)QObject::event()函數(shù)(需繼承QObject類),使用該方式丈挟,可以在事件到達默認(rèn)的事件處理函數(shù)之前捕獲到該事件刁卜。該方式常被用來處理Tab鍵的默認(rèn)意義。在重新實現(xiàn)event()函數(shù)時曙咽,必須對未明確處理的事件調(diào)用基類的event()函數(shù)蛔趴,比如,若子類化QWidget桐绒,并重寫了event()函數(shù)夺脾,但未調(diào)用父類的event()函數(shù)之拨,則程序可能會不能正常顯示界面茉继。
③、在QObject對象上安裝(或稱為注冊)事件過濾器(見后文)蚀乔。對象一旦使用installEventFilter()注冊烁竭,傳遞給目標(biāo)對象的所有事件都會先傳遞給這個監(jiān)視對象的eventFilter()函數(shù)〖酰或同一對象上安裝了多個事件處理器派撕,則按照安裝的逆序依次激活這些事件處理器婉弹。④、在QApplication上安裝(或稱為注冊)事件過濾器终吼。該方式與重新實現(xiàn)notify函數(shù)一樣強大镀赌。一旦在QApplication對象(程序中只有一個這種類型的對象)上注冊了事件過濾器,則程序中每個對象的每個事件都會在發(fā)送到其他事件過濾器之前际跪,發(fā)送到這個eventFilter()函數(shù)商佛。該方式對于調(diào)試非常有用。
⑤姆打、子類化QApplication良姆,并重新實現(xiàn)QCoreApplication::notify()函數(shù),由事件傳遞過程可知幔戏,該函數(shù)提供了對事件的完全控制玛追,因此功能非常強大,所以重寫該函數(shù)會很復(fù)雜闲延,也因此此方法很少被使用痊剖。這是唯一能在事件過濾器之前捕獲到事件的方式。
⑥垒玲、注意:exec()邢笙、notify()和event()函數(shù)都不會處理事件,他們只是根據(jù)事件的類型進行事件的傳遞(或分發(fā))侍匙。
示例:事件處理的方式
#include <QApplication>
#include<QWidget>
#include<QObject>
#include <iostream>
using namespace std;
class A:public QWidget{public:
bool event(QEvent* e); //事件處理方式1:重寫虛函數(shù)QObject::event()
void mousePressEvent(QMouseEvent* e); };//事件處理方式2:重寫QWidget類中的此虛函數(shù)
bool A::event(QEvent* e){ static int i=0; //i用于計數(shù)
cout<<"E"<<i++<<endl; //驗證該函數(shù)能在事件到達目標(biāo)對象之前被捕獲
if(e->type()==QEvent::KeyPress) //判斷是否是鍵盤按下事件氮惯。
cout<<"keyDwon"<<endl;
return QWidget::event(e); /*此處應(yīng)調(diào)用父類的event函數(shù)以對未處理的事件進行處理,若此處不調(diào)用父類的event想暗,則本例mousePressEvent處理函數(shù)中的內(nèi)容將不會被執(zhí)行妇汗。讀者可使此處返回1或者0進行驗證。*/
}
void A::mousePressEvent(QMouseEvent* e){//處理鼠標(biāo)按下事件说莫,這是Qt中最簡單的事件處理方式杨箭。
cout<<"mouseDwon"<<endl;
}
int main(int argc, char *argv[]){
QApplication a(argc,argv); //在Qt中QApplication類型的對象只能有一個A ma; //創(chuàng)建一個部件
ma.resize(333,222); //設(shè)置部件的大小
ma.show(); //顯示創(chuàng)建的部件
a.exec(); //在此處進入事件主循環(huán)。
return 0; }
程序運行結(jié)果及說明(見右圖) 只要窗口ma接收到事件就會輸出Exx储狭,可見event函數(shù)捕獲事件的
范圍及時機互婿,當(dāng)按下鍵盤上的任一鍵時會輸出"keyDown",當(dāng)按下
任一鼠標(biāo)鍵時辽狈,會輸出mouseDown
示例:重寫notify函數(shù)
#include <QApplication>
#include<QWidget>
#include<QObject>
#include <iostream>
using namespace std;
class A:public QWidget{public: bool event(QEvent* e); }; //重寫虛函數(shù)QObject::event()
class AA:public QApplication{public:
AA(int i,char *p[]):QApplication(i,p){}
bool notify(QObject *o,QEvent *e);}; //重寫虛函數(shù)notify
bool A::event(QEvent* e){ cout<<"E"<<endl; //用于驗證該函數(shù)是否被執(zhí)行
return QWidget::event(e); } //應(yīng)調(diào)用父類的event函數(shù)處理未處理的事件
bool AA::notify(QObject *o,QEvent *e){
static int i=0;
cout<<"N"<<i++<<endl;
if(o->objectName()=="ma"&&e->type()==QEvent::KeyPress) //若對象為ma且事件為鍵盤按下事件
cout<<"keyDwon"<<endl;
return QApplication::notify(o,e); } /*應(yīng)調(diào)用父類的notify函數(shù)慈参,否則本示例的event函數(shù)不會被執(zhí)行,同時無法關(guān)閉窗口*/
int main(int argc, char *argv[]){
AA aa(argc,argv);//在Qt中QApplication或其子類型的對象只能有一個
A ma; //創(chuàng)建一個部件
ma.setObjectName("ma"); //設(shè)置對象名刮萌,方便在notify函數(shù)中調(diào)用
ma.resize(333,222); //設(shè)置部件的大小
ma.show(); //顯示創(chuàng)建的部件
aa.exec();//在此處進入事件主循環(huán)驮配。
return 0; }
程序運行結(jié)果及說明(見右圖) 本示例可以看到notify()函數(shù)和event()函數(shù)的執(zhí)行順序,及notify函數(shù)和event
函數(shù)捕獲的事件的范圍和時機,當(dāng)用戶按下鍵盤上的鍵時會輸出keyDown
示例:事件傳遞
#include <QApplication>
#include<QWidget>
#include<QPushButton>
#include<QObject>
#include <iostream>
using namespace std;
class A:public QWidget{public: bool event(QEvent* e); };//子類化QWidget
class C:public QPushButton{public: bool event(QEvent* e);}; //子類化標(biāo)準(zhǔn)按鈕QPushButton
bool A::event(QEvent* e){
if(e->type()==QEvent::KeyPress) //判斷是否是鍵盤按下事件壮锻。
{cout<<objectName().toStdString()<<"=keyDwon"<<endl; }
if(e->type()==QEvent::MouseButtonPress) //判斷是否是鼠標(biāo)按下事件琐旁。
{cout<<objectName().toStdString()<<"=mouseDwon"<<endl; }
if(e->type()==QEvent::MouseButtonRelease) //判斷是否是鼠標(biāo)釋放事件。
{cout<<objectName().toStdString()<<"=mouseRelease"<<endl; }
return QWidget::event(e); }
bool C::event(QEvent* e){
if(e->type()==QEvent::KeyPress) //判斷是否是鍵盤按下事件猜绣。
{cout<<objectName().toStdString()<<"=keyDwon"<<endl;
return 0; }//將事件傳遞給父對象處理
if(e->type()==QEvent::MouseButtonPress) //判斷是否是鼠標(biāo)按下事件灰殴。
{cout<<objectName().toStdString()<<"=mouseDwon"<<endl;
return 1; } //事件不傳遞
return QWidget::event(e);}
int main(int argc, char *argv[]){
QApplication a(argc,argv); //在Qt中QApplication類型的對象只能有一個
A ma; C *mc=new C();
mc->setParent(&ma); /*設(shè)置父對象為ma,若mc未處理的對象會傳遞給此處設(shè)置的父對象ma處理掰邢,注意事件傳遞是在對象之間的父子關(guān)系验懊,而不是類之間的父子關(guān)系。*/
mc->setText("AAA");
mc->move(22,22); //設(shè)置mc相對于ma的位置
ma.setObjectName("ma"); //設(shè)置對象名
mc->setObjectName("mc");
ma.resize(333,222); //設(shè)置部件的大小
ma.show();
a.exec(); //在此處進入事件主循環(huán)尸变。
return 0; }
程序運行結(jié)果及說明(見圖)
1义图、當(dāng)在按鈕上按下鼠標(biāo)鍵時,調(diào)用C::event()
函數(shù)輸出mc=mouseDown召烂,
2碱工、此時該函數(shù)返回1,表示事件不再傳遞
3奏夫、但是該函數(shù)并未處理鼠標(biāo)釋放事件怕篷,因此
調(diào)用QWidget::event()對該事件作默認(rèn)處理,
因默認(rèn)未處理該事件酗昼,因此調(diào)用mc的父
對象ma處理該事件廊谓,此時調(diào)用A::event()
函數(shù)輸出ma=mouseRelease,至此鼠標(biāo)事件處理結(jié)束麻削。
4蒸痹、當(dāng)按鈕獲得焦點,按下鍵盤上的按鍵時的處理方式與鼠標(biāo)事件類似呛哟,只是C::event()函數(shù)直接把鍵按下事件傳遞給了父對象ma處理叠荠。