1. 程式人生 > 其它 >Qt對話方塊與視窗的關閉和隱藏

Qt對話方塊與視窗的關閉和隱藏

技術標籤:Qt

1、為方便講解,本文對以下概念作一區別
 刪除:是指視窗被銷燬,也就是說視窗不存在了。比如視窗使用new建立的,則表示視窗被delete了,被銷燬的視窗不能被再次使用,否則會發生記憶體錯誤。
 隱藏:是指視窗不可見,但視窗並未被銷燬,使用show()等函式,可以讓該視窗再次可見。
 關閉:是指視窗不可見,但視窗有可能是被刪除了,也有可能是被隱藏了,這要視情況而定。
 視窗被刪除時,會同時刪除其子物件,而隱藏則不會。
2、關閉視窗與終止程式
一個(應用)程式通常擁有多個視窗,關閉(或刪除)一個視窗,並不一定會使程式終止,Qt中關閉視窗使用QWidget::close()槽函式,終止程式使用的是QCoreApplication::quit()靜態槽函式或QCoreApplication::exit()靜態函式

3、與關閉部件和終止程式有關的屬性
注:以下屬性其實是Qt::WidgetAttribute列舉的成員,可使用QWidget::setAttribute()函式進行設定和清除。
①、Qt::WA_DeleteOnClose屬性:表示當部件接受到QCloseEvent事件時,是否讓Qt刪除部件。若該屬性為true,則刪除部件,否則部件只是隱藏。注意:設定了該屬性的部件需要使用new建立,否則會產生記憶體錯誤。
②、Qt::WA_QuitOnClose屬性:表示當擁有該屬性的最後一個部件接受到QCloseEvent事件時,讓Qt終止應用程式。預設情況下,所有Qt::Window型別的部件都具有該屬性。
4、QWidget類中與關閉視窗有關的函式如下:

①、bool QWidget::close();    //槽
	關閉(即刪除或隱藏)部件,若部件關閉成功,則返回true,否則返回false。
   ②、virtual void QWidget::closeEvent(QCloseEvent* e);     //虛擬的,受保護的
	這是QCloseEvent事件的處理函式,預設情況下,該函式接受QCloseEvent事件。該函式通常被重新實現,以確定使用者是否需要關閉視窗。

5、下面為用於終止程式的函式原型

   ①、static void QCoreApplication::quit();       //靜態的,槽
	退出程式,並返回程式碼0(成功),此函式與呼叫QCoreApplication::exit(0)等同。該槽函式通常與訊號連線使用,比如
              QPushButton *p = new QPushButton("quit"); 
              QObject::connect( p ,& QPushButton::clicked, &app, &QCoreApplication::quit);
   ②、static void QCoreApplication::exit( int returnCode = 0);   //靜態的
	使用returnCode退出程式,通常returnCode為0,表示成功,任何非零值都表示錯誤。該函式會使程式離開主事件迴圈,並返回到呼叫QCoreApplication::exec()處,exec()函式會返回returnCode的值,若事件迴圈未執行,則該函式什麼都不做。

6、close()函式的執行過程如下:
①、首先,向該部件傳送QCloseEvent事件(不管部件是否可見)
②、然後判斷部件是否接受QCloseEvent事件
 若部件接受該事件(預設值),則繼續下一步操作。
 若部件忽略該事件,則取消關閉操作,結束後續的操作。其中最重要的是,不會對Qt::WA_DeleteOnClose屬性進行判斷,此時該屬性不起作用。
