一   信號和槽

  GUI 程序除了要繪制控件,還要響應系統和用戶事件,例如重繪、繪制完成、點擊鼠標、敲擊鍵盤等。當事件發(fā)生時,UI 會產生相應的變化,讓用戶直觀地看到。
大部分編程(例如Win SDK、Web前端)中使用回調函數來響應事件,而 Qt 卻獨創(chuàng)了信號和槽機制。所謂回調函數,就是程序員提前定義一個函數,當事件發(fā)生時就調用該函數。
信號和槽是Qt的核心,它讓兩個互不相干的對象連接起來,當一個對象的狀態(tài)改變時,可以通知另一個對象。
我們先通過例子來演示一下信號和槽:

seo優(yōu)化培訓,網絡推廣培訓,網絡營銷培訓,SEM培訓,網絡優(yōu)化,在線營銷培訓

具體的代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "mainwindow.h"
#include <QApplication>
#include <QMainWindow>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>
 
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QMainWindow w;
    w.setWindowTitle("微浪游戲");
    w.resize(325, 120);
 
    QLineEdit lineEdit(&w);
    lineEdit.setGeometry(30, 20, 180, 36);
    lineEdit.setPlaceholderText("請輸入文本");
 
    QPushButton btn("取消", &w);
    btn.setGeometry(220, 20, 70, 36);
 
    QLabel label(&w);
    label.setGeometry(30, 70, 250, 30);
 
    //連接clicke()信號和quit()槽
    QObject::connect(&btn, SIGNAL(clicked()), &app, SLOT(quit()));
    //連接textChanged()信號和setText()槽
    QObject::connect(&lineEdit, SIGNAL(textChanged(QString)), &label, SLOT(setText(QString)));
 
    w.show();
    return app.exec();
}

 在上面的demo中創(chuàng)建了三個控件:lineEdit,btn,label,他們都是QMainWindow w的子控件。運行的結果如下:

seo優(yōu)化培訓,網絡推廣培訓,網絡營銷培訓,SEM培訓,網絡優(yōu)化,在線營銷培訓

點擊“取消”按鈕,程序就關閉了,這是第26行代碼的作用;在文本輸入框中輸入一段文本,下面的 Label 會隨時顯示出來,這是第28行代碼的作用。

這兩個對象都是通過信號和槽連接起來的,信號和槽用于兩個對象之間的通信。信號和槽是QT的核心特征,當一個特殊的事情發(fā)生時便可以發(fā)射一個信號,比如demo中的取消按鈕被點擊時,就會發(fā)射clicked()信號;而槽就是一個函數,它在信號發(fā)射后被調用來響應這個信號,Qt的部件類中已經定義了一些信號和槽,但是更常用的做法是子類化部件,然后添加自定義的信號和槽來實現想要的功能。

信號是只有函數聲明、沒有函數體的成員函數。槽是擁有完整函數體的普通成員函數,你可以在槽函數中實現各種功能,與普通函數相比并沒有區(qū)別,例如 quit() 的作用就是退出程序。

connect() 是 QObject 類的靜態(tài)成員函數;QObject 是 Qt 中所有類的基類,它就像“樹根”,從這里派生出了所有其他“樹枝”。
需要注意的是,信號不是事件。當用戶點擊“取消”按鈕時,Qt 會捕獲該點擊事件,進行預處理,然后發(fā)射 clicked() 信號; clicked() 和 quit() 關聯起來了,接下來就會調用 quit() 函數。
信號和槽機制歸根結底也是回調函數,只不過繞了個圈子。在這種機制下,程序員有兩次處理事件的機會,一是在捕獲事件后發(fā)射信號前進行預處理(事件不符合預期可以不發(fā)射信號),二是在槽函數中進行主要處理。
再來看第27行。textChange() 信號會在文本改變時發(fā)出,setText() 槽用來設置 Label 的文本,QString 是要傳遞的數據的類型。當用戶輸入文本時,lineEdit 會發(fā)出 textChange() 信號,該信號將攜帶數據,數據類型為 QString,數據內容為輸入的文本;setText() 槽接收到信號后先解析信號攜帶的數據,獲取用戶輸入的文本,然后填充到 Label 中。

二 信號和槽的關聯

信號和槽的關聯使用的是QObject類的connect()函數,connect() 是 QObject 類的靜態(tài)成員函數,它有多個原型:

1
2
3
4
5
6
7
8
9
connect(QObject *sender,   char *signal,
        QObject *receiver, char *method);
connect(QObject *sender,   PointerToMemberFunction signal,
        QObject *receiver, PointerToMemberFunction method);
connect(QObject *sender,   PointerToMemberFunction signal,
        QObject *context,  Functor functor);
connect(QObject *sender,   QMetaMethod &signal,
        QObject *receiver, QMetaMethod &method);
connect(QObject *sender,   PointerToMemberFunction signal,  Functor functor);

 簡單起見,上面省略了 connect() 的返回值和最后一個參數,以及某些參數前面的 const 修飾符,讀者可以在 Qt 幫助手冊中查看完整的原型。

connect() 函數返回值類型為QMetaObject::Connection,表示當前連接句柄。最后一個參數為Qt::ConnectionType type = Qt::AutoConnection,表示連接類型,一般默認即可。

觀察上面的原型,除了最后一個有3個參數,其他都有4個參數,其中:
1) sender 為信號發(fā)送者,receiver 為信號接收者,它們都是對象指針。

2) 第1個原型中,signal 為信號,method 為槽函數,它們都是字符串,必須借助 SIGNAL() 和 SLOT() 將函數形式轉換為字符串形式。SIGNAL() 和 SLOT() 是宏,而非函數。上面的示例中就使用了該原型,它是常用的原型,初學者必須要掌握。

3) 第2個原型中,PointerToMemberFunction 為指向成員函數的指針。你可以將示例中的代碼做如下更改:

QObject::connect(&btn, &QPushButton::clicked, &app, &QApplication::quit);
QObject::connect(&lineEdit, &QLineEdit::textChanged, &label, &QLabel::setText);

這是 Qt 5 新增的原型,可以在編譯期間進行檢查,如果信號和槽不存在或者不匹配,則會報錯。而第1種原型是從 Qt 誕生以來一直支持的,不能在編譯期進行檢測,如果信號和槽有誤,只會在程序運行期間給出警告并返回 false,不容易發(fā)現問題,這是它的一個缺陷。所以在 Qt 5 中我們鼓勵使用第2種原型。

感謝您的閱讀,若有不足之處,歡迎指教,共同學習、共同進步。 博主網址:http://www.cnblogs.com/majianchao/ 如您喜歡,麻煩推薦一下;如您有新想法,歡迎提出,郵箱:1145356699@qq.com。 本博客為博主原創(chuàng),歡迎轉載,但必須注明博客來源。 更多關于游戲開發(fā)的內容也可關注微信公眾號:微浪游戲