QT开发基础知识

Posted 想文艺一点的程序员

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QT开发基础知识相关的知识,希望对你有一定的参考价值。

并没有很详细,只有最基本的一些操作

一、QT开发基础

1 .pro 文件的配置

1.1 跨平台配置

之前我们分别在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


1.2 自定义变量

可以将公共的信息抽取成变量,然后使用 $$ 去访问。我们将 FFMPEG_HOME 设为自定义变量。

# 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



1.3 读取window环境变量

也可以通过 $$() 读取系统的环境变量。比如,我的Windows 中有个叫做 Path 的环境变量。

# 使用message打印环境变量JAVA_HOME的值
message($$(Path))

最后可以在 QT的概要信息 处看到 Path 的打印结果。


2 QT控件的基本使用

为了更好地学习Qt控件的使用,建议创建项目时先不要生成ui文件。(ui文件:在QT界面可以进行控件的拖动)

分析现象:直接运行会生成一个窗口

分析对应的代码:

  • 这个窗口对应的类为 : MainWindow ,继承于 QT 自带的类(QMainWindow
  • 项目分类也很清晰:MainWindow.h :存放 类的声明MainWindow.cpp:存放 类的定义

分析 Main 函数

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])

    QApplication a(argc, argv);
    MainWindow w;  		// 创建一个窗口类:w
    w.show();      			// 将这个窗口显示出来
    return a.exec();



2.1 窗口设置

既然窗口是一个类 MainWindow ,那么我们进行窗口设置的时候,一般在其构造函数当中进行设置。(当其生成的时候,自动调用构造函数,相当于进行初始化操作)

Qt坐标系如下图所示:

  • MainWindow 的父控件为 nullptr


2.2 添加一个子控件

(1)添加对应的控件,首先要包含对应的头文件

(2)new 出来的Qt控件是不需要程序员手动delete的。

(3)Qt内部会自动管理内存:当父控件销毁时,会顺带销毁子控件。

子控件没有父控件的时候:

  • 两个窗口会单独显示。
  • Pushbutton 必须使用其自己的 show 方法,才能将自己显示出来。

子控件有其父控件的时候:

  • this 指针:代表调用这个函数的对象的地址。
  • 这时候两个指针会叠在一起,并且 PushButton 不需要单独进行 show。在父控件 show 的时候,子空间也会跟着显示出来。


3 信号与槽

信号:点击控件的时候,会发出一个信号。(相当于是一个中断源)

槽:槽就是处理这个信号的函数。(相当于中断处理函数)

官方参考文档:Signals & Slots | Qt Core 5.15.7

特点:

  • 一个信号可以绑定多个槽函数,一个槽也可以处理多个信号。
  • QT 里面自带的组件,都有定义好的信号与槽

怎么查看QT当中自带的信号与槽呢?

  • 直接点击这个控件类的 F1,查看QT 官方文档。或者查看其头文件。
  • 这个类当中找不到的话,就去查看其对应的父类。(以 QPushButton 为例)


3.1 基本使用

按钮的clicked信号主窗口的close槽函数 互相进行绑定。

我们实现我们自己 信号与槽函数:

  • 信号的发送者和接收者都必须继承自QObject,Qt中的控件最终都是继承自QObject,比如QMainWindow、QPushButton等。
  • 信号与槽,一般以函数的形式呈现。
    • 信号:只需要进行声明,不需要进行定义。
    • 槽:既需要进行声明,也需要进行定义。

利用 QT IDE 直接生成对应的类文件:

依次填写类的名称、以及继承于哪一个类即可:

  • 随后即可生成一个头文件,一个 cpp 文件。


信号的发送者:

sender.h

  • single 可以只进行声明,不进行定义
  • 应该写在 signals: 下面。
#ifndef SENDER_H
#define SENDER_H

#include <QObject>

class Sender : public QObject

    Q_OBJECT   // 这个是使用 single 的时候,必须提前声明
public:
    explicit Sender(QObject *parent = nullptr);

signals:
    void exit();  // single 可以只进行声明,不进行定义

;

#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 HanderExit();
;

#endif // RECEIVER_H

receiver.cpp :

#include "receiver.h"
#include <QDebug>

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



void Receiver::HanderExit()

    qDebug() << "Receiver::HanderExit()";

进行连接:

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 <QDebug>
#include <QPushButton>
#include "receiver.h"
#include "sender.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)  // 窗口被创建的时候,会自动执行这个函数

    // 在堆空间,申请我们自己的控件
    Sender *s1 = new Sender;
    Receiver *r1 = new Receiver;

    // 进行连接
    connect(s1,&Sender::exit,r1,&Receiver::HanderExit);

    // 发送信号
    emit s1 -> exit();

    // 自己销毁子控件,只有 QT 自己的控件,才会随着父控件销毁。
    delete s1;
    delete r1;


MainWindow::~MainWindow()  // 窗口销毁的时候

    qDebug()<< "MainWindow 被销毁了";

运行结果:槽函数被调用


3.2 参数与返回值

之前我们定义的信号与槽都是 void 类型,其实是可以有的。

  • 发信号时的参数会传递给槽
    • 信号的参数个数必须大于等于槽的参数个数
  • 槽的返回值会返回到发信号的位置

什么是发信号的位置 ?

  • a就是接受槽函数的返回值。
int a = emit sender->exit(10, 20);

测试:

// 自定义信号
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;

3.3 连接两个信号

不仅是信号与槽可以进行连接,信号与信号之间也可以进行连接。

connect(Object1,Singal 1A, Object2, Singal 2A);

上图,连接了 Object 1 的 Signal 1A 和 Object 2 的 Signal 2A。而 Signal 1A 自己又连接 Slot X 、Signal 2A 自己又连接 Slot Y。

此时的特性:

  • 当 Object 1 发出 Signal 1A 时,会触发 Slot X、Slot Y
  • 当 Object 2 发出 Signal 2A 时,只会触发Slot Y,而不会触发Slot X 。

话句话说:

  • 当 Object 1 发出 Signal 1A 信号时,Object 2 会发出 Signal 2A 信号。(所以会触发两个槽函数)
  • 当 Object 2 发出 Signal 2A 信号时,Object 2 不会发出 Signal 2A 信号。(所以只会触发一个槽函数)

3.4 Lambda

也可以直接使用 Lambda 处理信号。

发送信号:

  • sender, &Sender::exit

槽函数:

  • qDebug() << “lambda handle exit”;
connect(sender, &Sender::exit, []() 
    qDebug() << "lambda handle exit";
);

//直接将 qDebug() << "lambda handle exit"; 作为 槽函数

3.5 ui 文件

如果你的控件是通过ui文件生成的,连接槽函数的步骤会更加简单。

首先建议给按钮们起个有意义的变量名,比如分别叫做:loginButtonregisterButton

对着登录按钮右键,选择转为槽。

选择 clicked信号,然后OK。

此时,Qt Creator已经帮你自动生成了槽函数的声明和实现,当我们点击登录按钮时,就会调用这个函数。

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);

以上是关于QT开发基础知识的主要内容,如果未能解决你的问题,请参考以下文章

QT开发(四十六)——QT数据库编程基础

QT软件开发之入门基础-搭建开发环境

QT软件开发之入门基础--1.5开发第一个QT程序

Debian系列-系统安装及Qt基础开发环境配置

Linux系列 Debian系统安装及Qt基础开发环境配置

我想开始 Qt 开发 - 我必须拥都有哪些 C++ 和 OS 基础知识? [关闭]