③、接著判斷部件是否被隱藏了,若未被隱藏,則隱藏,若已被隱藏,則什麼也不做。然後繼續下一步。
④、再接著判斷部件的Qt::_WA_QuitOnClose屬性,當具有Qt::WA_QuitOnClose屬性的最後一個可見主視窗(即沒有父視窗的視窗)被關閉時,會發送QApplication::lastWindowClosed()訊號。
⑤、最後判斷部件的Qt::WA_DeleteOnClose屬性,若該屬性為true,則刪除該部件,否則什麼也不做。至此整個過程結束。
⑥、總結:從以上過程可見,若部件接受QCloseEvent事件,且設定了Qt::WA_DeleteOnClose屬性,則會刪除該部件,若未設定該屬性則只會隱藏該部件。若部件忽略QCloseEvent事件,則直接取消對該部件的關閉操作,該部件既不會被隱藏也不會被刪除。由此可見對QCloseEvent事件接受還是忽略決定著對視窗關閉的處理方式,同時對該事件的處理方式與其他事件也是不同的,QCloseEvent::ignore()表示取消關閉操作(也就是說QCloseEvent事件不會被傳遞給父物件),而QCloseEvent::accept()則表示讓Qt繼續關閉操作。
7、QCloseEvent事件的傳送時機如下:
從視窗選單選擇“關閉”,單擊標題欄上的X按鈕,呼叫QWidget::close()函式時。
8、可使用以下方式終止程式
 直接呼叫quit()或exit()函式
 最後一個具有Qt::WA_QuitOnClose屬性的主視窗關閉時,終止程式,若沒有這樣的主視窗,即使所有的視窗都關閉了程式也不會結束。
9、對話方塊的關閉過程
對話方塊的reject()、accept()、done()函式,與QWidget::close()函式相同,唯一的區別是對話方塊不會發送QCloseEvent事件,因此不能通過QCloseEvent事件來阻止對話方塊的關閉。注意:此規則僅限於上述3個函式,比如點選對話方塊視窗的X按鈕或右擊標題欄選擇“關閉”時,仍會發送QCloseEvent事件。若使用者在對話方塊中按下Esc鍵,會呼叫QDialog::reject()。為了修改對話方塊的關閉行為,可以重新實現accept()、reject()或done()函式。
10、刪除QObject物件時,會發送destroyed()訊號,該訊號原型如下:

      void QObject::destroyed(QObject* obj = Q_NULLPTR);    //訊號。
	當物件obj被銷燬之前傳送,且不能被阻止,傳送該訊號後,物件obj的孩子都會被立即銷燬。該訊號需配合Qt::WA_DeleteOnClose屬性使用,當Qt::WA_DeleteOnClose屬性為true的物件被刪除時,會發送該訊號。直接終止程式的執行,是不會發送該訊號的。

示例6.6:關閉一個視窗就結束程式(理解Qt::WA_QuitOnClose屬性)

#include<QtWidgets>
int main(int argc, char *argv[]){    QApplication aa(argc,argv);
QWidget w,w1;    QDialog pd;    QDialog pd1;    QDialog pd2;
    QPushButton *pb=new QPushButton("AAA",&w); w.setWindowTitle("w");    w1.setWindowTitle("w1");
//除視窗w1外,視窗w和對話方塊的Qt::WA_QuitOnCLose屬性都設定為0。
    w.setAttribute(Qt::WA_QuitOnClose,0);    	pd.setAttribute(Qt::WA_QuitOnClose,0);
    pd1.setAttribute(Qt::WA_QuitOnClose,0);    	pd2.setAttribute(Qt::WA_QuitOnClose,0);
    QObject::connect(pb,&QPushButton::clicked,&pd,&QDialog::show);//點選按鈕pb,彈出3個對話方塊。
    QObject::connect(pb,&QPushButton::clicked,&pd1,&QDialog::show);
    QObject::connect(pb,&QPushButton::clicked,&pd2,&QDialog::show);
    w.resize(300,200);    w.show();   w1.resize(300,200);    w1.show();  return aa.exec();}

執行結果及說明見圖6-8
在這裡插入圖片描述

示例6.7:理解Qt::WA_DeleteOnClose屬性和destroyed訊號

//m.h檔案的內容
#ifndef M_H
#define M_H
#include<QtWidgets>
#include<QDebug>
class A:public QObject{Q_OBJECT
public slots:	void f(QObject *p){	qDebug()<<"del="<<p->objectName();} };//輸出被刪除物件的名稱
#endif // M_H

