信號槽是QT中用於對象間通信的一種機制,也是QT的核心機制。在GUI編程中,我們經常需要在改變一個組件的同時,通知另一個組件做出響應。例如:
一開始我們的Find按鈕是未激活的,用戶輸入要查找的內容後,查找按鈕就被激活,這就是輸入框與Find按鈕這兩個組件間通信的例子。
早期,對象間的通信采用回調來實現。回調實際上是利用函數指針來實現,當我們希望某件事發生時處理函數能夠獲得通知,就需要將回調函數的指針傳遞給處理函數,這樣處理函數就會在合適的時候調用回調函數。回調有兩個明顯的缺點:
在QT中,我們有回調技術之外的選擇,也即是信號槽機制。所謂的信號與槽,其實都是函數。當特定事件被觸發時(如在輸入框輸入了字符)將發送一個信號,而與該信號建立的連接槽,則可以接收到該信號並做出反應(激活Find按鈕)。
QT組件預定義了很多信號和槽,而在GUI編程中,我們習慣於繼承那些組件,繼承後添加我們自己的槽,以便以我們的方式來處理信號。槽和普通的C++成員函數幾乎是一樣的,它可以是虛函數,可以被重載,可以是共有、私有或是保護的,也同樣可以被其他成員函數調用。它的函數參數也可以是任意類型的。唯一不同的是:槽還可以和信號連接在一起。
與回調不同,信號槽機制是類型安全的。這體現在信號的函數簽名與槽的函數簽名必須匹配上,才能夠發生信號的傳遞。實際上,槽的參數個數可以比信號的參數個數少,因為槽能夠忽略信號形參中多出來的參數。信號和槽是松耦合的:發出信號的類不關心哪些類將接收它的信號。QT的信號槽機制吧哦這裡在正確的時間,槽能夠接收到信號的參數並調用。信號和槽都可以有任意個數的參數,它們都是類型安全的。
首先我們要知道的是,所有繼承自QObject或者它的子類(如QWidget)都可以包含信號槽。我們寫的類須繼承自QObject(或其子類)。所有包含了信號槽的類都必須在聲明的上部含有Q_OBJECT宏。
一個基於QObject的C++簡單類:
//MyStr.h
# ifndef MYSTR
# define MYSTR
#include<QObject>
#include<QString>
class MyStr :public QObject
{
Q_OBJECT //必須包含的宏
public:
MyStr (){m_value = "zero";}
QString value() const{return m_value;}
public slots :
void setValue(QString value );
signals: //信號
void valueChanged(QString newValue);
private:
QString m_value;
};
#endif
# define signals public
Signal的代碼會由 moc 自動生成,開發人員一定不能在自己的C++代碼中實現它。
反之,槽應該由編程人員來實現,下面提供MyStr::setVaule()的一種可能實現
#include"MyStr.h"
void MyStr::setValue(QString value)
{
if(value != m_value)
{
m_value = value;
emit valueChanged(value);
}
}
setValue函數首先比較新參的值與數據成員的值是否是一樣的(後面有解釋為何這樣做),如果不是,則設置好數據成員m_value的值,然後,把信號valueChanged()發送出去。發送給誰?類並沒有寫,這並不是類設計者所關心的,也不是類所關心的,它只管把信號發送出去就行。然後,我們再來設置誰來接收這個信號。
int main(int argc, char *argv[])
{
MyStr a;
MyStr b;
QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SLOT(setValue(QString)));
a.setValue("this is A");
return 0;
}
我們定義了兩個類對象a/b,使用 QObject::connect()函數指定了發送方、信號、接收方、槽等信息,connect函數的格式如下:
QObject::connect( 發送方, SIGNAL(...), 接收方, SLOT(..) );
當我們調用a的成員函數setValue時,該函數除了把a.m_value設置為"this is A",也把信號valueChanged()發送出去,被b.setValue所接收,從而,把b.m_value設置為"this is A",同時b.setValue又把valueChanged信號發射出去,然而該信號並沒有對象接收,因為我們沒有建立以b為發送方的任何連接。此時你應該明白,為何在emit前需要判斷value != m_value,因為如果沒有此步驟,且恰巧設置了
QObject::connect(&b,SIGNAL(valueChanged(QString)),&a,SLOT(setValue(QString)));
則b的信號被a接收,a又發送信號被b接收,如此進入死循環。
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyStr a;
MyStr b;
QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SLOT(setValue(QString)));
a.setValue("this is A");
QLabel* label = new QLabel;
label->setText( b.value());
label->show();
return app.exec();
}
我們使用label輸出來看看b是否接收到a的信號,如果是,則b的內容應該是"this is A",輸出在label上,程序運行結果:
這個例子展示了對象之間通信的一種方式。對象間可以一起工作,而不需要知道彼此的任何信息。為了達到通信的目的,只需要將它們連接起來,而這只需要通過 調用 QObject::connect() 函數指定一些簡單信息就好。
回到頂部要把信號成功連接到槽,它們的參數必須具有相同的順序和相同的類型,或者允許信號的參數比槽多,槽會自動忽略掉多出來的參數而進行調用。
使用QObject::connect可以把一個信號連接到多個槽,而當信號發射時,將按聲明聯系時的順序依次調用槽。
MyStr a;
MyStr b;
MyStr c;
//信號連接到兩個槽
QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SLOT(setValue(QString)));
QObject::connect(&a,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
a.setValue("this is A");
//依次調用b.setValue()、c.setValue()
同樣的,可以讓多個信號連接到同一個槽上 ,而且其中的每一個信號的發送,都會調用了那個槽。
MyStr a;
MyStr b;
MyStr c;
//兩個信號連接到同一個槽
QObject::connect(&a,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
QObject::connect(&b,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
//下面的操作皆會調用到槽c.setValue()
a.setValue("this is A");
b.setValue("this is B");
當發射第一個信號的時候,也會把第二個信號一個發送出去。
MyStr a;
MyStr b;
MyStr c;
//兩個信號相連接
QObject::connect(&a,SIGNAL(valueChanged(QString)),&b,SIGNAL(valueChanged(QString)));
//再建立b與c的連接
QObject::connect(&b,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
//下面的操作同時發送了信號a.valueChanged與b.valueChanged
a.setValue("this is A");
//從而信號b.valueChanged被槽c.setValue所接收
//移除b 與 c之間的連接
QObject::disconnect(&b,SIGNAL(valueChanged(QString)),&c,SLOT(setValue(QString)));
實際上當對象被delete時,其關聯的所有鏈接都會失效,QT會自動移除和這個對象的所有鏈接。
感謝您耐心的閱讀。
Qt入門學習——Qt 5 幫助文檔的使用 http://www.linuxidc.com/Linux/2015-09/123102.htm
Ubuntu 環境下Gtk與QT編譯環境安裝與配置 http://www.linuxidc.com/Linux/2013-08/88539.htm
Linux系統下QT環境搭建 http://www.linuxidc.com/Linux/2013-07/87576.htm
Ubuntu下QT控制台程序無法運行的解決方案以及XTerm的配置方法 http://www.linuxidc.com/Linux/2013-06/86244.htm
Ubuntu 10.04下QT4.7.4移植詳解 http://www.linuxidc.com/Linux/2013-01/77930.htm
Ubuntu 14.04下安裝部署Qt5開發環境 http://www.linuxidc.com/Linux/2014-05/101774.htm
Qt 的詳細介紹:請點這裡
Qt 的下載地址:請點這裡