QT学习笔记(二) 创建类, 信号和槽

一. QT简介

一. 窗口

1. 设置窗口大小

resize(600,400);
setFixedSize(600,400); // 设置固定窗口大小

2. 设置窗口标题

setWindowTitle(“窗口1”)

例子:

#include "mywidget.h"
#include 
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyWidget w;

    w.resize(600,400);
    w.setWindowTitle("hello");
    w.show();
    return a.exec();
}

3. 创建第二个窗口

在main文件中实例化窗口2

#include "mainwidget.h"
#include "subwidget.h"  //导入窗口2头文件
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWidget w;
    w.setWindowTitle("窗口1");
    w.resize(600,400);
    w.show();

    SubWidget w2;
    w2.setWindowTitle("窗口2");
    w2.resize(300,200);
    w2.show();
    return a.exec();
}

二. 按钮

#include "mywidget.h"
#include 
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MyWidget w;

    QPushButton btn1;
    btn1.setParent(& w);
    btn1.setText("OK");
    btn1.move(300,100);

    QPushButton btn2(&w);
    btn2.setText("OK2");
    btn2.move(300,150);

    w.show();
    return a.exec();
}

三. 以后不再修改main.cpp

在main.cpp执行过程中, 我们实例化了一个w (MyWidget w;), 这个w的实例化过程, 调用了 MyWidget 类的构造函数, 所以以后我们写程序不要修改main文件了, 直接去下游操作.

MyWidget 类的构造函数在MyWidget.cpp中, 我们以后在这写程序

我们把刚刚的按钮程序修改成在MyWidget.cpp中的构造函数中

首先在MainWidget.h中声明变量(下例中我的主窗口类命名为了MainWidget), 这里我们声明了两个按钮, 一个是普通变量, 一个是指针

#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include 
#include 

class MainWidget : public QWidget
{
    Q_OBJECT

public:
    MainWidget(QWidget *parent = nullptr);
    ~MainWidget();
private:
    QPushButton btn1;
    QPushButton *btn2;
};
#endif // MAINWIDGET_H

然后在MainWidget.cpp中的构造函数中实现

#include "mainwidget.h"

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
{
    btn1.setParent(this);
    btn1.setText("OK1");
    btn1.move(300,100);

    btn2 = new QPushButton(this);
    btn2->setText("OK2");
    btn2->move(300,150);
}

MainWidget::~MainWidget()
{
}

四. 对象树机制

qt有对象树机制, 使其有了自动内存管理机制, 很多时候不用我们自己清理内存了.

简单的说 : 指定父对象后, 子对象如果是动态分配空间的(new出来的), 系统会自动析构并释放空间

五. 创建类

因为对象树的机制, 在QT中创建类要按照以下方法

创建类, 并指定父类

注意 : 关于继承哪个类, 要看你新类的需求

完成后, 系统会自动添加.h .cpp文件, 同时自动挂到对象树中

六. 信号与槽机制

  • 信号发送端负责发生事件 ( 信号, signal )
  • 信号接收端负责接收并处理信号 ( 槽, slot )
  • 信号发送端 和 信号接收端是独立的, 通过connect函数链接在一起,完成耦合

以下是举例:

#include "mainwidget.h"

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
{
    btn1.setParent(this);
    btn1.setText("关闭窗口1");
    btn1.move(300,100);
    //按钮发射被点击的信号, 窗口接收, 并给槽中传入了close函数, 完成关闭
    connect(&btn1,& QPushButton::clicked,this,& QWidget::close);
    //      发射者             信号      接收者     槽


    btn2 = new QPushButton(this);
    btn2->setText("关闭窗口2");
    btn2->move(300,150);
    //按钮发射被点击的信号, 窗口接收, 并给槽中传入了close函数, 完成关闭
    connect(btn2,& QPushButton::clicked,this,& QWidget::close);
    //      发射者             信号     接收者     槽
}

MainWidget::~MainWidget()
{
}
这里说一下此例子中, 如何在帮助手册中 找 信号

