官网地址:https://doc.qt.io/qt-5/signalsandslots.html
0. 简介
slot是连接多个object的一种方法,很多别的框架都是使回调函数,但是QT中使用SLOT,相对与回调来说,不必考虑传入参数的类型匹配等问题。我们可以认为SLOT是一个函数,它可以被Signal激活,同时连接两个object。Qt’s signals and slots mechanism ensures that if you connect a signal to a slot, the slot will be called with the signal’s parameters at the right time.
1. 机制
- 任何继承自QObject或者其子类(e.g., QWidget) 的类都可以包含 signals and slots。
- Signals are emitted by objects when they change their state in a way that may be interesting to other objects. This is all the object does to communicate. It does not know or care whether anything is receiving the signals it emits.
- You can connect as many signals as you want to a single slot, and a signal can be connected to as many slots as you need. It is even possible to connect a signal directly to another signal. (This will emit the second signal immediately whenever the first is emitted.)
2. example
一个常见的C++工程如下:
class Counter
{
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
void setValue(int value);
private:
int m_value;
};
一个继承自QObject
的类的写法如下:
#include <QObject>
class Counter : public QObject
{
Q_OBJECT
public:
Counter() { m_value = 0; }
int value() const { return m_value; }
public slots:
void setValue(int value);
signals:
void valueChanged(int newValue);
private:
int m_value;
};
这个类的状态可以被激发的信号valueChanged()改变, 并且它有slot可以让其他的function通过这个slot发送这个激活的信号。
它的slot为void setValue(int value);
,下面一个它的可能实现。在这个实现中我们可以看到有一行为emit valueChanged(value);
,这一句就是可以发送valuechanged(value)
这个signal
void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
emit valueChanged(value);
}
}
下面我们通过QObject::connect()
这个函数来链接signal
,slot
和两个object
,用法如下:
Counter a, b;
QObject::connect(&a, &Counter::valueChanged,
&b, &Counter::setValue);
a.setValue(12); // a.value() == 12, b.value() == 12
b.setValue(48); // a.value() == 12, b.value() == 48
也就是说,a通过setValue这个slot链接b,并通过valueChanged这个signal通知b。
当a调用setvalue时,会激活valueChanged这个信号,那么b就会在setValue() slot中接收它,所以b会调用它的setValue.
Calling a.setValue(12) makes a emit a valueChanged(12) signal, which b will receive in its setValue() slot, i.e. b.setValue(12) is called. Then b emits the same valueChanged() signal, but since no slot has been connected to b’s valueChanged() signal, the signal is ignored.
在链接之后,我们也可以通过disconnect()
来断开两个object
之间的链接。
3. A Real Example
#ifndef LCDNUMBER_H
#define LCDNUMBER_H
#include <QFrame>
class LcdNumber : public QFrame
{
Q_OBJECT
public:
LcdNumber(QWidget *parent = nullptr);
signals:
void overflow();
public slots:
void display(int num);
void display(double num);
void display(const QString &str);
void setHexMode();
void setDecMode();
void setOctMode();
void setBinMode();
void setSmallDecimalPoint(bool point);
};
#endif
A slot is a receiving function used to get information about state changes in other widgets.
我们可以将不同的对象之间通过同一个信号不同的slot链接,这样就会调用不同的slot。
4. Signals And Slots With Default Arguments
如果我们要捕获一个对象销毁的信号:
The signatures of signals and slots may contain arguments, and the arguments can have default values.
比如说QObject的destory就是一个默认的信号:
void destroyed(QObject* = nullptr);
如果我们要捕获这个信号,我们可以这样实现,先自定义一个slot
void objectDestroyed(QObject* obj = nullptr);
然后把信号与对象进行链接,当sender销毁的时候,本对象会调用objectDestroyed
slot:
connect(sender, &QObject::destroyed, this, &MyObject::objectDestroyed);
我们也可以直接使用默认的类来实现:这三种实现方式都可以
connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(Qbject*)));
connect(sender, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed()));
connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
但是下面这种不可以
connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed(QObject*)));
because the slot will be expecting a QObject that the signal will not send. This connection will report a runtime error.