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

版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接和本聲明。
本文連接:https://blog.csdn.net/GoForwardToStep/article/details/53667566
1、簡述
先簡單介紹一下模態與非模態對話框。
模態對話框
簡單一點講就是在彈出模態對話框時,除了該對話框整個應用程序窗口都沒法接受用戶響應,處於等待狀態,直到模態對話框被關閉。這時通常須要點擊對話框中的肯定或者取消等按鈕關閉該對話框,程序獲得對話框的返回值(即點擊了肯定仍是取消),並根據返回值進行相應的操做,以後將操做權返回給用戶。這個時候用戶能夠點擊或者拖動程序其餘窗口。html

說白了就至關於阻塞同一應用程序中其它可視窗口的輸入的對話框,用戶必須完成這個對話框中的交互操做而且關閉了它以後才能訪問應用程序中的其它窗口。windows

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

非模態對話框
又叫作無模式對話框,即彈出非模態對話框時,用戶仍然能夠對其餘窗口進行操做,不會由於這個對話框未關閉就不能操做其餘窗口。函數

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

Qt中的模態&非模態&半模態
QWidget
QWidget提供了setWindowModality()方法設置窗口半模態or非模態;學習

Qt::NonModal The window is not modal and does not block input to other windows.
非模態對話框ui

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.
窗口級模態對話框,即只會阻塞父窗口、父窗口的父窗口及兄弟窗口。(半模態對話框)this

Qt::ApplicationModal The window is modal to the application and blocks input to all windows.
應用程序級模態對話框,即會阻塞整個應用程序的全部窗口。(半模態對話框).net

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()方法。

2、代碼之路
實現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
3、 Qt事件循環的一些理解(exec、eventloop)
一、事件循環通常用exec()函數開啓。QApplicaion::exec()、QMessageBox::exec()都是事件循環。其中前者又被稱爲主事件循環。

事件循環首先是一個無限「循環」,程序在exec()裏面無限循環,能讓跟在exec()後面的代碼得不到運行機會,直至程序從exec()跳出。從exec()跳出時,事件循環即被終止。QEventLoop::quit()可以終止事件循環。

其次,之因此被稱爲「事件」循環,是由於它能接收事件,並處理之。當事件太多而不能立刻處理完的時候,待處理事件被放在一個「隊列」裏,稱爲「事件循環隊列」。當事件循環處理完一個事件後,就從「事件循環隊列」中取出下一個事件處理之。當事件循環隊列爲空的時候,它和一個啥事也不作的永真循環有點相似,可是和永真循環不一樣的是,事件循環不會大量佔用CPU資源。

事件循環的本質就是以隊列的方式再次分配線程時間片。

二、事件循環是能夠嵌套的,一層套一層,子層的事件循環執行exec()的時候,父層事件循環就處於中斷狀態;當子層事件循環跳出exec()後,父層事件循環才能繼續循環下去。
另外,子層事件循環具備父層事件循環的幾乎全部功能。Qt會把事件送到當前生效的那個事件循環隊列中去,其中包括Gui的各類事件。因此用戶在主線程中執行各類exec()(如QMessageBox::exec(),QEventLoop::exec())的時候,雖然這些exec()打斷了main()中的QApplication::exec(),可是Gui界面仍然可以正常響應。

三、若是某個子事件循環仍然有效,但其父循環被強制跳出,此時父循環不會當即執行跳出,而是等待子事件循環跳出後,父循環纔會跳出。

摘自 http://blog.chinaunix.net/uid-27685749-id-3847998.html。


關於模態、非模態、半模態窗口的定義也很好理解,其實也就是跟用戶操做過程當中進行交互的問題。

同時咱們也經過簡單的代碼來模擬出了QDialog的exec()方法。有問題直接找Qt助手,在這裏基本上便能找到咱們須要的答案。因此說遇到一些問題不必定非要立馬到網上找各類資料或者到學習羣中詢問問題的解決辦法,多看看幫助問題仍是頗有好處的。

http://www.kuqin.com/qtdocument/classes.html , 這個網址裏提供了Qt文檔的中文翻譯 ,有須要的小夥伴能夠看看。 ———————————————— 版權聲明:本文爲CSDN博主「前行中的小豬」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。 原文連接:https://blog.csdn.net/GoForwardToStep/article/details/53667566

相關文章
相關標籤/搜索