[Qt教程] 第44篇 进阶(四)信号和槽
生活随笔
收集整理的這篇文章主要介紹了
[Qt教程] 第44篇 进阶(四)信号和槽
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
[Qt教程]?第44篇 進(jìn)階(四)信號和槽
??|?查看: 298|?回復(fù): 0| 信號和槽 版權(quán)聲明 該文章原創(chuàng)于Qter開源社區(qū) 導(dǎo)語 在前面的內(nèi)容中已經(jīng)多次用到過信號和槽了,這一節(jié)我們將詳細(xì)講解信號和槽的機(jī)制和使用方式。大家可以在幫助中查看Signals& Slots關(guān)鍵字。 環(huán)境:Windows Xp + Qt 4.8.5+QtCreator2.8.0 目錄 一、信號和槽機(jī)制 二、信號和槽的自動(dòng)關(guān)聯(lián) 三、信號和槽的高級應(yīng)用 正文 一、信號和槽機(jī)制 信號和槽用于兩個(gè)對象之間的通信,信號和槽機(jī)制是Qt的核心特征,也是Qt不同于其他開發(fā)框架的最突出的特征。在GUI編程中,當(dāng)改變了一個(gè)部件時(shí),總希望其他部件也能了解到該變化。更一般來說,我們希望任何對象都可以和其他對象進(jìn)行通信。例如,如果用戶點(diǎn)擊了關(guān)閉按鈕,我們希望可以執(zhí)行窗口的close()函數(shù)來關(guān)閉窗口。為了實(shí)現(xiàn)對象間的通信,一些工具包中使用了回調(diào)(callback)機(jī)制,而在Qt中,使用了信號和槽來進(jìn)行對象間的通信。當(dāng)一個(gè)特殊的事情發(fā)生時(shí)便可以發(fā)射一個(gè)信號,比如按鈕被單擊;而槽就是一個(gè)函數(shù),它在信號發(fā)射后被調(diào)用,來響應(yīng)這個(gè)信號。在Qt的部件類中已經(jīng)定義了一些信號和槽,但是更多的做法是子類化這個(gè)部件,然后添加自己的信號和槽來實(shí)現(xiàn)想要的功能。 在前面使用過的信號和槽的關(guān)聯(lián),都是一個(gè)信號對應(yīng)一個(gè)槽。其實(shí),一個(gè)信號可以關(guān)聯(lián)到多個(gè)槽上,多個(gè)信號也可以關(guān)聯(lián)到同一個(gè)槽上,甚至,一個(gè)信號還可以關(guān)聯(lián)到另一個(gè)信號上,如下圖所示。如果存在多個(gè)槽與某個(gè)信號相關(guān)聯(lián),那么,當(dāng)這個(gè)信號被發(fā)射時(shí),這些槽將會(huì)一個(gè)接一個(gè)地執(zhí)行,但是它們執(zhí)行的順序是隨機(jī)的,無法指定它們的執(zhí)行順序。 ? 下面通過一個(gè)簡單的例子來進(jìn)一步講解信號和槽的相關(guān)知識。這個(gè)例子實(shí)現(xiàn)的效果是:在主界面中創(chuàng)建一個(gè)對話框,在這個(gè)對話框中可以輸入數(shù)值,當(dāng)按下確定按鈕時(shí)關(guān)閉對話框并且將輸入的數(shù)值通過信號發(fā)射出去,而在主界面中接收該信號并且顯示數(shù)值。 新建Qt Gui應(yīng)用,項(xiàng)目名稱為“mySignalSlot”,基類選擇QWidget,然后類名保持“Widget”不變。項(xiàng)目建立完成后,向項(xiàng)目中添加新文件,模板選擇Qt分類中的“Qt設(shè)計(jì)師界面類”,界面模板選擇“Dialog without Buttons”,類名為“MyDialog”。完成后首先在mydialog.h文件中添加代碼來聲明一個(gè)信號: signals: ? ??void?dlgReturn(int);? ?? ?? ?? ?? ?? ?//?自定義的信號 聲明一個(gè)信號要使用signals關(guān)鍵字,在signals前面不能使用public、private和protected等限定符,因?yàn)橹挥卸x該信號的類及其子類才可以發(fā)射該信號。而且信號只用聲明,不需要也不能對它進(jìn)行定義實(shí)現(xiàn)。還要注意,信號沒有返回值,只能是void類型的。因?yàn)橹挥?/span>QObject類及其子類派生的類才能使用信號和槽機(jī)制,這里的MyDialog類繼承自QDialog類,QDialog類又繼承自QWidget類,QWidget類是QObject類的子類,所以這里可以使用信號和槽。不過,使用信號和槽,還必須在類聲明的最開始處添加Q_OBJECT宏,在這個(gè)程序中,類的聲明是自動(dòng)生成的,已經(jīng)添加了這個(gè)宏。 在mydialog.ui對應(yīng)的界面中添加一個(gè)Spin Box部件和一個(gè)Push Button部件,將pushButton的顯示文本改為“確定”。然后轉(zhuǎn)到pushButton的單擊信號clicked()槽,更改如下: void?MyDialog::on_pushButton_clicked()? ?//?確定按鈕 { ? ?int?value?=?ui->spinBox->value();? ? //?獲取輸入的數(shù)值 ? ?emit?dlgReturn(value);? ?? ?? ?? ?? ?//?發(fā)射信號 ? ?close();? ?? ?? ?? ?? ?? ?? ?? ?? ???//?關(guān)閉對話框 } 當(dāng)單擊確定按鈕時(shí),便獲取spinBox部件中的數(shù)值,然后使用自定義的信號將其作為參數(shù)發(fā)射出去。發(fā)射一個(gè)信號要使用emit關(guān)鍵字,例如程序中發(fā)射了dlgReturn()信號。 然后到widget.h文件中添加自定義槽的聲明: private?slots: ? ??void?showValue(int value); 聲明一個(gè)槽需要使用slots關(guān)鍵字。一個(gè)槽可以是private、public或者protected類型的,槽也可以被聲明為虛函數(shù),這與普通的成員函數(shù)是一樣的,也可以像調(diào)用一個(gè)普通函數(shù)一樣來調(diào)用槽。槽的最大特點(diǎn)就是可以和信號關(guān)聯(lián)。 下面打開widget.ui文件,向界面上拖入一個(gè)Label部件,然后更改其文本為“獲取的值是:”。然后進(jìn)入widget.cpp文件中添加頭文件#include "mydialog.h",再在構(gòu)造函數(shù)中添加代碼: MyDialog?*dlg?=?new?MyDialog(this); //?將對話框中的自定義信號與主界面中的自定義槽進(jìn)行關(guān)聯(lián) connect(dlg,SIGNAL(dlgReturn(int)),this,SLOT(showValue(int))); dlg->show(); 這里創(chuàng)建了一個(gè)MyDialog,并且使用Widget作為父部件。然后將MyDialog類的dlgReturn()信號與Widget類的showValue()槽進(jìn)行關(guān)聯(lián)。信號和槽進(jìn)行關(guān)聯(lián),使用的是QObject類的connect()函數(shù),這個(gè)函數(shù)的原型如下: bool QObject::connect ( const QObject *sender, const char * signal, const QObject * receiver, const char * method,Qt::ConnectionType type = Qt::AutoConnection ) 它的第一個(gè)參數(shù)為發(fā)送信號的對象,例如這里的dlg;第二個(gè)參數(shù)是要發(fā)送的信號,這里是SIGNAL(dlgReturn(int));第三個(gè)參數(shù)是接收信號的對象,這里是this,表明是本部件,即Widget,當(dāng)這個(gè)參數(shù)為this時(shí),也可以將這個(gè)參數(shù)省略掉,因?yàn)閏onnect()函數(shù)還有另外一個(gè)重載形式,該參數(shù)默認(rèn)為this;第四個(gè)參數(shù)是要執(zhí)行的槽,這里是SLOT(showValue(int))。對于信號和槽,必須使用SIGNAL()和SLOT()宏,它們可以將其參數(shù)轉(zhuǎn)化為const char* 類型。connect()函數(shù)的返回值為bool類型,當(dāng)關(guān)聯(lián)成功時(shí)返回true。還要注意,在調(diào)用這個(gè)函數(shù)時(shí)信號和槽的參數(shù)只能有類型,不能有變量,例如寫成SLOT(showValue(int value))是不對的。對于信號和槽的參數(shù)問題,基本原則是信號中的參數(shù)類型要和槽中的參數(shù)類型相對應(yīng),而且信號中的參數(shù)可以多于槽中的參數(shù),但是不能反過來,如果信號中有多余的參數(shù),那么它們將被忽略。下面介紹一下connect()函數(shù)的最后一個(gè)參數(shù),它表明了關(guān)聯(lián)的方式,其默認(rèn)值是Qt::AutoConnection,這里還有其他幾個(gè)選擇,在編程中一般使用默認(rèn)值,例如這里,在MyDialog類中使用emit發(fā)射了信號之后,就會(huì)執(zhí)行槽,只有等槽執(zhí)行完了以后,才會(huì)執(zhí)行emit語句后面的代碼。大家也可以將這個(gè)參數(shù)改為Qt::QueuedConnection,這樣在執(zhí)行完emit語句后便會(huì)立即執(zhí)行其后面的代碼,而不管槽是否已經(jīng)執(zhí)行。當(dāng)不再使用這個(gè)關(guān)聯(lián)時(shí),還可以使用disconnect()函數(shù)來斷開關(guān)聯(lián)。 下面是自定義槽的實(shí)現(xiàn),在這里只是簡單的將參數(shù)傳遞來的數(shù)值顯示在了標(biāo)簽上。因?yàn)檫@里使用了中文,所以大家記著在main.cpp文件中添加相關(guān)代碼。 void?Widget::showValue(int value)? ?? ?? ?//?自定義槽 { ? ?ui->label->setText(tr("獲取的值是:%1").arg(value)); } 現(xiàn)在大家可以運(yùn)行一下程序查看效果。如下圖所示。 這個(gè)程序中自定義了信號和槽,可以看到它們的使用是很簡單的,只需要對它們進(jìn)行關(guān)聯(lián),然后在適當(dāng)?shù)臅r(shí)候發(fā)射信號就行。下面列舉一下使用信號和槽應(yīng)該注意的幾點(diǎn):
二、信號和槽的自動(dòng)關(guān)聯(lián) 信號和槽還有一種自動(dòng)關(guān)聯(lián)方式,例如前面程序中在設(shè)計(jì)模式直接生成的按鈕的單擊信號的槽,就是使用的這種方式:on_pushButton_clicked(),它由“on”、部件的objectName和信號三部分組成,中間用下劃線隔開。這樣組織的名稱的槽就可以直接和信號關(guān)聯(lián),而不用再使用connect()函數(shù)。不過使用這種方式還要進(jìn)行其他設(shè)置,而前面之所以可以直接使用,是因?yàn)槌绦蛑心J(rèn)已經(jīng)進(jìn)行了設(shè)置。下面來看一個(gè)簡單的例子。 新建Qt Gui應(yīng)用,項(xiàng)目名稱為“mySignalSlot2”,基類選擇QWidget,然后類名保持“Widget”不變。完成后先在widget.h文件中進(jìn)行函數(shù)聲明: private?slots: ? ??void?on_myButton_clicked(); 這里自定義了一個(gè)槽,它使用自動(dòng)關(guān)聯(lián)。然后在widget.cpp文件中添加頭文件#include <QPushButton>,再將構(gòu)造函數(shù)的內(nèi)容更改如下: Widget::Widget(QWidget?*parent)?: ? ?QWidget(parent), ? ?ui(new?Ui::Widget) { ? ?QPushButton?*button?=?new?QPushButton(this);?//?創(chuàng)建按鈕 ? ?button->setObjectName("myButton");? ?? ?? ???//?指定按鈕的對象名 ? ?ui->setupUi(this);? ?? ?? ?? ?? ?? ?? ? //要在定義了部件以后再調(diào)用這個(gè)函數(shù) } 因?yàn)樵?/span>setupUi()函數(shù)中調(diào)用了connectSlotsByName()函數(shù),所以要使用自動(dòng)關(guān)聯(lián)的部件的定義都要放在setupUi()函數(shù)之前,而且還必須使用setObjectName()函數(shù)指定它們的objectName,只有這樣才能正常使用自動(dòng)關(guān)聯(lián)。下面是槽的定義: void?Widget::on_myButton_clicked()? ?? ?? ? //?使用自動(dòng)關(guān)聯(lián) { ? ?close(); } 這里進(jìn)行了關(guān)閉部件的操作。對于槽的函數(shù)名,中間要使用前面指定的objectName,這里是“myButton”。現(xiàn)在運(yùn)行一下程序,單擊按鈕,發(fā)現(xiàn)可以正常關(guān)閉窗口。 可以看到,如果要使用信號和槽的自動(dòng)關(guān)聯(lián),就必須在connectSlotsByName()函數(shù)之前進(jìn)行部件的定義,而且還要指定部件的objectName,并且自動(dòng)關(guān)聯(lián)中必須使用Qt中已經(jīng)定義的信號,而不能是自定義的信號。鑒于這些約束,雖然自動(dòng)關(guān)聯(lián)形式上很簡單,但是實(shí)際編寫代碼時(shí)卻很少使用。而且,在定義一個(gè)部件時(shí),很希望明確的使用connect()函數(shù)來對其進(jìn)行信號和槽的關(guān)聯(lián),這樣當(dāng)別人看到這個(gè)部件定義時(shí),就可以知道和它相關(guān)的信號和槽的關(guān)聯(lián)了。而使用自動(dòng)關(guān)聯(lián),卻沒有這么明了。 三、信號和槽的高級應(yīng)用 有時(shí)我們希望獲得信號發(fā)送者的信息,在Qt中提供了QObject::sender()函數(shù)來返回發(fā)送該信號的對象的指針。但是如果有多個(gè)信號關(guān)聯(lián)到了同一個(gè)槽上,而在該槽中需要對每一個(gè)信號進(jìn)行不同的處理,使用上面的方法就很麻煩了。對于這種情況,便可以使用QSignalMapper類。QSignalMapper可以被叫做信號映射器,它可以實(shí)現(xiàn)對多個(gè)相同部件的相同信號進(jìn)行映射,為其添加字符串或者數(shù)值參數(shù),然后再發(fā)射出去。對于這個(gè)類的使用,大家可以參考《Qt及Qt Quick開發(fā)實(shí)戰(zhàn)精解》的1.3.3小節(jié),那里有這個(gè)類的實(shí)際應(yīng)用。還有就是Qt的演示程序中的Tools分類下的Input Panel示例程序中也使用了這個(gè)類,大家也可以參考一下這個(gè)程序。在這里便不再詳細(xì)講述這個(gè)類的使用了。 在本節(jié)的最后,來看一下信號和槽機(jī)制的特色和優(yōu)越性:
結(jié)語 雖然信號和槽機(jī)制提供了高度的靈活性,但就其性能而言,還是慢于回調(diào)機(jī)制的。當(dāng)然,這點(diǎn)性能差異通常在一個(gè)應(yīng)用程序中是很難體現(xiàn)出來的。 涉及到的代碼:??mySignalSlot.rar???mySignalSlot2.rar?? |
總結(jié)
以上是生活随笔為你收集整理的[Qt教程] 第44篇 进阶(四)信号和槽的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [Qt教程] 第43篇 进阶(三)对象树
- 下一篇: [Qt教程] 第45篇 进阶(五)Qt样