Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Qt實現窗口間數據交換

Qt實現窗口間數據交換

編輯:關於Android編程

Qt實現窗口間數據交換: 對話框的出現用於完成一個簡單的或者是短期的任務。對話框與主窗口之間的數據交互相當重要。本節將講解如何在對話框和主窗口之間進行數據交互。按照前文的講解,對話框分為模態和非模態兩種。我們也將以這兩種為例,分別進行闡述。

 

模態對話框使用了 exec() 函數將其顯示出來。exec() 函數的真正含義是開啟一個新的事件循環(我們會在後面的章節中詳細介紹有關事件的概念)。所謂事件循環,可以理解成一個無限循環。Qt 在開啟了事件循環之後,系統發出的各種事件才能夠被程序監聽到。這個事件循環相當於一種輪詢的作用。既然是無限循環,當然在開啟了事件循環的地方,代碼就會被阻塞,後面的語句也就不會被執行到。因此,對於使用了 exec() 顯示的模態對話框,我們可以在 exec() 函數之後直接從對話框的對象獲取到數據值。



看一下下面的代碼:

  1. void MainWindow::open()
  2. {
  3. QDialog dialog(this);
  4. dialog.setWindowTitle(tr("Hello, dialog!"));
  5. dialog.exec();
  6. qDebug() << dialog.result();
  7. }
復制代碼

上面的代碼中,我們使用 exec() 顯示一個模態對話框。最後一行代碼,qDebug() 類似於 std::cout 或者Java的 System.out.println(); 語句,將後面的信息輸出到標准輸出,一般就是控制台。使用 qDebug() 需要引入頭文件。在 exec() 函數之後,我們直接可以獲取到 dialog 的數據值。注意,exec() 開始了一個事件循環,代碼被阻塞到這裡。由於 exec() 函數沒有返回,因此下面的 result() 函數也就不會被執行。直到對話框關閉,exec() 函數返回,此時,我們就可以取得對話框的數據。



需要注意的一點是,如果我們設置 dialog 的屬性為 WA_DeleteOnClose,那麼當對話框關閉時,對象被銷毀,我們就不能使用這種辦法獲取數據了。在這種情況下,我們可以考慮使用 parent 指針的方式構建對話框,避免設置 WA_DeleteOnClose 屬性;或者是利用另外的方式。



實際上,QDialog::exec() 是有返回值的,其返回值是 QDialog::Accepted 或者 QDialog::Rejected。一般我們會使用類似下面的代碼:

  1. QDialog dialog(this);
  2. if (dialog.exec() == QDialog::Accepted) {
  3. // do something
  4. } else {
  5. // do something else
  6. }
復制代碼

來判斷對話框的返回值,也就是用戶是點擊了“確定”還是“取消”。更多細節請參考 QDialog 文檔。



模態對話框相對簡單,如果是非模態對話框,QDialog::show() 函數會立即返回,如果我們也這麼寫,就不可能取得用戶輸入的數據。因為 show() 函數不會阻塞主線程,show() 立即返回,用戶還沒有來得及輸入,就要執行後面的代碼,當然是不會有正確結果的。那麼我們就應該換一種思路獲取數據,那就是使用信號槽機制。



由於非模態對話框在關閉時可以調用 QDialog::accept() 或者 QDialog::reject() 或者更通用的 QDialog::done() 函數,所以我們可以在這裡發出信號。另外,如果找不到合適的信號發出點,我們可以重寫 QDialog::closeEvent() 函數,在這裡發出信號。在需要接收數據的窗口(這裡是主窗口)連接到這個信號即可。類似的代碼片段如下所示:

  1. //!!! Qt 5
  2. // in dialog:
  3. void UserAgeDialog::accept()
  4. {
  5. emit userAgeChanged(newAge); // newAge is an int
  6. QDialog::accept();
  7. }
  8.  
  9. // in main window:
  10. void MainWindow::showUserAgeDialog()
  11. {
  12. UserAgeDialog *dialog = new UserAgeDialog(this);
  13. connect(dialog, &UserAgeDialog::userAgeChanged, this, &MainWindow::setUserAge);
  14. dialog->show();
  15. }
  16.  
  17. // ...
  18.  
  19. void MainWindow::setUserAge(int age)
  20. {
  21. userAge = age;
  22. }
