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 自定義槽
槽函式就是訊號的處理動作,自定義槽函式和自定義的普通函式寫法是一樣的。
要求:
- 返回值是 void 型別
- 槽也是函式, 因此也支援過載
- 槽函式需要指定多少個引數, 需要看連線的訊號的引數個數
- 槽函式的引數是用來接收訊號傳送的資料的, 訊號傳送的資料就是訊號的引數
- 舉例:
訊號函式: void testsig(int a, double b);
槽函式: void testslot(int a, double b); - 總結:
槽函式的引數應該和對應的訊號的引數個數, 型別一一對應
訊號的引數可以大於等於槽函式的引數個數 == 訊號傳遞的資料被忽略了
訊號函式: void testsig(int a, double b);
槽函式: void testslot(int a);
這裡槽函式只接受訊號函式中的第一個引數
- Qt中槽函式的型別:
- 類的成員函式
- 全域性函式
- 靜態函式
- lambda表示式(匿名函式) - 槽函式可以使用關鍵字進行宣告: 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檔案,目錄結構如下圖
- 在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關鍵字,這個就是用來定義訊號的地方,訊號函式只需要定義,不需要實現!
- 在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中,與普通函式一樣,槽函式不僅需要定義,也需要實現。
- 到me.cpp中實現Me類的槽函式eat()
#include "me.h"
#include <QDebug>
Me::Me(QObject *parent) : QObject(parent)
{
}
void Me::eat()
{
qDebug() << "帶你去吃麻辣燙...";
}
ok,現在槽函式和訊號函式都已經定義實現了,那怎麼樣實現事件的響應呢,一個簡單的想法是,設定一個按鈕,點選按鈕傳送訊號:hungry,然後讓eat()響應
-
到mainwindow中新增一個按鈕Hungry,取名為hungry
如果這個時候出現在mainwindow.cpp中,無法識別這個按鈕,可以參考我的這篇部落格
Qt專案ui檔案新新增的控制元件在程式碼中不識別的問題解決 -
新增這個按鈕後,我們需要在mainwindow類中新增兩個成員指標
-
在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傳送訊號
- 在mainwindow中定義並實現函式hungrySlot,函式定義的程式碼我就不放了,大家自己去定義
下面是實現程式碼
void MainWindow::hungrySlot()
{
// 發射自定義訊號
emit m_girl->hungry();
}
注意!
這裡的emit關鍵字也是可有可不有的 ,但是還是建議大家寫,用來提醒開發人員這個函式是發射自定義訊號的函式
- 現在是最關鍵的一步啦,將按鈕與girl發射hungry訊號的函式繫結,按鈕被點選這個事件是可以由框架實現傳送的,所以不需要我們擔心
在mainwindow的建構函式中繫結,程式碼如下
connect(ui->hungry,&QPushButton::clicked,this,&MainWindow::hungrySlot);
這樣,自定義訊號槽的使用就歐克啦
接下來我們去測試一下,run一手
- 執行結果
可以看到,每次當我點選Hungry按鈕,底下就會出現 “帶你去吃麻辣燙”,證明我們自定義訊號槽成功了
編寫不易,大家要是轉載啥的記得標明一下哦~