【C2】对话框【S2】Signals and Slots in Depth

    信号和槽的机制是Qt编程的基本方法。它使程序员能把两个对象绑定在一起,而对象之间并不用知道彼此的存在。我们已经把一些信号和槽链接在一起了,并声明了我们自己的信号和槽,实现了我们自己的槽,发送了我们自己的信号。让我们花些时间更深的研究这个机制。

    槽跟传统的C++成员函数基本一致。槽可以是虚函数;可以被重载;可以是公有的,保护的,或者私有的,可以向其它C++成员函数一样直接被调用;它们的形参可以是任何类型。不同之处在于一个槽可以到一个信号,当信号被发送时槽就自动被调用。

    connect()语句像下面这样:

Code:
  1. connect(sender, SIGNAL(signal), receiver, SLOT(slot));   

    发送者(sender)和接收者(receiver)都是指向QObject对象的指针。信号和槽是没有形参名的函数签名。SIGNAL()和SLOT宏其实把它们的实参转化为一个字符串。

    在我们看过的例子中,我们总是把不同的信号和不同的槽连接在一起。但还有如下的各种不同的形式的连接。

    一个信号可以被连接到许多不同的槽:

Code:
  1. connect(slider, SIGNAL(valueChanged(int)),   
  2.         spinBox, SLOT(setValue(int)));   
  3. connect(slider, SIGNAL(valueChanged(int)),   
  4.         this, SLOT(updateStatusBarIndicator(int)));   

    当这个信号被发送时,槽就以不确定的顺序相继被调用。

    多个信号可以被连接到同一个槽:

Code:
  1. connect(lcd, SIGNAL(overflow()),   
  2.         this, SLOT(handleMathError()));   
  3. connect(calculator, SIGNAL(divisionByZero()),   
  4.         this, SLOT(handleMathError()));   

    当任一个信号被发送时,槽就被调用。

    一个信号可以与另一个信号连接:

Code:
  1. connect(lineEdit, SIGNAL(textChanged(const QString &)),   
  2.         this, SIGNAL(updateRecord(const QString &)));   

    当第一个信号被发送时,第二个信号也被发送。此外,信号之间的连接(signalsignal connections)与信号和槽的连接(signalslot connections)是难以区分的。

    连接可以被去除

Code:
  1. disconnect(lcd, SIGNAL(overflow()),   
  2.            this, SLOT(handleMathError()));   

    这很少用到,因为当一个对象被delete时Qt会把此对象相关的所有的连接都去掉。

    为了成功的把一个信号和一个槽连接起来(或者两个信号之间的连接),它们必须都有相同的形参表,即同样的形参类型和顺序:

Code:
  1. connect(ftp, SIGNAL(rawCommandReply(intconst QString &)),   
  2.         this, SLOT(processReply(intconst QString &)));   

    有一个例外是如果一个信号的形参个数比它连接的槽的形参个数多,则多余的形参被简单的省略:

Code:
  1. connect(ftp, SIGNAL(rawCommandReply(intconst QString &)),   
  2.          this, SLOT(checkErrorCode(int)));   

    如果形参类型不匹配,或者如果这个信号和槽不存在,如果程序在debug模式下建立的Qt就会发出一个运行时警告。同样的,如果形参名出现在信号或槽的签名中Qt也将发出警告。

    到目前为止,我们仅仅对widgets使用了信号和槽。但是这种机制不仅局限在GUI编程,也在QObject有所实现。这种机制可以被任何QObject的子类使用:

Code:
  1. class Employee : public QObject   
  2. {   
  3.     Q_OBJECT   
  4. public:   
  5.     Employee() { mySalary = 0; }   
  6.     int salary() const { return mySalary; }   
  7. public slots:   
  8.     void setSalary(int newSalary);   
  9. signals:   
  10.     void salaryChanged(int newSalary);   
  11. private:   
  12.     int mySalary;   
  13. };   
  14. void Employee::setSalary(int newSalary)   
  15. {   
  16.     if (newSalary != mySalary) {   
  17.         mySalary = newSalary;   
  18.         emit salaryChanged(mySalary);   
  19.     }   
  20. }   

    注意到setSalary()是如何实现的。我们仅仅在newSalary != mySalary时发出了salary-Changed()信号。这确保了这个循环的连接不会导致无限循环。

    Qt的原元对象系统(Meta-Object System)

    Qt的一个主要成就是实现了创建独立的软件组件的机制,这种机制在组件之间互相不知道彼此存在的情况下绑定组件,这大大扩展了基本的C++。

    这种机制成为元对象系统,它提供了两个关键的服务:信号和槽,以及反射。信号和槽的实现离不开反射的功能,并能使程序员在运行时得到继承自QObject的类的元信息,包括了这个类支持的信号和槽的列表和类本身的名字。这种机制也支持Qt Designer的属性(properties)和国际化(for internationalization)的文本翻译。而且还为Qt Script的应用(QSA)奠定了基础。

    标准C++并不提供Qt元对象系统需要的动态元信息的支持。Qt提供了一个分离的工具解决了这个问题,就是moc,它能解析在类定义中的Q_OBJECT宏并通过C++函数的形式使这些信息可用。因为moc用纯C++实现了所有的的功能,所以Qt的元对象系统能使用任何C++编译器工作。

    这种机制如下面的描述工作:

    Q_OBJECT宏声明了一些每个QObject的子类必须实现的反射函数:metaObject(),TR(),qt_metacall(),等等。

    Qt的moc工具实现了在Q_OBJECT声明的一些函数和所有的信号。

    QObject类的成员函数诸如connect()和disconnect()使用反射函数来完成它们的工作。

    上述这些都自动被qmake,moc,QObject所掌控,所以你很少会思考这方面的问题。但是如果你很好奇,你可以查看QMetaObject类的文档,且也可以通过查看moc生成的C++源码来看看这些实现是如何工作的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值