Qt學習筆記

Qt筆記

本筆記適用于Qt5,教材來源為嗶哩嗶哩Up主“愛編程的大丙”,視頻地址:https://www.bilibili.com/video/BV1Jp4y167R9?p=31&spm_id_from=pageDriver

## Qt下載和安裝

Qt在其官網即可下載,下載鏈接為:`http://download.qt.io/archive/qt/`,可根據自己的系統版本下載對應的安裝包。

下載完成之后,在Linux系統中,首先要將安裝包權限改為可執(zhí)行文件:

```powershell

sudo chomd +x xxxx.run

```

然后運行安裝命令進行安裝:

```powershell

./xxxx.run

```

運行之后會彈出安裝界面,會提示需要賬號和密碼,因此需要提前去官網注冊好,輸入完成之后一路下一步即可,此處不再贅述。

## Qt工程目錄及含義

### Qt工程創(chuàng)建基本流程

安裝完成后打開Qt,在File選項中選擇新建文件或工程,然后選擇新建工程中的Project->Application,選擇Qt Widgets Application選項,如下圖所示:

![](./02.gif)

然后點擊choose進入下一步,設置項目名稱和存儲地址,進入下一步,Build system選擇qmake,Classname設定之后繼續(xù)下一步,剩下的一路下一步即可,其他的都不用操作。

Qt工程目錄分為四個部分:

- project文件:描述Qt項目的文件及依賴關系

- Headers:QT項目中的頭文件

- Sources:Qt項目中的源文件

- Forms:Qt項目中的ui文件,其可用QtDesinger打開編輯

## 信號和槽

### 概述

所謂信號槽,實際就是ROS的發(fā)布者和訂閱者的模式,分為信號和槽兩部分。

當某個事件發(fā)生之后對應的對象發(fā)出特定信號(Signal),這個信號沒有明確的接收者,所有接收到這個信號的觀察者只要需要都可以通過connect函數對這個信號做出響應,將想要處理的信號和自己的一個函數(稱為槽,slot)綁定來處理這個信號,也就是說**當信號發(fā)出時,被連接的槽函數會自動被回調**,這就類似觀察者模式:當感興趣的事發(fā)生時,某一個操作就會被自動觸發(fā)。

### 信號槽本質

#### 信號本質

信號是由于用戶對窗口或控件進行了某項操作,導致窗口或控件產生了某個特定事件,這時Qt對應的窗口類會發(fā)出某個信號,以此對用戶的操作做出反應。

因此根據上述的描述我們得到一個結論:信號的本質就是事件。比如:

- 單擊、雙擊按鈕

- 窗口刷新

- 鼠標移動、按下、釋放

- 鍵盤輸入

那么在Qt中信號通過什么方式呈獻給用戶呢?

- 我們對哪個窗口進行操作,該窗口就可以捕捉到這些被觸發(fā)的事件

- 對于使用者來說觸發(fā)了一個事件我們就可以得到Qt框架給我們發(fā)出的某個特定信號

- 信號的呈現形式就是函數,也就是說某個事件產生了,Qt框架就會調用某個對應的信號函數,通知使用者。

在Qt中信號的發(fā)出者是**某個實例化的類對象**,對象內部可以進行相關事件的檢測。

#### 槽本質

在Qt中槽是一種特殊的功能函數,在編碼中,也可以作為類的普通函數來使用。之所以稱為槽函數,是因為他們還有一個職責就是對Qt框架中產生的信號進行處理。

例如:

女朋友(一種不存在的東西)說:“我餓了”,于是我?guī)コ燥?/p>

上面例子中相當于女朋友發(fā)出了一個信號,我收到了信號并將其處理掉了。

- 女朋友->發(fā)送信號的對象

- 信號內容:我餓了

- 我->接收信號的對象

- 處理方式:帶她去吃飯

在Qt中槽函數的所有者也是**某個類的實例對象。**

#### 信號和槽的關系

在Qt中信號和槽都是獨立的個體,本身沒有任何聯系,但由于某種特性需求我們可以將二者連接到一起,好比牛郎和織女需要喜鵲為他們搭橋一樣。在Qt中我們需要QObject中的connect函數進行二者的鏈接。

connect函數的函數原型:

```C++

QMetaObject::Connection QObject::connect(const QObject *sender,

const char *signal,

const QObject *receiver,

const char *method,

Qt::ConnectionType type = Qt::AutoConnection)

```

參數:

- sender:發(fā)出信號的對象

- signal:屬于sender對象,信號是一個函數,**這個參數的類型是函數指針,指向信號函數的地址**

- receiver:接收信號的對象

