QT Creator 快速入门教程 读书笔记(三)

一   信号和槽

  GUI 程序除了要绘制控件,还要响应系统和用户事件,例如重绘、绘制完成、点击鼠标、敲击键盘等。当事件发生时,UI 会产生相应的变化,让用户直观地看到。
大部分编程(例如Win SDK、Web前端)中使用回调函数来响应事件,而 Qt 却首创了信号和槽机制。所谓回调函数,就是程序员提早定义一个函数,当事件发生时就调用该函数。
信号和槽是Qt的核心,它让两个互不相干的对象链接起来,当一个对象的状态改变时,能够通知另外一个对象。
咱们先经过例子来演示一下信号和槽:前端

具体的代码:程序员

#include "mainwindow.h"
#include <QApplication>
#include <QMainWindow>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QMainWindow w;
    w.setWindowTitle("微浪游戏");
    w.resize(325, 120);

    QLineEdit lineEdit(&w);
    lineEdit.setGeometry(30, 20, 180, 36);
    lineEdit.setPlaceholderText("请输入文本");

    QPushButton btn("取消", &w);
    btn.setGeometry(220, 20, 70, 36);

    QLabel label(&w);
    label.setGeometry(30, 70, 250, 30);

    //链接clicke()信号和quit()槽
    QObject::connect(&btn, SIGNAL(clicked()), &app, SLOT(quit()));
    //链接textChanged()信号和setText()槽
    QObject::connect(&lineEdit, SIGNAL(textChanged(QString)), &label, SLOT(setText(QString)));

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

 在上面的demo中建立了三个控件:lineEdit,btn,label,他们都是QMainWindow w的子控件。运行的结果以下:编程

点击“取消”按钮,程序就关闭了,这是第26行代码的做用;在文本输入框中输入一段文本,下面的 Label 会随时显示出来,这是第28行代码的做用。app

这两个对象都是经过信号和槽链接起来的,信号和槽用于两个对象之间的通讯。信号和槽是QT的核心特征,当一个特殊的事情发生时即可以发射一个信号,好比demo中的取消按钮被点击时,就会发射clicked()信号;而槽就是一个函数,它在信号发射后被调用来响应这个信号,Qt的部件类中已经定义了一些信号和槽,可是更经常使用的作法是子类化部件,而后添加自定义的信号和槽来实现想要的功能。函数

信号是只有函数声明、没有函数体的成员函数。槽是拥有完整函数体的普通成员函数,你能够在槽函数中实现各类功能,与普通函数相比并无区别,例如 quit() 的做用就是退出程序。ui

connect() 是 QObject 类的静态成员函数;QObject 是 Qt 中全部类的基类,它就像“树根”,从这里派生出了全部其余“树枝”。
须要注意的是,信号不是事件。当用户点击“取消”按钮时,Qt 会捕获该点击事件,进行预处理,而后发射 clicked() 信号; clicked() 和 quit() 关联起来了,接下来就会调用 quit() 函数。
信号和槽机制归根结底也是回调函数,只不过绕了个圈子。在这种机制下,程序员有两次处理事件的机会,一是在捕获事件后发射信号前进行预处理(事件不符合预期能够不发射信号),二是在槽函数中进行主要处理。
再来看第27行。textChange() 信号会在文本改变时发出,setText() 槽用来设置 Label 的文本,QString 是要传递的数据的类型。当用户输入文本时,lineEdit 会发出 textChange() 信号,该信号将携带数据,数据类型为 QString,数据内容为输入的文本;setText() 槽接收到信号后先解析信号携带的数据,获取用户输入的文本,而后填充到 Label 中。
spa

二 信号和槽的关联

信号和槽的关联使用的是QObject类的connect()函数,connect() 是 QObject 类的静态成员函数,它有多个原型:指针

connect(QObject *sender,   char *signal,
        QObject *receiver, char *method);
connect(QObject *sender,   PointerToMemberFunction signal,
        QObject *receiver, PointerToMemberFunction method);
connect(QObject *sender,   PointerToMemberFunction signal,
        QObject *context,  Functor functor);
connect(QObject *sender,   QMetaMethod &signal,
        QObject *receiver, QMetaMethod &method);
connect(QObject *sender,   PointerToMemberFunction signal,  Functor functor);

 简单起见,上面省略了 connect() 的返回值和最后一个参数,以及某些参数前面的 const 修饰符,读者能够在 Qt 帮助手册中查看完整的原型。code

connect() 函数返回值类型为 QMetaObject::Connection,表示当前链接句柄。最后一个参数为 Qt::ConnectionType type = Qt::AutoConnection,表示链接类型,通常默认便可。

观察上面的原型,除了最后一个有3个参数,其余都有4个参数,其中:
1) sender 为信号发送者,receiver 为信号接收者,它们都是对象指针。

2) 第1个原型中,signal 为信号,method 为槽函数,它们都是字符串,必须借助 SIGNAL() 和 SLOT() 将函数形式转换为字符串形式。SIGNAL() 和 SLOT() 是宏,而非函数。上面的示例中就使用了该原型,它是经常使用的原型,初学者必需要掌握。

3) 第2个原型中,PointerToMemberFunction 为指向成员函数的指针。你能够将示例中的代码作以下更改:对象

QObject::connect(&btn, &QPushButton::clicked, &app, &QApplication::quit);
QObject::connect(&lineEdit, &QLineEdit::textChanged, &label, &QLabel::setText);

这是 Qt 5 新增的原型,能够在编译期间进行检查,若是信号和槽不存在或者不匹配,则会报错。而第1种原型是从 Qt 诞生以来一直支持的,不能在编译期进行检测,若是信号和槽有误,只会在程序运行期间给出警告并返回 false,不容易发现问题,这是它的一个缺陷。因此在 Qt 5 中咱们鼓励使用第2种原型。

相关文章
相关标签/搜索