1. 程式人生 > >漫談QWidget及其派生類(四)

漫談QWidget及其派生類(四)

很多人覺得QDialog比QWidget高階很多,為什麼呢?

  • QDialog(this) 和 QWidget(this) 前者是視窗,後者不是。
  • QDialog 除 show()外,還有 exec() 和 open() 兩個成員用來顯示視窗。
  • QDialog ....

本文內容:如何使用QWidget來實現QDialog的常用功能,通過定義一個名為HDailog的類希望對大家理解QWidget有所幫助。

本文目的:QDialog只不過是QWidget的派生類,沒有什麼神祕的,離開它你依然都什麼都能做

,當然,本文不是鼓勵大家不使用QDialog。

例子一

  • 這是很多新手容易迷惑的一個問題:用QDialog則彈出視窗,用QWidget則不會彈出。

void Widget::onXXXX()
{
     QDialog * dlg = new QDialog(this);
     //QWidget * dlg = new QWidget(this);
     dlg->show();
}

這是我們在漫談QWidget及其派生類(一)中重點解釋的內容。如果你看了我們下面的程式碼,依然不明所以,不妨再回過頭看看。

我們的新類HDialog,就從這兒起步嘍:

class HDialog:public QWidget
{
Q_OBJECT
public:
    HDialog(QWidget * parent=0)
        :QWidget(parent, Qt::Window)
    {
    }
};

恩,只需如此,new HDialog(this)便是一個視窗。

例子二

很多人有這個疑問?為什麼用show()看不到視窗,而用exec()才可以!

void Widget::onXXXX()
{
     QDialog dlg(this);
     //dlg.show();
     dlg.exec();
}

答案很簡單,因為exec()是個死迴圈,使得你始終在這個函式內不退出,dlg不被銷燬。關於它的分析,詳見QEventLoop 的使用兩例。由於篇幅所限,本文不再重複。

大家都有這個常識,對話方塊有模態和非模態之分:

如果需要

則使用

模態(應用程式級)

QDialog::exec()

如果對這3個尚不瞭解,請看Manual,本文幾乎不做解釋

模態(視窗級)

QDailog::open()

非模態

QDialog::show()

我們在QDialog 模態對話方塊與事件迴圈一文中也反覆強調了,模態還是非模態,與QDialog無關!接下來,我們用QWidget來實現open()和exec(),為什麼不實現show()?呵呵,原因自己想唄。

open()/exec() 初版

先看最簡單的open() 與 exec():

void HDialog::open()
{
    setWindowModality(Qt::WindowModal);
    show();
}
void HDialog::exec()
{
    setAttribute(Qt::WA_ShowModal, true);
    show();
    QEventLoop loop;
    loop.exec();
}

有什麼感觸沒?

  • 無論是open() 還是 exec(),裡面都是呼叫了 show()這個東西。
  • 之所以模態不同,是由QWidget的成員函式setWindowModality()或setAttribute()進行的。
  • exec() 內部啟用了局部事件迴圈(死迴圈),所以本節開頭的 dlg.exec() 才能讓你留住視窗。

對應的類定義如下:

class HDialog:public QWidget
{
Q_OBJECT
public:
    HDialog(QWidget * parent=0)
        :QWidget(parent, Qt::Window)
    {
    }
public slots:
    void open();
    void exec();
};

完善exec()

前面的exec()有個重大的問題:什麼問題,你無法退出那個事件迴圈(死迴圈)

我們必須解決這個問題,不能退出可不行。什麼時候退出呢?是close的時候麼?是但不止。是隱藏視窗的時候!

  • 注意:我們要能訪問那個loop?顯然不能是區域性變量了,需要做成類變數QEventLoop * m_loop;

  • 注意:無論是show()/hide(),還是setHidden(bool),都是通過呼叫的虛擬函式 setVisible(bool)來實現的。
void HDialog::setVisible(bool visible)
{
   if (!visible && m_loop) {
       m_loop->exit();
   }
}

恩,相應的,修改一下exec()

void HDialog::exec()
{
    setAttribute(Qt::WA_ShowModal, true);
    show();

    QEventLoop loop;
    m_loop = &loop; //new line
    loop.exec();
    m_loop = 0; //new line
}

返回值何在?

我們都這樣用exec() 的!

    QDialog dlg(this);
    if (dlg.exec()==QDialog::Accepted) {}

或者

    QDialog dlg(this);
    dlg.exec();
    if (dlg.result() == QDialog::Accepted) {}

呵呵,我們接下來看看如何弄:

