用QT創建的第一個工程


摘要:本文主要是利用向導建立了第一個qt工程文件,主要介紹了工程文件的結構、main函數、按鈕的建立、qt中的對象樹、坐標系、qt中的信號和槽等概念。

1、工程文件的結構

利用qt導向建立好工程文件以后,會自動生成main函數、頭文件、源文件和Pro文件,如下圖:

2、main函數

在這個main函數中,主要創建了一個窗口對象w,調用構造函數,實現一些按鈕、信號和槽的功能。

 1 #include "mywidget.h" //包含頭文件
 2 #include <QApplication> //包含QApplication頭文件
 3 
 4 //程序入口 argc命令行變量的數量 argv命令行變量數組
 5 int main(int argc, char *argv[])
 6 {
 7     QApplication a(argc, argv); //a 應用程序對象,對於Qt項目必須有應用程序對象,而且有且僅有一個
 8     MyWidget w; //創建一個MyWidget對象
 9     w.show(); //創建出的窗口對象並不會直接顯示,需要調用show方法
10 
11     return a.exec(); //進入消息循環機制,阻塞狀態
12 //    while(true)
13 //    {
14 //        if(點擊叉子)
15 //            break;
16 //    }
17 }

3、myweiget.cpp和myweiget.h

在頭文件中,重點注意類中加了“Q_OBJECT”,構造函數有默認參數。

 1 #ifndef MYWIDGET_H
 2 #define MYWIDGET_H
 3 
 4 #include <QWidget>
 5 
 6 //繼承於QWidget
 7 class MyWidget : public QWidget
 8 {
 9     //支持Qt中的信號和槽使用
10     Q_OBJECT
11 
12 public:
13     MyWidget(QWidget *parent = 0); //構造
14     ~MyWidget(); //析構
15 };
16 
17 #endif // MYWIDGET_H

在源文件中,注意函數變量的名字兩個單詞之間用大寫字母隔開;注意創建按鈕的兩種方法;為按鈕添加文本,設置按鈕位置,設置按鈕大小;重置窗口的大小和窗口名稱等方法。重點是學會查看幫助文檔。

 1 #include "mywidget.h"
 2 #include<QPushButton>
 3 #include "mybutton.h"
 4 #include <QDebug>
 5 
 6 MyWidget::MyWidget(QWidget *parent)
 7     : QWidget(parent)
 8 {
 9     QPushButton * btn =new QPushButton;
10 
11     // btn->show();
12     //btn應該依賴於主窗口
13     btn->setParent(this);
14     //顯示文字
15     btn->setText("德瑪");
16 
17     //第二種創建方式
18     QPushButton * btn2 = new QPushButton("德瑪西亞",this);
19     //移動窗口
20     btn2->move(100,100);
21     //重置窗口大小
22     resize(960,640);
23 
24     //btn可不可以 resize? 可以
25     btn2->resize(50,50);
26 
27     //設置窗口標題名稱
28     this->setWindowTitle("德瑪西亞萬歲");
29 
30     //對象樹
31     MyButton * myBtn = new MyButton();
32     myBtn->setParent(this);
33     myBtn->move(200,200);
34     myBtn->setText("我的按鈕");
35 
36     //窗體的坐標系
37     //左上角為 0 0 點
38     // x 以右側為正方向  y 以下側為正方向
39 
40     //需求 點擊“我的按鈕” ,關閉窗口
41     //連接信號槽的關鍵字 connect
42     //4個參數 參數1 信號發送者   參數2  發送的信號   參數3  信號的接受者  參數4 處理的槽函數
43     //參數2 和參數4 需要的都是函數地址
44 
45     connect(myBtn,&QPushButton::clicked,this,&MyWidget::close);
46 
47 }
48 
49 MyWidget::~MyWidget()
50 {
51     qDebug("MyWidget析構了!");
52 }

4、對象樹

先來看一幅圖:

(1)在qt中,QObject是以對象樹的形式組織起來的。

  •  當你創建一個QObject對象時,會看到QObject的構造函數接收一個QObject指針作為參數,這個參數就是 parent,也就是父對象指針。這相當於,在創建QObject對象時,可以提供一個其父對象,我們創建的這個QObject對象會自動添加到其父對象的children()列表例如上面的3中的構造函數MyWidget::MyWidget(QWidget *parent): QWidget(parent)
  • 當父對象析構的時候,這個列表中的所有對象也會被析構。(注意,這里的父對象並不是繼承意義上的父類!)這種機制在 GUI 程序設計中相當有用。例如,一個按鈕有一個QShortcut(快捷鍵)對象作為其子對象。當我們刪除按鈕的時候,這個快捷鍵理應被刪除。這是合理的。

(2)QWidget是能夠在屏幕上顯示的一切組件的父類。

  • QWidget繼承自QObject,因此也繼承了這種對象樹關系。一個孩子自動地成為父組件的一個子組件。因此,它會顯示在父組件的坐標系統中,被父組件的邊界剪裁。例如,當用戶關閉一個對話框的時候,應用程序將其刪除,那么,我們希望屬於這個對話框的按鈕、圖標等應該一起被刪除。事實就是如此,因為這些都是對話框的子組件。
  • 當然,我們也可以自己刪除子對象,它們會自動從其父對象列表中刪除比如,當我們刪除了一個工具欄時,其所在的主窗口會自動將該工具欄從其子對象列表中刪除,並且自動調整屏幕顯示。
  • Qt 引入對象樹的概念,在一定程度上解決了內存問題。

(3)當一個QObject對象在堆上創建的時候,Qt 會同時為其創建一個對象樹。不過,對象樹中對象的順序是沒有定義的。這意味着,銷毀這些對象的順序也是未定義的。

(4)任何對象樹中的 QObject對象 delete 的時候,如果這個對象有 parent,則自動將其從 parent 的children()列表中刪除;如果有孩子,則自動 delete 每一個孩子。Qt 保證沒有QObject會被 delete 兩次,這是由析構順序決定的。

(5)注意的問題:

如果QObject在棧上創建,Qt 保持同樣的行為。正常情況下,這也不會發生什么問題。來看下下面的代碼片段:

1 {
2     QWidget window;
3     QPushButton quit("Quit", &window);
4 }

作為父組件的 window 和作為子組件的 quit 都是QObject的子類(事實上,它們都是QWidget的子類,而QWidget是QObject的子類)。這段代碼是正確的,quit 的析構函數不會被調用兩次,因為標准 C++要求,局部對象的析構順序應該按照其創建順序的相反過程。因此,這段代碼在超出作用域時,會先調用 quit 的析構函數,將其從父對象 window 的子對象列表中刪除,然后才會再調用 window 的析構函數。

但是,如果我們使用下面的代碼:

1 {
2     QPushButton quit("Quit");
3     QWidget window;
4     quit.setParent(&window);
5 }

情況又有所不同,析構順序就有了問題。我們看到,在上面的代碼中,作為父對象的 window 會首先被析構,因為它是最后一個創建的對象。在析構過程中,它會調用子對象列表中每一個對象的析構函數,也就是說, quit 此時就被析構了。然后,代碼繼續執行,在 window 析構之后,quit 也會被析構,因為 quit 也是一個局部變量,在超出作用域的時候當然也需要析構。但是,這時候已經是第二次調用 quit 的析構函數了,C++ 不允許調用兩次析構函數,因此,程序崩潰了。

由此我們看到,Qt 的對象樹機制雖然幫助我們在一定程度上解決了內存問題,但是也引入了一些值得注意的事情。這些細節在今后的開發過程中很可能時不時跳出來煩擾一下,所以,我們最好從開始就養成良好習慣,在 Qt 中,盡量在構造的時候就指定 parent 對象,並且大膽在堆上創建。

(6)總結:

  • 所有new出來的對象 不用管釋放
  • 原因 children表中的對象會在窗口關閉后進行自動釋放

(7)測試代碼

mybutton.h

 1 #ifndef MYBUTTON_H
 2 #define MYBUTTON_H
 3 
 4 #include <QWidget>
 5 #include <QPushButton>
 6 
 7 class MyButton : public QPushButton
 8 {
 9     Q_OBJECT
10 public:
11     explicit MyButton(QWidget *parent = 0);
12     ~MyButton();
13 signals:
14 
15 public slots:
16 };
17 
18 #endif // MYBUTTON_H

mybutton.cpp

 1 #include "mybutton.h"
 2 #include <QDebug>
 3 MyButton::MyButton(QWidget *parent) : QPushButton(parent)
 4 {
 5 
 6 }
 7 
 8 MyButton::~MyButton()
 9 {
10     qDebug() << "MyButton調用析構了!";
11 
12 }

上面代碼的執行結果是:

MyWidget析構了!

MyButton調用析構了!

這樣的析構順序和我我們想象的不太一致,我們會認為是先析構按鈕再析構窗口,然而顯示的結果卻不是這樣的。實際上我們的想法是正確的,這是因為在執行到窗口析構的過程中,會出現按鈕的析構,等按鈕析構完成以后,再返回窗口析構。

5、窗口的坐標系

原點在左上角

6、信號槽的基本使用

具體的使用見代碼


免責聲明!

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



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