- method:屬于receiver對象,當檢測到sender發(fā)出了signal信號,receiver對象調用method方法,進行信號發(fā)出之后的處理動作

注意事項:

- connect函數相當于對信號處理動作的注冊;

- 調用connect函數的sender對象的信號并沒有馬上產生,因此receiver對象的method函數也不會馬上被調用;

- method槽函數的本質是一個回調函數,調用的時機是信號產生之后,調用是Qt框架來執(zhí)行的;

- connect中的sender和receiver兩個指針必須被實例化了,否則connect不會成功

函數原型精簡之后可以寫作:

```C++

connect (

const QObject *sender,const &QObject::signal,

const QObject *receiver,const &QObject::method

)

```

## 標準信號槽使用

### 標準信號/槽

Qt提供的很多標準類中紅都可以對用戶觸發(fā)的某些信號進行檢測,因此當用戶做了這些操作以后,事件被觸發(fā),類的內部就會產生對應的信號,這些信號都是Qt類內部自帶的,因此稱之為標準信號。

同樣的,在Qt的很多類內部為我們提供了很多功能函數,并且這些功能函數也可以作為觸發(fā)的信號的處理動作,有這類性質的函數在Qt中稱之為標準槽函數。

系統自帶的信號和槽如何查找呢?可以通過幫助文檔,如下圖所示:

#### 使用舉例

實現點擊按鈕關閉窗口的功能。

設計對象:按鈕和窗口。

- 按鈕發(fā)出點擊信號->`QPushbutton`,`QPushbutton::clicked`

- 窗口接收點擊信號并進行處理->`this`,`QMainwindow::close`

首先在創(chuàng)建的工程中的ui文件夾Forms中雙擊mainwindow.ui進行編輯,在其中添加一個按鈕:

![](./03.png)

在其中修改兩個部分:

1. PushButton的名字,

2. PushButton的objectName選項

修改之后的界面如下圖所示:

![](./07.png)

然后對source中的mainwindow.cpp文件進行編輯,加入如下代碼片段

```C++

connect(ui->closeButton,&QPushButton::clicked,this,&QMainWindow::close);

//信號發(fā)出對象為ui->closeButtopn按鈕,發(fā)送的信號為&QPushButton::clicked

//信號接收對象為this,也就是本窗口,接收信號后的處理槽函數為&QMainWindow::close

//注意,connect函數中的信號函數和槽函數參數分別是這兩個函數的地址。

```

然后編譯程序并運行,其界面如下:

![](./04.png)

### 自定義信號/槽

Qt框架提供的信號槽在某些特定的場景下無法滿足我們的項目需求,因此我們還需要設計自己需要的信號和槽,同樣還是使用connect函數對自定義的信號槽進行鏈接

如果要使用自己定義的信號槽,首先要編寫新的類并讓其繼承Qt的某些標準類,如果我們自己編寫的類想要在Qt中使用信號槽機制,那么必須要滿足如下條件:

- 這個類必須從QObject類或者是其子類進行派生

- 在自定義的頭文件中加入Q_OBJECT宏

在頭文件派生的時候,首先像下面那樣引入Q_OBJECT宏:

```C++

class MyMainWindow : public QWidget

{

? ? Q_OBJECT

? ? ......

}

```

#### 自定義信號

自定義信號的要求:

- 信號是類的成員函數

- 返回值必須是void類型

- 信號的名字可以根據實際情況進行指定

- 信號可以隨意指定,信號也支持重載

- 信號需要使用signal關鍵字進行生命,使用方法類似于public等關鍵字

- 信號函數只需要生命,不需要定義

- 在程序中發(fā)送自定義信號:發(fā)從信號的本質就是調用信號函數,習慣性在信號前面加上關鍵字emit,emit提示這是在發(fā)送信號,沒有其他含義和功能

舉例:信號重載

Qt中的類想要使用信號槽機制必須要從QObject類派生(直接或間接派生都可以)

```C++

class test:public QObject

{

? ? Q_OBJECT

? ? signals:

? ? ? ? void testsignal();

? ? ? ? void testsignal(int a); //重載

};

```

#### 自定義槽

槽就是信號的處理函數,自定義槽和自定義其他函數一樣,沒什么區(qū)別

自定義槽的要求:

- 返回值是void類型

- 槽也是函數,也支持重載:

? - 槽函數需要指定多少個參數,需要看鏈接的信號的參數個數

? - 槽函數的參數是用來接收信號發(fā)送的數據的,信號發(fā)送的數據就是信號的參數。例如:

? ? - 信號函數:`void testsig(int a , double b)`

? ? - 槽函數:`void testslot(int a , double b)`