class HDialog:public QWidget
{
    enum{Accepted, Rejected};
public:
   int result()const {return m_result;}
   int exec();
    ...
private:
   int m_result;
   ...

很簡單吧,只需要一個成員變數即可:然後你可以隨便設定其值(常用的兩個是Accepted,Rejected),而後,我們的exec()

int HDialog::exec()
{
...
    return m_result;
}

還能如何拓展?

從返回值開始拓展一點。如果我點選了視窗裝飾器上的關閉,返回值應該是Rejected吧?如何實現??

如何處理關閉事件?

void HDialog::closeEvent(QCloseEvent *e)

恩,收到close事件,我們需要覆蓋(override)這個函式

void HDialog::closeEvent(QCloseEvent *e)
{
    hide();//呼叫我們前面的setVisible,如果有事件迴圈,將退出!
    m_result = Rejected;
    e->accept();
}

還繼續麼?

東西是在太多了,比如,呼叫open()或exec()是,我們設定了視窗模態,理想狀態,我們需要在hide()的時候,恢復視窗先前的模態!

不過只要前面的理清了,這些都難不倒我們,對吧?所以不繼續了。

例子三

例子二太長了,看個短的,放鬆一下

我們前面噼裡啪啦說了一堆,也沒有提到Qt::Dialog這個東西。沒有它都可以實現這堆東西,要它何用??

想想:QDialog 和 普通的 Widget,視窗裝飾器上的按鈕是不是有所不同???

這些按鈕受神馬控制??windowFlags中的哪些Hint麼?恩。

可是,我們也沒有設定哪些Hint啊??

Qt::Dialog是什麼?

看看這個吧,重溫一下本系列第一篇“視窗與普通widget”

    enum WindowType {
        Widget = 0x00000000,
        Window = 0x00000001,
        Dialog = 0x00000002 | Window,
        Sheet = 0x00000004 | Window,
        Drawer = 0x00000006 | Window,
        Popup = 0x00000008 | Window,
        Tool = 0x0000000a | Window,
        ToolTip = 0x0000000c | Window,
        SplashScreen = 0x0000000e | Window,
        Desktop = 0x00000010 | Window,
...

視窗類別,其實是將視窗細分了,之所以細分,是要提供各種預設的flags。

我們在第一篇中提到,對於一個普通的(即,沒有Qt::Window標記)且沒有parent的widget,顯示之前,會被強制設定一個Qt::Window標記。使得其變成視窗。

其實是在同一個地方,還有這樣的程式碼:

if (customize)
    ; // don't modify window flags if the user explicitly set them.
else if (type == Qt::Dialog || type == Qt::Sheet)
    flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
else if (type == Qt::Tool)
    ....

如果你沒有顯示設定標記位,對於Qt::Dialog,它會設定這3種Hint(含義?看Manual唄,呵呵)

本文完(限於篇幅,也為了不至於太亂,只能寫這些了。有問題歡迎討論)

其他

對了,你可能會對下面兩篇blog感興趣:


相關推薦

漫談QWidget及其派生()

漫談QWidget及其派生類(三)中我們了QMainWindow到底是個什麼東西,接下來就看看QDialog吧,沒辦法,誰讓它這麼常用呢? 很多人覺得QDialog比QWidget高階很多,為什麼呢? QDialog(this) 和 QWidget(this) 前者是

MFC中CWnd及其派生對話方塊、訊息處理、視窗操作

CWnd類 我們在螢幕上看到的所有物件都和視窗有關,它們或者派生於CWnd,屬繼承關係,如對話方塊、工具欄、狀態列、子控制元件;或者被CWnd合成,屬服務員與服務物件關係,如圖示、選單、顯示裝置。 CWnd類封裝的視窗操作主要包含視窗的建立和銷燬、操作視窗風格、操作視窗狀態

UI組件之AdapterView及其)Gallery畫廊控件使用

convert cal instance ram scaletype 循環 reat targe 外觀 聽說 Gallery如今已經不使用了,API使用ViewPaper取代了,以後再學專研ViewPaper吧如今說說Gallery畫廊,就是不停顯示圖片的意思 Gall

C++派生的建構函式和解構函式執行順序及其構造形式

