Qt自定義信號槽的使用淺析+實例


1. Qt中自定義信號槽的使用

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

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

  • 這個類必須從QObject類或者是其子類進行派生
  • 在定義類的頭文件中加入 Q_OBJECT 宏

1.1 自定義信號

要求:
1. 信號是類的成員函數
2. 返回值是 void 類型
3. 信號的名字可以根據實際情況進行指定
4. 參數可以隨意指定, 信號也支持重載
5. 信號需要使用 signals 關鍵字進行聲明, 使用方法類似於public等關鍵字
6. 信號函數只需要聲明, 不需要定義(沒有函數體實現)
7. 在程序中發送自定義信號: 發送信號的本質就是調用信號函數

  • 習慣性在信號函數前加關鍵字: emit
  • emit只是顯示的聲明一下信號要被發送, 沒有特殊含義
  • 底層 emit == #define emit

示例

class Test : public QObject
{
    Q_OBJECT
signals:
    void testsignal();
	// 參數的作用是數據傳遞, 誰調用信號函數誰就指定實參
	// 實參最終會被傳遞給槽函數
    void testsignal(int a);
};

1.2 自定義槽

槽函數就是信號的處理動作,自定義槽函數和自定義的普通函數寫法是一樣的。

要求:

  1. 返回值是 void 類型
  2. 槽也是函數, 因此也支持重載
  • 槽函數需要指定多少個參數, 需要看連接的信號的參數個數
  • 槽函數的參數是用來接收信號發送的數據的, 信號發送的數據就是信號的參數
  • 舉例:
    信號函數: void testsig(int a, double b);
    槽函數: void testslot(int a, double b);
  • 總結:
    槽函數的參數應該和對應的信號的參數個數, 類型一一對應
    信號的參數可以大於等於槽函數的參數個數 == 信號傳遞的數據被忽略了
    信號函數: void testsig(int a, double b);
    槽函數: void testslot(int a);
    這里槽函數只接受信號函數中的第一個參數
  1. Qt中槽函數的類型:
    - 類的成員函數
    - 全局函數
    - 靜態函數
    - lambda表達式(匿名函數)
  2. 槽函數可以使用關鍵字進行聲明: slots (Qt5中slots可以省略不寫)
    - public slots:
    - private slots:
    - protected slots:
// 舉例
// 類中的這三個函數都可以作為槽函數來使用
class Test : public QObject
{
public:
    void testSlot();
    static void testFunc();

public slots:
    void testSlot(int id);
};

1.3 自定義信號槽實例

現在有一個場景,女朋友餓了,我請她吃飯,那么實現這個功能應該怎么做呢
首先明確發送者,接收者,信號和槽分別是哪些

  • 發送者: 女朋友
  • 接收者: 我
  • 信號: 餓了
  • 槽:請她吃飯

ok,明確了這些,接下來我們就可以開始寫代碼了

首先創建兩個類,GirlFriend 和 Me
Qt Creator中會自動為我們添加頭文件和CPP文件,目錄結構如下圖
在這里插入圖片描述

  1. 在GirlFriend類中,添加信號hungry,代碼如下
#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關鍵字,這個就是用來定義信號的地方,信號函數只需要定義,不需要實現!

  1. 在Me這個類中添加槽函數eat();
#ifndef ME_H
#define ME_H

#include <QObject>

class Me : public QObject
{
    Q_OBJECT
public:
    explicit Me(QObject *parent = nullptr);

    // 槽函數
public slots:
    // 槽函數
    void eat();

};

#endif // ME_H

注意!
這里用public slots主要是為了提醒開發者,這是一個槽函數,事實上,可以不用單獨用public slots,可以直接將這個槽函數放到public中,與普通函數一樣,槽函數不僅需要定義,也需要實現。

  1. 到me.cpp中實現Me類的槽函數eat()
#include "me.h"
#include <QDebug>
Me::Me(QObject *parent) : QObject(parent)
{

}