//m.cpp檔案的內容
#include "m.h"
int main(int argc, char *argv[]){    QApplication aa(argc,argv);
    QWidget w;    QDialog pd;    QDialog *pd1=new QDialog;    QDialog *pd2=new QDialog;
    QPushButton *pb=new QPushButton("show",&w);
    QPushButton *pb1=new QPushButton("quit",&w); pb1->move(77,0);
w.setWindowTitle("w");    		pd.setWindowTitle("pd");		//設定視窗標題
pd1->setWindowTitle("pd1");    	pd2->setWindowTitle("pd2");
//設定Qt::WA_DeleteOnClose屬性
    pd.setAttribute(Qt::WA_DeleteOnClose,1);	pd1->setAttribute(Qt::WA_DeleteOnClose,1);
w.setObjectName("W");    	pd.setObjectName("pd");   //設定物件名
 	pd1->setObjectName("pd1");	pd2->setObjectName("pd2");
A ma;
    QObject::connect(pb,&QPushButton::clicked,&pd,&QDialog::show);//單擊按鈕pb彈出3個對話方塊
    QObject::connect(pb,&QPushButton::clicked,pd1,&QDialog::show);
QObject::connect(pb,&QPushButton::clicked,pd2,&QDialog::show);
//單擊按鈕pb1直接終止程式
QObject::connect(pb1,&QPushButton::clicked,&aa,&QApplication::quit);
QObject::connect(&pd,&QDialog::destroyed,&ma,&A::f);//把destroyed訊號連線到槽f
QObject::connect(pd1,&QDialog::destroyed,&ma,&A::f);
    QObject::connect(pd2,&QDialog::destroyed,&ma,&A::f);
w.resize(300,200);    w.show();    return aa.exec();  }

執行結果見圖6-9,測試步驟如下
在這裡插入圖片描述

1)、點選show彈出對話方塊之後,若關閉pd(點選右上角的X按鈕),則程式可能會崩潰(因為pd未使用new建立),關閉pd1則會銷燬pd1,程式此時輸出"del=pd1",關閉pd2,則只會隱藏pd2,因為各視窗之間不存在父子關係,因此本例需點選quit才能正常結束程式(因為最終在關閉視窗pd時,可能會崩潰)。
2)、重新執行程式,點選show按鈕,然後關閉pd1和pd2(注意,不要關閉pd),然後再次點選show按鈕,對話方塊pd2會被再次顯示,但pd1未被顯示(因為被銷燬了)。
3)、再次重新執行程式,點選show,彈出3個對話方塊,然後點選quit直接退出程式,程式無任何輸出,可見,訊號destroyed未被髮射。

示例6.8:關閉視窗時詢問使用者(QCloseEvent事件的應用)
#ifndef M_H
#define M_H
#include<QtWidgets>
class B:public QWidget{    Q_OBJECT
public:    QDialog *pd1;    QPushButton *pb,*pb1,*pb2,*pb3;
    B(QWidget* p=0):QWidget(p){
    		pd1=new QDialog(this);    pd1->setWindowTitle("PD1");		pd1->resize(222,111);
    		pb=new QPushButton("close",this); pb1=new QPushButton("exit",this); pb1->move(88,0);
    		pb2=new QPushButton("Yes",pd1);   pb3=new QPushButton("No",pd1); 	pb3->move(88,0);
    		connect(pb,&QPushButton::clicked,this,&B::f);
    		connect(pb1,&QPushButton::clicked,this,&B::f1);
    		connect(pb2,&QPushButton::clicked,pd1,&QDialog::accept);
    		connect(pb3,&QPushButton::clicked,pd1,&QDialog::reject);  }
void f(){		close();		}   		//呼叫close()函式關閉視窗,該函式會發送QCloseEvent事件。
void f1(){    QApplication::quit();} 	//注意:quit()函式不會產生QCloseEvent事件。
void closeEvent(QCloseEvent* e){   	//重新實現closeEvent函式,以處理QCloseEvent事件
        int i = pd1->exec();        	//彈出對話方塊pd1
//根據使用者在對話方塊中選擇的按鈕,決定怎樣關閉視窗
        if(i==1)e->accept();       	//接受QCloseEvent事件,繼續關閉視窗。
        if(i==0)e->ignore();  }};  	//忽略QCloseEvent事件,阻止關閉視窗。
