Chinaunix首页 | 论坛 | 博客
  • 博客访问: 272704
  • 博文数量: 62
  • 博客积分: 1912
  • 博客等级: 上尉
  • 技术积分: 747
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-13 12:56
文章分类
文章存档

2014年(2)

2013年(1)

2012年(5)

2011年(27)

2010年(27)

分类: 嵌入式

2011-03-18 14:48:06

最近由于项目的需求,一直在研究Qt。信号与槽机制是Qt的一大特色,该机制允许两者间传递参数,依次来实现对象间的通信。这个参数会分别存在于信号的参数列表和槽函数的参数列表中。需要注意的是,若将槽函数绑定至信号,槽函数的参数列表元素数目只能少于等于信号的参数列表元素数目。而且顺序和类型不能改变。至于缺少的参数应从信号参数尾部开始缺少。
突然今天想起来一个问题,如果一个对象发出信号,将内部的一个成员变量(非简单类型)作为参数向外发送,槽函数就可以接收到这个对象,那么槽函数是否可以完全操作这个对象呢?如果可以完全操作,那么这个信号与槽机制就是一个双向的通讯过程,即A可以触发B,B又能对A进行操作;如果操作无效,则说明该机制是一个单向的通讯过程,即A触发B,B进行处理,但不能反向写数据。
闲言少叙,让实例说话
先建立一个console application
建立头文件CustomClasses.h
代码如下:
view plaincopy to clipboardprint?
#ifndef CUSTOMCLASSES_H  
#define CUSTOMCLASSES_H  
 
#include   
 
class TPerson  
{  
private:  
    QString _Name;  
public:  
    TPerson();  
    void SetName(QString Name);  
    QString GetName();  
};  
 
class TSender:public QObject  
{  
    Q_OBJECT  
private:  
    TPerson _p;  
public:  
    TSender();  
    void click();  
    void disp();  
signals:  
    void NewName(TPerson P);  
};  
 
class TReceiver:public QObject  
{  
    Q_OBJECT  
public:  
    TReceiver();  
public slots:  
    void GetNewName(TPerson P);  
};  
#endif // CUSTOMCLASSES_H 
#ifndef CUSTOMCLASSES_H
#define CUSTOMCLASSES_H
#include
class TPerson
{
private:
    QString _Name;
public:
    TPerson();
    void SetName(QString Name);
    QString GetName();
};
class TSender:public QObject
{
    Q_OBJECT
private:
    TPerson _p;
public:
    TSender();
    void click();
    void disp();
signals:
    void NewName(TPerson P);
};
class TReceiver:public QObject
{
    Q_OBJECT
public:
    TReceiver();
public slots:
    void GetNewName(TPerson P);
};
#endif // CUSTOMCLASSES_H
这里定义了三个类TPerson、TSender和TReceiver。
TPerson是一个很简单的类,包含了一个私有成员_Name,分别具有Get和Set方法。
TSender类中包含一个私有的TPerson实例_p,click()方法模拟按钮单击,当执行click()方法后会发出NewName(TPerson P)信号。
TReceiver类中包含一个GetNewName(TPerson P)槽,用于接收来自TSender的NewName(TPerson P)信号。
建立对应的CustomClasses.cpp
代码如下:
 view plaincopy to clipboardprint?
#include "CustomClasses.h"  
#include   
#include   
TPerson::TPerson()  
{  
    //  
}  
void TPerson::SetName(QString Name)  
{  
    this->_Name=Name;  
}  
 
QString TPerson::GetName()  
{  
    return this->_Name;  
}  
 
TSender::TSender()  
{  
    this->_p.SetName("Jack");  
}  
 
void TSender::click()  
{  
    qDebug()<<"now begin to send person";  
    emit this->NewName(this->_p);  
    qDebug()<<"Person has sended";  
}  
 
void TSender::disp()  
{  
    qDebug()<<"current person name is:"<_p.GetName();  
}  
 
TReceiver::TReceiver()  
{  
    //  
}  
 
