寫在前面
本文主要介紹Qt信號槽(signal and slot)機(jī)制,介紹Qt信號槽機(jī)制的含義,用途以及如何簡單的使用。本文基于Qt5,大部分內(nèi)容來源于其文檔,文檔鏈接在此
為什么要使用信號槽機(jī)制
Qt是一種基于C++的GUI(graphic user inferface)工具庫,而在GUI應(yīng)用中,涉及到人機(jī)交互,也就是程序需要對用戶的各種操作進(jìn)行響應(yīng),這個需求本質(zhì)上就是GUI編程中的控件之間的通信問題。基于Qt的GUI程序存在著大量的控件,如按鈕,標(biāo)簽,候選框等,這些控件在編程層面也就是以對象形式存在,因此這樣控件間的通信問題實(shí)際上就是對象間的通信問題。好了,在Qt中使用的是信號槽機(jī)制實(shí)現(xiàn)對象間的通信問題,而在其他的GUI工具庫使用稱為回調(diào)(callback)的機(jī)制。
Qt信號槽機(jī)制的構(gòu)成
在Qt中,一個對象可以向另一個對象發(fā)送信號, 一個對象可以使用槽接收其他對象發(fā)送的信號,示意圖如下:通過使用connect操作,將一個對象的信號與另一個對象的槽構(gòu)建聯(lián)系,即對象的信號發(fā)出將會被另一個對象的槽接收,并且進(jìn)行設(shè)定的操作。
信號(Signal)
信號定義在Qt對象中,可以使用關(guān)鍵詞emit
發(fā)射信號,一旦信號發(fā)出,與之相連接的槽會立即執(zhí)行對應(yīng)操作。信號在對象中通常是公開可訪問的,因此可以在任何地方發(fā)射。需注意的是使用emit
發(fā)射信號時(shí),一般會立即觸發(fā)連接槽對應(yīng)的操作,因此只有當(dāng)所有與該信號相關(guān)的槽都產(chǎn)生了返回才會進(jìn)行執(zhí)行emit
后面的語句,不過這個行為與信號與槽的連接方式有關(guān),可參考如下鏈接。
在Qt中,默認(rèn)定義了很多信號,而不需要我們考慮。MOC(meta-object compiler)會對用戶編寫的.cpp
文件進(jìn)行掃描處理,生成包含Qt內(nèi)容的.cpp
文件。
槽(Slot)
槽在Qt對象中定義為函數(shù),當(dāng)所連接的信號發(fā)出時(shí),槽函數(shù)會立即被調(diào)用,由于槽函數(shù)也是定義為C++函數(shù),因此可以也直接調(diào)用。在C++定義中,一般使用關(guān)鍵詞slots
聲明Qt對象包含的槽函數(shù)。
Qt信號槽機(jī)制的使用
在實(shí)踐中,我們?nèi)绻胍褂肣t的信號槽機(jī)制就需要按照Qt的語法規(guī)范進(jìn)行編程。在自定義的對象中,我們除了需要繼承QObject
之外,還需要在對象定義中使用關(guān)鍵詞Q_OBJECT
。因?yàn)镼t會使用MOC對所有編寫的源文件進(jìn)行掃描,只有包含Q_OBJECT
的自定義對象才會有Qt提供的各種功能。一個類似的對象定義如下:
#include <QObject>
class Counter : public QObject
{
Q_OBJECT
public:
Counter() { m_value = 0; }
// 以下成員函數(shù)是const的特殊用法,表示該成員函數(shù)不能對類的數(shù)據(jù)成員進(jìn)行修改。
int value() const { return m_value; }
// 使用關(guān)鍵詞slots表示該對象存在的槽函數(shù),槽函數(shù)可以直接調(diào)用也可以通過信號觸發(fā)
public slots:
void setValue(int value);
// 使用關(guān)鍵詞signals表示該對象可發(fā)射的信號,類似于一個函數(shù)聲明,參數(shù)表示信號發(fā)射時(shí)將給槽提供的參數(shù)。
signals:
void valueChanged(int newValue);
private:
int m_value;
};
在類對象的定義中,槽函數(shù)是需要我們自己編程實(shí)現(xiàn)的,而信號那塊不需要我們考慮。
void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
// 使用關(guān)鍵詞emit發(fā)射信號
emit valueChanged(value);
}
}
在完成對象的定義后,槽函數(shù)及信號都已經(jīng)存在,接下來就是需要根據(jù)我們的需要將信號和槽進(jìn)行連接,使用connect
進(jìn)行連接,一個示例如下:
Counter a, b;
QObject::connect(&a, &Counter::valueChanged,
&b, &Counter::setValue);
a.setValue(12); // a.value() == 12, b.value() == 12
b.setValue(48); // a.value() == 12, b.value() == 48
將信號與槽進(jìn)行連接后,如果信號發(fā)出,則對應(yīng)的槽函數(shù)將被調(diào)用。在上述示例中,對象a
設(shè)置值后會發(fā)出valueChanged
的信號,這會導(dǎo)致對象b
中的槽函數(shù)觸發(fā),而對象b
的信號雖然發(fā)出,但是并沒有與對象b
中的槽函數(shù)進(jìn)行連接,因此對象a
中槽函數(shù)不會被觸發(fā)。
最后
我們知道在Qt中使用的是信號槽機(jī)制進(jìn)行對象間的通信,因此如果有對象間通信的需求,可以依據(jù)Qt約定的規(guī)范進(jìn)行編程。信號與槽的連接是基本的一步,但是在實(shí)際運(yùn)用中我們可能會遇到信號槽連接的高級運(yùn)用或者不同類型的連接,這些高級用法仍然是在基本的概念上進(jìn)行的,在構(gòu)建基本的認(rèn)識后,可以慢慢分析一些高級用法的原理。