#endif // M_H

//m.cpp檔案的內容
#include "m.h"
int main(int argc, char *argv[]){    QApplication aa(argc,argv);
    B w;    w.resize(300,200);    w.show();    return aa.exec();  }

執行結果(見圖6-10)及說明
在這裡插入圖片描述
當用戶點選視窗qt右上角的X按鈕或點選close按
鈕時,會彈出對話方塊PD1,以確定使用者是否需要關閉窗
口,當用戶點選PD1中的Yes按鈕時,會關閉視窗qt,
若點點No按鈕,則qt不會被關閉。若使用者直接點選qt
中的exit按鈕,則程式直接終止,不會彈出對話方塊。

示例6.9:對話方塊與QCloseEvent事件
//m.h檔案的內容
#ifndef M_H
#define M_H
#include<QtWidgets>
#include <iostream>
using namespace std;
class D:public QDialog{    Q_OBJECT
public: 	D(QWidget* p=0):QDialog(p){  }
    void closeEvent(QCloseEvent* e){   cout<<"D"<<endl;   setResult(2);    }		};
class B:public QWidget{    Q_OBJECT
public:    D *pd1;    QPushButton *pb,*pb1,*pb2,*pb3,*pb4,*pb5;
    B(QWidget* p=0):QWidget(p){
    		pd1=new D(this);    pd1->setWindowTitle("PD1");    pd1->resize(222,111);
pb=new QPushButton("exec",this); pb1=new QPushButton("show",this);  pb1->move(88,0);
    		pb2=new QPushButton("Yes",pd1);  pb3=new QPushButton("No",pd1); 	   pb3->move(88,0);
    		pb4=new QPushButton("result",pd1);	pb4->move(0,33);
    		connect(pb,&QPushButton::clicked,this,&B::f);
    		connect(pb1,&QPushButton::clicked,this,&B::f1);
    		connect(pb2,&QPushButton::clicked,pd1,&QDialog::accept);
    		connect(pb3,&QPushButton::clicked,pd1,&QDialog::reject);
    		connect(pb4,&QPushButton::clicked,this,&B::f2);	}
void f(){    int i=pd1->exec();    cout<<"exec="<<i<<endl;	}
void f1(){  pd1->show();   cout<<"show="<<pd1->result()<<endl;	}
void f2(){    cout<<"result="<<pd1->result()<<endl;	}				};
#endif // M_H

//m.cpp檔案的內容
#include "m.h"
int main(int argc, char *argv[]){    QApplication aa(argc,argv);
    B w;  w.resize(300,200);  w.show();  return aa.exec();}

執行結果(見圖6-11)及說明
在這裡插入圖片描述

1)、驗證QCloseEvent事件未觸發:點選exec以使用exec()函式顯示對話方塊PD1,點選Yes按鈕,程式輸出exec=1,可見QDialog::accept()槽函式未觸發QCloseEvent事件。
2)、驗證exec()函式重置結果程式碼為0:再次點選exec按鈕,顯示對話方塊PD1,然後點選result按鈕,程式輸出result=0,可見exec()函式把對話方塊的結果程式碼重置為0了。
3)、驗證QCloseEvent事件被觸發:然後點選PD1右上角的X關閉對話方塊,此時程式輸出D,可見此時觸發了QCloseEvent事件,此時PD1的結果程式碼為2。
4)、驗證show()不會重置結果程式碼為0:然後點選show按鈕,以使用show()函式顯示對話方塊PD1,此時輸出show=2(注意show()函式呼叫後會立即反回); 然後點選result,輸出result=2;可見PD1的結果程式碼此時為2。然後點選yes按鈕關閉對話方塊,再點選show按鈕顯示對話方塊,此時輸出show=1;然後點選result,輸出result=1;可見show()函式未把結果程式碼重置為0。