Qt學習第三篇(訊號槽函式的連線)

ivanlee717發表於2024-05-30

訊號與槽函式

在 Qt 中,訊號(Signals)和槽(Slots)是用於物件間通訊的一種機制,是 Qt 中事件處理和訊息傳遞的重要方式。訊號和槽機制使得物件之間的通訊更加靈活和方便,同時也實現了很好的解耦。

當某個事件發生後,如某個按鈕被點選了一下,它就會發出一個被點選的訊號(signal)。

某個物件接收到這個訊號之後,就會做一些相關的處理動作(稱為槽slot)。

但是Qt物件不會無故收到某個訊號,要想讓一個物件收到另一個物件發出的訊號,這時候需要建立連線(connect)

在Qt中,有一種回撥技術的替代方法:那就是訊號和槽機制。當特定事件發生時,會發出一個訊號。Qt的小部件中有許多預定義的訊號,但我們可以將小部件子類化,向它們新增自定義的訊號。槽是響應特定訊號的函式。Qt的小部件有許多預定義的槽函式,但是通常是子類化小部件並新增自己的槽函式,這樣就可以處理與之相關聯的訊號了。如下圖所示:

image-20240518225711864

訊號和槽用於多個物件之間的通訊。訊號和槽機制是Qt的核心特性,也是與其他框架最大的不同之處。Qt的元物件系統是訊號和槽實現的基礎。

在GUI程式設計中,當更改一個小部件時,通常希望另一個小部件得到通知。希望任何型別的物件都能夠相互通訊。例如,如果使用者單擊關閉按鈕,可能希望呼叫視窗的Close()函式。

image-20240518225553016

這是 Qt 中的 connect 函式的宣告,它用於連線訊號和槽。讓我解釋一下每個引數的含義:

  • const typename QtPrivate::FunctionPointer<Func1>::Object *sender:表示發出訊號的物件指標。這裡使用了模板超程式設計的技巧,Func1 是訊號函式的型別,QtPrivate::FunctionPointer 用於提取函式指標的物件型別。
  • Func1 signal:表示訊號的函式指標。在這裡,Func1 代表訊號函式的型別,通常是一個成員函式指標,用於指示發出訊號的具體函式。
  • Func2 &&slot:表示槽的函式指標。Func2 代表槽函式的型別,通常也是一個成員函式指標。&& 表示這是一個右值引用,用於接收槽函式的引數。

該函式返回一個 QMetaObject::Connection 物件,用於表示訊號和槽之間的連線。

總的來說,這個 connect 函式的作用是將一個發出訊號的物件(sender)的特定訊號(signal)連線到一個槽函式(slot)上。當訊號被髮出時,與之連線的槽函式會被呼叫。

image-20240518231448027

像之前的程式碼就是預設使用的自動連線。

QObject::connect(lineEdit,&QLineEdit::textChanged,[&](){
    QString text = lineEdit->text();
    qDebug() << "文字已改變:" << text;
});

當使用 Qt 進行訊號和槽的連線時,可以根據需要選擇不同的連線型別。以下是一些常見的連線型別及其示例:

  1. 直接連線

    • 直接連線是預設的連線方式,它會在發射訊號時立即呼叫槽函式。

    • 示例:

      connect(sender, &SenderClass::signal, receiver, &ReceiverClass::slot);
      
  2. 排隊連線

    • 排隊連線會將訊號放入接收者的事件佇列中,以便稍後被處理。這在多執行緒應用程式中很有用。

    • 示例:

      connect(sender, &SenderClass::signal, receiver, &ReceiverClass::slot, Qt::QueuedConnection);
      
  3. 自動連線

    • 自動連線會根據傳送者和接收者物件是否處於同一執行緒來決定使用直接連線還是排隊連線。

    • 示例:

      connect(sender, &SenderClass::signal, receiver, &ReceiverClass::slot, Qt::AutoConnection);
      
  4. 連線型別組合

    • 除了單獨使用直接連線、排隊連線和自動連線外,還可以對其進行組合,以實現更復雜的連線行為。

    • 示例:

      connect(sender, &SenderClass::signal, receiver, &ReceiverClass::slot, Qt::UniqueConnection | Qt::QueuedConnection);
      

一般這個sender就是程式碼裡定義好會發生事件的物件,signal是訊號,Receiver是訊號的接收者,n slot:接收物件在接收到訊號之後所需要呼叫的函式(槽函式)。這裡要注意的是connect的四個引數都是指標,訊號和槽是函式指標。

系統自帶的訊號和槽如何查詢呢,這個就需要利用幫助文件了,在幫助文件中比如我們上面的按鈕的點選訊號,點選signals索引到系統自帶的訊號有如下幾個

image-20240527154953425

1.1 自定義訊號和槽

Qt框架預設提供的標準訊號和槽不足以完成我們日常應用開發的需求,比如說點選某個按鈕讓另一個按鈕的文字改變,這時候標準訊號和槽就沒有提供這樣的函式。但是Qt訊號和槽機制提供了允許我們自己設計自己的訊號和槽。

條件:

  1. 宣告在類的signals域下

  2. 沒有返回值,void型別的函式

  3. 只有函式宣告,沒有定義

  4. 可以有引數,可以過載

  5. 透過emit關鍵字來觸發訊號,形式:emit object->sig(引數);

區域性變數引入方式

image-20240530100942314

[ ],標識一個Lambda的開始。由於lambda表示式可以定義在某一個函式體A裡邊,所以lambda表示式有可能會去訪問A函式中的區域性變數。中括號裡邊內容是描述了在lambda表示式裡邊可以使用的外部區域性變數的列表:

  • []:表示lambda表示式不能訪問外部函式體的任何區域性變數
  • [a]:在函式體內部使用值傳遞的方式訪問a變數
  • [&b]:在函式體內部使用引用傳遞的方式訪問b變數
  • [=]:表示透過傳值方式捕獲外部變數。使用 [=] 捕獲方式時,Lambda 表示式內部可以訪問外部作用域中的變數,並且這些變數會被複制到 Lambda 表示式內部,因此對外部變數的修改不會影響外部作用域中的變數。
  • [&]:表示透過引用方式捕獲外部變數。使用 [&] 捕獲方式時,Lambda 表示式內部可以訪問外部作用域中的變數,並且直接引用這些變數,因此對外部變數的修改會影響外部作用域中的變數。
  • [=,&foo]:foo使用引用方式, 其餘是值傳遞的方式
  • [&,foo]:foo使用值傳遞方式, 其餘是引用傳遞的方式
  • [this]:在函式內部可以使用類的成員函式和成員變數,=和&形式也都會預設引入

由於引用方式捕獲物件會有區域性變數釋放了而lambda函式還沒有被呼叫的情況。如果執行lambda函式那麼引用傳遞方式捕獲進來的區域性變數的值不可預知。

所以在無特殊情況下建議使用\[=\](){}的形式

在程式碼裡面,我們使用了一個lambda表示式,Lambda 表示式中的 [&] 捕獲了所有外部變數,所以假設 lineEdit 是一個指向 QLineEdit 物件的指標,那麼在 Lambda 表示式中可以直接訪問 lineEdit,獲取其文字並輸出到除錯資訊中。

1.1.2 選項Opt

Opt 部分是可選項,最常用的是mutable宣告,這部分可以省略。外部函式區域性變數透過值傳遞引進來時,其預設是const,所以不能修改這個區域性變數的複製,加上mutable就可以

image-20240530103825588

因為用的是=捕獲外部變數,所以無法對外部變數進行修改,傳入方式是引用。但是加上mutable就沒啥問題了

image-20240530104122962

1.1.3 函式返回值 ->retType

image-20240530104536833

retType,標識lambda函式返回值的型別。這部分可以省略,但是省略了並不代表函式沒有返回值,編譯器會自動根據函式體內的return語句判斷返回值型別,但是如果有多條return語句,而且返回的型別都不一樣,編譯會報錯

而且使用Lambda表示式作為槽的時候不需要填入訊號的接收者。一旦文字被改變,lambda表示式也會直接執行。當然lambda表示式還可以指定函式引數,這樣也就能夠接收到訊號函式傳遞過來的引數了。由於lambda表示式比我們自己自定義槽函式要方便而且靈活得多,所以在實現槽函式的時候優先考慮使用Lambda表示式。一般我們的使用習慣也是lambda表示式外部函式的區域性變數全部透過值傳遞捕獲進來,也就是:[=](){ }的形式

相關文章