天天看點

Qt中的信号與槽機制解析

注:要想使用Qt的核心機制信号與槽,就必須在類的私有資料區聲明Q_OBJECT宏,然後會有moc編譯器負責讀取這個宏進行代碼轉化,進而使Qt這個特有的機制得到使用。

所謂信号槽,簡單來說,就像是插銷一樣:一個插頭和一個插座。當某種事件發生之後,比如,點選一下滑鼠,或者按下某個按鍵,此時,這個元件就回發出一個信号。如果有一個槽,正好對應上這個信号,那麼,這個槽函數就回被調用。

槽函數和普通的c++成員函數沒有很大的差別,它們也可以是virtual的;可以被重寫;可以使public、protected或者private的;可以被其他c++函數調用;參數可以是任何類型。槽函數可以和一個信号相連接配接,當這個信号發生時,它就被自動調用。

連接配接信号與槽是connect()函數,原型如下:

bool QObject::connect ( const QObject * sender, const QMetaMethod & signal, const QObject * receiver, const QMetaMethod & method, Qt::ConnectionType type = Qt::AutoConnection ) [static]

它可以把一個對象(sender)發送的信号(signal)和接收者(receiver)的槽函數(method)關聯起來,這樣當信号産生時與之關聯的槽函數就會被執行。在connect函數裡面我們用到了Qt提供的兩個宏SIGNAL()和SLOT();這是Qt要求的,要關聯信号和槽必須借助于這兩個宏,兩個宏的定義如下:

#define SLOT(name) "1"#name

#define SIGNAL(name) "2"#name

通過這兩個宏,就可以把我們傳遞進去的槽和信号的名字轉化成字元串,并在這兩個字元串前面加上附加的字元。

如:

connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(myslots()));

經過moc處理後就變成:

connect(ui->pushButton, "2clicked()", this, "1myslots()");

connect函數的最後一個參數type可以指定傳遞信号的方式,它是Qt::ConnectionType枚舉類型常量,常用連接配接類型如下表所列。

Constant Value Description
Qt::AutoConnection 當信号發送者和接收者處于同一線程内時,這個類型等同于DirectConnection,反之等同于QueuedConnection,這個類型也是connect函數的預設連接配接類型
Qt::DirectConnection 1 信号一旦發射,與之關聯的槽函數立即執行
Qt::QueuedConnection 2 當信号産生,信号會暫時被緩沖到一個消息隊列中,等待接收者的事件循環處理去隊列中擷取消息,然後執行和信号關聯的槽函數,這種方式既可以在同一線程内傳遞消息也可以跨線程操作
Qt::BlockingQueuedConnection                                                                                                                                     4 這種類型類似于QueuedConnection,但是它隻能應用于跨線程操作即發送者和接收者處于不同的線程中的情況,并且信号發送者線程會阻塞等待接收者的槽函數執行結束
Qt::AutoCompatConnection 3 當相容Qt3程式是的預設連接配接類型

一個信号可以和多個槽相連(槽會一個接一個地被調用,但是調用的順序是不确定的);

多個信号可以連接配接一個槽(隻要任意一個信号産生,這個槽就回被調用);

一個信号可以連接配接到另一個信号;

槽可以被取消連結:

bool QObject::disconnect ( const QObject * sender, const QMetaMethod & signal, const QObject * receiver, const QMetaMethod & method ) [static]

這種情況并不經常出現,因為當一個對象delete之後,Qt自動取消所有連接配接到這個對象上面的槽。

要連接配接在一起的信号和槽,如果帶了參數,那麼這些參數的順序和類型都要一緻,并且信号帶的參數的個數可以多于槽,信号在發射的時候可以把參數傳遞給槽函數接收,這也是不同對象互動資料的一個方法,信号可以發送多個類型的資料給槽函數,但是槽函數不一定需要全部接收處理,這樣槽函數參數的個數可以少于或者等于信号參數的個數,但一定不能多于所連接配接信号的參數個數。為了正确的連接配接信号槽,信号和槽的參數類型以及出現的順序必須相同。

下面舉兩個例子,一個例子是使用connect函數連接配接信号槽,另個例子是使用Qt Creator的designer來設定信号槽(本質還是connect,隻是圖形化)。

例子一:

Qt中的信号與槽機制解析
1 #include <QAPPlication>
 2 #include <QPushButton>
 3 
 4 int main (int argc, char *argv[])
 5 {
 6       QApplication app(argc, argv);  
 7       QPushButton *button = new QPushButton("Close");
 8       QObject::connect(button, SIGNAL(clicked()), button, SLOT(close()));
 9       button->show();
10 
11      return app.exec();
12 }          
Qt中的信号與槽機制解析

輸入以下指令編譯:

qmake -project

qmake

make

然後運作。

