細談QT信號與槽機制

轉自個人博客

信號與槽是我個人認為QT中最牛的機制之一,最近沒有其他的內容可寫,今天就來細細總結一下這個信號與槽機制。

1. 信號與槽機制概述

信號與槽機制可以理解為QT中的一種通信手段,在運行相關代碼前,分別聲明信號和槽,再利用connect()方法將信號和對應的槽連接起來,之后再需要的地方使用emit觸發信號,那么就可以讓槽響應。

其中,槽可以是特定的槽函數,也可以是其他各種普通函數;信號是一種特定對象,其結構類似函數,也可以像函數那樣帶有參數,將值通過參數傳遞槽函數,便于槽函數處理變量,如果使用參數,則信號和槽的參數要保持一致。

示例:

class MyClass : public QObject 
{
protected:MyClass(QObject *parent = nullptr);~MyClass();void myFunc();signals:void dataArrived(string data);slots:void onDataArrived(string data);
}///MyClass::MyClass(QObject *parent = nullptr)
{// 在使用信號與槽函數之前連接信號和槽connect(this, &MyClass::dataArrived, this, &myClass::onDataArrived);
}
MyClass::~MyClass()
{// 在不用的時候釋放連接disconnect(this, &MyClass::dataArrived, this, &myClass::onDataArrived);
}void MyClass::onDataArrived(string data)
{// 處理data或其他操作
}void MyClass::myFunc()
{....// 在需要的地方觸發信號,執行onDataArrived槽函數,并傳遞數據emit dataArrived(data);
}

這個示例中,只在一個類中使用信號和槽傳遞數據,其效果與直接調用函數無異,但信號與槽的功能遠超如此。

可以使用信號與槽實現跨類通信、跨線程通信,保證線程不會阻塞,實現異步處理。一個槽可以同時與多個信號連接,便于代碼中應對不同情況,不用命名各種函數。而且信號與槽可以任意連接、解耦,也可以實現在不同的地方將一個信號與不同槽函數連接,輕松改寫結構,而不會影響代碼其他部分。

2. QT中信號與槽

2.1 基礎用法

我這里只提QT5中的用法,舊版本的用法幾乎不再使用。

2.1.1 信號signal
  1. 聲明的格式

    需要在頭文件中使用signal關鍵字修飾單一對象,以指定信號對象,或者使用signals:來批量修飾對象,以批量指定信號對象。另外,Q_SIGNAL等同于signalQ_SIGNALS:等同于signals:

    信號本體格式等同于函數,但不需要再在CPP文件中定義“函數體”,返回值為void。也可聲明參數,以傳遞數據,但參數格式與槽保持一致,至少參數數目一樣多,并且參數之間可以隱式轉換,好比函數的調用。

    示例:

    class MyClass : public QObject 
    {// 修飾單一信號對象// Q_SIGNAL等同signalQ_SIGNAL void fileDeleted(); // 批量修飾信號對象
    // signals等同Q_SIGNALS
    signals: // 可聲明參數void fileCreated(string filePath);void fileModifed();
    }
    
  2. 使用

    先使用connect()方法連接信號與槽,在需要的地方,使用emit觸發信號,如有參數,就需要保證格式的正確。

    emit fileDeleted();
    Q_EMIT fileCreated(path);
    
