【秒懂音视频开发】06_Qt开发基础
.pro文件的配置
跨平台配置
之前我们分别在Windows、Mac环境的Qt项目中集成了FFmpeg。
可以发现在.pro文件的配置中,FFmpeg库在Mac、Windows上的位置是有所差异的。这样就会导致.pro文件无法跨平台使用。
复制代码Shell# windows INCLUDEPATH += F:/Dev/ffmpeg-4.3.2/include # mac INCLUDEPATH += /usr/local/Cellar/ffmpeg/4.3.2/include
为了实现跨平台配置,可以在配置前面加上平台标识的前缀,表示这个配置只会在对应的平台生效。
复制代码Shell# 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 += ...
以后针对每个平台的配置可能会比较多,可以使用大括号来简化。
复制代码Shell# 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 }
自定义变量
可以将公共的信息抽取成变量,然后使用$${}去访问。
复制代码Shell# 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的环境变量。
复制代码Shell# 使用message打印环境变量JAVA_HOME的值 message($$(JAVA_HOME))
最后可以在概要信息处看到JAVA_HOME的打印结果。
控件的基本使用
为了更好地学习Qt控件的使用,建议创建项目时先不要生成ui文件。
打开mainwindow.cpp,在MainWindow的构造函数中编写界面的初始化代码。
窗口设置
复制代码C++MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { // 设置窗口标题 setWindowTitle("主窗口"); // 设置窗口大小 // 窗口可以通过拖拽边缘进行自由伸缩 // resize(400, 400); // 设置窗口的固定大小 // 窗口不能通过拖拽边缘进行自由伸缩 setFixedSize(400, 400); // 设置窗口的位置 // 以父控件的左上角为坐标原点 // 没有父控件,就以屏幕的左上角作为坐标原点 move(100, 100); }
Qt坐标系如下图所示。
添加子控件
复制代码C++#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函数可以将信号的发送者、信号、信号的接收者、槽连接在一起。
复制代码C++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:下面
- 自定义的信号只需要声明,不需要实现
复制代码C++#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
复制代码C++#include "sender.h" Sender::Sender(QObject *parent) : QObject(parent) { }
信号的接收者
- receiver.h
- 自定义的槽建议写在public slots:下面
复制代码C++#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
复制代码C++#include "receiver.h" #include <QDebug> Receiver::Receiver(QObject *parent) : QObject(parent) { } // 实现槽函数,编写处理信号的代码 void Receiver::handleExit() { qDebug() << "Receiver::handleExit()"; }
连接
- mainwindow.h
复制代码C++#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
复制代码C++#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() { }
参数和返回值
信号与槽都可以有参数和返回值:
- 发信号时的参数会传递给槽
- 槽的返回值会返回到发信号的位置
复制代码C++// 自定义信号 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;
需要注意的是:信号的参数个数必须大于等于槽的参数个数。比如下面的写法是错误的:
复制代码C++// 自定义信号 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
可以利用connect函数连接2个信号
- 当sender发出exit信号时,sender2会发出exit2信号
- 当sender2发出exit2信号时,sender并不会发出exit信号
复制代码C++connect(sender, &Sender::exit, sender2, &Sender2::exit2);
Lambda
也可以直接使用Lambda处理信号。
复制代码C++connect(sender, &Sender::exit, []() { qDebug() << "lambda handle exit"; });
ui文件
如果你的控件是通过ui文件生成的,连接槽函数的步骤会更加简单。
首先建议给按钮们起个有意义的变量名,比如分别叫做:loginButton、registerButton。
对着登录按钮右键,选择转为槽。
选择clicked信号,然后OK。
此时,Qt Creator已经帮你自动生成了槽函数的声明和实现,当我们点击登录按钮时,就会调用这个函数。
复制代码C++class MainWindow : public QMainWindow { Q_OBJECT private slots: // 槽函数的声明 void on_loginButton_clicked(); }; // 槽函数的实现 void MainWindow::on_loginButton_clicked() { qDebug() << "on_loginButton_clicked"; }
其实,认真观察函数名可以发现一个规律,函数名的命名规则是:on_控件的变量名_事件名。
于是,我们可以尝试编写以下代码。
复制代码C++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->变量名访问。
复制代码C++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); }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步