摘要

我們使用的標準 C++,其設計的物件模型雖然已經提供了非常高效的 RTTI 支援,但是在某些方面還是不夠靈活。比如在 GUI 程式設計方面,既需要高效的執行效率也需要強大的靈活性,諸如刪除某視窗時可不想把子視窗用程式碼一個個去析構。Qt 將這兩者的優點完美的結合在了一起,創造出了特有的物件模型(Qt Object Model)。


一,Qt的基本框架

在上一篇中,我們已經完成了Qt的安裝和VS的環境配置。QT從入門到入土(一)——Qt5.14.2安裝教程和VS2019環境配置 - 唯有自己強大 - 部落格園 (cnblogs.com)

在講解物件樹之前,我們先來熟悉一下Qt的基本框架。首先新建一個專案:

  •  main.cpp分析

開啟sources裡面的main.cpp,可以看到以下程式碼:

注意:

  1. 每個Qt程式有且只能有一個QApplication物件,沒有會報錯。
  2. Qt裡面的標頭檔案和類名是一致的,知道標頭檔案就知道類名,反之亦然
  3. Qt標頭檔案是沒有.h的,基本都是以大寫的Q開頭

根據以上的分析,我們可以得出Qt的程式框架程式碼:

#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
/*
在這裡寫你的程式碼
*/
return a.exec();
}
  • widget.h和widget.cpp分析

開啟Headers裡面的widget.h,和sources裡面的widget.app,可以看到以下程式碼:

最上面的MyfirstQt.pro,是管理專案的檔案,用來儲存專案設定。

字尾為“.pro”的檔案是專案的管理檔案,檔名就是專案的名稱,如本專案中的 MyfirstQt.pro。(類似與VS工程的.sln檔案)

例項(用程式碼建立一個button):

幫助文件的快捷鍵:第一種方式:F1 第二種方式:Qt左側按鈕 第三種方式:D:\Qt\Qt5.14.2\5.14.2\mingw73_64\bin\assistant

因為建立一個button需要QPushButton 類,因此我們可以在幫助文件中查到相關的資訊:

程式碼實現:

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>//按鈕控制元件的標頭檔案 Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this); //建立第一個按鈕
QPushButton *btn=new QPushButton;
//不能用btn->show();//show是以頂層方式彈出控制元件
//讓btn在widget視窗顯示
btn->setParent(this);//this指向當前物件的指標(即widget的地址)
//顯示文字
btn->setText("第一個按鈕");
//建立第二個按鈕
//注意:這種方法是按照按鈕的大小建立視窗
QPushButton *btn2=new QPushButton("第二個按鈕",this);
//移動btn2的位置(由於建立的兩個按鈕位置重疊了)
btn2->move(100,100);
//因此需要重置視窗大小
resize(600,400); //設定視窗標題
setWindowTitle("唯有自己強大");
}

二,物件模型(物件樹)

什麼是物件樹?

我們常常聽到 QObject 會用物件樹來組織管理自己,那什麼是物件樹?

這個概念非常好理解。因為 QObject 類就有一個私有變數 QList<QObject *>,專門儲存這個類的子孫後代們。比如建立一個 QObject 物件並指定父物件時,就會把自己加入到父物件的 childre() 列表中,也就是 QList<QObject *> 變數中。

父物件析構的時候,這個列表中的所有物件也會被析構。(注意,這 裡的父物件並不是繼承意義上的父類!)

舉個例子,有一個視窗 Window,裡面有 Label標籤、TextEdit文字輸入框、Button按鈕這三個元素,並且都設定 Window 為它們的父物件。這時候我做了一個關閉視窗的操作,作為程式設計師的你是不是自然想到將所有和視窗相關的物件析構啊?古老的辦法就是一個個手動 delete 唄。是不是很麻煩?Qt 運用物件樹模式,當父物件被析構時,子物件自動就 delete 掉了,不用再寫一大堆的程式碼了。

QWidget 是能夠在螢幕上顯示的一切元件的父類(QWidget 繼承自 QObject,因此也繼承了這種物件樹關係。)

注意構建/析構 QObject 的順序問題

正常情況下,最後被創建出來的會先被析構掉。就好比我有一個大桌子,上面先擺放一個盤子,再擺放一個碗。當我要把桌子撤掉的時候,會先撤掉碗,再撤掉盤子,最後撤掉桌子。

用程式碼來演示一下:

int main()
{
QWidget window;
QPushButton quit("Quit", &window);
}

後建立的 quit 物件指定了 window 為其父物件。那麼關閉程式時,會先呼叫它的解構函式,然後呼叫 window 的解構函式。

這就牽扯到一個特殊情況:

int main()
{
QPushButton quit("Quit");
QWidget window; quit.setParent(&window);
}

如果反過來,由於 window 後建立,程式關閉時先呼叫 window 的解構函式(此時 quit 被第一次析構)。接著呼叫 quit 的解構函式(此時 quit 被第二次析構),這時由於被兩次析構,所以出問題了。

這種特殊情況在程式設計中很隱蔽,不容易發現。因為編譯的時候不會報錯,只有執行時才會產生問題。

我們最好從開始就養成良好習慣,在 Qt 中,儘量在構 造的時候就指定 parent 物件,並且大膽在堆上建立。

三,Qt視窗座標體系

Qt的視窗座標系以左上角為原點,X 向右增加,Y 向下增加。(和opencv一樣)

對於巢狀視窗,其座標是相對於父視窗來說的。