信號槽是QT中用於對象間通訊的一種機制,也是QT的核心機制。在GUI編程中,咱們常常須要在改變一個組件的同時,通知另外一個組件作出響應。例如:
編程
一開始咱們的Find按鈕是未激活的,用戶輸入要查找的內容後,查找按鈕就被激活,這就是輸入框與Find按鈕這兩個組件間通訊的例子。
安全
早期,對象間的通訊採用回調來實現。回調其實是利用函數指針來實現,當咱們但願某件事發生時處理函數可以得到通知,就須要將回調函數的指針傳遞給處理函數,這樣處理函數就會在合適的時候調用回調函數。回調有兩個明顯的缺點:app
在QT中,咱們有回調技術以外的選擇,也便是信號槽機制。所謂的信號與槽,其實都是函數。當特定事件被觸發時(如在輸入框輸入了字符)將發送一個信號,而與該信號創建的鏈接槽,則能夠接收到該信號並作出反應(激活Find按鈕)。
QT組件預約義了不少信號和槽,而在GUI編程中,咱們習慣於繼承那些組件,繼承後添加咱們本身的槽,以便以咱們的方式來處理信號。槽和普通的C++成員函數幾乎是同樣的,它能夠是虛函數,能夠被重載,能夠是共有、私有或是保護的,也一樣能夠被其餘成員函數調用。它的函數參數也能夠是任意類型的。惟一不一樣的是:槽還能夠和信號鏈接在一塊兒。
與回調不一樣,信號槽機制是類型安全的。這體如今信號的函數簽名與槽的函數簽名必須匹配上,纔可以發生信號的傳遞。實際上,槽的參數個數能夠比信號的參數個數少,由於槽可以忽略信號形參中多出來的參數。信號和槽是鬆耦合的:發出信號的類不關心哪些類將接收它的信號。QT的信號槽機制吧哦這裏在正確的時間,槽可以接收到信號的參數並調用。信號和槽均可以有任意個數的參數,它們都是類型安全的。函數
首先咱們要知道的是,全部繼承自QObject或者它的子類(如QWidget)均可以包含信號槽。咱們寫的類須繼承自QObject(或其子類)。全部包含了信號槽的類都必須在聲明的上部含有Q_OBJECT宏。
一個基於QObject的C++簡單類:this
//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()發送出去。發送給誰?類並無寫,這並非類設計者所關心的,也不是類所關心的,它只管把信號發送出去就行。而後,咱們再來設置誰來接收這個信號。code
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,由於若是沒有此步驟,且恰巧設置了blog
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會自動移除和這個對象的全部連接。
感謝您耐心的閱讀。