QT入门-信号与槽

1.QT基本框架

#include "myWindow.h"
 
#include 
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    myWindow w;
    w.show();
    return a.exec();
}

QApplicata:应用程序对象,必须有且只能有一个

Qwidget:qt自带空窗口类,即运行时的窗口。

myWindow:自定义窗口类,继承自QWidget。

a.exec():进入消息循环,除非人为结束进程,否则阻塞在这里等待用户输入,死循环。

qmake:qt的编译器。

.pro文件:qt的项目文件。

 拖动按钮到窗口中,然后右键转到槽,自定义实现功能QT入门-信号与槽_第1张图片

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


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

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_pushButton_clicked()
{
//this表示设置父对象为MainWindow
    QMessageBox::information(this,"hello","my first qt application!");
}

运行:
 QT入门-信号与槽_第2张图片

2.对象树

对象树是qt中非常重要的内容,主要用于自动析构对象。

在qt中,我们自定义的类可以继承自qt中已有的类。比如上述myWindow就是继承自QWidget。

QWidget又是继承自QObject。

在qt中QObject是所有类的祖先,向下生成了许多子类,这样一个关系就叫做对象树。

 (注意这里的父子关系并不是语法里面的父子继承关系,而是qt里面的一种机制)

QT入门-信号与槽_第3张图片

使用时,需要通过setParent函数将对象间确定父子关系,之后子对象会进入父对象的children列表。 

当析构时,会先从父对象开始,先走父对象析构函数(注意,只是析构没有真正释放),然后依旧children列表析构子对象,直到走到叶子对象后,释放叶子对象,再返回父对象释放,直到回到一开始的父对象。

当释放一个对象时,会把它所有的子对象都释放掉,因此,之后不能再去释放子对象,否则会发生二次释放的错误。

错误代码1:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    myWidget * c = new myWidget;
    QWidget * s = new QWidget;
    c->setParent(s);//s是c的父对象
    delete s;//删释放s同时也会将c释放
    delete c;//错误,二次释放
    return 0;
}

更改:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    myWidget * c = new myWidget;
    QWidget * s = new QWidget;
    c->setParent(s);//s是c的父对象
    delete s;//删释放s同时也会将c释放
    return 0;
}

错误代码2:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    myWidget c;
    QWidget s;// s后定义,结束时先析构
    c.setParent(&s);//s为父,c为子
    return 0;
    //发生错误,s先析构会把c也析构,
    //之后c调用析构函数时会二次析构
}

更改:

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget s;//s析构子对象时发现c已经被析构了,就不会在析构了,这是qt中实现的
    myWidget c;
    c.setParent(&s);
    return 0;
}

防止二次析构的实现:

Qt 并不直接使用智能指针来管理对象树中的对象生命周期(如 std::shared_ptrQSharedPointer),但它的机制类似于引用计数。每个 QObject 都知道有多少子对象以及它们是谁,而子对象知道它们的父对象是谁。当对象被删除时,它会通知其父对象和子对象调整它们的指针和列表

3.信号与槽

1.1 信号和槽

1,信号和槽(Signal&Slot)

  • 信号与槽(Signal&Slot)是Qt的基础,也是Qt的一大创新,因为有了信号与槽的编程机制,在Qt中处理 界面各组件的交互操作时,变得更加直观和简单,它可以让app开发人员把互不了解的对象绑定在一起

2,信号(Signal)

  • 信号就是在特定情况下被发送的事件,eg:PushButton最常见的信号就是鼠标单击时发送的clicked()信号,一个ComboBox最常见的信号是选择的列表项变化时发送的CurrentIndexChanged()信号。
  • GUI程序设计主要就是对界面各组件的信号相应,只需要知道什么情况下发送哪些信号,合理去响应和处理这些信号即可。

3,槽(Slot)

  • 槽就是对信号的响应函数,与一般的C++函数一样,可以定义在类的public,private,protect,可以具有任何参数,也可以直接被调用
  • 槽函数与一般函数不同的是,槽函数可以与一个信号关联,当信号被发送,关联的槽函数被自动执行
  • 信号和槽关联是用,QObject.connect()函数实现的,基本格式为:
QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));
参数1:信号的发送者
参数2:发送的信号
参数3:信号的接收者
参数4:处理函数(槽函数)

实例:信号与槽示例QPushButton

信号

1.我们打开Qt助手,搜索QPushButton,发现没有signal,只有个public slot

QT入门-信号与槽_第4张图片

2.找QPushButton的父类,QAbstractButton,找到signals 

QT入门-信号与槽_第5张图片

3.可以看到里面有4个信号

QT入门-信号与槽_第6张图片

槽函数:

1.自动生成的槽函数一般都是定义在窗口类中的

QT入门-信号与槽_第7张图片

2.同时,我们使用的很多槽函数都是定义在QWidge(MainWindow的父类)中的

QT入门-信号与槽_第8张图片

代码实现:

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


#include
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //由于connect时QOBJECT下的成员函数,而QWidget继承于QOBJECT,因此阔以直接使用
    connect(ui->mybutton,&QPushButton::clicked,this,&QWidget::close);
}