轉:http://www.cnblogs.com/heguoyuan/p/4279597.html

=========================

 因為最近在研究Boost,而Boost裡涉及到事件處理的問題,裡面用的是信号和槽的機制,先拿Qt裡的這個機制預研一下。诶,Boost這是夠厲害的,什麼先進就包含什麼!要知道我以前一直以為這是Qt的專利呢。當然,這也是大多數厲害的開源軟體庫的高人之處,像Qt這種GUI庫也包含了很多資料庫,字元處理等内容,多學學吧!

    signal/slot是Qt對象以及其派生類對象之間的一種高效通信接口,它是Qt的核心特性,也是差別與其他工具包的重要地方。它完全獨立于标準的C/C++語言,是以用正确的處理好信号和槽,必須借助于一個成為MOC(Meta Object Compiler)的qt工具,該工具是一個C++預處理程式,能為高層次的事件處理自動生成所需要的附加代碼。

盡管它的機制很像回調函數,但是這裡要注意它和與回調函數間的不同,回調函數傳遞的是函數指針,很容易造成程式崩潰,另一方面,回調方式緊緊的綁定了圖形使用者接口的功能元素,是以很難開發進行獨立的分類。而signal/slot機制也能攜帶任意數量和任意參數,并且不會像回調函數那樣産生core dumps。

    信号signal和槽Slot是用來在對象間通訊的方法,當一個特定事件發生的時候,signal會被emit出來,slot調用是用來響應相應的signal的。QT對象已經包含了許多預定義的 signal,但我們總是可以在派生類中添加新的signal。QT對象中也已經包含了許多預定義的slog,但我們可以在派生類中添加新的slot來處理我們感興趣的signal。

    signal 和 slot 機制是類型安全的,signal 和 slot必須互相比對(實際上,一個solt的參數可以比對應的signal的參數少,因為它可以忽略多餘的參數)。signal 和 slot是松散的配對關系,發出signal的對象不關心是那個對象連結了 這個signal,也不關心是那個或者有多少slot連結到了這個 signal。QT的signal 和 slot機制保證了,如果一個signal和slot相連結,slot會在正确的時機被調用,并且是使用正确的參數。Signal和slot都可以攜帶任何數量和類型的參數,他們都是類型安全的。

    所有從QObject直接或者間接繼承出來的類都能包含信号和槽,當一個對象的狀态發生變化的時候,信号就可以被emit出來,這可能是某個其它的 對象所 關心的。這個對象并不關心有那個對象或者多少個對象連結到這個信号了,這是真實的資訊封裝,它保證了這個對象可以作為一個軟體元件來被使用。

    槽(slot)是用來接收信号的,但同時他們也是一個普通的類成員函數,就象一個對象不關心有多少個槽連結到了它的某個信号,一個對象也不關心一個槽連結了多少個信号。這保證了用QT建立的對象是一個真實的獨立的軟體元件。

    一個信号可以連結到多個槽,一個槽也可以連結多個信号。同時,一個信号也可以連結到另外一個信号。所有使用了信号和槽的類都必須包含 Q_OBJECT 宏,而且這個類必須從QObject類派生(直接或者間接派生)出來,

    當一個signal被emit出來的時候,連結到這個signal的slot會立刻被調用,就好像是一個函數調用一樣。當這件事情發生的時候,signal和slot機制與GUI的事件循環完全沒有關系,當所有連結到這個signal的slot執行完成之後,在 emit 代碼行之後的代碼會立刻被執行。當有多個slot連結到一個signal的時候,這些slot會一個接着一個的、以随機的順序被執行。

    Signal 代碼會由 moc 自動生成,開發人員一定不能在自己的C++代碼中實作它,并且,它永遠都不能有傳回值。Slot其實就是一個普通的類函數,并且可以被直接調用,唯一特殊的地方是它可以與signal相連結。C++的預處理器更改或者删除 signal, slot, emit 關鍵字,是以,對于C++編譯器來說,它處理的是标準的C++源檔案。

    此外,使用者可以将N多個信号和單個槽相連接配接,或者将将N個槽和單個信号連接配接,甚至是一個信号和另外一個信号連接配接。這樣,當信号發射時,是以與之相連的信号或者槽都會按一定的次序(沒有預定的順序,也就是說執行的先後順序是随機的)執行,當所有與之相連的信号和槽傳回後,emit才會傳回。

    下面是Qt中關于信号和槽的函數,Qt中信号的定義:

[cpp]  view plain copy

  1. siganls:  
  2. void mySignal();  
  3. void mySignal( int x );  
  4. void mySignal( int x, int y );  

    其中signals是Qt的關鍵字,而不是C/C++的關鍵字。此外信号與一般函數的差別是,它的所有傳回值都是void,并且它沒有函數實作體,它的函數體是moc自動生成的。

