1. 程式人生 > >Qt中exec()

Qt中exec()

QDialog的顯示有兩個函式show()和exec()。他們的區別在參考文件上的解釋如下:

show():
顯示一個非模式對話方塊。控制權即刻返回給呼叫函式。
彈出視窗是否模式對話方塊,取決於modal屬性的值。
(原文:Shows the dialog as a modeless dialog. Control returns immediately to the calling code. 
The dialog will be modal or modeless according to the value of the modal property. )

exec():
顯示一個模式對話方塊,並且鎖住程式直到使用者關閉該對話方塊為止。函式返回一個DialogCode結果。
在對話方塊彈出期間,使用者不可以切換同程式下的其它視窗,直到該對話方塊被關閉。
(原文:Shows the dialog as a modal dialog, blocking until the user closes it. The function returns a DialogCode result. 
Users cannot interact with any other window in the same application until they close the dialog. )

先簡單說一下我對模式和非模式對話方塊的一點點理解:
模式對話方塊,就是在彈出視窗的時候,整個程式就被鎖定了,處於等待狀態,直到對話方塊被關閉。這時往往是需要對話方塊的返回值進行下面的操作。如:確認視窗(選擇“是”或“否”)。
非模式對話方塊,在呼叫彈出視窗之後,呼叫即刻返回,繼續下面的操作。這裡只是一個呼叫指令的發出,不等待也不做任何處理。如:查詢框。

從字面上看,show()即可以顯示非模式也可以顯示模式對話方塊(設定modal值)。當modal=true的時候是否跟exec()就一樣了呢?
經過測試,還是有區別的。
使用show(),雖然在對話方塊彈出的時候,程式的其它操作(按鈕、視窗切換等)都失效了;但是程式仍然在呼叫對話方塊之後,馬上返回繼續執行後面的程式碼。這樣,你就不會得到視窗的返回值。以這個來看,show()只能算是“半模式”。
而使用exec(),在呼叫之後,程式就被鎖定在原地。等待視窗的關閉。

實際上,QDialog的show()函式來自其父類QWidget。而exec()則是自己的。

我最近特別喜歡繼承QWidget類來做彈出視窗,它的好處就是方便、靈活,既可以做為彈出視窗用也可以嵌入另外的頁面裡(QDialog是不行的)。但問題是,QWidget沒有exec()函式。所以想彈出這樣模擬出來的模式對話方塊就不行了。
也查過一些資料,有說用while(true)迴圈的,有說用接收事件的,但都感覺不太好。

所以,目前還沒有找到比較好的解決辦法。

續:
上面遺留的問題經過檢視QTE原始碼(沒有找到QT的)總算解決了。
我查看了QDialog類的exec()函式。發現裡面同樣呼叫了show(),只是在後面又呼叫了一句qApp->enter_loop()巢狀一個新的訊息迴圈來阻塞當前事件的執行;然後在hide()函式裡呼叫了qApp->exit_loop()來退出當前的訊息迴圈並繼續執行原事件。

到QT幫助裡查了一下這兩個函式,解釋如下:
enter_loop():
這個函式被廢棄了。它仍然被保留下來是為了使舊的程式碼能夠繼續工作。我們強烈建議不要在新寫的程式碼裡使用它。這個函式直接介入主訊息迴圈裡(遞迴地)。除非你真的知道你在做什麼,否則不要呼叫它。建議使用:QApplication::eventLoop()->enterLoop()。
exit_loop():
同樣被廢棄了。建議使用:QApplication::eventLoop()->exitLoop()。
提醒:這兩個操作都會進入主訊息迴圈,慎用!

那就按照建議的做吧,反正效果都是一樣的。修改原來的自定義視窗,在裡面增加了兩個函式,分別實現開啟和關閉視窗,封裝了eventLoop()的呼叫。程式碼如下:
#include <qapplication.h>
#include <qeventloop.h>
/*模擬QDialog::exec(),以模式對話方塊方式顯示*/
void MyWidget::doExec()
{
    this->show();
    in_loop = TRUE;
    //qApp->enter_loop();
    QApplication::eventLoop()->enterLoop();
}
/*關閉視窗*/
void MyWidget::doClose()
{
    if ( in_loop ) {
in_loop = FALSE;
//qApp->exit_loop();
        QApplication::eventLoop()->exitLoop();
    }
    this->close();
}