Qt入門(3)——訊號和槽
訊號和槽用於物件間的通訊。訊號/槽機制是Qt的一箇中心特徵並且也許是Qt與其它工具包的最不相同的部分。
在圖形使用者介面程式設計中,我們經常希望一個視窗部件的一個變化被通知給另一個視窗部件。更一般地,我們希望任何一類的物件可以和其它物件進行通訊。例如,如果我們正在解析一個XML檔案,當我們遇到一個新的標籤時,我們也許希望通知列表檢視我們正在用來表達XML檔案的結構。
較老的工具包使用一種被稱作回撥的通訊方式來實現同一目的。回撥是指一個函式的指標,所以如果你希望一個處理函式通知你一些事件,你可以把另一個函式(回撥)的指標傳遞給處理函式。處理函式在適當的時候呼叫回撥。回撥有兩個主要缺點。首先他們不是型別安全的。我們從來都不能確定處理函式使用了正確的引數來呼叫回撥。其次回撥和處理函式是非常強有力地聯絡在一起的,因為處理函式必須知道要呼叫哪個回撥。
在Qt中我們有一種可以替代回撥的技術。我們使用訊號和槽。當一個特定事件發生的時候,一個訊號被髮射。Qt的視窗部件有很多預定義的訊號,但是我們總是可以通過繼承來加入我們自己的訊號。槽就是一個可以被呼叫處理特定訊號的函式。Qt的視窗部件又很多預定義的槽,但是通常的習慣是你可以加入自己的槽,這樣你就可以處理你所感興趣的訊號。
訊號和槽的機制是型別安全的:一個訊號的簽名必須與它的接收槽的簽名相匹配。(實際上一個槽的簽名可以比它接收的訊號的簽名少,因為它可以忽略額外的簽名。)因為簽名是一致的,編譯器就可以幫助我們檢測型別不匹配。訊號和槽是寬鬆地聯絡在一起的:一個發射訊號的類不用知道也不用注意哪個槽要接收這個訊號。Qt的訊號和槽的機制可以保證如果你把一個訊號和一個槽連線起來,槽會在正確的時間使用訊號的引數而被呼叫。訊號和槽可以使用任何數量、任何型別的引數。它們是完全型別安全的:不會再有回撥核心轉儲(core dump)。
從QObject類或者它的一個子類(比如QWidget類)繼承的所有類可以包含訊號和槽。當物件改變它們的狀態的時候,訊號被髮送,從某種意義上講,它們也許對外面的世界感興趣。這就是所有的物件通訊時所做的一切。它不知道也不注意無論有沒有東西接收它所發射的訊號。這就是真正的資訊封裝,並且確保物件可以用作一個軟體元件。
槽可以用來接收訊號,但它們是正常的成員函式。一個槽不知道它是否被任意訊號連線。此外,物件不知道關於這種通訊機制和能夠被用作一個真正的軟體元件。
你可以把許多訊號和你所希望的單一槽相連,並且一個訊號也可以和你所期望的許多槽相連。把一個訊號和另一個訊號直接相連也是可以的。(這時,只要第一個訊號被髮射時,第二個訊號立刻就被髮射。)
總體來看,訊號和槽構成了一個強有力的元件程式設計機制。
一個最小的C++類宣告:
一個小的Qt類如下:
這個類有同樣的內部狀態,和公有方法來訪問狀態,但是另外它也支援使用訊號和槽的元件程式設計:這個類可以通過發射一個訊號,valueChanged(),來告訴外面的世界它的狀態發生了變化,並且它有一個槽,其它物件可以傳送訊號給這個槽。
所有包含訊號和/或者槽的類必須在它們的宣告中提到Q_OBJECT。
槽可以由應用程式的編寫者來實現。這裡是Foo::setValue()的一個可能的實現:
這個類有同樣的內部狀態,和公有方法來訪問狀態,但是另外它也支援使用訊號和槽的元件程式設計:這個類可以通過發射一個訊號,valueChanged(),來告訴外面的世界它的狀態發生了變化,並且它有一個槽,其它物件可以傳送訊號給這個槽。
所有包含訊號和/或者槽的類必須在它們的宣告中提到Q_OBJECT。
槽可以由應用程式的編寫者來實現。這裡是Foo::setValue()的一個可能的實現:
emit valueChanged(v)這一行從物件中發射valueChanged訊號。正如你所能看到的,你通過使用emit signal(arguments)來發射訊號。
下面是把兩個物件連線在一起的一種方法:
呼叫a.setValue(79)會使a發射一個valueChanged() 訊號,b將會在它的setValue()槽中接收這個訊號,也就是b.setValue(79) 被呼叫。接下來b會發射同樣的valueChanged()訊號,但是因為沒有槽被連線到b的valueChanged()訊號,所以沒有發生任何事(訊號消失了)。
注意只有當v != val的時候setValue()函式才會設定這個值並且發射訊號。這樣就避免了在迴圈連線的情況下(比如b.valueChanged() 和a.setValue()連線在一起)出現無休止的迴圈的情況。
這個例子說明了物件之間可以在互相不知道的情況下一起工作,只要在最初的時在它們中間建立連線。
預處理程式改變或者移除了signals、slots和emit 這些關鍵字,這樣就可以使用標準的C++編譯器。
在一個定義有訊號和槽的類上執行moc。這樣就會生成一個可以和其它物件檔案編譯和連線成引用程式的C++原始檔。
在圖形使用者介面程式設計中,我們經常希望一個視窗部件的一個變化被通知給另一個視窗部件。更一般地,我們希望任何一類的物件可以和其它物件進行通訊。例如,如果我們正在解析一個XML檔案,當我們遇到一個新的標籤時,我們也許希望通知列表檢視我們正在用來表達XML檔案的結構。
較老的工具包使用一種被稱作回撥的通訊方式來實現同一目的。回撥是指一個函式的指標,所以如果你希望一個處理函式通知你一些事件,你可以把另一個函式(回撥)的指標傳遞給處理函式。處理函式在適當的時候呼叫回撥。回撥有兩個主要缺點。首先他們不是型別安全的。我們從來都不能確定處理函式使用了正確的引數來呼叫回撥。其次回撥和處理函式是非常強有力地聯絡在一起的,因為處理函式必須知道要呼叫哪個回撥。
在Qt中我們有一種可以替代回撥的技術。我們使用訊號和槽。當一個特定事件發生的時候,一個訊號被髮射。Qt的視窗部件有很多預定義的訊號,但是我們總是可以通過繼承來加入我們自己的訊號。槽就是一個可以被呼叫處理特定訊號的函式。Qt的視窗部件又很多預定義的槽,但是通常的習慣是你可以加入自己的槽,這樣你就可以處理你所感興趣的訊號。
訊號和槽的機制是型別安全的:一個訊號的簽名必須與它的接收槽的簽名相匹配。(實際上一個槽的簽名可以比它接收的訊號的簽名少,因為它可以忽略額外的簽名。)因為簽名是一致的,編譯器就可以幫助我們檢測型別不匹配。訊號和槽是寬鬆地聯絡在一起的:一個發射訊號的類不用知道也不用注意哪個槽要接收這個訊號。Qt的訊號和槽的機制可以保證如果你把一個訊號和一個槽連線起來,槽會在正確的時間使用訊號的引數而被呼叫。訊號和槽可以使用任何數量、任何型別的引數。它們是完全型別安全的:不會再有回撥核心轉儲(core dump)。
從QObject類或者它的一個子類(比如QWidget類)繼承的所有類可以包含訊號和槽。當物件改變它們的狀態的時候,訊號被髮送,從某種意義上講,它們也許對外面的世界感興趣。這就是所有的物件通訊時所做的一切。它不知道也不注意無論有沒有東西接收它所發射的訊號。這就是真正的資訊封裝,並且確保物件可以用作一個軟體元件。
槽可以用來接收訊號,但它們是正常的成員函式。一個槽不知道它是否被任意訊號連線。此外,物件不知道關於這種通訊機制和能夠被用作一個真正的軟體元件。
你可以把許多訊號和你所希望的單一槽相連,並且一個訊號也可以和你所期望的許多槽相連。把一個訊號和另一個訊號直接相連也是可以的。(這時,只要第一個訊號被髮射時,第二個訊號立刻就被髮射。)
總體來看,訊號和槽構成了一個強有力的元件程式設計機制。
一個最小的C++類宣告:
class Foo
{
public:
Foo();
int value() const { return val; }
void setValue( int );
private:
int val;
};
一個小的Qt類如下:
class Foo : public QObject
{
Q_OBJECT
public:
Foo();
int value() const { return val; }
public slots:
void setValue( int );
signals:
void valueChanged( int );
private:
int val;
};
這個類有同樣的內部狀態,和公有方法來訪問狀態,但是另外它也支援使用訊號和槽的元件程式設計:這個類可以通過發射一個訊號,valueChanged(),來告訴外面的世界它的狀態發生了變化,並且它有一個槽,其它物件可以傳送訊號給這個槽。
所有包含訊號和/或者槽的類必須在它們的宣告中提到Q_OBJECT。
槽可以由應用程式的編寫者來實現。這裡是Foo::setValue()的一個可能的實現:
void Foo::setValue( int v )
{
if ( v != val ) {
val = v;
emit valueChanged(v);
}
}
這個類有同樣的內部狀態,和公有方法來訪問狀態,但是另外它也支援使用訊號和槽的元件程式設計:這個類可以通過發射一個訊號,valueChanged(),來告訴外面的世界它的狀態發生了變化,並且它有一個槽,其它物件可以傳送訊號給這個槽。
所有包含訊號和/或者槽的類必須在它們的宣告中提到Q_OBJECT。
槽可以由應用程式的編寫者來實現。這裡是Foo::setValue()的一個可能的實現:
void Foo::setValue( int v )
{
if ( v != val ) {
val = v;
emit valueChanged(v);
}
}
emit valueChanged(v)這一行從物件中發射valueChanged訊號。正如你所能看到的,你通過使用emit signal(arguments)來發射訊號。
下面是把兩個物件連線在一起的一種方法:
Foo a, b;
connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));
b.setValue( 11 ); // a == undefined b == 11
a.setValue( 79 ); // a == 79 b == 79
b.value();
呼叫a.setValue(79)會使a發射一個valueChanged() 訊號,b將會在它的setValue()槽中接收這個訊號,也就是b.setValue(79) 被呼叫。接下來b會發射同樣的valueChanged()訊號,但是因為沒有槽被連線到b的valueChanged()訊號,所以沒有發生任何事(訊號消失了)。
注意只有當v != val的時候setValue()函式才會設定這個值並且發射訊號。這樣就避免了在迴圈連線的情況下(比如b.valueChanged() 和a.setValue()連線在一起)出現無休止的迴圈的情況。
這個例子說明了物件之間可以在互相不知道的情況下一起工作,只要在最初的時在它們中間建立連線。
預處理程式改變或者移除了signals、slots和emit 這些關鍵字,這樣就可以使用標準的C++編譯器。
在一個定義有訊號和槽的類上執行moc。這樣就會生成一個可以和其它物件檔案編譯和連線成引用程式的C++原始檔。
相關文章
- QT從入門到入土(三)——訊號和槽機制QT
- Qt入門(18)——使用訊號和槽連線控制元件QT控制元件
- 02_QT訊號和槽QT
- 《Qt5:訊號和槽使用示例》QT
- Qt之訊號與槽QT
- Qt Connect 訊號 槽QT
- Qt5的訊號和槽函式QT函式
- Qt 5 中的訊號槽QT
- Qt 訊號槽傳遞指標QT指標
- Qt 訊號槽如何傳遞引數(或帶引數的訊號槽)QT
- QT 控制檯訊號與槽簡例QT
- QT學習筆記1(安裝、建立和訊號與槽)QT筆記
- Qt 5中使用lambda表示式連線訊號和槽QT
- QT槽函式獲取訊號傳送物件QT函式物件
- C++ Qt開發:如何使用訊號與槽C++QT
- qt多執行緒訊號槽傳輸方式QT執行緒
- Qt 自動連線機制訊號與槽QT
- Qt - 訊號與槽的第五個引數QT
- Qt訊號與槽使用方法最完整總結QT
- Qt自定義訊號槽的使用淺析+例項QT
- Qt 中多執行緒對應的訊號槽QT執行緒
- C++《QT之按鍵QPushButton設定訊號與槽》C++QT
- QT入門QT
- QT中的訊號-槽比我們常用的callback到底牛在哪裡?QT
- Qt學習第三篇(訊號槽函式的連線)QT函式
- C++訊號槽C++
- QT快速入門QT
- Qt入門(11)——Qt外掛QT
- Linux下應用程式開發:QT的訊號與槽機制(轉)LinuxQT
- Qt訊號 lamda 表示式使用QT
- Qt入門(12)——Qt國際化QT
- Qt入門(13)——Qt的呼叫退出QT
- Qt入門(20)——Qt模組簡介QT
- Qt Quick 如何入門?QTUI
- Qt入門之概述QT
- Qt入門(8)——事件和事件過濾器QT事件過濾器
- 訊號與槽N對N
- PyQt4(簡單訊號槽)QT