Qt 信號和槽函數


信號和槽是一種高級接口,應用於對象之間的通信,它是 QT 的核心特性。當某個信號被發射,就需要調用與之相綁定的槽函數。這與Windows下的消息機制類似,消息機制是基於回調函數。一個回調即是一個函數的指針,因此如果希望一個處理函數通知一些事件,可以傳遞一個函數(回調函數)的指針給這個處理函數。這個處理函數就會在適當的時候調用回調函數。但是回調函數有兩大缺點:第一,它們不是類型安全的。我們從來不敢確定處理函數會用正確的參數來調用回調函數;第二,回調函數被強力和處理函數聯系着,因為處理函數必須知道去調用哪個回調函數。

信號和槽的機制是類型安全的:一個信號的簽名必須與接收槽的簽名相匹配。(實際上一個槽可能有一個比它所接收到的信號的簽名更短的簽名因為它能夠忽略額外的參數。)因為簽名是一致的,所以編譯器能夠幫助我們發現類型不匹配。信號和槽是松散的聯系在一起的:一個發射信號的類從來不知道也不關心哪個槽接收這個信號。Qt的信號和槽機制確保如果你將一個信號和一個槽連接起來,這個槽將在正確的時間被用這個信號的參數所調用。信號和槽可以帶任何數量任何類型的參數。它們完全是類型安全的。

信號(Signals)

當對象改變其狀態時,信號就由該對象發射 (emit) 出去,而且對象只負責發送信號,它不知道另一端是誰在接收這個信號。這樣就做到了真正的信息封裝,能確保對象被當作一個真正的軟件組件來使用。

信號只需要在頭文件中進行聲明,不需要在cpp中實現。放在Qt自定義關鍵字signals下,在此之前一定要加上Q_OBJECT宏。

在編程中,一般使用的是控件內部定義好的信號。如:QTreeWidget類下的 Signals:

void    currentItemChanged ( QTreeWidgetItem * current, QTreeWidgetItem * previous );
void    itemActivated ( QTreeWidgetItem * item, int column );
void    itemChanged ( QTreeWidgetItem * item, int column );
void    itemClicked ( QTreeWidgetItem * item, int column );
void    itemCollapsed ( QTreeWidgetItem * item );
void    itemDoubleClicked ( QTreeWidgetItem * item, int column );
void    itemEntered ( QTreeWidgetItem * item, int column );
void    itemExpanded ( QTreeWidgetItem * item );
void    itemPressed ( QTreeWidgetItem * item, int column );
void    itemSelectionChanged ();

也可以自定義信號,並通過emit在代碼中發射信號。

class sender : public QObject
{
    Q_OBJECT

    public:
        void doSend();
    signals:
        void send(int);
};
// ------------ sender.cpp -----------
#include "sender.h"
void sender :: doSend()
{
    emit send(40);
}

槽函數(Slots)

槽和普通的C++成員函數幾乎是一樣的(可以是虛函數,可以被重載,可以是public slots、protected slots、private slots,可以被其他C++成員函數直接調用;唯一不同的是:槽還可以和信號連接在一起,在這種情況下,信號被發射時,會自動調用這個槽。)槽不需要信號傳過來的參數時,可以不要參數;但槽一旦要參數,其參數個數,類型,順序必須要和對應的信號保持一致。另外,槽的參數不能有缺省值。

class receiver : public QObject
{
    Q_OBJECT
 
    public slots:
        //帶有參數的槽函數,需和綁定的信號的參數保持一致
        void recv(int);
};
// ------------ Receiver.cpp -----------
void receiver :: recv(int n)
{
    qDebug()<<"recv number: "<<n<<endl;
}

關聯信號和槽(connect)

可以使用QObject類的靜態成員函數connect來建立信號的槽的關聯

bool QObject::connect (const QObject * sender, const char * signal, const QObject * receiver, const char * slot) [static]

具體的調用為:connect(sender, SIGNAL(signal), receiver, SLOT(slot)); 其中sender和receiver為QObject類對象的指針; SIGNAL宏和SLOT宏將信號的槽轉換成字符串。

sender s;
receiver r;
QObject::connect(&s, SIGNAL(send(int)), &r, SLOT(recv(int)));

注:在connect函數中信號函數和槽函數若有參數,只能寫出參數類型,而不能也將變量名寫出;否則,連接會失敗!

  • 一個信號可以連接多個槽

當信號發射時,會以不確定的順序一個接一個的調用各個槽。

  • 多個信號可以連接同一個槽

即無論是哪一個信號被發射,都會調用這個槽。

  • 信號直接可以相互連接

發射第一個信號時,也會發射第二個信號。

斷開信號和槽(disconnect)

當信號和槽沒有必要繼續保持連接時,可以通過調用disconnect來斷開它們。

bool QObject::disconnect (const QObject * sender, const char * signal,  const Object * receiver, const char * slot) [static]

有三種情況必須使用 disconnect() 函數:

(1)斷開與某個對象相關聯的任何對象。

disconnect(sender, 0, 0, 0) ;
//或者
sender->disconnect();

(2)斷開與某個特定信號的任何關聯。

disconnect(sender, SIGNAL(signal()), 0, 0);
//或者
sender->disconnect(SIGNAL(signal()));

(3)斷開兩個對象之間的關聯。 

disconnect(sender, 0, receiver, 0);
//或者
sender->disconnect(receiver);

應注意的問題

  1. 信號與槽機制與普通函數的調用一樣,如果使用不當的話,在程序執行時也有可能產生死循環。因此,在定義槽函數時一定要注意避免間接形成無限循環,即在槽中再次發射所接收到的同樣信號。例如 , 在前面給出的例子中如果在 mySlot() 槽函數中加上語句 emit mySignal() 即可形成死循環。
  2. 如果一個信號與多個槽相聯系的話,那么,當這個信號被發射時,與之相關的槽被激活的順序將是隨機的。
  3. 宏定義不能用在 signal 和 slot 的參數中。
  4. 信號和槽的參數個數與類型必須一致。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM