信号槽是 Qt 框架引以为豪的机制之一。熟练使用和理解信号槽,能够设计出解耦的非常漂亮的程序,有利于增强我们的技术设计能力。
所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,用自己的一个函数(成为槽(slot))来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。(这里提一句,Qt 的信号槽使用了额外的处理来实现,并不是 GoF 经典的观察者模式的实现方式。)
• 是绝大多数类的基类
• 所有的QWidgets都是QObject
• 提供对象树和对象的关系
• QObject在整个Qt的概念体系中处在一个非常重要的位置。
• 提供了信号-槽的通信机制
• 内存管理
所有子对象的内存管理都转移给了父对象
• 使用new在堆上分配内存
• 子对象可自动被父对象删除内存
• 手动删除不会引起二次删除,因为子对象删除时会通知父对象
没有父对象的QObject对象都需要手动删除,一般把这种无父亲的对象分配在栈上,可以避免内存泄露的问题
Qt没有类似于自动回收站的机制,只需要关注对象的父子关系和功能!
• Introspection(内省 )
• 事件处理
每一个QObject实例都可以有一个父亲的参数。
孩子会通知他的父亲自己的存在,父亲会把它加入到自己的孩子列
表中。如果一个widget对象没有父亲,那么他就是一个窗口。
父控件可以:
• 当父控件隐藏或显示自己的时候,会自动的隐藏和显示子控件。
• 当父控件Enables and disables时,子控件的状态也随之变化。
注意: 在父控件可见的时候,子控件也可以单独隐藏自己
• 信号槽机制可以在对象之间彼此并不了解的情况下将它们的行为联系起来。
• 槽函数能和信号相连接,只要信号发出了,这个槽函数就会自动被调用。
• 比回调函数更安全,比多态更加灵活
可以有多对多的对应关系
在QObject中实现
注意: 发送者和接收者都需要是 QObject 的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
信号发生时,会通知那些对该信号感兴趣的观察者
信号是一个类的成员方法,该方法的实现是由meta-object自动实现的,对于开发者只需要在类中声明这个信号,并不需要实现。
发送一个信号使用 emit
emit clicked();
emit someSignal(7, "Hello");
• 槽是类的一个成员方法,当信号触发时该方法执行.
• 可以是虚函数(virtual)、可被重载(overload)、可以说公有的(public)、保护的(protective)或者私有的(private)。
• 可以象任何c++成员函数一样被直接调用,可以传递任何类型的参数,可以使用默认参数。
class NewClass : public QObject
{
Q_OBJECT
signals:
void newSignal(int myInt, QString myString);
void anotherSignal();
public slots:
void newSlot(int i, QString s);
void someSlot();
}
假设,有很多相似的控件的信号连接同一个槽,我们如何区分是哪个控件触发槽函数?
• 直接使用QObject * QObject::sender () const
• 使用工具类QSignalMapper
QSignalMapper *signalMapper = new QSignalMapper(this);
QPushButton *okButton = new QPushButton(“Ok”, this);
QPushButton *cancelButton = new QPushButton(“Cancel”, this);
QPushButton *resetButton = new QPushButton(“Reset”, this);
connect(okButton, SIGNAL(clicked()), signalMapper, SLOT(map()));
connect(cancelButton, SIGNAL(clicked()), signalMapper, SLOT(map()));
connect(resetButton, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(okButton, “Ok”);
signalMapper->setMapping(cancelButton, “Cancel”);
signalMapper->setMapping(resetButton, “Reset”);
connect(signalMapper, SIGNAL(mapped(const QString &)), this,
SLOT(buttonClicked(const QString &)));
此枚举描述信号和槽之间可使用的连接类型。具体来说,它决定了一个特定的信号是立即传递到槽位,还是排队等待稍后的传递。
该类型为信号槽的默认连接类型。
当信号和槽函数在同一线程内,则使用Qt::DirectConnection。否则,将使用Qt::QueuedConnection。在发出信号时确定连接类型。
信号和槽函数在同一线程内,当信号发出时,立即调用槽函数。
信号和槽函数在不同线程,当控制返回到接收线程的事件循环时,将调用槽函数。槽在接收器的线程中执行。
与Qt::QueuedConnection相同,不同之处在于发出信号的线程阻塞,直到槽位返回。如果信号和槽函数在同一线程内,则不能使用此连接,否则应用程序将死锁。
这是一个可以使用位或与上面任何一种连接类型组合的标志。当Qt::UniqueConnection被设置时,如果连接已经存在,QObject::connect()将会失败。