Qt 之 模態、非模態、半模態窗口的介紹及 實現QDialog的exec()方法


一、簡述

先簡單介紹一下模態與非模態對話框。

模態對話框

簡單一點講就是在彈出模態對話框時,除了該對話框整個應用程序窗口都無法接受用戶響應,處於等待狀態,直到模態對話框被關閉。這時一般需要點擊對話框中的確定或者取消等按鈕關閉該對話框,程序得到對話框的返回值(即點擊了確定還是取消),並根據返回值進行相應的操作,之后將操作權返回給用戶。這個時候用戶可以點擊或者拖動程序其他窗口。

說白了就相當於阻塞同一應用程序中其它可視窗口的輸入的對話框,用戶必須完成這個對話框中的交互操作並且關閉了它之后才能訪問應用程序中的其它窗口。

其實模態對話框的作用就是得到用戶選擇的結果,根據結果來進行下面的操作。

非模態對話框

又叫做無模式對話框,即彈出非模態對話框時,用戶仍然可以對其他窗口進行操作,不會因為這個對話框未關閉就不能操作其他窗口。

半模態對話框

半模態對話框區別於模態與非模態對話框,或者說是介於兩者之間,也就是說半模態對話框會阻塞窗口的響應,但是不會影響后續代碼的執行。


Qt中的模態&非模態&半模態

QWidget

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助手中的show()方法——非模態對話框

這里寫圖片描述

Qt助手中的介紹很簡單,就是顯示窗口以及他的子窗口。


Qt助手中的setWindowModality()方法

這里寫圖片描述

setWindowModality()方法可以設置窗口是否是模態窗口,從上圖中我們可以看到Qt::WindowModality的默認值為Qt::NonModal,也就是非模態窗口。

所以,如果沒有設置Qt::WindowModality屬性值,我們每次用show()方法顯示出的窗口都是非模態窗口。


QDialog

我們知道QWidget是大部分 控件的父類,也就是說QWidget是控件的始祖類,處於最上層,而QDialog也繼承自QWidget。

在Qt助手中我們發現在QDialog除了繼承QWidget的show()方法外,多了兩個方法用來顯示窗口,分別是open() 和 exec()方法。


Qt助手中的open()方法——半模態對話框

這里寫圖片描述

可以看到使用open()方法顯示出的對話框為窗口級模態對話框,並且立即返回,這樣open()方法后的代碼將會繼續執行。open()方法就相當於如下代碼。

void showWindow() { QWidget* pWindow = new QWidget(); QWidget* childWindow = new QWidget(pWindow); childWindow->setWindowModality(Qt::WindowModal); childWindow->show(); // 上面三行代碼相當於下面兩行代碼; //QDialog* childDialog = new QDialog(pWindow); //childDialog->open(); // 下面的代碼可以執行; qDebug() << "這是一個半模態窗口"; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Qt助手中的exec()方法——模態對話框

這里寫圖片描述

可以看到使用exec()方法顯示出的對話框為模態對話框,同時會阻塞之前窗口的響應直到用戶關閉這個對話框,並且返回DialogCode(包括Accepted和Rejected兩個值)結果。

看紅色划線部分,如果沒有設置Qt::WindowModality屬性值,使用exec()方法顯示出的對話框默認為應用程序級模態對話框。所有使用exec()方法顯示對話框在窗口關閉前會阻塞整個程序所有窗口的響應。同時調用exec()方法后的代碼也不會執行直到對話框關閉才會繼續執行。在關閉對話框后exec()方法會返回Accepted或者Rejected,一般程序根據返回不同的結果進行相應的操作。


那我們是否可以用以下代碼來代替QDialog中的exec()方法呢?

void showModalWindow() { QWidget* pWindow = new QWidget(); QWidget* childWindow = new QWidget(pWindow); childWindow->setWindowModality(Qt::ApplicationModal); childWindow->show(); // 下面的代碼可以執行; qDebug() << "這是一個模態窗口嗎?"; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

顯然是不可以的,這里調用完show()方法后立即返回了,並不知道用戶選擇了Accepted還是Rejected。而exec()會阻塞后面代碼的執行,直到對話框關閉,返回結果。


下面用QDialog的exec()方法來顯示一個模態對話框。

void showModalWindow() { QWidget* pWindow = new QWidget(); QDialog* childDialog = new QDialog(pWindow); int resutl = childDialog ->exec(); if (resutl == QDialog::Accepted) { qDebug() << "You Choose Ok"; } else { qDebug() << "You Choose Cancel"; } // 在關閉對話框之后,下面的代碼才可以執行; qDebug() << "這是一個模態窗口"; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

模式對話框有自己的事件循環。按照我的理解,實際上 exec() 方法是先設置modal屬性為Qt::ApplicationModal,然后調用 show() 顯示對話框,最后啟用事件循環來阻止exec() 方法的結束。直到窗口關閉,得到返回結果(DialogCode),退出事件循環,最后exec()方法調用結束,exec()方法后的代碼將繼續執行。

QDialog的exec() 方法的實現 整體上就是按照上方所講的思路進行實現的。關於exec() 方法返回的結果可以通過對界面上的按鈕綁定相應的槽,比如確定按鈕綁定accept()槽,取消按鈕綁定reject()槽,這樣在點擊確定或者取消按鈕時exec()方法就會返回Accepted 或者 Rejected,可以根據返回的值做出相應的操作。

下面就直接上代碼實現exec()方法。

二、代碼之路

實現QDialog的exec()方法

void MyDialog::init() { connect(ui.pButtonOk, SIGNAL(clicked()), this, SLOT(onOkClicked())); connect(ui.pButtonCancel, SIGNAL(clicked()), this, SLOT(onCancelClicked())); } int MyDialog::exec() { // 設置為模態; this->setWindowModality(Qt::ApplicationModal); show(); // 使用事件循環QEventLoop ,不讓exec()方法結束,在用戶選擇確定或者取消后,關閉窗口結束事件循環,並返回最后用戶選擇的結果; // 根據返回結果得到用戶按下了確定還是取消,采取相應的操作。從而模擬出QDialog類的exec()方法; m_eventLoop = new QEventLoop(this); m_eventLoop->exec(); return m_chooseResult; } void MyDialog::onOkClicked() { m_chooseResult = Accepted; close(); } void MyDialog::onCancelClicked() { m_chooseResult = Rejected; close(); } void MyDialog::closeEvent(QCloseEvent *event) { // 關閉窗口時結束事件循環,在exec()方法中返回選擇結果; if (m_eventLoop != NULL) { m_eventLoop->exit(); } event->accept(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 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文檔的中文翻譯 ,有需要的小伙伴可以看看。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM