您的位置:首页 > 其它

深入理解信号槽

2017-06-07 14:47 134 查看
将 Qt 的信号槽系统与 Boost.Signals 结合使用

将 Qt 的信号槽系统与 Boost.Signals 结合使用,最大的障碍是,Qt 使用预处理器定义了关键字 signals,slots 以及 emit。这些可以看做是 Qt 对 C++ 语言的扩展。同时,Qt 也提供了另外一种方式,即使用宏来实现这些关键字。为了屏蔽掉这些扩展的关键字,Qt 4.1 的 pro 文件引入了 no_keywords 选项,以便使用标准 C++ 的方式,方便 Qt 与其他 C++ 同时使用。你可以通过打开 no_keywords 选项,来屏蔽掉这些关键字。下面是一个简单的实现:
# TestSignals.pro (platform independent project file, input to qmake)
#   showing how to mix Qt Signals and Slots with Boost.Signals
#
# Things you'll have in your .pro when you try this...
#
CONFIG += no_keywords
# so Qt won't #define any non-all-caps `keywords'
INCLUDEPATH += . /usr/local/include/boost-1_33_1/
# so we can #include <boost/someheader.hpp>
macx:LIBS   += /usr/local/lib/libboost_signals-1_33_1.a
# ...and we need to link with the Boost.Signals library.
# This is where it lives on my Mac,
#   other platforms would have to add a line here
#
# Things specific to my demo
#
CONFIG -= app_bundle
# so I'll build a command-line tool instead of a Mac OS X app bundle
HEADERS += Sender.h Receiver.h
SOURCES += Receiver.cpp main.cpp


请注意,我们已经在 pro 文件中打开了 no_keywords 选项,那么,类似 signals 这样的关键字已经不起作用了。所以,我们必须将这些关键字修改成相应的宏的版本。例如,我们需要将 signals 改为 Q_SIGNALS,将 slots 改为 Q_SLOTS 等等。请看下面的代码:
// Sender.h
#include <QObject>
#include <string>
#include <boost/signal.hpp>
class Sender : public QObject
{
Q_OBJECT
Q_SIGNALS:  // a Qt signal
void qtSignal( const std::string& );
// connect with
//  QObject::connect(sender, SIGNAL(qtSignal(const std::string&)), ...
public:     // a Boost signal for the same signature
boost::signal< void ( const std::string& ) >  boostSignal;
// connect with
//  sender->boostSignal.connect(...
public:     // an interface to make Sender emit its signals
void sendBoostSignal( const std::string& message ) {
boostSignal(message);
}

void sendQtSignal( const std::
f429
string& message ) {
qtSignal(message);
}
};


接收者:
// Receiver.h
#include <QObject>
#include <string>

class Receiver : public QObject
{
Q_OBJECT
public Q_SLOTS:
void qtSlot( const std::string& message );
// a Qt slot is a specially marked member function
// a Boost slot is any callable signature
};

// Receiver.cpp
#include "Receiver.h"
#include <iostream>

void Receiver::qtSlot( const std::string& message )
{
std::cout << message << std::endl;
}


测试:
// main.cpp
#include <boost/bind.hpp>
#include "Sender.h"
#include "Receiver.h"

int main( int /*argc*/, char* /*argv*/[] )
{
Sender* sender = new Sender;
Receiver* receiver = new Receiver;

// connect the boost style signal
sender->boostSignal.connect(boost::bind(&Receiver::qtSlot, receiver, _1));
// connect the qt style signal
QObject::connect(sender, SIGNAL(qtSignal(const std::string&)),
receiver, SLOT(qtSlot(const std::string&)));
sender->sendBoostSignal("Boost says 'Hello, World!'");
sender->sendQtSignal("Qt says 'Hello, World!'");
return 0;
}


这段代码将会有类似下面的输出:
[506]TestSignals$ ./TestSignals
Boost says 'Hello, World!'
Qt says 'Hello, World!'


我们可以看到,这两种实现的不同之处在于,Boost.Signals 的信号,boostSignal,是 public 的,任何对象都可以直接发出这个信号。也就是说,我们可以使用如下的代码:
sender->boostSignal("Boost says 'Hello, World!', directly");


从而绕过我们设置的 sendBoostSignal() 这个触发函数。另外,我们可以看到,boostSignal 完全可以是一个全局对象,这样,任何对象都可以使用这个信号。而对于 Qt 来说,signal 必须是一个成员变量,在这里,只有 Sender 可以使用我们定义的信号。

这个例子虽然简单,然而已经很清楚地为我们展示了,如何通过 Qt 发出信号来获取 Boost 的行为。在这里,我们使用一个公共的 sendQtSignal() 函数发出 Qt 的信号。然而, 为了从 Boost 的信号获取 Qt 的行为,我们需要多做一些工作:隐藏信号,但是需要提供获取连接的函数。这样看上去有些麻烦:
class Sender : public QObject
{
// just the changes...
private:
// our new public connect function will be much easier to understand
//  if we simplify some of the types
typedef boost::signal< void ( const std::string& ) > signal_type;
typedef signal_type::slot_type                       slot_type;
signal_type  boostSignal;
// our signal object is now hidden
public:
boost::signals::connection
connectBoostSignal( const slot_type& slot,
boost::signals::connect_position pos = boost::signals::at_back ) {
return boostSignal.connect(slot, pos);
}
};


应该说,这样的实现相当丑陋。实际上,我们将 Boost 的信号与连接分割开了。我们希望能够有如下的实现:
// WARNING: no such thing as a connect_proxy
class Sender
{
public:
connect_proxy< boost::signal< void ( const std::string& ) > >
someSignal() {
return someSignal_;
// ...automatically wrapped in the proxy
}
private:
boost::signal< void ( const std::string& ) > someSignal_;
};
sender->someSignal().connect(someSlot);


注意,这只是我的希望,并没有做出实现。如果你有兴趣,不妨尝试一下。

总结

信号和槽的机制实际上是观察者模式的一种变形。它是面向组件编程的一种很强大的工具。现在,信号槽机制已经成为计算机科学的一种术语,也有很多种不同的实现。

Qt 信号槽是 Qt 整个架构的基础之一,因此它同 Qt 提供的组件、线程、反射机制、脚本、元对象机制以及可视化 IDE 等等紧密地集成在一起。Qt 的信号是对象的成员函数,所以,只有拥有信号的对象才能发出信号。Qt 的组件和连接可以由非代码形式的资源文件给出,并且能够在运行时动态建立这种连接。Qt 的信号槽实现建立在 Qt 元对象机制之上。Qt 元对象机制由 Qt 提供的 moc 工具实现。moc 也就是元对象编译器,它能够将用户指定的具有 Q_OBJECT 宏的类进行一定程度的预处理,给这个增加元对象能力。

Boost.Signals 是具有静态的类型安全检查的,基于模板的信号槽系统的实现。所有的信号都是模板类 boost::signal 的一个特化;所有的槽函数都具有相匹配的可调用的签名。Boost.Signals 是独立的,不需要内省、元对象系统,或者其他外部工具的支持。然而,Boost.Signals 没有从资源文件动态建立连接的能力。

任何基于 Qt GUI 的系统都会自然而然的使用信号槽。你可以从中获取很大的好处。任何大型的系统,如果希望能够降低组件之间的耦合程度,都应该借鉴这种思想。在正确的地方使用信号槽,可以让你的系统更易于理解、更灵活、高度可重用,并且你的工作也会完成得更快。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: