深入信号与插槽

 

深入信号与插槽

Signals and Slots in Depth—《Prentice Hall PTR. C++ GUI Programming with Qt 4.Jun.2006》选自Chapter 2

 

 

信号与插槽是QT编程的基础。它使得程序编写者不需要了解对象之间的任何关系来帮定对象。我们已经把一些信号和插槽连接在一起了,并声明了我们自己的信号与插槽,实现了我们自己的插槽,发射了我们自己的信号。让我们花点时间更进一步地了解这种机制。

 

插槽几乎和普通C++成员函数一样。可以是虚函数;可以被重载;可以是公共的、保护或者是私有的,可以像C++成员函数一样直接访问;并且参数可以是任何类型。不同的是插槽可以和一个信号连接,在这种情况下每次当信号发送时它就被自动地调用。

 

connect()语句的声明像下面:

connect(sender, SIGNAL(signal), receiver, SLOT(slot));

 

上面的senderreceiver是指向QObjects类型的指针,signalslot是不带参数名的函数名。SIGNAL()SLOT()宏本质上就是把它们的参数转换成一个字符串。

 

在下面的例子中我们要扯得远一点,我们总习惯于把不同的信号连接到不同的插槽,下面是需要考虑的其他一些情况。

 

1. 一个信号可以连接到多个插槽:

connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int)));

connect(slider, SIGNAL(valueChanged(int)), this, SLOT(updateStatusBarIndicator(int)));

当信号发射时,插槽一个接一个地被调用,调用次序不确定。

 

2. 多个信号连接到同一个插槽:

connect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError()));

connect(calculator, SIGNAL(divisionByZero()), this, SLOT(handleMathError()));

无论哪个信号发射,插槽都被调用。

 

3. 一个信号可以连接到另一个信号:

connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SIGNAL(updateRecord(const QString &)));

当前面一个信号发射时,后面这个信号也发射。除了这点之外,信号信号的连接和信号插槽的连接不能分别。

 

4. 连接可以被解除:

disconnect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError()));

这较少用到,因为当一个对象被删除时,QT会自动移除这个对象所有关联的连接。

 

为了成功地连接一个信号到插槽(或另一个信号),必须有相同的参数类型,正确的参数顺序:

connect(ftp, SIGNAL(rawCommandReply(int, const QString &)), this, SLOT(processReply(int, const QString &)));

 

特别地,如果一个信号的参数多于所连接插槽的参数个数,多余的参数将被直接忽略:

connect(ftp, SIGNAL(rawCommandReply(int, const QString &)), this, SLOT(checkErrorCode(int)));

 

如果参数类型不兼容,信号或者插槽不存在,如果程序在Debug模式下,QT会在运行时发出一个警告。类似地,如果参数名包含在信号或插槽签名中,QT也会给出一个警告。

 

到目前为止,我们仅在widgets中用了信号和插槽。但这种机制本身在QObject中实现,并不局限于GUI编程。这种机制可以被使用在任何QObject的子类当中:

 

class Employee : public QObject

{

    Q_OBJECT

public:

    Employee() { mySalary = 0; }

    int salary() const { return mySalary; }

public slots:

    void setSalary(int newSalary);

signals:

    void salaryChanged(int newSalary);

private:

    int mySalary;

};

void Employee::setSalary(int newSalary)

{

    if (newSalary != mySalary) {

        mySalary = newSalary;

        emit salaryChanged(mySalary);

    }

}

 

注意setSalary()插槽是如何被实现的。如果newSalary != mySalary,我们仅仅发射salaryChanged()信号,这保证了循环连接不会进入无限循环。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值