QT入门语法——signal,slot
Qt 5 之前的語法
在 Qt 5 之前,我們需要使用下面的語句來鏈接 signal 和 slot:
| 12 | connect(sender, SIGNAL(valueChanged(QString, QString)),????????receiver, SLOT(updateValue(QString))); |
Qt 實際上利用SIGNAL和SLOT這兩個宏,把其后的函數名轉換成一個字符串。隨后,moc 將會掃描全部文件,將所有的 signal 和 slot 提取出來做成一個映射表。QObject::connect()函數則會從這個映射表里面找到該字符串,從 signal 的名字就可以找到 slot 的名字,因此也就知道了在 signal?emit 的時候,該去調用哪一個 slot 函數。
Qt 5 之前的 signal/slot 語法的問題
從上面的解釋可以看出,Qt 5 之前版本提供的這種語法其實有一些問題:
- 沒有編譯期檢查:因為函數名被處理成字符串,所有的檢查都是在運行時完成的。這就是為什么有時會發生編譯通過了,但 slot 并沒有被調用。此時,你就應該去檢查 console 的輸出,看看有沒有什么 warning 說明 connect 并沒有成功。
- 因為處理的是字符串,所以 slot 中的類型名字必須用 signal 的完全一致,而且在頭文件中的和實際 connect 語句中的也必須一致。也就是說,如果你使用了 typedef 或者 namespace,connect 就可能不成功(在 Qt 5 之前的版本中,我們當然也可以使用 namespace,但是必須保證頭文件中的和 connect 語句中的文本完全一致)。
新語法:使用函數指針
在 Qt5 提供了一套新的語法。之前的語法依然可以使用,但是現在,我們有了更好的選擇:
| 1 2 | connect(sender,&Sender::valueChanged, ????????receiver,&Receiver::updateValue); |
這個看起來和之前的版本很類似,因此很容易遷移到新的語法。下面我們看看新語法有什么好處:
編譯器檢查
如果把 signal 或者 slot 名字編寫錯誤,或者 slot 的參數同 signal 不一致,你會在編譯期就獲得一個錯誤。這肯定會在重構、或者修改 signal 或 slot 的名字時節省很多時間。
另一個影響是,Qt 可以利用static_cast返回更友好的錯誤信息。例如,如果我們少了Q_OBJECT宏,則會有:
| 123456789 | #include <QtCore/QtCore>class Goo : public QObject {????Goo() {????????connect(this, &Goo::someSignal, this, &QObject::deleteLater);????}signals:????void someSignal();}; |
其錯誤信息是:
| 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 |
參數的自動類型轉換
現在,我們不僅可以更好地使用 typedef 或 namespace,而且可以利用隱式類型轉換。在下面的例子中,我們的 signal 有一個QString參數,而 slot 需要的是QVariant。在新語法中,QString將被自動轉換成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 &);}; |
連接到任意函數
如果你留心上面的例子,就會發現,我們的 signal 被連接到了一個 public 函數,但這個函數并不是 slot。Qt 的新語法通過函數指針直接調用函數,而不需要 moc 的特殊處理(但是 signal 依然需要)。
更進一步,我們可以將 signal 連接到任意函數:
| 1 2 3 4 5 6 | staticvoidsomeFunction(){ ????qDebug()<<“pressed”; } // … somewhere else QObject::connect(button,&QPushButton::clicked,someFunction); |
這樣處理,就可以讓你很方便的同 boost 或者 tr1::bind 這樣的第三方庫協作。
C++11 lambda 表達式
至此之前,我們所有的示例都是基于 C++98. 標準的。但是,如果你的編譯器支持 c++11,我相信你一看到“函數指針”這幾個字,就一定會想到 C++11 的新特性:Lambda 表達式。現在,Lambda 表達式至少被 MSVC 2010,GCC 4.5,clang 3.1 這幾個編譯器支持。不過對于后面兩個編譯器,你需要在編譯時加上?-std=c++0x?參數。
現在我們可以用 Lambda 表達式重寫了:
| 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(); ????}); } |
這種語法允許我們更方便地編寫異步代碼。
總結
以上是生活随笔為你收集整理的QT入门语法——signal,slot的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 牛逼了!PDF 版本 5000 页 Ja
- 下一篇: 30 岁程序员:关于编程,我终于想清楚这