Qt開發學習(3)自定義訊號槽

2021-09-23 10:35:32 字數 3313 閱讀 1808

使用 connect() 可以讓我們連線系統提供的訊號和槽。但是,qt 的訊號槽機制並不僅僅是使用系統提供的那部分,還會允許我們自己設計自己的訊號和槽。

訊號槽不是 gui 模組提供的,而是 qt 核心特性之一。因此,可以在普通的控制台程式使用訊號槽。

經典的觀察者模式在講解舉例的時候通常會舉報紙和訂閱者的例子。有乙個報紙類 news*****,有乙個訂閱者類 subscriber。subscriber 可以訂閱 news*****。這樣,當 news***** 有了新的內容的時候,subscriber 可以立即得到通知。在這個例子中,觀察者是 subscriber,被觀察者是 news*****。在經典的實現**中,觀察者會將自身註冊到被觀察者的乙個容器中(比如 subscriber.registerto(news*****))。被觀察者發生了任何變化的時候,會主動遍歷這個容器,依次通知各個觀察者(news*****.notifyallsubscribers())。

//!!! qt5

#include

// news*****.h

class news***** : public qobject

void

send()

signals:

void

new*****

(const qstring &name);

private:

qstring m_name;};

// reader.h

#include

#include

class reader : public qobject

void

receivenews*****

(const qstring & name)};

// main.cpp

#include

#include

"news*****.h"

#include

"reader.h"

intmain

(int argc,

char

*ar**)

這段**放在了三個檔案,分別是 news*****.h,reader.h 和 main.cpp。為了減少檔案數量,可以把 news*****.h 和 reader.h 都放在 main.cpp 的 main() 函式之前嗎?答案是,可以,但是需要有額外的操作。具體問題,我們在下面會詳細說明。

首先看 news***** 這個類。這個類繼承了 qobject 類。只有繼承了 qobject 類的類,才具有訊號槽的能力。所以,為了使用訊號槽,必須繼承 qobject。凡是 qobject 類(不管是直接子類還是間接子類),都應該在第一行**寫上 q_object。不管是不是使用訊號槽,都應該新增這個巨集。這個巨集的展開將為我們的類提供訊號槽機制、國際化機制以及 qt 提供的不基於 c++ rtti 的反射能力。因此,如果你覺得你的類不需要使用訊號槽,就不新增這個巨集,就是錯誤的。其它很多操作都會依賴於這個巨集。注意,這個巨集將由 moc(我們會在後面章節中介紹 moc。這裡你可以將其理解為一種預處理器,是比 c++ 預處理器更早執行的預處理器。) 做特殊處理,不僅僅是巨集展開這麼簡單。moc 會讀取標記了 q_object 的標頭檔案,生成以 moc_ 為字首的檔案,比如 news*****.h 將生成 moc_news*****.cpp。你可以到構建目錄檢視這個檔案,看看到底增加了什麼內容。注意,由於 moc 只處理標頭檔案中的標記了 q_object 的類宣告,不會處理 cpp 檔案中的類似宣告。因此,如果我們的 news***** 和 reader 類位於 main.cpp 中,是無法得到 moc 的處理的。解決方法是,我們手動呼叫 moc 工具處理 main.cpp,並且將 main.cpp 中的 include 「news*****.h」 改為 include 「moc_news*****.h」 就可以了。不過,這是相當繁瑣的步驟,為了避免這樣修改,我們還是將其放在標頭檔案中。許多初學者會遇到莫名其妙的錯誤,一加上 q_object 就出錯,很大一部分是因為沒有注意到這個巨集應該放在標頭檔案中。

news***** 類的 public 和 private **塊都比較簡單,只不過它新加了乙個 signals。signals 塊所列出的,就是該類的訊號。訊號就是乙個個的函式名,返回值是 void(因為無法獲得訊號的返回值,所以也就無需返回任何值),引數是該類需要讓外界知道的資料。訊號作為函式名,不需要在 cpp 函式中新增任何實現(我們曾經說過,qt 程式能夠使用普通的 make 進行編譯。沒有實現的函式名怎麼會通過編譯?原因還是在 moc,moc 會幫我們實現訊號函式所需要的函式體,所以說,moc 並不是單純的將 q_object 展開,而是做了很多額外的操作)。

news***** 類的 send() 函式比較簡單,只有乙個語句 emit new*****(m_name);。emit 是 qt 對 c++ 的擴充套件,是乙個關鍵字(其實也是乙個巨集)。emit 的含義是發出,也就是發出 new*****() 訊號。感興趣的接收者會關注這個訊號,可能還需要知道是哪份報紙發出的訊號?所以,我們將實際的報紙名字 m_name 當做引數傳給這個訊號。當接收者連線這個訊號時,就可以通過槽函式獲得實際值。這樣就完成了資料從發出者到接收者的乙個轉移。

reader 類更簡單。因為這個類需要接受訊號,所以我們將其繼承了 qobject,並且新增了 q_object 巨集。後面則是預設建構函式和乙個普通的成員函式。qt 5 中,任何成員函式、static 函式、全域性函式和 lambda 表示式都可以作為槽函式。與訊號函式不同,槽函式必須自己完成實現**。槽函式就是普通的成員函式,因此作為成員函式,也會受到 public、private 等訪問控制符的影響。(我們沒有說訊號也會受此影響,事實上,如果訊號是 private 的,這個訊號就不能在類的外面連線,也就沒有任何意義。)

main() 函式中,我們首先建立了 news***** 和 reader 兩個物件,然後使用 qobject::connect() 函式。這個函式我們上一節已經詳細介紹過,這裡應該能夠看出這個連線的含義。然後我們呼叫 news***** 的 send() 函式。這個函式只有乙個語句:發出訊號。由於我們的連線,當這個訊號發出時,自動呼叫 reader 的槽函式,列印出語句。

這樣我們的示例程式講解完畢。我們基於 qt 的訊號槽機制,不需要觀察者的容器,不需要註冊物件,就實現了觀察者模式。

下面總結一下自定義訊號槽需要注意的事項:

傳送者和接收者都需要是 qobject 的子類(當然,槽函式是全域性函式、lambda 表示式等無需接收者的時候除外);

使用 signals 標記訊號函式,訊號是乙個函式宣告,返回 void,不需要實現函式**;

槽函式是普通的成員函式,作為成員函式,會受到 public、private、protected 的影響;

使用 emit 在恰當的位置傳送訊號;

使用 qobject::connect() 函式連線訊號和槽。

Qt自定義訊號槽

qt自定義訊號槽,在控制台程式中實現 qt5 如下 qt5 include news h class news public qobject void send signals void new const qstring name private qstring m name reader.h i...

Qt 自定義訊號槽

使用 connect 可以讓我們連線系統提供的訊號和槽。但是,qt 的訊號槽機制並不僅僅是使用系統提供的那部分,還會允許我們自己設計自己的訊號和槽。這也是 qt 框架的設計思路之一,用於我們設計解耦的程式。本節將講解如何在自己的程式中自定義訊號槽。訊號槽不是 gui 模組提供的,而是 qt 核心特性...

QT 3 自定義訊號槽

include subwdget.h subwdget subwdget qwidget parent qwidget parent void subwdget senslot ifndef subwdget h define subwdget h include include class sub...