一、簡述
簡單一點講就是在彈出模态對話框時,除了該對話框整個應用程式視窗都無法接受使用者響應,處于等待狀态,直到模态對話框被關閉。這時一般需要點選對話框中的确定或者取消等按鈕關閉該對話框,程式得到對話框的傳回值(即點選了确定還是取消),并根據傳回值進行相應的操作,之後将操作權傳回給使用者。這個時候使用者可以點選或者拖動程式其他視窗。 說白了就相當于阻塞同一應用程式中其它可視視窗的輸入的對話框,使用者必須完成這個對話框中的互動操作并且關閉了它之後才能通路應用程式中的其它視窗。 其實模态對話框的作用就是得到使用者選擇的結果,根據結果來進行下面的操作。
又叫做無模式對話框,即彈出非模态對話框時,使用者仍然可以對其他視窗進行操作,不會因為這個對話框未關閉就不能操作其他視窗。
半模态對話框差別于模态與非模态對話框,或者說是介于兩者之間,也就是說半模态對話框會阻塞視窗的響應,但是不會影響後續代碼的執行。
QWidget提供了setWindowModality()方法設定視窗半模态or非模态;
Qt::NonModal The window is not modal and does not block input to other windows. 非模态對話框 Qt::WindowModal The window is modal to a single window hierarchy and blocks input to its parent window, all grandparent windows, and all siblings of its parent and grandparent windows. 視窗級模态對話框,即隻會阻塞父視窗、父視窗的父視窗及兄弟視窗。(半模态對話框) Qt::ApplicationModal The window is modal to the application and blocks input to all windows. 應用程式級模态對話框,即會阻塞整個應用程式的所有視窗。(半模态對話框)

Qt助手中的介紹很簡單,就是顯示視窗以及他的子視窗。
setWindowModality()方法可以設定視窗是否是模态視窗,從上圖中我們可以看到Qt::WindowModality的預設值為Qt::NonModal,也就是非模态視窗。 是以,如果沒有設定Qt::WindowModality屬性值,我們每次用show()方法顯示出的視窗都是非模态視窗。
我們知道QWidget是大部分 控件的父類,也就是說QWidget是控件的始祖類,處于最上層,而QDialog也繼承自QWidget。
在Qt助手中我們發現在QDialog除了繼承QWidget的show()方法外,多了兩個方法用來顯示視窗,分别是open() 和 exec()方法。
可以看到使用open()方法顯示出的對話框為視窗級模态對話框,并且立即傳回,這樣open()方法後的代碼将會繼續執行。open()方法就相當于如下代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
可以看到使用exec()方法顯示出的對話框為模态對話框,同時會阻塞之前視窗的響應直到使用者關閉這個對話框,并且傳回DialogCode(包括Accepted和Rejected兩個值)結果。 看紅色劃線部分,如果沒有設定Qt::WindowModality屬性值,使用exec()方法顯示出的對話框預設為應用程式級模态對話框。所有使用exec()方法顯示對話框在視窗關閉前會阻塞整個程式所有視窗的響應。同時調用exec()方法後的代碼也不會執行直到對話框關閉才會繼續執行。在關閉對話框後exec()方法會傳回Accepted或者Rejected,一般程式根據傳回不同的結果進行相應的操作。
顯然是不可以的,這裡調用完show()方法後立即傳回了,并不知道使用者選擇了Accepted還是Rejected。而exec()會阻塞後面代碼的執行,直到對話框關閉,傳回結果。
16
17
18
19
模式對話框有自己的事件循環。按照我的了解,實際上 exec() 方法是先設定modal屬性為Qt::ApplicationModal,然後調用 show() 顯示對話框,最後啟用事件循環來阻止exec() 方法的結束。直到視窗關閉,得到傳回結果(DialogCode),退出事件循環,最後exec()方法調用結束,exec()方法後的代碼将繼續執行。
QDialog的exec() 方法的實作 整體上就是按照上方所講的思路進行實作的。關于exec() 方法傳回的結果可以通過對界面上的按鈕綁定相應的槽,比如确定按鈕綁定accept()槽,取消按鈕綁定reject()槽,這樣在點選确定或者取消按鈕時exec()方法就會傳回Accepted 或者 Rejected,可以根據傳回的值做出相應的操作。
下面就直接上代碼實作exec()方法。
二、代碼之路
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
三、 Qt事件循環的一些了解(exec、eventloop)
1、事件循環一般用exec()函數開啟。QApplicaion::exec()、QMessageBox::exec()都是事件循環。其中前者又被稱為主事件循環。 事件循環首先是一個無限“循環”,程式在exec()裡面無限循環,能讓跟在exec()後面的代碼得不到運作機會,直至程式從exec()跳出。從exec()跳出時,事件循環即被終止。QEventLoop::quit()能夠終止事件循環。 其次,之是以被稱為“事件”循環,是因為它能接收事件,并處理之。當事件太多而不能馬上處理完的時候,待處理事件被放在一個“隊列”裡,稱為“事件循環隊列”。當事件循環處理完一個事件後,就從“事件循環隊列”中取出下一個事件處理之。當事件循環隊列為空的時候,它和一個啥事也不做的永真循環有點類似,但是和永真循環不同的是,事件循環不會大量占用CPU資源。 事件循環的本質就是以隊列的方式再次配置設定線程時間片。
2、事件循環是可以嵌套的,一層套一層,子層的事件循環執行exec()的時候,父層事件循環就處于中斷狀态;當子層事件循環跳出exec()後,父層事件循環才能繼續循環下去。 另外,子層事件循環具有父層事件循環的幾乎所有功能。Qt會把事件送到目前生效的那個事件循環隊列中去,其中包括Gui的各種事件。是以使用者在主線程中執行各種exec()(如QMessageBox::exec(),QEventLoop::exec())的時候,雖然這些exec()打斷了main()中的QApplication::exec(),但是Gui界面仍然能夠正常響應。
3、如果某個子事件循環仍然有效,但其父循環被強制跳出,此時父循環不會立即執行跳出,而是等待子事件循環跳出後,父循環才會跳出。
摘自 http://blog.chinaunix.net/uid-27685749-id-3847998.html。
尾
關于模态、非模态、半模态視窗的定義也很好了解,其實也就是跟使用者操作過程中進行互動的問題。
同時我們也通過簡單的代碼來模拟出了QDialog的exec()方法。有問題直接找Qt助手,在這裡基本上便能找到我們需要的答案。是以說遇到一些問題不一定非要立馬到網上找各種資料或者到學習群中詢問問題的解決辦法,多看看幫助問題還是很有好處的。
http://www.kuqin.com/qtdocument/classes.html , 這個網址裡提供了Qt文檔的中文翻譯 ,有需要的小夥伴可以看看。