Qt中槽的定義:

[cpp]  view plain copy

  1. public slots:  
  2. void mySlot();  
  3. void mySlot( int x );  
  4. <span style="font-family:SimSun;font-size:18px;">不同類型的slot有不同的操作權限,具體看slot是public、protected還是private。</span>  

Qt中信号與信号或者與槽的連接配接:

[cpp]  view plain copy

  1. QObeject::connect( obj1, SIGNAL( mySignal() ), obj2, SLOT( mySlot() ) );  
  2. QObeject::connect( obj1, SIGNAL( mySignal() ), obj2, SIGNAL( mySignal2() ) );  

Qt中信号與槽的斷開:

[cpp]  view plain copy

  1. QObeject::disconnect( obj1, SIGNAL( mySignal() ), obj2, SLOT( mySlot() ) );  
  2. QObeject::disconnect( obj1, SIGNAL( mySignal() ), obj2, SIGNAL( mySignal2() ) );  

這種機制GUI控件的操作來說很是友善,當然也要用的恰當,用的規範和科學。

    下面是一個簡單的樣例程式,程式中定義了三個信号和三個槽函數,然後将信号與槽進行了關聯,每個槽函數都隻是彈出一個視窗(widget),信号和槽函數的聲明一般位于頭檔案中,同時在類聲明的開始位置必須加上Q_OBJECT語句,這條語句是不可缺少的,它将告訴編譯器在編譯之前必須先應用 moc工具進行擴充。關鍵字signals指出随後開始信号的聲明,這裡signals用的是複數形式而非單數,siganls沒有public、 private、protected等屬性,這點不同于slots。另外,signals、slots關鍵字是QT自己定義的,不是C++中的關鍵字。信号的聲明類似于函數的聲明而非變量的聲明,左邊要有類型,右邊要有括号,如果要向槽中傳遞參數的話,在括号中指定每個形式參數的類型,當然,形式參數的個數可以多于一個。

[cpp]  view plain copy

  1. //tsignal.h  
  2. ...  
  3. class TsignalApp:public QMainWindow  
  4. {  
  5. Q_OBJECT  
  6. ...  
  7. //信号聲明區  
  8. signals:  
  9. //聲明信号mySignal()  
  10. void mySignal();  
  11. //聲明信号mySignal(int)  
  12. void mySignal(int x);  
  13. //聲明信号mySignalParam(int,int)  
  14. void mySignalParam(int x,int y);  
  15. //槽聲明區  
  16. public slots:  
  17. //聲明槽函數mySlot()  
  18. void mySlot();  
  19. //聲明槽函數mySlot(int)  
  20. void mySlot(int x);  
  21. //聲明槽函數mySignalParam (int,int)  
  22. void mySignalParam(int x,int y);  
  23. }  
  24. ...  
  25. //tsignal.cpp  
  26. ...  
  27. TsignalApp::TsignalApp()  
  28. {  
  29. ...  
  30. //将信号mySignal()與槽mySlot()相關聯  
  31. connect(this,SIGNAL(mySignal()),SLOT(mySlot()));  
  32. //将信号mySignal(int)與槽mySlot(int)相關聯  
  33. connect(this,SIGNAL(mySignal(int)),SLOT(mySlot(int)));  
  34. //将信号mySignalParam(int,int)與槽mySlotParam(int,int)相關聯  
  35. connect(this,SIGNAL(mySignalParam(int,int)),SLOT(mySlotParam(int,int)));  
  36. }  
  37. // 定義槽函數mySlot()  
  38. void TsignalApp::mySlot()  
  39. {  
  40. QMessageBox::about(this,"Tsignal", "This is a signal/slot sample without  
  41. parameter.");  
  42. }  
  43. // 定義槽函數mySlot(int)  
  44. void TsignalApp::mySlot(int x)  
  45. {  
  46. QMessageBox::about(this,"Tsignal", "This is a signal/slot sample with one  
  47. parameter.");  
  48. }  
  49. // 定義槽函數mySlotParam(int,int)  
  50. void TsignalApp::mySlotParam(int x,int y)  
  51. {  
  52. char s[256];  
  53. sprintf(s,"x:%d y:%d",x,y);  
  54. QMessageBox::about(this,"Tsignal", s);  
  55. }  
  56. void TsignalApp::slotFileNew()  
  57. {  
  58. //發射信号mySignal()  
  59. emit mySignal();  
  60. //發射信号mySignal(int)  
  61. emit mySignal(5);  
  62. //發射信号mySignalParam(5,100)  
  63. emit mySignalParam(5,100);  
  64. }  

轉:http://blog.csdn.net/juana1/article/details/6633184

另可參考;http://www.ibm.com/developerworks/cn/linux/guitoolkit/qt/signal-slot/