2.1.2 槽slot
  1. 聲明的格式

    可選的在頭文件中使用slot關鍵字修飾單一對象,以指定槽函數,或者使用slots:來批量修飾對象,以批量指定槽函數。另外,Q_SLOT等同于slotQ_SLOTS:等同于slots:

    槽函數的本身就是函數,即使用函數格式,需要在CPP文件中定義函數體,任意定義返回值以應對其他需要調用此函數的地方。不同于信號必須使用signal關鍵字告訴編譯器這是信號對象,槽函數也可以直接使用普通函數作為連接對象。

    示例:

    class MyClass : public QObject 
    {
    // 普通函數可直接被當作槽函數用connect方法與信號連接
    // 可以有返回值
    bool onFileModifed();public: // 需要修飾訪問權限// Q_SLOT等同slotQ_SLOT void onFileDeleted();// 需要修飾訪問權限, slots等同Q_SLOTS
    public slots:// 可聲明參數void onFileCreated(string filePath);
    }
    
  2. 使用

    作為一個函數,需要在CPP文件中定義函數體,在需要的時候使用connect()方法連接信號與槽,以便之后被信號調用。

    bool MyClass::onFileModifed()
    {/// 函數體...
    }void MyClass::onFileCreated(string filePath)
    {/// 函數體...   
    }
    
2.1.3 connect()方法

1. QT5中的connect()方法:

使用connect()方法將信號和槽建立連接,以便信號觸發時,執行指定槽函數。

connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction slot, Qt::ConnectionType type = Qt::AutoConnection)

  • 參數一sender:信號發出的對象
  • 參數二signal:信號所在類名和信號名稱,格式為**&類名::信號名**
  • 參數三receiver:信號接收的對象,槽函數所在的對象
  • 參數四slot:槽函數所在類名和槽函數名稱,格式為**&類名::槽函數名**
  • 參數五(在2.2中詳細介紹)

示例:

QLabel *label = new QLabel;
QLineEdit *lineEdit = new QLineEdit;
QObject::connect(lineEdit, &QLineEdit::textChanged, label, &QLabel::setText);

2. disconnect()方法:

使用disconnect()方法釋放信號和槽連接,便于之后此信號和不同槽函數建立連接,一般不使用此信號和槽時就要記得釋放連接。

disconnect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction slot)

格式與connect時保持一致,但disconnect()方法沒有第五參數,即便connect()時指定了第五參數,disconnect()時直接省略。

示例:

// 建立連接
QObject::connect(lineEdit, &QLineEdit::textChanged, label, &QLabel::setText);
// 釋放此連接
QObject::disconnect(lineEdit, &QLineEdit::textChanged, label, &QLabel::setText);

3. 使用Lambda表達式作為槽函數:

當然,前面又說到槽可以為其他函數,也就可以使用Lambda表達式來作為槽函數,使用Lambda表達式可以簡化槽函數的聲明,也可以通過Lambda表達式隨著信號調用參數格式與信號不一致的函數,或增加更多的使用條件。

關于C++ Lambda表達式可見我另一篇文章:C++ Lambda表達式

示例:

class MyClass : public QObject 
{
protected:MyClass(QObject *parent = nullptr);void myFunc(string data, int size);signals:void dataArrived(string data);
}///
MyClass::MyClass(QObject *parent = nullptr)
{connect(this, &MyClass::dataArrived, this, [&](){myFunc(data, data.lenght());/// 或進行其他處理});
}

2.2 connect()第五參數與多線程

connnect()方法通過使用第五個參數來設定槽函數的調用關系,此參數有五個固定的枚舉值:

  1. Qt::AutoConnection:自動連接,默認模式,根據信號和槽調用的線程自動進行選擇。如果信號和槽在同一線程中,則使用Qt::DirectConnection類型連接;如果在不同線程,則使用Qt::QueuedConnection連接。
  2. Qt::DirectConnection:直接連接。當信號和槽運行在同一線程中時使用,其效果類似函數調用,直接在信號發送的位置調用槽函數。
  3. Qt::QueuedConnection:隊列連接。當信號和槽運行在不同線程中時使用,信號發出時,不會立即調用槽,而是將其放入接收者所在線程的事件隊列中,等到接收者當前函數執行完,進入事件循環之后,槽函數才會被調用。
  4. Qt::BlockingQueuedConnection:阻塞隊列連接。當信號和槽運行在不同線程中時使用,類似于 QueuedConnection,但在調用槽時會阻塞信號的發送者,直到槽執行完成。所以如果信號和槽在同一線程時,就會造成死鎖,適用于需要確保槽完成執行再繼續其他操作的場景。
  5. Qt::UniqueConnection:唯一連接,可以與上述四個通過或字符|結合使用。此枚舉值與上述四個不同,其不指定槽函數的調用方式,而是確保該連接在相同的信號和槽組合中是唯一的。如果嘗試將同一信號和槽再次連接,連接將不會生效。這可以防止重復連接,避免一次觸發信號多次運行槽函數。

示例:

QObject::connect(sender, &Sender::signal, receiver, &Receiver::slot, Qt::QueuedConnection | Qt::UniqueConnection);

3. 于QML中使用信號與槽

QML中的信號與槽機制與C++中的使用方法類似。但QML中定義的任意屬性變量都有自己的信號,無需用戶自己定義,幾乎所有組件都是如此,一般使用時,更多情況會使用QML為這些對象設定的信號。當然也可以自定義信號和槽函數以及相互的連接。

3.1 監控QML默認定義的信號

QML中為組件默認屬性或自定義屬性都默認設置了信號,定義對應槽函數就可以在信號觸發后調用。槽函數的格式為**on+信號名(首字母大寫)**,可由此自定義觸發信號后的執行代碼。

1. 組件屬性

大多數QML組件都有內置信號,這些信號可以直接連接到槽函數來響應特定事件。

一般的默認屬性都有自己的指定觸發方式,比如Button組件通過點擊觸發clicked()信號,當然也可以直接調用clicked()手動觸發信號。

示例:

// Button組件的clicked信號在用戶點擊按鈕時觸發
// 通過onClicked自定義代碼,在信號觸發時輸出指定內容
Button {  onClicked: {  console.log("Button was clicked!");  }  
}  

2. 自定義屬性變量

QML中的自定義屬性變量也會自動創建變化信號,當屬性值發生變化時會觸發相應的信號。

對于QML中使用 property 聲明的自定義屬性,首字母必須小寫,其發生改變時觸發信號的槽函數格式為 on+屬性名(首字母大寫)+Changed

示例:

property int num: 0  // 自動創建的信號,當num發生變化時觸發  
onNumChanged: {  console.log("num changed to:", counter);  
}    

3.2 自定義信號與槽

1. 自定義信號signal

QML中也可以直接使用 signal 關鍵字定義專門的信號屬性,可以帶有參數,格式參考下方示例,同樣首字母小寫。

槽函數格式即為**on+信號名(首字母大寫)**。

觸發信號直接使用信號名調用信號即可,觸發信號后就直接執行槽函數。

注意: signal 關鍵字定義信號只能在QML文件的根組件使用

示例:

ColumeLayout { // 根組件signal mySignal(string data)  //信號觸發時調用,同槽函數onMySignal: {  console.log(data)  }Button: {  text: "Emit mySignal"  onClicked: {// 觸發信號mySignal("Hello World!"); }}  
}  

2. Connections{}

Connections 組件用于處理信號的連接。重要的是它可以在不同作用域之間連接信號和槽,適合在運行時動態處理信號,特別是在定義和使用信號的組件不在同一上下文時。

格式上,使用target屬性指定信號發出對象,然后就可以監聽發出對象內部的信號,信號對應的槽函數必須用function修飾

注意:

  1. 因為是跨作用域連接信號,對方根組件的信號自然是能監聽,但子組件的信號就監聽不了了。就好比可以調用根組件的屬性變量,調用不了子組件的屬性變量
  2. 這個方法同樣可以運用于C++注冊給QML的類,監聽C++中暴露的方法。

示例1:

// MyButton.qml
Button {property string myText: ''signal doubleClicked()onDoubleClicked: {console.log(myText + "double clicked")}
}// MyItem.qml
ColumeLayout { // 根組件MyButton {id: myButton}Connections{target: myButton // 信號發出對象// 注意變量的信號名格式,注意function修飾function onTextChanged(){myFunc() // 執行本作用域的函數}function onDoubleClicked(){// 在本作用域再定義一個此信號的槽函數}}function myFunc(){// ...}
}

示例2:

Connections{target: qwidget // C++注冊給QML的名稱// 監聽C++中暴露給QML的變量的變化function onWidthChanged(){// ...}function onHeightChanged(){// ...}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/86295.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/86295.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/86295.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Docker Swarm 與 Kubernetes 在集群管理上的主要區別

Docker Swarm 和 Kubernetes 是兩種流行的容器編排工具,它們都可以用于部署、管理和擴展容器化應用,但在集群管理方面有明顯的差異。 下面從多個維度對比它們在集群管理上的主要區別: ? 一、總體定位 項目Docker SwarmKubernetes官方支持D…

【StarRocks系列】查詢優化

步驟參考官網 分析查詢 | StarRocks StarRocks-Profile分析及優化指南 StarRocks-Profile分析及優化指南 - 經驗教程 - StarRocks中文社區論壇

軟測八股--測試理論 1 測試基礎

軟件測試? 發現程序中的側屋執行程序工程 目的:不僅是找出錯誤,還要分析錯誤產生原因和錯誤分布。檢查開發如阿健過程出現的bug,使開發人員及時修改。測試只能說明軟件中存在錯誤 目標:盡可能發現多的錯誤。一個好的…

mfc與vs成功在xp系統所需做的修改

目錄 前言一、MFC程序 inet_pton 、CT2A 未聲明問題1)問題1:inet_pton :undeclared identifier - inet_pton未聲明2)問題1:CT2A :undeclared identifier - CT2A未聲明 二、VS程序 使用事件、委托問題1&…

SpringMVC系列(三)(請求處理的十個實驗(上))

0 引言 作者正在學習SpringMVC相關內容,學到了一些知識,希望分享給需要短時間想要了解SpringMVC的讀者朋友們,想用通俗的語言講述其中的知識,希望與諸位共勉,共同進步! 本系列會持續更新!&…

Python案例練習:函數專題

用函數重新設計文章單詞出現次數程序 composition This is my family. We have a father, a mother and two brothers. My father is a doctor. He works in a hospital. My mother is a teacher. She teaches English in a school. My older brother is a student. He stu…

數據驅動 AI 時代:數據庫行業的技術躍遷與生態重構

在數據驅動的 AI 戰場,真正的決勝武器不是復雜精妙的算法模型,而是深埋在企業核心系統中的高維數據網絡(圖)。 時至今日,市場對AI的風向正從“狂飆突進”轉向“精耕細作”,就在上周(米國時間6月…

碼分復用通俗理解

一、碼分復用(CDMA)的通俗理解 碼分復用(Code Division Multiple Access,CDMA)是一種讓多個用戶在同一時間、同一頻段上同時傳輸數據的技術。它的核心思想是:給每個用戶分配一個唯一的“編碼鑰匙”&#x…

MAC、IP地址、TCP、UDP、SSL、OSI模型

目錄 一 概要 二 MAC和IP地址 (1)什么是 MAC 地址? (2)什么是 IP 地址? (3)MAC 地址 vs IP 地址對比表 (4)總結一下 二 TCP、UDP、SSL詳細介紹 &#…

弱口令介紹及破解方式筆記

一、弱口令的定義與危害 弱口令是指容易被猜測或破解的密碼,通常包含簡單數字組合(如“123456”)、鍵盤連續字符(如“qwerty”)、個人信息(如生日、姓名)或常見詞匯(如“password”…

Kotlin 中ArrayList、listOf、arrayListOf 和 mutableListOf區別

在 Kotlin 中&#xff0c;ArrayList、listOf、arrayListOf 和 mutableListOf 是常用的集合創建方式&#xff0c;但它們在類型、可變性和使用場景上有明顯區別。以下是核心對比&#xff1a; 一、基本定義與類型 創建方式類型可變性底層實現ArrayList<T>()ArrayList<T…

C盤清理技巧分享:釋放寶貴空間,提升電腦性能

Hi&#xff0c;我是布蘭妮甜 &#xff01;隨著電腦使用時間的增長&#xff0c;C盤空間逐漸被各種文件占據&#xff0c;導致系統運行緩慢、程序響應遲鈍。對于許多用戶來說&#xff0c;C盤空間不足是一個常見且令人頭疼的問題。本文將分享一系列實用的C盤清理技巧&#xff0c;幫…

【 感知集群】大規模分布式基礎設施的AI賦能藍圖

第一部分&#xff1a;AIOps基礎——從被動響應到預測性運維 在本報告的初始部分&#xff0c;我們將為管理一個復雜的大規模集群建立關鍵的基線。我們認為&#xff0c;在追求高級優化或未來主義架構之前&#xff0c;一個強大、由人工智能驅動的運維基礎是不可或缺的。本部分將詳…

《R循環:深度解析與高效使用技巧》

《R循環:深度解析與高效使用技巧》 引言 R語言作為一種功能強大的統計計算和圖形顯示語言,被廣泛應用于科研、數據分析、金融等領域。R循環是R語言中的核心概念之一,對于提高編程效率、處理復雜數據至關重要。本文將深度解析R循環,并介紹高效使用技巧,幫助讀者更好地掌握…

Windows WSL安裝Emscripten?/emsdk(失敗)

前面說過&#xff0c;在CMD、PS下&#xff0c;根本安裝不了。那只能試試在WSL安裝了。 安裝ubuntu 管理員運行PowerShell wsl --install ubuntu自帶python3&#xff0c;不需要安裝。 安裝 我就奇怪了&#xff0c;都說得頭頭是道&#xff0c;實際上一操作就出問題&#xff…

SpringBoot + EMQX 通過MQTT協議和下位機建立通訊并獲取下位機的監測數據-->測試

一、說明 在上一個博客中&#xff0c;我們部署了 EMQX 服務器&#xff0c;實現了 SpringBoot EMQX 通過MQTT協議和下位機建立通訊并獲取下位機的監測數據的編碼。現在我們對編碼進行進一步的測試。 二、編碼測試 2.1 在 EMQX 服務器開啟客戶端認證 這里的客戶端認證&#xf…

#17 修改開源模型以適配新任務

在Kaggle的評論區看到有選手想用Swin Transformer來做回歸任務,從這個片段可以窺見修改模型的一些思路,于是記錄下來學習一下 基于 torchvision Swin_B 模型的特征提取 from torchvision.models.swin_transformer import swin_b, Swin_B_Weights, swin_v2_b, Swin_V2_B_Weig…

變電站自動化系統有哪些設備?

變電站自動化系統是由多類設備協同工作的復雜系統&#xff0c;其設備按功能層級可分為過程層設備?&#xff08;直接連接一次設備&#xff09;、間隔層設備?&#xff08;實現保護控制&#xff09;和站控層設備?&#xff08;數據匯總與監控&#xff09;&#xff0c;同時還包括…

樹莓派超全系列教程文檔--(66)rpicam-apps可用選項介紹之視頻選項

這里寫目錄標題 視頻選項qualitybitrateintraprofilelevelcodecsave-ptskeypresssignalinitialsplitsegmentcircularinlinelistenframesframerate 文章來源&#xff1a; http://raspberry.dns8844.cn/documentation 原文網址 視頻選項 本節中指定的命令行選項僅適用于視頻輸…

C++98和C++11的for循環使用對比

1、C98 中的 傳統for 循環 語法結構&#xff1a;這種形式的 for 循環由三個部分組成&#xff1a;初始化語句&#xff08;int i 0&#xff09;、循環條件&#xff08;i < 10&#xff09;和迭代語句&#xff08;i&#xff09;。通常用于遍歷一個已知大小的循環范圍。使用場景…