復制代碼

上面的代碼很簡單,這裡不再贅述。另外,上述代碼的 Qt 4 版本也應該可以很容易地實現。



不要擔心如果對話框關閉,是不是還能獲取到數據。因為 Qt 信號槽的機制保證,在槽函數在調用的時候,我們始終可以使用 sender() 函數獲取到 signal 的發出者。關於 sender() 函數,可以在文檔中找到更多的介紹。順便說一句,sender() 函數的存在使我們可以利用這個函數,來實現一個只能打開一個的非模態對話框(方法就是在對話框打開時在一個對話框映射表中記錄下標記,在對話框關閉時利用 sender() 函數判斷是不是該對話框,然後從映射表中將其刪除)。


對話框是 GUI 程序中不可或缺的組成部分。很多不能或者不適合放入主窗口的功能組件都必須放在對話框中設置。對話框通常會是一個頂層窗口,出現在程序最上層,用於實現短期任務或者簡潔的用戶交互。盡管 Ribbon 界面的出現在一定程度上減少了對話框的使用幾率,但是,我們依然可以在最新版本的 Office 中發現不少對話框。因此,在可預見的未來,對話框會一直存在於我們的程序之中。


Qt 中使用 QDialog 類實現對話框。就像主窗口一樣,我們通常會設計一個類繼承 QDialog。QDialog(及其子類,以及所有 Qt::Dialog 類型的類)的對於其 parent 指針都有額外的解釋:如果 parent 為 NULL,則該對話框會作為一個頂層窗口,否則則作為其父組件的子對話框(此時,其默認出現的位置是 parent 的中心)。頂層窗口與非頂層窗口的區別在於,頂層窗口在任務欄會有自己的位置,而非頂層窗口則會共享其父組件的位置。

  1. MainWindow::MainWindow(QWidget *parent)
  2. : QMainWindow(parent)
  3. {
  4. setWindowTitle(tr("Main Window"));
  5.  
  6. openAction = new QAction(QIcon(":/images/doc-open"), tr("&Open..."), this);
  7. openAction->setShortcuts(QKeySequence::Open);
  8. openAction->setStatusTip(tr("Open an existing file"));
  9. connect(openAction, &QAction::triggered, this, &MainWindow::open);
  10.  
  11. QMenu *file = menuBar()->addMenu(tr("&File"));
  12. file->addAction(openAction);
  13.  
  14. QToolBar *toolBar = addToolBar(tr("&File"));
  15. toolBar->addAction(openAction);
  16. }
  17.  
  18. MainWindow::~MainWindow()
  19. {
  20. }
  21.  
  22. void MainWindow::open()
  23. {
  24. QDialog dialog;
  25. dialog.setWindowTitle(tr("Hello, dialog!"));
  26. dialog.exec();
  27. }
復制代碼

上面我們使用了前面的示例代碼。注意看的是 open() 函數裡面的內容。我們使用 QDialog 創建了一個對話框,設置其標題為“Hello, dialog!”,然後調用 exec() 將其顯示出來。注意看的是任務欄的圖標,由於我們沒有設置對話框的 parent 指針,我們會看到在任務欄出現了對話框的位置:



 


我們修改一下 open() 函數的內容:

  1. void MainWindow::open()
  2. {
  3. QDialog dialog(this);
  4. dialog.setWindowTitle(tr("Hello, dialog!"));
  5. dialog.exec();
  6. }
復制代碼

重新運行一下,對比一下就會看到 parent 指針的有無對 QDialog 實例的影響。


對話框分為模態對話框和非模態對話框。所謂模態對話框,就是會阻塞同一應用程序中其它窗口的輸入。模態對話框很常見,比如“打開文件”功能。你可以嘗試一下記事本的打開文件,當打開文件對話框出現時,我們是不能對除此對話框之外的窗口部分進行操作的。與此相反的是非模態對話框,例如查找對話框,我們可以在顯示著查找對話框的同時,繼續對記事本的內容進行編輯。


