【秒懂音視頻開發】06_Qt開發基礎


.pro文件的配置

跨平台配置

之前我們分別在WindowsMac環境的Qt項目中集成了FFmpeg。

可以發現在.pro文件的配置中,FFmpeg庫在Mac、Windows上的位置是有所差異的。這樣就會導致.pro文件無法跨平台使用。

# windows
INCLUDEPATH += F:/Dev/ffmpeg-4.3.2/include

# mac
INCLUDEPATH += /usr/local/Cellar/ffmpeg/4.3.2/include

為了實現跨平台配置,可以在配置前面加上平台標識的前綴,表示這個配置只會在對應的平台生效。

# windows
win32:INCLUDEPATH += F:/Dev/ffmpeg-4.3.2/include
win32:LIBS += -LF:/Dev/ffmpeg-4.3.2/lib \
              -lavcodec \
              -lavdevice \
              -lavfilter \
              -lavformat \
              -lavutil \
              -lpostproc \
              -lswscale \
              -lswresample

# mac
macx:INCLUDEPATH += /usr/local/Cellar/ffmpeg/4.3.2/include
macx:LIBS += -L/usr/local/Cellar/ffmpeg/4.3.2/lib \
            -lavcodec \
            -lavdevice \
            -lavfilter \
            -lavformat \
            -lavutil \
            -lpostproc \
            -lswscale \
            -lswresample \
            -lavresample

# linux
# linux:INCLUDEPATH += ...
# linux:LIBS += ...

以后針對每個平台的配置可能會比較多,可以使用大括號來簡化。

# windows
win32 {
    INCLUDEPATH += F:/Dev/ffmpeg-4.3.2/include
    LIBS += -LF:/Dev/ffmpeg-4.3.2/lib \
            -lavcodec \
            -lavdevice \
            -lavfilter \
            -lavformat \
            -lavutil \
            -lpostproc \
            -lswscale \
            -lswresample
}

# mac
macx {
    INCLUDEPATH += /usr/local/Cellar/ffmpeg/4.3.2/include
    LIBS += -L/usr/local/Cellar/ffmpeg/4.3.2/lib \
            -lavcodec \
            -lavdevice \
            -lavfilter \
            -lavformat \
            -lavutil \
            -lpostproc \
            -lswscale \
            -lswresample \
            -lavresample
}

自定義變量

可以將公共的信息抽取成變量,然后使用$${}去訪問。

# mac
macx {
    FFMPEG_HOME = /usr/local/Cellar/ffmpeg/4.3.2
    INCLUDEPATH += $${FFMPEG_HOME}/include
    LIBS += -L$${FFMPEG_HOME}/lib \
            -lavcodec \
            -lavdevice \
            -lavfilter \
            -lavformat \
            -lavutil \
            -lpostproc \
            -lswscale \
            -lswresample \
            -lavresample
}

讀取系統環境變量

也可以通過$$()讀取系統的環境變量。比如,我的Windows中有個叫做JAVA_HOME的環境變量。

環境變量

# 使用message打印環境變量JAVA_HOME的值
message($$(JAVA_HOME))

最后可以在概要信息處看到JAVA_HOME的打印結果。

概要信息

控件的基本使用

為了更好地學習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, &QPushButton::clicked, this, &MainWindow::close);

// 可以通過disconnect斷開連接
disconnect(btn, &QPushButton::clicked, this, &MainWindow::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>

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

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent) {
    // 創建對象
    Sender *sender = new Sender;
    Receiver *receiver = new Receiver;

    // 連接
    connect(sender,
            &Sender::exit,
            receiver,
            &Receiver::handleExit);

    // 發出信號
    // 最終會調用Receiver::handleExit函數
    emit sender->exit();
        
    // 銷毀對象
    delete sender;
    delete receiver;
}

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 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(sender,
        &Sender::exit,
        sender2,
        &Sender2::exit2);

Lambda

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

connect(sender, &Sender::exit, []() {
    qDebug() << "lambda handle exit";
});

ui文件

如果你的控件是通過ui文件生成的,連接槽函數的步驟會更加簡單。

首先建議給按鈕們起個有意義的變量名,比如分別叫做:loginButtonregisterButton

起名

對着登錄按鈕右鍵,選擇轉為槽

轉為槽

選擇clicked信號,然后OK

clicked

此時,Qt Creator已經幫你自動生成了槽函數的聲明和實現,當我們點擊登錄按鈕時,就會調用這個函數。

class MainWindow : public QMainWindow {
    Q_OBJECT
private slots:
    // 槽函數的聲明
    void on_loginButton_clicked();
};

// 槽函數的實現
void MainWindow::on_loginButton_clicked() {
    qDebug() << "on_loginButton_clicked";
}	

其實,認真觀察函數名可以發現一個規律,函數名的命名規則是:on_控件的變量名_事件名

於是,我們可以嘗試編寫以下代碼。

class MainWindow : public QMainWindow {
    Q_OBJECT
private slots:
    // 槽函數的聲明
    void on_registerButton_clicked();
};

// 槽函數的實現
void MainWindow::on_registerButton_clicked() {
    qDebug() << "on_registerButton_clicked";
}	

然后,你點擊一下注冊按鈕,會發現成功調用了MainWindow::on_registerButton_clicked函數。

於是得知:ui文件中的控件會自動跟符合命名規則的槽函數建立連接

最后,再提示一個知識點:ui文件中的控件可以在代碼中通過ui->變量名訪問。

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);

    // 通過ui->訪問ui文件中的2個按鈕
    ui->loginButton->setFixedSize(100, 30);
    ui->registerButton->setFixedSize(100, 30);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM