天天看點

Qt學習筆記(二):信号和槽

一、建立一個基于 QWidget 的項目,不帶有界面設計器:

Qt學習筆記(二):信号和槽

二、在主視窗下添加兩個按鈕對象,連接配接按鈕發出的信号和槽函數:

mywidget.h:

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>

// 引入按鈕頭檔案
#include <QPushButton>

class MyWidget : public QWidget
{
    Q_OBJECT
    
public:
    MyWidget(QWidget *parent = 0);
    ~MyWidget();
    
private:
    // 聲明兩個按鈕對象
    QPushButton b1;  // 普通對象
    QPushButton *b2; // 指針對象
};

#endif // MYWIDGET_H
           

mywidget.cpp:

#include "mywidget.h"

MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
{
    // 通過 setParent 方法指定父對象
    b1.setParent(this);
    b1.setText("Button1");
    b1.resize(80, 40);
    b1.move(200, 100);
    
    // 通過構造函數方法指定父對象
    b2 = new QPushButton(this);
    b2->setText("Button2");
    b2->resize(80, 40);
    b2->move(30, 20);
    
    /*
     * 連接配接信号和槽:點選按鈕的時候,關閉目前視窗
     * 參數1:信号的發送者(指針類型)
     * 參數2:發送的信号;&發送者的名字::信号名稱
     * 參數3:信号的接收者(指針類型)
     * 參數4:處理信号的函數(叫做槽函數);&接收者的名字::槽函數名稱
     */
    connect(&b1, &QPushButton::clicked, this, &MyWidget::close);
    
    // 連接配接信号和槽:按下按鈕的時候,目前視窗最大化顯示
    connect(b2, &QPushButton::pressed, this, &MyWidget::showMaximized);
    
    // 重置主視窗的大小
    this->resize(600, 400);
}

MyWidget::~MyWidget()
{
    
}
           

其中信号 &QPushButton::clicked、&QPushButton::pressed,

和槽函數 &MyWidget::close、&MyWidget::showMaximized 都是 Qt 定義好的;

可以在 Qt 幫助文檔裡查找:

Qt學習筆記(二):信号和槽
Qt學習筆記(二):信号和槽

三、我們也可以自定義槽函數:

自定義槽函數的特點:槽函數的本質就是一個普通函數,在 Qt5 裡面,自定義槽函數可以是成員函數、普通全局函數、或者靜态函數都行;槽函數的傳回值和參數需要和信号一緻;

注意:信号和槽函數都沒有傳回值,但是可以帶參數;

在 mywidget.h 中聲明一個自定義的槽函數:

Qt學習筆記(二):信号和槽

在 mywidget.cpp 中連接配接信号和自定義槽函數:

注意:一個信号可以連接配接多個槽函數;

Qt學習筆記(二):信号和槽

四、自定義信号:

案例:添加一個子視窗,在主視窗上點選按鈕的時候,主視窗隐藏,子視窗顯示;

點選子視窗上的按鈕的時候,子視窗隐藏,主視窗顯示;

1、在 Qt 項目上添加子視窗:

Qt學習筆記(二):信号和槽
Qt學習筆記(二):信号和槽
Qt學習筆記(二):信号和槽

2、在子視窗頭檔案中添加自定義信号:

childwidget.h 檔案為:

#ifndef CHILDWIDGET_H
#define CHILDWIDGET_H

#include <QWidget>
#include <QPushButton>

class ChildWidget : public QWidget
{
    Q_OBJECT
public:
    explicit ChildWidget(QWidget *parent = 0);
    
signals:
    // 自定義信号必須由 signals 關鍵字聲明;
    // 自定義信号沒有傳回值,但可以有參數;
    // 自定義信号就是函數的聲明,隻需聲明,無需定義;
    // 發送資訊使用關鍵字 emit;
    void mySignal();
    
public slots:
    // Qt5 裡面自定義槽函數不是必須由 public slots 聲明;
    // 但是 Qt4 裡面槽函數必須由 public slots 聲明;
    void sendSignal();
    
private:
    // 建立一個按鈕對象
    QPushButton btn;
};

#endif // CHILDWIDGET_H
           

3、在子視窗中點選按鈕時,發送自定義信号:

childwidget.cpp 檔案為:

#include "childwidget.h"

ChildWidget::ChildWidget(QWidget *parent) :
    QWidget(parent)
{
    this->resize(400, 300);         // 設定視窗大小
    this->setWindowTitle("子視窗"); // 設定視窗标題
    
    btn.setParent(this);
    btn.setText("切換到主視窗");
    btn.move(200, 100);

    // 點選按鈕,調用自定義槽函數(sendSignal),發送自定義信号(mySignal)
    connect(&btn, &QPushButton::clicked, this, &ChildWidget::sendSignal);
}

// 自定義槽函數
void ChildWidget::sendSignal()
{
    // 發送自定義信号
    emit mySignal();
}
           

4、在主視窗中添加子視窗對象,并自定義槽函數處理子視窗發出的信号:

修改 mywidget.h 檔案為:

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include <QPushButton>
// 在主視窗中引入子視窗的頭檔案
#include "childwidget.h"

class MyWidget : public QWidget
{
    Q_OBJECT
    
public:
    MyWidget(QWidget *parent = 0);
    ~MyWidget();
   
    // 自定義槽函數
    void mySlot();    
    // 處理自定義信号的槽函數
    void dealSignal(); 
    
private:
    QPushButton b1;
    
    // 在主視窗中聲明子視窗對象
    ChildWidget child;
};

#endif // MYWIDGET_H

           

5、點選主視窗上的按鈕,隐藏主視窗,并顯示子視窗;

同時連接配接子視窗發出的信号和槽函數,當接收到信号時,顯示主視窗,隐藏子視窗:

修改 mywidget.cpp 檔案為:

#include "mywidget.h"

MyWidget::MyWidget(QWidget *parent)
    : QWidget(parent)
{
    this->resize(400, 300);         // 設定視窗大小
    this->setWindowTitle("主視窗"); // 設定視窗标題

    b1.setParent(this);
    b1.setText("切換到子視窗");
    b1.move(200, 100);
    
    // 點選按鈕,調用自定義槽函數(mySlot),隐藏主視窗,顯示子視窗
    connect(&b1, &QPushButton::clicked, this, &MyWidget::mySlot);
    
    // 連接配接子視窗的自定義信号和主視窗中的自定義槽函數
    // 參數1:信号的發送者(子視窗)
    // 參數2:發送的信号(子視窗自定義的信号)
    // 參數3:信号的接收者(主視窗)
    // 參數4:處理信号的自定義槽函數
    connect(&child, &ChildWidget::mySignal, this, &MyWidget::dealSignal);
}

// 自定義槽函數
void MyWidget::mySlot()
{
    // 隐藏主視窗
    this->hide();
    // 顯示子視窗
    child.show();
}

// 處理信号的自定義槽函數
void MyWidget::dealSignal()
{
    // 顯示主視窗
    this->show();
    // 隐藏子視窗
    child.hide();
}

MyWidget::~MyWidget()
{
    
}
           

五、帶參數的自定義信号和自定義槽函數:

在上面案例的子視窗頭檔案(childwidget.h)中自定義一個帶參信号:

Qt學習筆記(二):信号和槽

并在子視窗上點選按鈕的時候,發送帶參信号:

Qt學習筆記(二):信号和槽

在主視窗頭檔案(mywidget.h)中自定義一個相對應的帶參槽函數:

Qt學習筆記(二):信号和槽

在主視窗中處理無參和有參的信号:

// 因為信号和槽函數都重載了,是以直接使用 connect 連接配接信号和槽的時候會報錯,
    // 因為存在二義性,不确定調用的是無參的,還是有參的;
    // 可以使用函數指針來解決:
    // 無參數的信号和槽函數的函數指針:
    void(ChildWidget::*noParamSignal)(void) = &ChildWidget::mySignal;
    void(MyWidget::*noParamSlot)(void) = &MyWidget::dealSignal;

    // 有參數的信号和槽函數的函數指針:
    void(ChildWidget::*haveParamSignal)(int, QString) = &ChildWidget::mySignal;
    void(MyWidget::*haveParamSlot)(int, QString) = &MyWidget::dealSignal;
    
    // 連接配接無參數的自定義信号和無參數的槽函數
    // 函數指針 noParamSignal 就表示無參的信号,
    // 函數指針 noParamSlot 就表示無參的槽函數;
    connect(&child, noParamSignal, this, noParamSlot);
    
    // 連接配接帶參數的自定義信号和帶參數的槽函數
    connect(&child, haveParamSignal, this, haveParamSlot);
           

測試結果如下:

Qt學習筆記(二):信号和槽

老師的視訊在示範的時候,qDebug() 輸出 QString 資料是中文時會出現亂碼,如下:

Qt學習筆記(二):信号和槽

但我的可以直接輸出中文;如果中文輸出亂碼,可以将 QString 類型轉換成 char* 類型:

Qt學習筆記(二):信号和槽

六、上面使用 函數指針 的方式差別無參和有參信号的方法是 Qt5 之後才有的,在 Qt4 的時候是另一種方法區分無參和有參的信号,如下:

// 連接配接無參的信号和無參的槽函數
    connect(&child, SIGNAL(mySignal()), this, SLOT(dealSignal()));
    
    // 連接配接有參的信号和有參的槽函數
    connect(&child, SIGNAL(mySignal(int,QString)), this, SLOT(dealSignal(int,QString)));
           

Qt4 的這種方式用起來比較友善,但是有一個缺點:SIGNAL 和 SLOT 宏會将信号名稱和槽函數名稱轉換成字元串,在項目編譯的時候不進行錯誤檢查;也就是說信号名稱和槽函數名稱寫錯了,也可以編譯通過,但是程式會在運作的時候直接崩掉。