Qt 支持模態對話框和非模態對話框。其中,Qt 有兩種級別的模態對話框:應用程序級別的模態和窗口級別的模態,默認是應用程序級別的模態。應用程序級別的模態是指,當該種模態的對話框出現時,用戶必須首先對對話框進行交互,直到關閉對話框,然後才能訪問程序中其他的窗口。窗口級別的模態是指,該模態僅僅阻塞與對話框關聯的窗口,但是依然允許用戶與程序中其它窗口交互。窗口級別的模態尤其適用於多窗口模式,更詳細的討論可以看以前發表過的文章。


Qt 使用 QDialog::exec() 實現應用程序級別的模態對話框,使用 QDialog::open() 實現窗口級別的模態對話框,使用 QDialog::show() 實現非模態對話框。回顧一下我們的代碼,在上面的示例中,我們調用了 exec() 將對話框顯示出來,因此這就是一個模態對話框。當對話框出現時,我們不能與主窗口進行任何交互,直到我們關閉了該對話框。


下面我們試著將 exec() 修改為 show(),看看非模態對話框:

  1. void MainWindow::open()
  2. {
  3. QDialog dialog(this);
  4. dialog.setWindowTitle(tr("Hello, dialog!"));
  5. dialog.show();
  6. }
復制代碼

是不是事與願違?對話框竟然一閃而過!這是因為,show() 函數不會阻塞當前線程,對話框會顯示出來,然後函數立即返回,代碼繼續執行。注意,dialog 是建立在棧上的,show() 函數返回,MainWindow::open() 函數結束,dialog 超出作用域被析構,因此對話框消失了。知道了原因就好改了,我們將 dialog 改成堆上建立,當然就沒有這個問題了:

  1. void MainWindow::open()
  2. {
  3. QDialog *dialog = new QDialog;
  4. dialog->setWindowTitle(tr("Hello, dialog!"));
  5. dialog->show();
  6. }
復制代碼

對於一下這個非模態對話框和之前的模態對話框。我們在對話框出現的時候可以與主窗口交互,因此我們可以建立多個相同的對話框:


 


如果你足夠細心,應該發現上面的代碼是有問題的:dialog 存在內存洩露!dialog 使用 new 在堆上分配空間,卻一直沒有 delete。解決方案也很簡單:將 MainWindow 的指針賦給 dialog 即可。還記得我們前面說過的 Qt 的對象系統嗎?


不過,這樣做有一個問題:如果我們的對話框不是在一個界面類中出現呢?由於 QWidget 的 parent 必須是 QWidget 指針,那就限制了我們不能將一個普通的 C++ 類指針傳給 Qt 對話框。另外,如果對內存占用有嚴格限制的話,當我們將主窗口作為 parent 時,主窗口不關閉,對話框就不會被銷毀,所以會一直占用內存。在這種情景下,我們可以設置 dialog 的 WindowAttribute:

  1. void MainWindow::open()
  2. {
  3. QDialog *dialog = new QDialog;
  4. dialog->setAttribute(Qt::WA_DeleteOnClose);
  5. dialog->setWindowTitle(tr("Hello, dialog!"));
  6. dialog->show();
  7. }
復制代碼

setAttribute() 函數設置對話框關閉時,自動銷毀對話框。另外,QObject 還有一個 deleteLater() 函數,該函數會在當前事件循環結束時銷毀該對話框(具體到這裡,需要使用 exec() 開始一個新的事件循環)。關於事件循環,我們會在後面的文章中詳細說明。




對話框的出現用於完成一個簡單的或者是短期的任務。對話框與主窗口之間的數據交互相當重要。本節將講解如何在對話框和主窗口之間進行數據交互。按照前文的講解,對話框分為模態和非模態兩種。我們也將以這兩種為例,分別進行闡述。



 

模態對話框使用了 exec() 函數將其顯示出來。exec() 函數的真正含義是開啟一個新的事件循環(我們會在後面的章節中詳細介紹有關事件的概念)。所謂事件循環,可以理解成一個無限循環。Qt 在開啟了事件循環之後,系統發出的各種事件才能夠被程序監聽到。這個事件循環相當於一種輪詢的作用。既然是無限循環,當然在開啟了事件循環的地方,代碼就會被阻塞,後面的語句也就不會被執行到。因此,對於使用了 exec() 顯示的模態對話框,我們可以在 exec() 函數之後直接從對話框的對象獲取到數據值。