    在C++程式設計師的面試中,經常會出現派生類與基類的建構函式、解構函式的執行順序。其實這是一個很基本的問題,沒有什麼難度,只需要記住就OK了。      1.派生類的建構函式和解構函式的執行順序     首先執行基類的建構函式,隨後執行派生類的建構函式,當撤銷派生類物件時,限執行派生類的解構函

,Java集合(2)——Set介面及其實現

1,Set介面及其實現類 Set集合與Collection基本相同,沒有提供任何額外的方法。實際上Set就是Collection,只是行為略有不同。Set集合不允許包含相同的元素,如果試圖把兩個相同的元素加入同一個Set集合中,新增操作失敗,add()方法返回

派生

ffi 是我 etag plus prot 間接 ron img tco 看了c#的教程,也上網絡上搜索基類與派生類的概念,一直沒有一個很明確的定義.弄得偶一直很迷糊,現在搜索到c++的教程裏倒是有比較明確的說明,特意貼在這裏,幫助自己理解. 繼承機制提供了無限重復利用程序

SRS學習筆記10-SrsConnection及其分析

when red ins parse discovery bsp for port std SrsConnection類代表一個client的連接,其中封裝了st thread,用於在一個單獨的st thread裏處理一個client的服務請求. SrsConnection

UI組件:TextView及其

時間 raw 界面 realtime 字體 框圖 相對 mage 導入   TextView(文本框)   一、TextView作用類似於JLable用於在界面上顯示文本    二、TextView沒有邊框,如果需要邊框可以導入背景框的圖片,背景框可以自定義為背景顏色漸變

UI組件:ImageView及其

button 聯系人 round span 按鈕 界面 bad -a color   ImageView     用於顯示所有Drawable對象  ImageButton(圖片按鈕) 註意點:和Button的區別是:Button可以顯示文字,而ImageButton不

派生的指針和成員函數調用原理

而且 font 重新定義 -s 繼承 轉型 center span enter 基類與派生類的指針和成員函數調用原理 1.如果以一個基礎類指針指向一個衍生類對象(派生類對象),那麽經由該指針只能訪問基礎類定義的函數(靜態聯翩) 2.如果以一個衍生類指針指向一個基礎類對象,必

C++基派生的構造函數和析構函數的調用

str 生命 ons stream all 兩種 col 生命期 析構函數 C++基類和派生類的構造函數和析構函數的調用 1.調用順序   當創建一個派生類的對象時,系統首先自動創建一個基類對象,也就是說,在調用派生類構造函數創建派生類對象之前,系統首先調用基類的構造函數創

虛析構函數,派生調用基構造方法

div pub urn new turn col blog 徹底 () #include <iostream> using namespace std; class A{ public: A() { cout<<"construct A

Java 集合-Set接口及其

允許 ret ins ict amp println out ++ || 2017-10-31 19:20:45 Set:無序且唯一 實現子類:HashSet, HashSet 此類實現 Set 接口,由哈希表(實際上是一個 HashMap 實例)支持。它不保

protected 成員與派生

span clas con count pos 函數 沒有 disco virt 受保護的成員對於類的用戶來說時不可訪問的 受保護的成員對於派生類的成員和友元來說是可訪問的 其中,只能通過派生類對象來訪問基類的受保護成員,派生類對於一個基類對象中的受保護成員沒有任何

Python()-反射

調用 創建 utf post __init__ 布爾型 img 返回 info 反射即通過字符串映射或修改程序運行時的狀態、屬性、方法 有4個方法: hasattr(): hasattr(object,string):object為實例化的對象,string為字符串 判斷對

Java中的基本數據及其封裝

實例化 too shadow color ant 基本類 實用 anti 泛型 Java中的數據類型有兩種,基本數據類型和引用數據類型,引用數據類型的創建是需要去new一個對象,該對象的內存分配在堆區,同時棧區會保存一個指向該對象的引用,但是對於一些簡單數據的創建,

.NET(C#):XML序列化時派生的處理

ali main 基類 bsp 處理 program ext serial pub .NET(C#):XML序列化時派生類的處理 針對基類的XmlSerializer序列化派生類 第一種方法是在基類添加XmlInclude特性,這樣的話基類的XmlSerializer可以

OOP1(定義基派生)

isbn 類的構造函數 不能 turn title item 內存 derived 構造 面向對象程序設計基於三個基本概念:數據抽象,繼承和動態綁定 數據抽象是一種依賴於接口和實現分離的編程技術。繼承和動態綁定對程序的編號有兩方面的影響:一是我們可以更容易地定義與其它類相似

派生,父指針指向子對象

namespace 簡單工廠模式 為什麽 對象創建 簡單工廠 pos 釋放 自己的 分享 先看一段代碼: 1 #include<iostream> 2 3 using namespace std; 4 5 class Base{ 6 publi

Java八種基本及其包裝總結

HA class byte 及其 int pos log character 編譯 原始類型 包裝類 原始類型所占的字節數 short Short 2個字節 int Integer