void Me::eat()
{
    qDebug() << "帶你去吃麻辣燙...";
}

ok,現在槽函數和信號函數都已經定義實現了,那怎么樣實現事件的響應呢,一個簡單的想法是,設置一個按鈕,點擊按鈕發送信號:hungry,然后讓eat()響應

  1. 到mainwindow中添加一個按鈕Hungry,取名為hungry
    在這里插入圖片描述
    如果這個時候出現在mainwindow.cpp中,無法識別這個按鈕,可以參考我的這篇博客
    Qt項目ui文件新添加的控件在代碼中不識別的問題解決

  2. 添加這個按鈕后,我們需要在mainwindow類中添加兩個成員指針
    在這里插入圖片描述

  3. 在mainwindow.cpp中通過connect函數來綁定

在這里我再復習一遍Qt中的 connect() 函數

QMetaObject::Connection QObject::connect(
    	const QObject *sender, PointerToMemberFunction signal, 
        const QObject *receiver, PointerToMemberFunction method, 
		Qt::ConnectionType type = Qt::AutoConnection);
- 參數:
	- sender: 發出信號的對象
	- signal: 屬於sender對象, 信號是一個函數, 這個參數的類型是函數指針, 信號函數地址
    - receiver: 信號接收者
	- method: 屬於receiver對象, 當檢測到sender發出了signal信號, 
              receiver對象調用method方法,信號發出之后的處理動作
                  
// connect函數相對於做了信號處理動作的注冊
// 調用conenct函數的sender對象的信號並沒有產生, 因此receiver對象的method也不會被調用
// method槽函數本質是一個回調函數, 調用的時機是信號產生之后, 調用是Qt框架來執行的
// connect中的sender和recever兩個指針必須被實例化了, 否則conenct不會成功
connect(const QObject *sender, &QObject::signal, 
        const QObject *receiver, &QObject::method);

知道connect函數的用法之后,我們先將my_girl發送信號,m_me(m_girl和m_me 是上面加的兩個成員指針)接受信號綁定在一起
在mainwindow.cpp的構造函數中添加如下語句

	m_me = new Me;
    m_girl = new GirlFriend;

    // hungry信號是自定義的,它不能由框架去發送,因為框架壓根就不知道有這個信號的存在,因此需要在特定的時機,使用者自己去發射這個信號
    connect(m_girl,&GirlFriend::hungry,m_me,&Me::eat);
    

注意connect上面的注釋
hungry信號是自定義的,它不能由框架去發送,因為框架壓根就不知道有這個信號的存在,因此需要在特定的時機,使用者自己去發射這個信號

簡單理解就是,你的girl要發送hungry這個信號是不能自動完成的,因為Qt框架不知道hungry這個信號,你要通過點擊按鈕來讓girl發送信號,因此需要另外一個函數,取名為hungrySlot(),用來實現點擊按鈕,讓girl發送信號

  1. 在mainwindow中定義並實現函數hungrySlot,函數定義的代碼我就不放了,大家自己去定義
    下面是實現代碼
void MainWindow::hungrySlot()
{
    // 發射自定義信號
    emit m_girl->hungry();
}

注意!
這里的emit關鍵字也是可有可不有的 ,但是還是建議大家寫,用來提醒開發人員這個函數是發射自定義信號的函數

  1. 現在是最關鍵的一步啦,將按鈕與girl發射hungry信號的函數綁定,按鈕被點擊這個事件是可以由框架實現發送的,所以不需要我們擔心

在mainwindow的構造函數中綁定,代碼如下


connect(ui->hungry,&QPushButton::clicked,this,&MainWindow::hungrySlot);

這樣,自定義信號槽的使用就歐克啦

接下來我們去測試一下,run一手

  1. 運行結果

在這里插入圖片描述
可以看到,每次當我點擊Hungry按鈕,底下就會出現 “帶你去吃麻辣燙”,證明我們自定義信號槽成功了

編寫不易,大家要是轉載啥的記得標明一下哦~


免責聲明!

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



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