看一下下面的代碼:

  1. void MainWindow::open()
  2. {
  3. QDialog dialog(this);
  4. dialog.setWindowTitle(tr("Hello, dialog!"));
  5. dialog.exec();
  6. qDebug() << dialog.result();
  7. }
復制代碼

上面的代碼中,我們使用 exec() 顯示一個模態對話框。最後一行代碼,qDebug() 類似於 std::cout 或者Java的 System.out.println(); 語句,將後面的信息輸出到標准輸出,一般就是控制台。使用 qDebug() 需要引入頭文件。在 exec() 函數之後,我們直接可以獲取到 dialog 的數據值。注意,exec() 開始了一個事件循環,代碼被阻塞到這裡。由於 exec() 函數沒有返回,因此下面的 result() 函數也就不會被執行。直到對話框關閉,exec() 函數返回,此時,我們就可以取得對話框的數據。



需要注意的一點是,如果我們設置 dialog 的屬性為 WA_DeleteOnClose,那麼當對話框關閉時,對象被銷毀,我們就不能使用這種辦法獲取數據了。在這種情況下,我們可以考慮使用 parent 指針的方式構建對話框,避免設置 WA_DeleteOnClose 屬性;或者是利用另外的方式。



實際上,QDialog::exec() 是有返回值的,其返回值是 QDialog::Accepted 或者 QDialog::Rejected。一般我們會使用類似下面的代碼:

  1. QDialog dialog(this);
  2. if (dialog.exec() == QDialog::Accepted) {
  3. // do something
  4. } else {
  5. // do something else
  6. }
復制代碼

來判斷對話框的返回值,也就是用戶是點擊了“確定”還是“取消”。更多細節請參考 QDialog 文檔。



模態對話框相對簡單,如果是非模態對話框,QDialog::show() 函數會立即返回,如果我們也這麼寫,就不可能取得用戶輸入的數據。因為 show() 函數不會阻塞主線程,show() 立即返回,用戶還沒有來得及輸入,就要執行後面的代碼,當然是不會有正確結果的。那麼我們就應該換一種思路獲取數據,那就是使用信號槽機制。



由於非模態對話框在關閉時可以調用 QDialog::accept() 或者 QDialog::reject() 或者更通用的 QDialog::done() 函數,所以我們可以在這裡發出信號。另外,如果找不到合適的信號發出點,我們可以重寫 QDialog::closeEvent() 函數,在這裡發出信號。在需要接收數據的窗口(這裡是主窗口)連接到這個信號即可。類似的代碼片段如下所示:

  1. //!!! Qt 5
  2. // in dialog:
  3. void UserAgeDialog::accept()
  4. {
  5. emit userAgeChanged(newAge); // newAge is an int
  6. QDialog::accept();
  7. }
  8.  
  9. // in main window:
  10. void MainWindow::showUserAgeDialog()
  11. {
  12. UserAgeDialog *dialog = new UserAgeDialog(this);
  13. connect(dialog, &UserAgeDialog::userAgeChanged, this, &MainWindow::setUserAge);
  14. dialog->show();
  15. }
  16.  
  17. // ...
  18.  
  19. void MainWindow::setUserAge(int age)
  20. {
  21. userAge = age;
  22. }
復制代碼

上面的代碼很簡單,這裡不再贅述。另外,上述代碼的 Qt 4 版本也應該可以很容易地實現。



不要擔心如果對話框關閉,是不是還能獲取到數據。因為 Qt 信號槽的機制保證,在槽函數在調用的時候,我們始終可以使用 sender() 函數獲取到 signal 的發出者。關於 sender() 函數,可以在文檔中找到更多的介紹。順便說一句,sender() 函數的存在使我們可以利用這個函數,來實現一個只能打開一個的非模態對話框(方法就是在對話框打開時在一個對話框映射表中記錄下標記,在對話框關閉時利用 sender() 函數判斷是不是該對話框,然後從映射表中將其刪除)。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved