Signals & Slots(Qt5) (二)

2014-11-24 02:54:24 · 作者: · 浏览: 8
以上的signal发出; 你可以调用一个disconnect()打断这些关联; 使用Qt::UniqueConnect类型参数, 这样只有在不重复的情况下, 信号关联才会建立; 如果已经有了一个重复的关联(同样的object上的完全一样的signal和完全一样的slot), connect()会失败并且返回false;

>这个例子说明对象间不需要知道对方的任何信息, 照样可以互相通信; 为了实现通信机制我们只需要将对象互相关联, 调用简单的QObject::connect(), 或者使用uic的自动关联特性(名字关联 QMetaObject::connectSlotsByName(this));


构建Example


>C++预处理会改变或者移除signal-slot和emit关键字, 这样编译器可以按照标准C++来处理代码;


>对于包含signal-slot的类定义, 进行moc会产生一个C++源文件, 这个文件需要和其他的object文件一同编译和链接; 如果你使用qmake, makefile规则中自动调用moc的部分会加到你工程中的makefile;

Signals

>Signals会在对象的内部状态改变的时候被发出, 状态的改变可能被对象的client或owner所注意; Signals是public的函数, 可以在任何地方被发出, 但是我们推荐只在定义signal的类或者子类中发signal;


>当一个signal被发出, 相关联的slot一般会立即被执行, 就像普通函数的调用; 这个情况下, signal-slot机制完全独立于任GUI的事件循环event loop; 在emit代码段后面的代码会在所有的slots都返回了以后被执行; 当使用queued connections的时候情况稍有不同, queued情况下在emit关键字后面的代码还会继续立即执行, slots则会在稍后执行;

>如果多个slots关联到了一个signal上, 这些slots会一个接一个地被执行, 先后次序是按照它们被connected的次序来排列;

>Signals会被moc自动生成, 并且不能在cpp文件中实现. Signal不可以有返回值.(e.g. 使用void)

Note: 关于参数, 如果signal-slot不用特殊类型的参数, 他们可以更多地被重用; e.g. 如果QScrollBar::valueChanged()试图使用一个特别类型, 假设是QScrollBar::Ranger, 那么它只能被关联到特别为QScrollBar设计的slots上了, 想要关联到其他的input widget上基本不可能;

Slots

>Slot会在关联的signal被发送的时候被调用; Slot是普通的C++函数, 可以被正常调用; 它们的特别之处只是可以和signal关联起来;


>直接调用Slot时它就是普通的函数, 遵循一般的C++规则. 不过, 作为Slot, 忽略权限级别的话, 它可以被任何组件通过signal-slot关联来激发. 这表示随意一个类的实例发送了一个signal, 都可能激发其他不相关的类的实例的私有slot.

>Slot也可以定义成virtual的, 在实际使用中很有用;

>虽然在实际的应用程序中区别很小, 但是和callback相比, signal-slot会稍微慢一些, 因为它提供了更多的灵活性. 不考虑虚函数调用时, 普遍来说, 发出一个和一些slots相关联的signal, 大约比直接调用接收函数receivers慢10倍. 主要的消耗是在: 安全地遍历所有的connections, 锁定关联的对象(检查后续的receivers没有在发送的过程中被销毁), 然后按照通用的方式安置参数; 如果有10个非虚函数被调用, 听起来好像很多, 实际的损耗比任何new或delete操作要小很多. e.g. 当你在操作一个string, vector或list的时候, 如果需要new或delete, signal-slot的消耗只占了整个函数花费的效率很小的一个比例; 如果你调用系统函数或者间接调用10个函数, 情况是类似的; 简单和灵活是signal-slot机制值得那一点小小消耗的理由;

Note 如果有其他库定义的变量调用了signals和slots, 可能会在同时编译Qt-based应用的时候导致编译器的warnning和error. 解决的办法是 #undef掉这些offending预编译符号;

Meta-Object信息

>meta-object compiler(moc)会在一个C++文件中解析类的声明并且生成C++代码, 初始化meta-object. meta-object包含了所有signal和slot成员的名字, 还有指向这些函数的指针;

>meta-object还包含其他信息: 比如对象的类名. 你可以检查对象是否继承自某个特定的类; example:


[cpp]
if (widget->inherits("QAbstractButton")) {
QAbstractButton *button = static_cast(widget);
button->toggle();
}

if (widget->inherits("QAbstractButton")) {
QAbstractButton *button = static_cast(widget);
button->toggle();
}
>meta-object信息也可以被qobject_cast()使用, 和QObject::inherits()类似, 但是更加安全less error-prone;


[cpp]
if (QAbstractButton *button = qobject_cast(widget))
button->toggle();

if (QAbstractButton *button = qobject_cast(widget))
button->toggle();
实际例子

>LcdNumber继承自QObject, 通过QFrame和QWidget具备signal-slot机制; 某种程度上和内建的QLcdNumber类似;


>Q_OBJECT宏会被预编译展开来去声明多个被moc实现的成员函数; 如果你在编译时遇到"undefined reference to vtable for LcdNumber"的错误, 你可能忘了先要运行moc命令, 把moc命令输出的moc文件Include进来.


[cpp]
//略过moc不关心的一些析构函数和成员函数
class LcdNumber : public QFrame
{
Q_OBJECT
public:
LcdNumber(QWidget *parent = 0);
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(