【秒懂音視訊開發】05_Qt開發基礎

M了個J發表於2021-03-04

控制元件的基本使用

為了更好地學習Qt控制元件的使用,建議建立專案時先不要生成ui檔案。

不生成ui檔案

開啟mainwindow.cpp,在MainWindow的建構函式中編寫介面的初始化程式碼。

視窗設定

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // 設定視窗標題
    setWindowTitle("主視窗");

    // 設定視窗大小
    // 視窗可以通過拖拽邊緣進行自由伸縮
//    resize(400, 400);
    
    // 設定視窗的固定大小
    // 視窗不能通過拖拽邊緣進行自由伸縮
    setFixedSize(400, 400);

    // 設定視窗的位置
    // 以父控制元件的左上角為座標原點
    // 沒有父控制元件,就以螢幕的左上角作為座標原點
    move(100, 100);
}

Qt座標系如下圖所示。
Qt座標系

新增子控制元件

#include <QPushButton>

// 建立按鈕
QPushButton *btn = new QPushButton;
// 設定按鈕的文字
btn->setText("登入");
// 設定父控制元件為當前視窗
btn->setParent(this);
// 設定按鈕的位置和大小
btn->move(50, 50);
btn->resize(100, 50);

// 建立第2個按鈕
new QPushButton("註冊", this);

new出來的Qt控制元件是不需要程式設計師手動delete的,Qt內部會自動管理記憶體:當父控制元件銷燬時,會順帶銷燬子控制元件。

訊號與槽

基本使用

  • 訊號(Signal):比如點選按鈕就會發出一個點選訊號
  • 槽(Slot):一般也叫槽函式,是用來處理訊號的函式
  • 官方文件參考:Signals & Slots

訊號與槽

上圖中的效果是:

  • Object1發出訊號signal1,交給Object2的槽slot1、slot2去處理
    • Object1是訊號的傳送者,Object2是訊號的接收者
  • Object1發出訊號signal2,交給Object4的槽slot1去處理
    • Object1是訊號的傳送者,Object4是訊號的接收者
  • Object3發出訊號signal1,交給Object4的槽slot3去處理
    • Object3是訊號的傳送者,Object4是訊號的接收者
  • 1個訊號可以由多個槽進行處理,1個槽可以處理多個訊號

通過connect函式可以將訊號的傳送者訊號訊號的接收者連線在一起。

connect(訊號的傳送者, 訊號, 訊號的接收者, 槽);

// 比如點選按鈕,關閉當前視窗
// btn發出clicked訊號,就會呼叫this的close函式
connect(btn, &QAbstractButton::clicked, this, &QWidget::close);

// 可以通過disconnect斷開連線
disconnect(btn, &QAbstractButton::clicked, this, &QWidget::close);

自定義訊號與槽

訊號的傳送者和接收者都必須繼承自QObject,Qt中的控制元件最終都是繼承自QObject,比如QMainWindow、QPushButton等。

訊號的傳送者

  • sender.h
    • Q_OBJECT用以支援自定義訊號和槽
    • 自定義的訊號需要寫在signals:下面
    • 自定義的訊號只需要宣告,不需要實現
#ifndef SENDER_H
#define SENDER_H

#include <QObject>

class Sender : public QObject
{
    Q_OBJECT
public:
    explicit Sender(QObject *parent = nullptr);

    // 自定義訊號
signals:
    void exit();
};

#endif // SENDER_H
  • sender.cpp
#include "sender.h"

Sender::Sender(QObject *parent) : QObject(parent)
{

}

訊號的接收者

  • receiver.h
    • 自定義的槽建議寫在public slots:下面
#ifndef RECEIVER_H
#define RECEIVER_H

#include <QObject>

class Receiver : public QObject
{
    Q_OBJECT
public:
    explicit Receiver(QObject *parent = nullptr);

    // 自定義槽
public slots:
    void handleExit();
};

#endif // RECEIVER_H
  • receiver.cpp
#include "receiver.h"
#include <QDebug>

Receiver::Receiver(QObject *parent) : QObject(parent)
{

}

// 實現槽函式,編寫處理訊號的程式碼
void Receiver::handleExit()
{
    qDebug() << "Receiver::handleExit()";
}

連線

  • mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "sender.h"
#include "receiver.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT
private:
    Sender *_sender;
    Receiver *_receiver;
    void test1();

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
};
#endif // MAINWINDOW_H
  • mainwindow.cpp
#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    this->_sender = new Sender;
    this->_receiver = new Receiver;

    // 連線
    connect(this->_sender,
            &Sender::exit,
            this->_receiver,
            &Receiver::handleExit);

    // 發出訊號
    // 最終會呼叫Receiver::handleExit函式
    emit this->_sender->exit();
}

MainWindow::~MainWindow()
{

}

引數和返回值

訊號與槽都可以有引數和返回值:

  • 發訊號時的引數會傳遞給槽
  • 槽的返回值會返回到發訊號的位置
// 自定義訊號
signals:
    int exit(int a, int b);

// 自定義槽
public slots:
    int handleExit(int a, int b);

int Receiver::handleExit(int a, int b)
{
    // Receiver::handleExit() 10 20
    qDebug() << "Receiver::handleExit()" << a << b;
    return a + b;
}

// 發出訊號
int a = emit this->_sender->exit(10, 20);
// 30
qDebug() << a;

需要注意的是:訊號的引數個數必須大於等於槽的引數個數。比如下面的寫法是錯誤的:

// 自定義訊號
signals:
    void exit(int a);

// 自定義槽
public slots:
    void handleExit(int a, int b);

連線2個訊號

比如下圖,連線了Object 1的Signal 1A和Object 2的Signal 2A

  • 當Object 1發出Signal 1A時,會觸發Slot X、Slot Y
  • 當Object 2發出Signal 2A時,只會觸發Slot Y,而不會觸發Slot X

連線2個訊號

可以利用connect函式連線2個訊號

  • 當_sender發出exit訊號時,_sender2會發出exit2訊號
  • 當_sender2發出exit2訊號時,_sender並不會發出exit訊號
connect(this->_sender,
        &Sender::exit,
        this->_sender2,
        &Sender2::exit2);

Lambda

也可以直接使用Lambda處理訊號。

connect(this->_sender, &Sender::exit, []() {
    qDebug() << "lambda handle exit";
});

相關文章