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

一. QT简介

一. 窗口

1. 设置窗口大小

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

2. 设置窗口标题

setWindowTitle(“窗口1”)

例子:

#include "mywidget.h"
#include <QPushButton>
#include <QApplication>

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 <QApplication>

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 <QPushButton>
#include <QApplication>

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 <QWidget>
#include <QPushButton>

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 <QWidget>
#include <QPushButton>
#include <QDebug>

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 <QWidget>
#include <QPushButton>
#include <QDebug>
#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 <QDebug>

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:"<<a;}); //把a传入lambda
    connect(btn3,&QPushButton::clicked,this,[btn3](){btn3->setText("OK3");}); //把btn3传入lambda

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

MainWidget::~MainWidget()
{
}

3. 带参数的lambda

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

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

connect(btn1,&QPushButton::clicked,this,[](bool checked_flag){qDebug()<<"checked_flag:"<<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());
    }
}

十六. 花式传参

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容