void TReceiver::GetNewName(TPerson P)  
{  
    qDebug()<<"receive a person name is:"<    qDebug()<<"begin to change name";  
    P.SetName("Rose");  
    qDebug()<<"finish to change name";  

#include "CustomClasses.h"
#include
#include
TPerson::TPerson()
{
    //
}
void TPerson::SetName(QString Name)
{
    this->_Name=Name;
}
QString TPerson::GetName()
{
    return this->_Name;
}
TSender::TSender()
{
    this->_p.SetName("Jack");
}
void TSender::click()
{
    qDebug()<<"now begin to send person";
    emit this->NewName(this->_p);
    qDebug()<<"Person has sended";
}
void TSender::disp()
{
    qDebug()<<"current person name is:"<_p.GetName();
}
TReceiver::TReceiver()
{
    //
}
void TReceiver::GetNewName(TPerson P)
{
    qDebug()<<"receive a person name is:"<    qDebug()<<"begin to change name";
    P.SetName("Rose");
    qDebug()<<"finish to change name";
}

 

该文件是上述内容的实现,但值得注意的是,在void TReceiver::GetNewName(TPerson P)的实现中,对P参数进行了操作,调用了SetName方法。

 
 
主文件main.cpp
代码如下:

view plaincopy to clipboardprint?
#include   
#include   
#include   
#include "CustomClasses.h"  
 
int main(int argc, char *argv[])  
{  
    QCoreApplication a(argc, argv);  
    TSender send;  
    TReceiver recv;  
    QObject::connect(&send, SIGNAL(NewName(TPerson)), &recv, SLOT(GetNewName(TPerson)));  
    send.disp();  
    send.click();  
    send.disp();  
    return a.exec();  

#include
#include
#include
#include "CustomClasses.h"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    TSender send;
    TReceiver recv;
    QObject::connect(&send, SIGNAL(NewName(TPerson)), &recv, SLOT(GetNewName(TPerson)));
    send.disp();
    send.click();
    send.disp();
    return a.exec();
}
该代码实例化TSender类和TReceiver类,并绑定NewName信号与GetNewName槽。代码很简单,其他的就不再多做介绍了。
下面看运行结果:
 
我们来分析一下这个程序。在声明了send实例和recv实例后,绑定了相关事件。首先查看了一下当前send对象中的TPerson类实例 _p的Name属性。第一行显示的是初始的Jack;然后调用了send的click()方法,该方法放出了NewName信号,此时该信号被recv的GetNewName槽函数接收,该函数显示了当前接收到对象的Name值。然后调用了接收对象的SetName方法,将Name设置为“Rose”。槽函数执行完毕后,代码跳转到触发NewName信号的位置,即click()方法的“emit this->NewName(this->_p); ”后面;最后再显示一下send实例内部的_p实例Name属性。此时观察到虽然在槽函数中重新设置了Name属性,但并没有改变信号发起方实例的属性值(依旧是Jack)。因此我们可以说,这种传递对象的信号与槽机制是单向通信的。传递方向是信号发出方到信号接收方。当然这个例子中只是传递的一个对象,本人试验了一下,如果将上述代码进行小幅修改,将传递参数变为对象指针,则最终会影响到信号发送发的数据。如下图所示:

也就是说,如果传递的是指针,则该机制是双向通讯的,关键还是看如何去使用它们。我个人不推荐使用指针传递。因为一个信号可以绑定多个槽函数,若其中有一个对数据进行了修改,则会影响后续的槽函数执行,此时的参数状态未知,也不易于维护。当然了,什么事也不是绝对的,特殊情况下除外。
另外补充两点:
第一:上文中提到的“槽函数”这个词并不贴切。因为该方法是不能包含返回值的。必须是void,因此称之为“槽过程”比较贴切,但是已经广泛认同了这个叫法了,所以就没有深究。
第二:开始时为了图省事,将TPerson、TSender和TReceiver三个类的定义和实现都放到了main.cpp中。结果无论如何也编译不过去。提示“undefined reference vTable for class ...”, ...是这三个类的类名。后来查到资料得知在Qt程序编译时,遇到signal和slots定义的地方会调用moc工具对其进行转换,转换为标准c++代码(signal和slots不是标准C++的关键字),而moc工具是只识别.h文件的,因此还是老老实实用标准的C++类定义方法,别偷懒了吧:)
 
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/chaijunkun/archive/2011/03/14/6249573.aspx
阅读(2457) | 评论(1) | 转发(0) |
0

上一篇:QT是什么

下一篇:Python建立简单web服务器

给主人留下些什么吧!~~

fengfei_xue2011-06-16 09:31:29

好像和双向通信单向通信没关系