MainWindow::~MainWindow()
{
    delete ui;
}

//使用
void MainWindow::on_pushButton_clicked()
{
    QMessageBox::information(this,"hello","my first qt application!");
//    QWidget::close();
}

注意

1:

通过ui界面拖动的组件,定义在ui对象中,要想使用就通过ui->来调用

private:
    Ui::MainWindow *ui;

 2:
信号和槽传入的指针而不是函数,下面的写法是错的:

    connect(ui->mybutton,&QPushButton::clicked(),this,&QWidget::close());

 3:一个信号可以触发多个槽,这里就将ui定义的组件重新connect到close上了

结果:点击按键后窗口关闭

 1.2 自定义信号与槽

 在上面的工程添加类

QT入门-信号与槽_第9张图片

定义两个类 

 QT入门-信号与槽_第10张图片

 1.signal

头文件如下: 

#ifndef MYSIGNAL_H
#define MYSIGNAL_H

#include
//为signals添加头文件
class mysignal : public QObject
{
    Q_OBJECT
public:
    explicit mysignal(QObject *parent = nullptr);
signals:
    void send_signal();
};

#endif // MYSIGNAL_H

signal是一个宏,不是c++语法内容,我们定义一个信号后,不需要实现,直接通过

//emit是发送信号的标识,不写会报警告,版本问题,(非必须)
 emit obj.send_signal();

 就能实现信号的发送,因此,cpp文件不用写

2.slot

头文件如下: 

#ifndef MYSLOT_H
#define MYSLOT_H
#include


class myslot : public QObject//要写上public
{
    Q_OBJECT
public:
    explicit myslot(QObject *parent = nullptr);//防止隐式转换的写法
signals:
public slots:
    void ack();//需要实现
};

#endif // MYSLOT_H

注意slots的格式

cpp文件:

#include "myslot.h"
#include
myslot::myslot(QObject * parent) : QObject(parent)
{

}

void myslot::ack()
{
    qDebug()<<"recv!!!";
}

3.main

#include "mainwindow.h"

#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTranslator translator;
    const QStringList uiLanguages = QLocale::system().uiLanguages();
    for (const QString &locale : uiLanguages) {
        const QString baseName = "myfirst_qt_" + QLocale(locale).name();
        if (translator.load(":/i18n/" + baseName)) {
            a.installTranslator(&translator);
            break;
        }
    }
    MainWindow w;

    mysignal s(&w);
    myslot r(&w);

    w.connect(&s,&mysignal::send_signal,&r,&myslot::ack);
    emit s.send_signal();

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

效果:
QT入门-信号与槽_第11张图片

1.3 信号与槽函数的重载

1.直接重载

QT入门-信号与槽_第12张图片

QT入门-信号与槽_第13张图片

报错如下:

2.两种通过函数指针实现重载的方式

 1.普通函数指针
返回类型 (*指针名称)(参数类型列表);

 案例:

int add(int x, int y) {
    return x + y;
}

int main() {
    // 声明一个指向函数的指针
    int (*funcPtr)(int, int) = &add;

    // 使用函数指针调用函数
    int result = funcPtr(3, 4); // 等同于 add(3, 4)

    return 0;
}
2.类成员函数指针 

 类如下:

class Calculator {
public:
    int add(int x, int y) {
        return x + y;
    }
};

实现:

int main() {
    Calculator calc;

    // 声明一个指向成员函数的指针
    int (Calculator::*calcPtr)(int, int) = &Calculator::add;

    // 使用成员函数指针调用函数
    int result = (calc.*calcPtr)(5, 6); // 等同于 calc.add(5, 6)

    return 0;
}
 方法一:
    void (mysignal::*send_str)(QString&) = &mysignal::send_signal;
    void (myslot::*ack_str)(QString&) = &myslot::ack;

    w.connect(&s,send_str,&r,ack_str);

先定义一个带指定参数的函数指针,然后指向对应的信号或者槽,由于参数类型确定,那么会自动给函数指针匹配合适的重载函数

方法二:
w.connect(&s,(void(mysignal::*)(QString&))&mysignal::send_signal,&r,(void(myslot::*)(QString&))&myslot::ack);

节约行数,但是可读性差,不推荐 

核心:注意信号重载函数指针指向了哪一个函数,对重载函数的信号连接时,要指明到底连接的是哪一个槽函数

效果:

 mian代码:

#include "mainwindow.h"

#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTranslator translator;
    const QStringList uiLanguages = QLocale::system().uiLanguages();
    for (const QString &locale : uiLanguages) {
        const QString baseName = "myfirst_qt_" + QLocale(locale).name();
        if (translator.load(":/i18n/" + baseName)) {
            a.installTranslator(&translator);
            break;
        }
    }
    MainWindow w;

    mysignal s(&w);
    myslot r(&w);

//    w.connect(&s,&mysignal::send_signal,&r,&myslot::ack);
#if 0
    void (mysignal::*send_str)(QString&) = &mysignal::send_signal;
    void (myslot::*ack_str)(QString&) = &myslot::ack;

    w.connect(&s,send_str,&r,ack_str);
