1. 程式人生 > >2.2深入訊號和槽(Signals and Slots in Depth)

2.2深入訊號和槽(Signals and Slots in Depth)

訊號和槽是Qt程式設計的一個重要部分。這個機制可以在物件之間彼此並不瞭解的情況下將它們的行為聯絡起來。在前幾個例程中,我們已經連線了訊號和槽,聲明瞭控制元件自己的訊號和槽,並實現了槽函式,傳送了訊號。現在來更深入瞭解這個機制。

槽和普通的c++成員函式很像。它們可以是虛擬函式(virtual),也可被過載(overload),可以是公有的(public),保護的(protective),也可是私有的(private),它們可以象任何c++成員函式一樣被呼叫,可以傳遞任何型別的引數。不同在於一個槽函式能和一個訊號相連線,只要訊號發出了,這個槽函式就會自動被呼叫。

connect函式語法如下:
connect(sender, SIGNAL(signal), receiver, SLOT(slot));
sender和receiver是QObject物件指標,signal和slot是不帶引數的函式原型。SIGNALE()和SLOT()巨集的作用是把他們轉換成字串。

在目前有的例子中,我們已經連線了不同的訊號和槽。實際使用中還要考慮如下一些規則:
1、一個訊號可以連線到多個槽:
connect(slider, SIGNAL(valueChanged(int)),spinBox, SLOT(setValue(int)));
connect(slider, SIGNAL(valueChanged(int)),this, SLOT(updateStatusBarIndicator(int)));
當訊號發出後,槽函式都會被呼叫,但是呼叫的順序是隨機的,不確定的。

2、多個訊號可以連線到一個槽
connect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError()));
connect(calculator, SIGNAL(divisionByZero()),this, SLOT(handleMathError()));
任何一個訊號發出,槽函式都會執行。

3、一個訊號可以和另一個訊號相連
connect(lineEdit, SIGNAL(textChanged(const QString &)),
        this, SIGNAL(updateRecord(const QString &)));
 第一個訊號發出後,第二個訊號也同時傳送。除此之外,訊號與訊號連線上和訊號和槽連線相同。
 
4、連線可以被刪除
disconnect(lcd, SIGNAL(overflow()),this, SLOT(handleMathError()));
這個函式很少使用,一個物件刪除後,Qt自動刪除這個物件的所有連線。

訊號和槽函式必須有著相同的引數型別,這樣訊號和槽函式才能成功連線:
connect(ftp, SIGNAL(rawCommandReply(int, const QString &)),this, SLOT(processReply(int, const QString &)));
如果訊號裡的引數個數多於槽函式的引數,多餘的引數被忽略:
connect(ftp, SIGNAL(rawCommandReply(int, const QString &)),this, SLOT(checkErrorCode(int)));
如果參速型別不匹配,或者訊號和槽不存在,在debug狀態時,Qt會在執行期間給出警告。如果訊號和槽連線時包含了引數的名字,Qt將會給出警告。
以前我們列舉的例子中都是控制元件的訊號和槽。但是訊號和槽機制在QObject中就實現了,可以實現在任何從QObject繼承的子類中。
class Employee : public QObject
{
    Q_OBJECT
public:
    Employee() { mySalary = 0; }
    int salary() const { return mySalary; }
public slots:
    void setSalary(int newSalary);
signals:
    void salaryChanged(int newSalary);
private:
    int mySalary;
};
void Employee::setSalary(int newSalary)
{
    if (newSalary != mySalary) {
        mySalary = newSalary;
        emit salaryChanged(mySalary);
    }
}
注意,只有newSalary != mySalary時才發出salary-Changed()訊號,這樣避免了死迴圈的出現。

×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
Qt的Meta-Object系統
Qt的一個最主要的成功是對C++擴充套件,即把彼此獨立的軟體模組連線起來,而不需要模組間的任何細節。
這個機制就是Meta-Object系統,它提供了兩個關鍵的用途:訊號和槽和introspection(內省)。introspection功能允許應用程式在執行時得到QObjec它子類的“meta-information”,這對實現訊號和槽是很必要的,包括全部訊號和槽的列表,和類的名字。這個機制還提供了屬性(在Qt Designer中使用)和文字翻譯(國際化)支援。它們構成了QSA(Qt Script for Application)的基礎。

標準C++不提供Qt meta-object系統需要的動態meta-information。Qt提供了一個獨立的工具moc,通過定義Q_OBJECT巨集實現到C++函式的轉變。moc是用純c++實現的,因此可以使用在任何C++編譯器中。

這個機制工作過程如下:
Q_OBJECT聲明瞭一些QObject子類必須實現的內省函式:metaObject(),TR(),qt_metacall()等。
Qt的moc工具實現Q_OBJECT巨集宣告的函式和所有的訊號。
QObject成員函式connect()和disconnect()使用這些內省函式實現訊號和槽的連線。
以上這些是通過qmake,moc和QObject自動處理的,程式設計師通常不用考慮它們。如果你感到對此好奇,可以檢視QMetaObject類文件和moc實現的c++程式碼。