QT入门语法——signal,slot
Qt 5 之前的語法
在 Qt 5 之前,我們需要使用下面的語句來鏈接 signal 和 slot:
| 12 | connect(sender, SIGNAL(valueChanged(QString, QString)),????????receiver, SLOT(updateValue(QString))); |
Qt 實(shí)際上利用SIGNAL和SLOT這兩個(gè)宏,把其后的函數(shù)名轉(zhuǎn)換成一個(gè)字符串。隨后,moc 將會(huì)掃描全部文件,將所有的 signal 和 slot 提取出來做成一個(gè)映射表。QObject::connect()函數(shù)則會(huì)從這個(gè)映射表里面找到該字符串,從 signal 的名字就可以找到 slot 的名字,因此也就知道了在 signal?emit 的時(shí)候,該去調(diào)用哪一個(gè) slot 函數(shù)。
Qt 5 之前的 signal/slot 語法的問題
從上面的解釋可以看出,Qt 5 之前版本提供的這種語法其實(shí)有一些問題:
- 沒有編譯期檢查:因?yàn)楹瘮?shù)名被處理成字符串,所有的檢查都是在運(yùn)行時(shí)完成的。這就是為什么有時(shí)會(huì)發(fā)生編譯通過了,但 slot 并沒有被調(diào)用。此時(shí),你就應(yīng)該去檢查 console 的輸出,看看有沒有什么 warning 說明 connect 并沒有成功。
- 因?yàn)樘幚淼氖亲址?#xff0c;所以 slot 中的類型名字必須用 signal 的完全一致,而且在頭文件中的和實(shí)際 connect 語句中的也必須一致。也就是說,如果你使用了 typedef 或者 namespace,connect 就可能不成功(在 Qt 5 之前的版本中,我們當(dāng)然也可以使用 namespace,但是必須保證頭文件中的和 connect 語句中的文本完全一致)。
新語法:使用函數(shù)指針
在 Qt5 提供了一套新的語法。之前的語法依然可以使用,但是現(xiàn)在,我們有了更好的選擇:
| 1 2 | connect(sender,&Sender::valueChanged, ????????receiver,&Receiver::updateValue); |
這個(gè)看起來和之前的版本很類似,因此很容易遷移到新的語法。下面我們看看新語法有什么好處:
編譯器檢查
如果把 signal 或者 slot 名字編寫錯(cuò)誤,或者 slot 的參數(shù)同 signal 不一致,你會(huì)在編譯期就獲得一個(gè)錯(cuò)誤。這肯定會(huì)在重構(gòu)、或者修改 signal 或 slot 的名字時(shí)節(jié)省很多時(shí)間。
另一個(gè)影響是,Qt 可以利用static_cast返回更友好的錯(cuò)誤信息。例如,如果我們少了Q_OBJECT宏,則會(huì)有:
| 123456789 | #include <QtCore/QtCore>class Goo : public QObject {????Goo() {????????connect(this, &Goo::someSignal, this, &QObject::deleteLater);????}signals:????void someSignal();}; |
其錯(cuò)誤信息是:
| 1 2 3 4 5 | qobject.h:Inmemberfunction‘void QObject::qt_check_for_QOBJECT_macro(const T&) const [with T = Goo]’: qobject.h:535:9:??instantiatedfrom‘static typename QtPrivate::QEnableIf::ArgumentCount) >= (int)(QtPrivate::FunctionPointer::ArgumentCount)), void*>::Type QObject::connect(const typename QtPrivate::FunctionPointer::Object*, Func1, const typename QtPrivate::FunctionPointer::Object*, Func2, Qt::ConnectionType) [with Func1 = void (Goo::*)(), Func2 = void (QObject::*)(), typename QtPrivate::QEnableIf::ArgumentCount) >= (int)(QtPrivate::FunctionPointer::ArgumentCount)), void*>::Type = void*, typename QtPrivate::FunctionPointer::Object = Goo, typename QtPrivate::FunctionPointer::Object = QObject]’ main.cc:4:68:??instantiatedfromhere qobject.h:353:5:error:voidvaluenotignoredasitoughttobe make:***[main.o]Error1 |
參數(shù)的自動(dòng)類型轉(zhuǎn)換
現(xiàn)在,我們不僅可以更好地使用 typedef 或 namespace,而且可以利用隱式類型轉(zhuǎn)換。在下面的例子中,我們的 signal 有一個(gè)QString參數(shù),而 slot 需要的是QVariant。在新語法中,QString將被自動(dòng)轉(zhuǎn)換成QVariant:
| 123456789101112 | class Test : public QObject{????Q_OBJECTpublic:????Test() {????????connect(this, &Test::someSignal, this, &Test::someSlot);????}signals:????void someSignal(const QString &);public:????void someSlot(const QVariant &);}; |
連接到任意函數(shù)
如果你留心上面的例子,就會(huì)發(fā)現(xiàn),我們的 signal 被連接到了一個(gè) public 函數(shù),但這個(gè)函數(shù)并不是 slot。Qt 的新語法通過函數(shù)指針直接調(diào)用函數(shù),而不需要 moc 的特殊處理(但是 signal 依然需要)。
更進(jìn)一步,我們可以將 signal 連接到任意函數(shù):
| 1 2 3 4 5 6 | staticvoidsomeFunction(){ ????qDebug()<<“pressed”; } // … somewhere else QObject::connect(button,&QPushButton::clicked,someFunction); |
這樣處理,就可以讓你很方便的同 boost 或者 tr1::bind 這樣的第三方庫協(xié)作。
C++11 lambda 表達(dá)式
至此之前,我們所有的示例都是基于 C++98. 標(biāo)準(zhǔn)的。但是,如果你的編譯器支持 c++11,我相信你一看到“函數(shù)指針”這幾個(gè)字,就一定會(huì)想到 C++11 的新特性:Lambda 表達(dá)式。現(xiàn)在,Lambda 表達(dá)式至少被 MSVC 2010,GCC 4.5,clang 3.1 這幾個(gè)編譯器支持。不過對于后面兩個(gè)編譯器,你需要在編譯時(shí)加上?-std=c++0x?參數(shù)。
現(xiàn)在我們可以用 Lambda 表達(dá)式重寫了:
| 1 2 3 4 5 6 7 8 9 10 11 | voidMyWindow::saveDocumentAs(){ ????QFileDialog *dlg=newQFileDialog(); ????dlg->open(); ????QObject::connect(dlg,&QDialog::finished,[=](intresult){ ????????if(result){ ????????????QFilefile(dlg->selectedFiles().first()); ????????????// … save document here … ????????} ????????dlg->deleteLater(); ????}); } |
這種語法允許我們更方便地編寫異步代碼。
總結(jié)
以上是生活随笔為你收集整理的QT入门语法——signal,slot的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 牛逼了!PDF 版本 5000 页 Ja
- 下一篇: 30 岁程序员:关于编程,我终于想清楚这