QT信號槽機制

信號槽

信號槽是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
  • 在這個簡單的類中,咱們能夠看到,使用slots來表示槽,而使用signals來表示信號。實際上沒有那麼神祕,它們都是宏定義,甚至signals只是public的宏定義:
#     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會自動移除和這個對象的全部連接。

感謝您耐心的閱讀。

相關文章
相關標籤/搜索