信號和槽用於兩個對象之間的通訊,信號和槽機制是Qt的核心特徵,也是Qt不一樣於其餘開發框架的最突出的特徵。編程
信號和槽機制的特點和優越性:
安全
1. 信號和槽機制是類型安全的,相關聯的信號和槽的參數必須匹配;
app
2. 信號和槽是鬆耦合的,信號發送者不知道也不須要知道接受者的信息;框架
3. 信號和槽可使用任意類型的任意數量的參數。函數
雖然信號和槽機制提供了高度的靈活性,但就其性能而言,仍是慢於回調機制的。
工具
/* 信號與槽機制是比較靈活的,但有些侷限性咱們必須瞭解,這樣在實際的使用過程當中作到有的放矢, 避免產生一些錯誤。下面就介紹一下這方面的狀況。 1.信號與槽的效率是很是高的,可是同真正的回調函數比較起來,因爲增長了靈活性,所以在速度上仍是有所損失, 固然這種損失相對來講是比較小的,經過在一臺i586-133的機器上測試是10微秒(運行Linux), 可見這種機制所提供的簡潔性、靈活性仍是值得的。 但若是咱們要追求高效率的話,好比在實時系統中就要儘量的少用這種機制。 2.信號與槽機制與普通函數的調用同樣,若是使用不當的話,在程序執行時也有可能產生死循環。 所以,在定義槽函數時必定要注意避免間接造成無限循環,即在槽中再次發射所接收到的一樣信號。 例如,在前面給出的例子中若是在mySlot()槽函數中加上語句emit mySignal()便可造成死循環。 3.若是一個信號與多個槽相聯繫的話,那麼,當這個信號被髮射時,與之相關的槽被激活的順序將是隨機的。 4. 宏定義不能用在signal和slot的參數中。 既然moc工具不擴展#define,所以,在signals和slots中攜帶參數的宏就不能正確地工做,若是不帶參數是能夠的。 例如,下面的例子中將帶有參數的宏SIGNEDNESS(a)做爲信號的參數是不合語法的: */
在GUI編程中,當改變了一個部件時,總但願其餘部件也能瞭解到該變化。更通常來講,咱們但願任何對象均可以和其餘對象進行通訊。性能
當一個特殊的事情發生時即可以發射一個信號,好比按鈕被單擊;而槽就是一個函數,它在信號發射後被調用,來響應這個信號。
測試
在Qt的部件類中已經定義了一些信號和槽,可是更多的作法是子類化這個部件,而後添加本身的信號和槽來實現想要的功能。
ui
一個信號對應一個槽。其實,一個信號能夠關聯到多個槽上,多個信號也能夠關聯到同一個槽上,甚至,一個信號還能夠關聯到另外一個信號上。若是存在多個槽與某個信號相關聯,那麼,當這個信號被髮射時,這些槽將會一個接一個地執行,可是它們執行的順序是隨機的,沒法指定它們的執行順序。
this
一個簡單的例子來進一步講解信號和槽的相關知識,這個例子實現的效果是:在主界面中建立一個對話框,在這個對話框中能夠輸入數值,當按下肯定按鈕時關閉對話框而且將輸入的數值經過信號發射出去,而在主界面中接收該信號而且顯示數值。
子類mydialog.h中聲明一個信號:
signals:
void dlgReturn(int); // 自定義的信號
聲明一個信號要使用signals關鍵字,在signals前面不能使用public、private和protected等限定符,由於只有定義該信號的類及其子類才能夠發射該信號。並且信號只用聲明,不須要也不能對它進行定義實現。還要注意,信號沒有返回值,只能是void類型的。
在mydialog.ui的「肯定」按鈕clicked()槽函數中添加:
void MyDialog::on_pushButton_clicked() // 肯定按鈕
{
int value = ui->spinBox->value(); // 獲取輸入的數值
emit dlgReturn(value); // 發射信號
close(); // 關閉對話框
}
而後到widget.h文件中添加自定義槽的聲明:
private slots:
void showValue(int value);
聲明一個槽須要使用slots關鍵字。一個槽能夠是private、public或者protected類型的,槽也能夠被聲明爲虛函數,這與普通的成員函數是同樣的,也能夠像調用一個普通函數同樣來調用槽。槽的最大特色就是能夠和信號關聯。
widget.cpp的構造函數中添加:
MyDialog *dlg = new MyDialog(this);
// 將對話框中的自定義信號與主界面中的自定義槽進行關聯
connect(dlg,SIGNAL(dlgReturn(int)),this,SLOT(showValue(int)));
dlg->show();
widget.cpp 中實現自定義槽函數:
void Widget::showValue(int value) // 自定義槽
{
ui->label->setText(tr("獲取的值是:%1").arg(value));
}
運行程序
connect()函數原型:
//connect()函數,這個函數的原型以下: bool QObject::connect ( const QObject *sender, const char * signal, const QObject * receiver, const char * method,Qt::ConnectionType type = Qt::AutoConnection ) /* 它的第一個參數爲發送信號的對象,例如這裏的dlg; 第二個參數是要發送的信號,這裏是SIGNAL(dlgReturn(int)); 第三個參數是接收信號的對象,這裏是this,代表是本部件,即Widget,當這個參數爲this時,也能夠將這個參數省略掉, 由於connect()函數還有另一個重載形式,該參數默認爲this; 第四個參數是要執行的槽,這裏是SLOT(showValue(int))。對於信號和槽,必須使用SIGNAL()和SLOT()宏,它們能夠將其參數轉化爲const char* 類型。 connect()函數的返回值爲bool類型,當關聯成功時返回true。 還要注意,在調用這個函數時信號和槽的參數只能有類型,不能有變量,例如寫成SLOT(showValue(int value))是不對的。 對於信號和槽的參數問題,基本原則是信號中的參數類型要和槽中的參數類型相對應,並且信號中的參數能夠多於槽中的參數,可是不能反過來,若是信號中有多餘的參數,那麼它們將被忽略。 下面介紹一下connect()函數的最後一個參數,它代表了關聯的方式,其默認值是Qt::AutoConnection, 這裏還有其餘幾個選擇,在編程中通常使用默認值, 例如這裏,在MyDialog類中使用emit發射了信號以後,就會執行槽,只有等槽執行完了之後,纔會執行emit語句後面的代碼。 你們也能夠將這個參數改成Qt::QueuedConnection,這樣在執行完emit語句後便會當即執行其後面的代碼,而無論槽是否已經執行。 當再也不使用這個關聯時,還可使用disconnect()函數來斷開關聯。 */
注意:
須要繼承自QObject或其子類;
在類聲明的最開始處添加Q_OBJECT宏;
槽中的參數的類型要和信號的參數的類型相對應,且不能比信號的參數多;
信號只用聲明,沒有定義,且返回值爲void類型。
信號和槽的高級應用:
有時咱們但願得到信號發送者的信息,在Qt中提供了QObject::sender()函數來返回發送該信號的對象的指針。可是若是有多個信號關聯到了同一個槽上,而在該槽中須要對每個信號進行不一樣的處理,使用上面的方法就很麻煩了。對於這種狀況,即可以使用QSignalMapper類。QSignalMapper能夠被叫作信號映射器,它能夠實現對多個相同部件的相同信號進行映射,爲其添加字符串或者數值參數,而後再發射出去。