Qt备忘录之三:slots与signals初探

slots和signals不是c++的关键字,刚接触真是雾煞煞。所以先不管slots和signals是什么碗糕,用起来再说!

slots和signals的一般用法:

还是看《C++ GUI Programming with Qt 4 2nd Edition》书上的例子。

age.pro:

[code lang="cpp" collapse="false"]
QT += widgets

TEMPLATE = app
SOURCES = age.cpp
[/code]

age.cpp:

[code lang="cpp" collapse="false"]
#include <QApplication>
#include <QHBoxLayout>
#include <QSlider>
#include <QSpinBox>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);

QWidget *window = new QWidget;//新建一个Widget实例
window->setWindowTitle("Enter Your Age");//设置window对象的标题

QSpinBox *spinBox = new QSpinBox;//新建一个QSpinBox实例
QSlider *slider = new QSlider(Qt::Horizontal);//新建一个水平放置的QSlider实例
spinBox->setRange(0, 130);//设置上述两个对象的值范围
slider->setRange(0, 130);

QObject::connect(spinBox, SIGNAL(valueChanged(int)),
slider, SLOT(setValue(int)));
//将spinBox的valueChanged(int)信号与slider的槽setValue(int)建立连接

QObject::connect(slider, SIGNAL(valueChanged(int)),
spinBox, SLOT(setValue(int)));
//将slider的valueChanged(int)信号与spinBox的槽setValue(int)建立连接

spinBox->setValue(35);

QHBoxLayout *layout = new QHBoxLayout;//新建一个布局对象
layout->addWidget(spinBox);//将上述的spinBox和slider作为layout的成员加入layout中
layout->addWidget(slider);
window->setLayout(layout);//将layout作为window的成员加入window中

window->show();//将window呈现

return app.exec();
}
[/code]

其他的东西都是c++的标准写法,一般都能看懂,关键是以下两句:

[code lang="cpp" collapse="false"]
QObject::connect(spinBox, SIGNAL(valueChanged(int)),
slider, SLOT(setValue(int)));

QObject::connect(slider, SIGNAL(valueChanged(int)),
spinBox, SLOT(setValue(int)));
[/code]

Qt Creator中,按住Ctrl再点具体的函数明或者参数名,会跳转到其声明。用这个方法查看第18行中的valueChange(int),跳转到QSpinBox类的头文件qspinbox.h中:

[code lang="cpp" collapse="false"]
Q_SIGNALS:
void valueChanged(int);
void valueChanged(const QString &);
[/code]

可见,valueChanged是QSpinBox类的一个“Q_SIGNALS”权限的成员。再去看Q_SIGNALS的定义,其实是一个宏。相应的,Q_SLOTS也是一个宏。暂时不去深究这些宏在编译的时候起什么作用,从效果上来看,就是当spinBox对象发出valueChanged(int)信号时,slider的setValue(int)槽起了作用。这就实现了两个对象之间的通信。

connect函数原型分析

根据qobject.h和Qt官方的帮助文档,connect函数有以下几种重载:

[code lang="cpp" collapse="false"]
QMetaObject::Connection connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoConnection)
QMetaObject::Connection connect(const QObject * sender, const QMetaMethod & signal, const QObject * receiver, const QMetaMethod & method, Qt::ConnectionType type = Qt::AutoConnection)
QMetaObject::Connection connect(const QObject * sender, PointerToMemberFunction signal, const QObject * receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection)
QMetaObject::Connection connect(const QObject * sender, PointerToMemberFunction signal, Functor functor)
QMetaObject::Connection connect(const QObject * sender, PointerToMemberFunction signal, const QObject * context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
[/code]

上面的示例使用的是第一个重载方法。其中,sender是信号端的对象指针,signal是信号名,receiver是槽端的对象指针,method是槽名。语句中的SIGNAL()和SLOT()的定义,则可以从qobjectdef.h中找到。

qobjectdef.h

[code lang="cpp" collapse="false"]
#ifndef QT_NO_META_MACROS
#ifndef QT_NO_DEBUG
# define QLOCATION "\0" __FILE__ ":" QT_STRINGIFY(__LINE__)
# ifndef QT_NO_KEYWORDS
# define METHOD(a) qFlagLocation("0"#a QLOCATION)
# endif
# define SLOT(a) qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a) qFlagLocation("2"#a QLOCATION)
#else
# ifndef QT_NO_KEYWORDS
# define METHOD(a) "0"#a
# endif
# define SLOT(a) "1"#a
# define SIGNAL(a) "2"#a
#endif
[/code]

这其实是两个宏,作用分别是提取出信号或者槽名,并加上标注以区分。如果定义2个QString,用于接收SIGNAL(valueChanged(int))和SLOT(setValue(int)),在调试模式可以看到SIGNAl()和SLOT()的结果如下:

SIGNAL()宏

SLOT()宏

这边涉及到Qt的元对象编译机制,后面如果有机会会深入研究。第二种和第三种重载,其实本质上和第一种一样,只不过现在是根据QMetaMethod对象和PointerToMemberFunction对象来指定signal和slot。

第五种,是将sender对象的signal与context对象的函数functor做连接。根据Qt的官方文档,这个functor还可以是lambda表达式。第四种是第五种把当前对象作为Receiver的特殊形式,相当于:

[code lang="cpp" collapse="false"]
QMetaObject::Connection connect(const QObject * sender, PointerToMemberFunction signal, this, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
[/code]

即将sender的signal与当前对象的functor函数连接。

以上五种重载中,最后一个参数是缺省的。在高级应用中有可能会用到,这边先列举一下官方的说明,做个备忘。如果以后有空闲再详细研究。

ConectionType

 

参考书目:

Qt5.4.0官方文档

《C++ GUI Programming with Qt 4 2nd Edition》(为了方便起见,以后的文章提到时简称为《2nd》)

豆子空间:《Qt学习之路》,《Qt学习之路2

二零一五年三月十一日

顾毅写于厦门