#else
    
    w.connect(&s,(void(mysignal::*)(QString&))&mysignal::send_signal,&r,(void(myslot::*)(QString&))&myslot::ack);
#endif


    emit s.send_signal();
    QString str = "jjj";
    emit s.send_signal(str);
    w.show();
    return a.exec();
}

QT入门-信号与槽_第14张图片

 1.4 信号和槽的扩展

1.信号链接信号

 定义一个按键,先按键触发信号,然后信号触发槽

    mysignal s(&w);
    myslot r(&w);
    QPushButton bnt;
    bnt.setParent(&w);
    bnt.setText("触发信号");
//    w.connect(&s,&mysignal::send_signal,&r,&myslot::ack);
#if 1
    void (mysignal::*send_str)() = &mysignal::send_signal;
    void (myslot::*ack_str)() = &myslot::ack;
    void (mysignal::*send_str1)() = &mysignal::send_signal;
    w.connect(&bnt,&QPushButton::clicked,&s,send_str1);
    w.connect(&s,send_str,&r,ack_str);
//    emit s.send_signal();
//    QString str = "jjj";
//    emit s.send_signal(str);
    w.show();
    return a.exec();

效果:

点击后触发槽函数:

QT入门-信号与槽_第15张图片

即:

信号连接信号,一个消息信号可以绑定多个槽,同样的,一个槽函数也可以绑定多个消息信号。(多对多关系

2.在ui函数中使用自定义变量

 1.如果变量定义在外面,无法进行connect:

QT入门-信号与槽_第16张图片

2.如果变量定义在里面:
 QT入门-信号与槽_第17张图片

由于时局部变量,无法长期维持, mysignalmyslot 的实例 sr 是在 MainWindow 构造函数的局部作用域中创建的。这意味着它们只在构造函数执行期间存在,一旦构造函数执行完毕,这些局部变量就会被销毁。因此,当您点击按钮试图触发信号时,sr 的实例可能已经不存在了,导致信号无法被成功触发或接收。

如果执意运行,会发现点击按钮无响应(s,r早被销毁了) 

3.使用成员函数:

QT入门-信号与槽_第18张图片

QT入门-信号与槽_第19张图片

效果:
QT入门-信号与槽_第20张图片

3.信号的断开-disconnect()

    connect(ui->mybutton,&QPushButton::clicked,&s,send_str1);
    connect(&s,send_str,&r,ack_str);
    disconnect(&s,send_str,&r,ack_str);

 效果:QT入门-信号与槽_第21张图片

点击无反应(连接已经断开) 

 1.5 lambda辅助槽函数

1.为何要用lambda 

有时候槽函数的内容不简单,但是需要在各处定义槽函数和其实现,那么全部重写很麻烦,由此引入lambda的使用

直接在要用槽函数的地方(connect处),把函数整个传入当作参数,这样就不用先定义再实现再调用了,简便很多

比如下例:

    connect(ui->bnt1,&QPushButton::clicked,this,[this]()mutable{a+=10;qDebug()<

简单的功能直接使用lambda实现,避免了重复的简单操作 

2.lambda解析

  • 捕获列表。在C++规范中也称为Lambda导入器, 捕获列表总是出现在Lambda函数的开始处。实际上,[]是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数,捕捉上下文中的变量以供Lambda函数使用。(能够使用的变量,不是传入参数)
    • []中括号函数对象里面对应参数有以下形式:
      空,没有使用任何函数对象参数
      =,函数体内使用Lambda所在范围内的可见局部变量,包括所在类的this的传值方式,相当于编译器给Lambda所在范围的所有局部变量**赋值**一份给Lambda函数
      &,函数体内使用Lambda所在范围内的可见局部变量,包括所在类的this的引用方式,相当于编译器给Lambda所在范围的所有局部变量**引用**一份给Lambda函数
      this,函数体内可以使用Lambda所在内的成员变量
      a,不是字母,而是指具体一个变量a,Lambda内拷贝一个变量a使用
      &a,Lambda内引用变量a
      a, &b,拷贝a,引用b
      =,&a,&b,除a,b引用外,其他变量做拷贝操作
      &,a,b,除a,b拷贝外,其他变量做引用操作
  • 参数列表与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略。
  • 可变规格。mutable修饰符, 默认情况下Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)使用后我们就可以改变捕获列表中变量的值
  • 异常说明。用于Lamdba表达式内部函数抛出异常。
  • 返回类型。 追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。
  • lambda函数体内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。

 3.lambda在QT中的使用-无参数的信号调用有参数的槽函数

在上文代码的基础上实现以下连接 

    connect(ui->mybutton,&QPushButton::clicked,&s,send_str1);
//    connect(&s,send_str,&r,ack_str);
    QString str = "hello";
    //注意QString& 不能接受const char字符串,因此这里将前面的ack参数改为了QString
    connect(&s,send_str,&r,[=](){r.ack("aaaaaaa");});

 效果:
QT入门-信号与槽_第22张图片

你可能感兴趣的:(qt,命令模式,开发语言)