參考書籍:《Qt學習之路》&《Qt Creator快速入門》
概述
信号和槽用于兩個對象之間的通信。信号和槽機制是Qt的核心特征,也是Qt不同于其它開發架構的最突出特征。熟練使用和了解信号槽,能夠設計出解耦的程式。
信号槽實際就是觀察者模式。當某個事件發生時,對象就會發出一個信号(signal)。這種發出是沒有目的的,類似于廣播。如果有對象對這個信号感興趣,它就會使用連接配接(connect)函數,用自己的一個函數(稱為槽(slot))來處理這個信号。
使用
為了使用信号槽,必須繼承QObject。凡是QObject類(不管是直接子類還是間接子類),都應該在第一行代碼寫上Q_OBJECT。這是一個宏,在預處理器之前展開。
一般形式:
//Qt5
connect (sender, signal, receiver, slot);
QObject::connection()的五個重載:
QMetaObject::Connection connect (const QObject *, const char *, const QObject *, const char *, Qt::ConnecionType);
QMetaObject::Connection connect (const QObject *, const QMetaObject &, const QObject *, const QMetaObject &, Qt::ConnecionType);
QMetaObject::Connection connect (const QObject *, const char *, const char *, Qt::ConnecionType) const;
QMetaObject::Connection connect (const QObject *, PointerToMemberFunction, const QObject *,PointerToMemberFunction, Qt::ConnecionType);
QMetaObject::Connection connect (const QObject *, PointerToMemberFunction, Functor);
借助Qt5的信号槽文法,可以将一個對象的信号連接配接到Lambda表達式:
// !!! Qt 5
#include <QApplication>
#include <QPushButton>
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPushButton button("Quit");
QObject::connect(&button, &QPushButton::clicked, [](bool) {
qDebug() << "You clicked me!";
});
button.show();
return app.exec();
}
自定義信号槽
//!!! Qt5
#include <QObject>
// newspaper.h
class Newspaper : public QObject
{
Q_OBJECT
public:
Newspaper(const QString & name) :
m_name(name)
{
}
void send()
{
emit newPaper(m_name);
}
signals:
void newPaper(const QString &name);
private:
QString m_name;
};
// reader.h
#include <QObject>
#include <QDebug>
class Reader : public Q Object
{
Q_OBJECT
public:
Reader() {}
void receiveNewspaper(const QString & name)
{
qDebug() << "Receives Newspaper: " << name;
}
};
// main.cpp
#include <QCoreApplication>
#include "newspaper.h"
#include "reader.h "
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Newspaper newspaper("Newspaper A");
Reader reader;
QObject::connect(&newspaper, &Newspaper::newPaper, &reader, &Reader::receiveNewspaper);
newspaper.send();
return app.exec();
}
signals塊所列出的,就是該類的信号。信号就是一個個的函數名,傳回值是void(因為無法獲得信号的傳回值,是以也就無需傳回任何值),參數是該類需要讓外界知道的資料。信号作為函數名,不需要在cpp函數中添加任何實作(我們曾經說過,Qt程式能夠使用普通的make進行編譯。沒有實作的函數名怎麼會通過編譯?原因還是在moc,moc會幫我們實作信号函數所需要的函數體,是以說,moc 并不是單純的将 Q_OBJECT展開,而是做了很多額外的操作)。
自定義信号槽需要注意的事項:
o 發送者和接收者都需要是 QObject 的子類(當然,槽函數是全局函數、Lambda表達式等無需接收者的時候除外);
o 使用signals标記信号函數,信号是一個函數聲明,傳回 void,不需要實作函數代碼;
o 槽函數是普通的成員函數,作為成員函數,會受到public、private、protected 的影響;
o 使用 emit 在恰當的位置發送信号;
o 使用 QObject::connect()函數連接配接信号和槽。
o 槽中參數的類型要和信号參數的類型相對應,且不能比信号的參數多。信号中多餘的參數将被忽略。
在調用connect()函數時,信号和槽的參數隻能有類型,不能有變量名。
此外,還可以用disconnection()來斷開關聯:
QObject::disconnect(const QMetaObject::Connect &connection);