信号和槽是Qt的一个核心特点,也是区别于其他框架的重要特性。因为有了信号和槽的编程机制,Qt中处理界面各个组件的交互操作时变得更加直观和简单。信号与槽是对象间通信的机制,由Qt的元对象系统的支持才能实现。
信号(Signal)会在某种特定情况或动作下被触发。就是特殊情况下被发射的事件,而GUI程序设计的主要内容就是对界面上各组件信号的响应。
槽(Slot)则等同于接收并处理信号的函数。它跟一般的C++函数是一样的,可以直接被调用。但槽函数与普通函数不同的是,槽函数与一个信号关联,当信号被发射时,关联的槽函数会自动执行。
对于这种对象间通信,Qt隐藏了复杂的底层实现。Qt的信号与槽机制类似于“事件——响应”,但更灵活。
1.connect
函数的不同参数形式
QObject::connect()
函数多重函数形式,一种参数原型是:1 2 3 4 5 6 7
| /× sender是发送信号的对象名称 signal()是信号名称,根据SIGNAL宏展开成相应字符串 receiver是接受信号的对象名称 slot()是槽函数名称,根据SLOT宏展开成字符串 ×/ QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
|
一般语法如下:1 2 3 4 5
|
QObject::connection(sender, SIGNAL(signal()), receiver, SLOT(slot()));
QObject::connection(spinNum, SIGNAL(valueChanged(int)), this, SLOT(updateStatus(int));
|
- 另一种参数形式的connect()函数原型是:
1
| QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMethod &signal, const QObject *receiver, const QMetaMethod, &method, Qt::ConnectionType type = Qt::Auto Connection)
|
这种具有默认参数,信号名称是唯一的,可以使用这种函数指针形式进行关联,语法如下:1 2
| QObject::connection(lineEdit, &QLineEdit::textChanged, this, &widget::on_textChanged);
|
但如果说类中定义有不同参数的同名信号就不能用函数指针的方式来关联信号和槽了:1 2 3
| void QSpinBox::valueChanged(int i); void QSpinBox::valueChanged(const QString &text);
|
1 2
| void onValueChanged(int i);
|
Ctrl+B编译时将会出错:1
| QObject::connect(spinNum, &QSpinBox::valueChanged, this, &widget::onValueChanged);
|
connect()
函数最后都有一个Qt::ConnectionType type
,缺省参数是Qt::AutoConnection
。Qt::ConnectionType
是枚举类型,表示信号与槽的关联方式:
1 2 3 4 5 6 7 8 9 10 11 12
| Qt::AutoConnection type;
Qt::DirectConnection type;
Qt::QueuedConnection type;
Qt::BlockingQueuedConnection type;
|
2.使用sender
获得信号发送者
在槽函数里,使用QObject::sender()可以获取信号发送者的指针,知道发射者的类型就可以将它类型转换成确定的类型。
1 2
| QSpinBox *spinBox = qobject_cast<QSpinBox *>(sender());
|
1 2 3 4 5
| QObject::connect(button1, &QPushButton::clicked, [this]{ QPushButton *button = qobject_cast<QPushButton *>(sender()); qDebug() << button->text(); });
|
3.自定义信号及其使用
通过在自己设计的类中自定义信号,这个信号就是声明的函数,但这个函数不用实现,只需要发射(emit):
1 2 3 4 5 6 7 8 9 10 11 12
| class QPerson : public QObject { Q_OBJECT private: int m_age = 10; public: void incAge(); signals: void ageChanged(int value); }
|
1 2 3 4 5 6
| void QPerson::incAge() { m_age++; emit ageChanged(m_age); }
|
4.关于信号和槽的使用上的规则
- 一个信号可以连接多个槽
- 多个信号可以连接同一个槽
- 一个信号可以连接另一个信号
- 信号与槽的个数和类型需要一致,至少信号的参数不能少于槽的参数
- 使用信号和槽的类中,需要在类定义中加入宏Q_OBJECT
- 当信号被发射时,关联的槽函数会被立即执行,直到执行完毕才会执行余下的程序流程。