比如: 我的btn1是 QPushButton类的实例, 我们选中QPushButton这几个字, 按F1查看帮助(按两下F1是全屏,按ESC是退出帮助), 去找 signals 相关内容, 并没有找到, 只找到了如下内容, 说明QPushButton的信号是继承自父类的

从图中我们可以看出: 它有4个信号继承自 QAbstractButton, 3个继承自QWidget....

我们点击链接, 去QAbstractButton类中, 发现它有signals信号机制, 有我们想触发的clicked

接收方应该填写槽函数, 查找自带的槽函数和上面的方法类似, 找slots就可以了

七. 自定义槽

刚刚,我们体验了信号和槽机制,连接的两个函数都是自带的函数,现在我们尝试绑定自定义槽

比如: 我们想点击按钮实现终端打印,说起来很简单,其实就是在通过信号和槽机制触发自定义的槽函数

在.h文件中声明槽函数

在.cpp中实现槽函数并连接

八. 自定义信号

现在, 我们来学习如何自定义信号, 比如每次我们让程序开始执行时, 加载完成后让窗口最大化, 那么我们可以在构造函数中发一个加载完成的信号, 去触发窗口最大化的槽函数

  • 信号函数可以只声明不实现,槽函数要声明和实现
  • 信号函数必须声明在signals关键字下

1. 首先, 我们在"mainwidget.h"中创建信号

#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include 
#include 
#include 

class MainWidget : public QWidget
{
    Q_OBJECT

public:
    MainWidget(QWidget *parent = nullptr);
    ~MainWidget();
signals:
    void loaded(); //创建信号
};
#endif // MAINWIDGET_H

2. 在 "mainwidget.cpp" 中触发信号

触发自定义信号使用 : emit 关键字 emit是关键字不是函数

#include "mainwidget.h"

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
{
    connect(this,&MainWidget::loaded,this,&QWidget::showMaximized);
    qDebug()<<"加载中...";
    qDebug()<<"加载中...";
    qDebug()<<"加载中...";
    qDebug()<<"加载中...";
    emit this->loaded();
    qDebug()<<"加载完成!";
}

MainWidget::~MainWidget()
{
}

3. 有个问题

可以看到, 程序运行成功了! 但是有一个警报:

这是因为: 自己给自己发信号是不合理的, 我们完全可以直接 this->showMaximized();

但, 通过上面的例子, 我们已经弄清了如何自定义信号

九. 自定义信号和槽 (窗口间)

现在我们来学习创建双窗口, 更准确的使用自定义信号和自定义槽,

我们创建一个子窗口, 方法见第二节, 不同的是, 我们在mainwidget中实例化它

这次我们想实现的功能是: 点击主窗口按钮, 跳转子窗口, 点击子窗口按钮, 跳转主窗口

思路 :

  • 在主窗口mainwidget.h中实例化第二窗口
  • 点击主窗口按钮时, 会发送点击信号, 创建一个点击信号槽函数, 用于处理点击事件
  • 点击事件中, 隐藏主窗口, 显示子窗口
  • 点击子窗口按钮时, 会发送点击信号, 创建一个点击信号槽函数, 用于处理点击事件
  • 点击事件中, 隐藏子窗口, 但子窗口找不到主窗口, 只能发出想返回的信号
  • 主窗口监听子窗口想返回的信号, 显示自己
#ifndef MAINWIDGET_H
#define MAINWIDGET_H

#include 
#include 
#include 
#include "subwidget.h"  //包含子窗口头文件


class MainWidget : public QWidget
{
    Q_OBJECT

public:
    MainWidget(QWidget *parent = nullptr);
    ~MainWidget();
private:
    SubWidget sub_w; //实例化子窗口
    QPushButton btn1; //创建一个按钮用于跳转窗口
    void btn1_clicked_handler(); // 主窗口按钮点击触发的槽
};
#endif // MAINWIDGET_H

十. 自定义信号和槽 (对象间)