? - 總結:

? ? - 槽函數的參數應該和對應的信號的參數個數和數據類型一一對應

? ? - 信號的參數可以大于等于槽函數的參數個數,信號傳遞的數據被忽略了

? ? ? - 信號函數:void testsig(int a, double b);

? ? ? - 槽函數:void testslot(int a);

- Qt中槽函數的類型:

? - 類的成員函數

? - 全局函數

? - 靜態(tài)函數

? - labmda表達式(匿名函數

- 槽函數可以使用關鍵字進行聲明:slots(Qt5中可以忽略不寫)

? - public slots

? - private slots

? - protected slots

舉例:類中的這三個函數都可以作為槽函數來使用:

```C++

class Test:public QObject

{

? ? public:

? ? ? ? void testSlot();

? ? ? ? static void testFunc();

? ? public slots:

? ? ? ? void testSlots(int id);

};

```

場景舉例:女朋友餓了 我請他吃飯

```c++

class GirlFriend;

class Me;

```

創(chuàng)建自定義類流程:

1. 首先在項目名右鍵點擊Add New,在彈出的窗口選擇C++類,

2. 在define class 中填入類名并選擇父類QObject:

![](./05.png)

3. 最后點擊下一步完成創(chuàng)建。

創(chuàng)建完成之后首先修改GirlFriend類,在頭文件中的signals中添加函數hungry:

```c++

//GirlFriend.h

#ifndef GIRLFRIEND_H

#define GIRLFRIEND_H

#include <QObject>

class GirlFriend : public QObject

{

? ? Q_OBJECT

public:

? ? explicit GirlFriend(QObject *parent = nullptr);

signals:

? ? void hungry();

};

#endif // GIRLFRIEND_H

```

此處需注意,**寫在signals里的hungry函數不需要定義,只需要聲明就好了**。

然后在Me類中添加處理動作,編輯Me.h,在其中加入處理動作(槽函數)聲明:

```c++

public slots:

? ? void feed();

```

在Me.cpp文件中添加槽函數的定義:

```c++

void Me::feed()

{

? ? qDebug()<<"let's go for food!"<<endl;

}

```

編輯完成之后的文件如下:

```c++

//Me.h

#ifndef ME_H

#define ME_H

#include <QObject>

class Me : public QObject

{

? ? Q_OBJECT

public:

? ? explicit Me(QObject *parent = nullptr);

public slots:

? ? void feed();

};

#endif // ME_H

```

```C++

//Me.cpp

#include "me.h"

#include <QDebug>

Me::Me(QObject *parent) : QObject(parent)

{

}

void Me::feed()

{

? ? qDebug()<<"let's go for food!"<<endl;

}

```

最后在窗口中用connect來鏈接這兩個對象。

首先在頭文件中添加這兩個對象:

```C++

#ifndef MAINWINDOW_H

#define MAINWINDOW_H

#include "me.h"

#include "girlfriend.h"

#include <QMainWindow>

QT_BEGIN_NAMESPACE

namespace Ui { class MainWindow; }

QT_END_NAMESPACE

class MainWindow : public QMainWindow

{

? ? Q_OBJECT

public:

? ? MainWindow(QWidget *parent = nullptr);

? ? ~MainWindow();

private:

? ? Ui::MainWindow *ui;

? ? Me *m_me;

? ? GirlFriend *m_girl;

};

#endif // MAINWINDOW_H

```

最后在CPP文件中鏈接二者:

```C++

connect(m_girl,&GirlFriend::hungry,m_me,&Me::feed);

```

此時有一個問題,hungry信號是自定義的,框架無法直接自動發(fā)送,因此需要設計一些觸發(fā)機制觸發(fā)hungry信號,這里使用按鈕觸發(fā)信號。

打開ui文件,添加新的按鈕如下:

![](./06.png)

并在mainwindow文件中添加按鈕和hungry信號的鏈接:

```C++

connect(ui->hungryButton,&QPushButton::clicked,this,&MainWindow::hungry_slot);

```

信號發(fā)出者為按鈕,發(fā)出信號為按鈕按下,信號接受者為m_girl,接收信號后的處理函數為&GirlFriend::hungry,即觸發(fā)hungry函數。

其中`MainWindow::hungry_slot`函數為自定義的槽函數,其作用是使m_girl對象發(fā)送hungry信號:

```c++

void MainWindow::hungry_slot()

{

? emit m_girl->hungry();//加不加emit沒區(qū)別,唯一的作用就在于提示程序員現在是在發(fā)送信號

}

```

### 信號槽拓展

#### 信號槽使用拓展

1. 一個信號可以鏈接多個槽函數,發(fā)送一個信號可以有多個處理動作

? - 需要寫多個connect函數

? - 槽函數的執(zhí)行順序是隨機的,和connect函數的調用順序沒有關系

? - 信號的接收者可以使一個對象,也可以是多個對象

2. 一個槽可以連接多個不同的信號

3. 信號可以連接信號,

? - 信號接收者可以不處理接收的信號,繼續(xù)發(fā)出新的信號,其傳遞了參數,但并未對其進行處理。

```C++

connect(m_girl,&GirlFriend::hungry,m_me,&Me::feed);

connect(ui->hungryButton,&QPushButton::clicked,this,&MainWindow::hungry_slot);

```

精簡為:

```C++

connect(ui->hungryButton,&QPushButton::clicked,m_girl,&GirlFriend::hungry);

```

4. 信號槽是可以斷開的

```C++

disconnect (

const QObject *sender,const &QObject::signal,

const QObject *receiver,const &QObject::method

)

```

斷開信號槽和鏈接信號槽除了函數名不同其他完全相同。

#### 信號槽的兩種連接方式

1. Qt5的連接方式:推薦的使用方式

? ```C++

? connect(ui->hungryButton,&QPushButton::clicked,m_girl,&GirlFriend::hungry);

? ```

2. Qt4的連接方式:不推薦的使用方式

? ```C++

? connect(ui->hungryButton,SIGNAL(QPushButton::clicked()),m_girl,SLOT(GirlFriend::hungry()));

? ```

### 信號槽之間如何傳遞信號

Qt5信號和槽的連接方式如下:

```C++

connect(ui->hungryButton,&QPushButton::clicked,m_girl,&GirlFriend::hungry);

```

這里面看不到信號是如何傳遞的,那兩個函數的信號如何傳遞呢?是通過兩個函數的參數來傳遞的。

如,信號函數為:

```c++

void hungry(QString msg);

```

則槽函數可以寫為:

```c++

void eat(QString msg);

```

然后在觸發(fā)信號函數中使用函數`hungry("XXXX")`,框架會將`"XXXX"`從hungry函數傳遞到eat函數,實現參數傳遞。

這里存在一個問題,如果是Qt4的鏈接方式,鏈接函數可寫為:

```C++

connect(m_girl,SIGNAL(GirlFriend::hungry(QString)),m_me,SLOT(Me::feed(String)));

```

這種調用方式可以明確的指出調用的是函數的哪個重載類型,這里就可以直接運行了。

但是對于QT5的連接方式,其鏈接函數寫為:

```C++

connect(m_girl,GirlFriend::hungry,m_me,Me::feed);

```

這種情況會報錯,因為編譯器搞不清楚你到底想調用signal和slot函數的哪個重載類型,針對這個問題有兩個解決方法:

1. 用Qt4的調用方法;

2. 創(chuàng)建函數指針,指明到底要采用哪個函數重載類型。

一般選擇第二種方法,其代碼如下:

```C++

void (GirlFriend::*girl_1)()=&GirlFriend::hungry;

void (GirlFriend::*girl_2)(QString msg)=&GirlFriend::hungry;

void (Me::*feed_1)()=&Me::feed;

void (Me::*feed_2)(QString msg)=&Me::feed;

connect(m_girl,girl_1,m_me,feed_1);

connect(m_girl,girl_2,m_me,feed_2);

```

## QT界面與ROS鏈接

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 98,786評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,986評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,964評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,354評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,554評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 49,106評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,918評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,093評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,342評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,839評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,107評論 2 375

推薦閱讀更多精彩內容

  • 1、信號和槽 Qt中的事件處理機制叫“信號和槽”,signal和slot。模型:對象a中有一個信號signal:X...
    行走行囊閱讀 428評論 0 0
  • 坐標系統 物理坐標,是指顯式出來的;邏輯坐標是不會變化的。 信號槽 signal 類似廣播,發(fā)出一個信號,感興趣的...
    TocomPass閱讀 1,265評論 0 0
  • (在b站學習QT,順便做做筆記 2019年最新QT從入門到實戰(zhàn)完整版|傳智播客) 1.顯示窗口; myWidget...
    冰糖葫蘆多加冰閱讀 1,100評論 0 1
  • 1、Widget Widget:在Qt中,把窗體稱作Widget。不僅僅是帶邊框的窗口叫Widget,其他所有界面...
    行走行囊閱讀 2,802評論 0 3
  • 界面 主窗口界面設計 標題欄:直接設Window-Title屬性;Window-icon屬性可加圖標。底部狀態(tài)欄:...
    碼園老農閱讀 3,784評論 1 13