QT学习笔记(2)—信号与槽

1 moc(元对象)系统简介

在 Qt 助手的索引里面输入“The Meta-Object System”,就可以看到元对象系统的英文文档。现在将其主要的内容描述如下:

Qt 元对象系统实现了对象之间通信机制——信号和槽,并提供了运行时类型信息和动态属性系统。元对象系统是 Qt 类库独有的功能,是 Qt 对标准 C++ 的扩展,并且元对象系统本身也是由纯 C++ 语言写成的,所以学好 C++ 是必须的。使用元对象系统的前提是需要三件事情:
(1)直接或间接地以 QObject 为基类,这样才能利用元对象系统的功能,Qt 的窗体和控件最顶层的基类都是 QObject。
(2)将 Q_OBJECT 放在类声明的私有段落,以启用元对象特性,如动态属性、信号和槽等。如Q_OBJECT 都是在类声明里的第一行,没有加 private 字样,因为类声明默认就是私有的。
(3)元对象编译器(Meta-Object Compiler,moc)为每个 QObject 的子类提供必要的代码以实现元对象特性。

注意:
只有继承了 QObject 类的类,才具有信号槽的能力。所以,为了使用信号槽,必须继承 QObject。凡是 QObject 类(不管是直接子类还是间接子类),都应该在第一行代码写上 Q_OBJECT。不管是不是使用信号槽,都应该添加这个宏。这个宏的展开将为我们的类提供信号槽机制、国际化机制以及 Qt 提供的不基于 C++ RTTI 的反射能力。

除了提供信号和槽机制用于对象之间的通信(这是主要任务),元对象系统还提供了更多的特性:
(1)QObject::metaObject() 函数返回当前类对象关联的元对象(meta-object)。
(2)QMetaObject::className() 函数返回当前对象的类名称字符串,而不需要 C++ 编译器原生的运行时类型信息(run-time type information,RTTI)支持。
(3)QObject::inherits() 函数判断当前对象是否从某个基类派生,判断某个基类是否位于从 QObject 到对象当前类的继承树上。
(4)QObject::tr() 和 QObject::trUtf8() 函数负责翻译国际化字符串,因为 Qt5 规定源文件字符编码是 UTF-8,所以这两个函数现在功能是一样的。
(5)QObject::setProperty() 和 QObject::property() 函数用于动态设置和获取属性,都通过属性名称字符串来 操作。
(6)QMetaObject::newInstance() 构建一个当前类的新实例对象。

2 信号与槽
(1)一个信号可以连接多个槽;
(2)多个信号可以连接在同一个槽上;
(3)一个信号可以与另外一个信号相连接;
(4)连接可以被移除;

3 信号与槽使用格式

(1)QT4和QT5通用的格式如下:

QMetaObject::Connection QObject::​connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection)
connect(sender, SIGNAL(signal), receiver, SLOT(slot))

头两个参数是源头对象和信号,后两个参数接收对象和槽函数,sender和receiver是指向QObject的指针,signal和slot是不带参数的函数名。这种句式可读性很好,信号和槽的标识也很清晰。connect 函数返回类型是 QMetaObject::Connection ,可以用于运行时判断关联是否正确,或者用于解除关联。

注意到 connect 函数参数里的 signal 和 method(槽函数)都是 char * 字符串类型,所以旧式语法的 connect 函数是根据信号和槽函数的字符串名称来关联的,不具备编译时类型检查,大家都是字符串,参数类型在编译时都不知道。关联出错只有在运行时才会体现。参数使用时要注意:
(1)将函数名转为char *字符串可使用SIGNAL和SLOT两个宏,注意函数包括函数名以及形参类型,但不包括形参。如:connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(FoodIsComing(int, double)));
(2)receiver是指向QObject的指针,是包含槽函数的类,当connect在类内部(成员函数内)使用时,receiver要指向这个类本身,可用this指针;当connect在类外部中使用时,receiver要指向类对象,用&符号。

最后的关联类型参数一般我们都使用默认值 Qt::AutoConnection,这在多线程编程的时候才会有讲究。对于单线程的,关联一般用直连类型(Qt::DirectConnection),信号一触发, 对应槽函数立即就被调用执行;对于多线程程序,跨线程的关联一般用入队关联(Qt::QueuedConnection),信号触发后,跨线程的槽函数被加入事件 处理队列里面执行,避免干扰接收线程里的执行流程。Qt::AutoConnection 会自动根据源头对象和接收对象所属的线程来处理,默认都用这种类型的关联,对于多线程程序这种关联也是安全的。

(2)Qt5 为了能够尽早检查关联类型和参数的匹配情况,引入了新的函数指针关联语法,这样在程序编译时就能发现关联正确与否。新式语法格式如下:

QMetaObject::Connection QObject::​connect(const QObject * sender, PointerToMemberFunction signal, const QObject * receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection)

与旧语法句式区别就在于 signal 和 method (槽函数),新句式用的是 PointerToMemberFunction (相当于函数指针),这个类型名称是不存在的,只是在文档里面显示,方便程序员看的,实际使用的是模板函数。具体模板函数定义比较复杂,比较关键的就是两个函数参数类型需要一致,或者信号声 明时的参数更多更广。因为信号本身是不干活的,它多点参数无所谓,但必须保证槽函数运行时需要的参数是一定有的。

connect(serial_ptr, SIGNAL(readyRead()), this, SLOT(serial_getdata()));
connect(serial_ptr, &QSerialPort::readyRead, this, &SerialWindow::serial_getdata);

注意使用新格式时,信号函数与槽函数只有函数名,而且必须加上类作用域,并在前加上&符号。

最后提醒一下,不管使用旧句式,还是新句式,关联的源头和接收端一定是实际存在的对象,ui->pushButton 这个按钮对象成功创建之后,上面的关联才能正常执行。虽然新句式可以用 &QPushButton::clicked 这个函数指针形式,但注意第一个和第三个参数是实际的对象,这是把源头对象关联到接收端对象,而不是把类关联到类!如果没有定义对象实体,关联函数就没意义。

4 自定义信号与槽

4.1 自定义信号与槽的一般步骤
(1)发送者和接收者都需要是 QObject 的子类(槽函数是全局函数、 Lambda 表达式等无需接收者的时候除外);
(2)使用 signals 标记信号函数,信号是一个函数声明(可以有参数),返回 void,不需要实现函数代码
(3)槽函数是普通的成员函数,作为成员函数,会受到 public、 private、 protected 的影响;
(4)使用 emit 在恰当的位置发送信号;
(5)使用 QObject::connect()函数连接信号和槽。

4.2 信号接力

connect(sender, SIGNAL(signal1), receiver, SIGNAL(signal2))

注意:这种方法signal2下的形参一般是void型,无法发出带参数的信号(可以在槽函数中使用emit来实现发出带参数的信号)。

signals:
    void SendVoid();

//clicked()信号发出后会立即发出SendVoid()信号
connect(ui->pushButton, SIGNAL(clicked()), this, SIGNAL(SendVoid())); 

public slot:
    void RecvVoid();

connet(&w, SIGNAL(SendVoid()), &s, SLOT(RecvVoid()));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值