我们先新建个Teacher类和Student类, 让teacher发送信号, 让同学接收并处理

  • 信号函数可以只声明不实现,槽函数要声明和实现
  • 信号函数 / 槽函数 的参数是一一对应的
  • 信号函数 / 槽函数 都没有返回值!
  • 与系统预设的信号/槽相比, 多了一个手动触发的环节

(1). 首先在teacher.h中, 声明要触发的信号

注意 : 这个信号, 只需要在.h中声明, 不需要在.cpp中实现

(2). 在student.h中, 声明槽函数

(3). 在student.cpp中, 实现这个槽函数

(4). 建立信号与槽的连接, 触发信号

十一. 用函数指针connect (为了方便重载)

进化上面的程序, 用函数指针代替函数地址(为了后续的重载)

十二. 信号传参, 信号和槽的重载问题

比如我有两个信号函数,两个槽函数,一对儿不带参数触发,一对儿带参数触发,该如何区分呢?

十三. lambda表达式 connect

1. lambda表达式的结构

[捕获列表](参数){函数体}

其中, 只有传送到捕获列表中的外部变量才能被lambda使用,
[] : 不捕获
[a] : 捕获变量a
[this] : 捕获对象 this
[=] : 捕获一切 (按值捕获)
[&] : 捕获一切 (按地址捕获)

2. lambda表达式用于信号/槽的连接

  • clash推荐使用4参数的connect函数
  • .h中声明的变量, 传this进去,不要单独传
  • 动态创建的变量可以单独传
  • 推荐使用[=]
  • 不推荐使用[&]捕获
#include "mainwidget.h"
#include 

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
{
    // btn1在.h中声明了(指针类型)
    btn1=new QPushButton(this);
    btn1->setParent(this);
    btn1->setText("btn1");

    //btn2在.h中声明了(变量类型)
    btn2.setParent(this);
    btn2.setText("btn2");
    btn2.move(100,0);

    // 声明一个变量
    int a = 5;
    // 动态声明一个按钮
    QPushButton *btn3 = new QPushButton;
    btn3->setParent(this);
    btn3->setText("btn3");
    btn3->move(200,0);

    //捕获列表不捕获外部变量
    connect(btn1,&QPushButton::clicked,this,[](){qDebug()<<"hello";});

    //把this传入lambda
    connect(btn1,&QPushButton::clicked,this,[this](){btn1->setText("OK");});
    connect(&btn2,&QPushButton::clicked,this,[this](){btn2.setText("OK2");});

    //把动态变量传入lambda
    connect(btn1,&QPushButton::clicked,this,[a](){qDebug()<<"a:"<setText("OK3");}); //把btn3传入lambda

    //把一切传入lambda
    connect(btn1,&QPushButton::clicked,this,[=](){qDebug()<<"a:"<

3. 带参数的lambda

比如: clicked信号就有一个参数

上面的程序我们都没一一对应接收, 那是因为这个参数是有默认值的, 现在我们用lambda接收过来

connect(btn1,&QPushButton::clicked,this,[](bool checked_flag){qDebug()<<"checked_flag:"<

十四. 断开连接

disconnect(ddd,teacherSingal,anny,studentSlot);

十五. 槽函数中找到信号发射者

比如我现在有两个按钮, 我给他们绑定同一个槽函数, 但是希望在槽函数中根据不同按钮做不同的事情, 那应该怎么做呢? 我们可以传参解决, 也可以在槽函数中找到信号发射者

我们使用sender()函数找到信号发射者
注意: sender()函数的返回值是QObject类型. 所以要强转一下

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->btn1,&QPushButton::clicked,this,&MainWindow::dealEvent);
    connect(ui->btn2,&QPushButton::clicked,this,&MainWindow::dealEvent);
}

MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::dealEvent()
{
    QObject *mySender = sender();
    QPushButton *p = (QPushButton *) mySender;
    if(p != NULL){
        ui->label->setText(p->text());
    }
}

十六. 花式传参

你可能感兴趣的:(QT学习笔记(二) 创建类, 信号和槽)