如何使用QT5中的lambdas将具有int参数的信号连接到具有枚举参数with的插槽



我正试图将信号currentIndexChanged(int)QComboBox连接到我的类中的一个插槽,该插槽接收枚举,如Foo::mySlot(EnumFoo)

我知道所有这些方法都不起作用:

  1. connect(cb, &QComboBox::currentIndexChanged, foo, &Foo::mySlot);
  2. connect(cb, static_cast<void (QComboBox::*)(EnumFoo)>(&QComboBox::currentIndexChanged), foo, &Foo::mySlot);
  3. connect(cb, &QComboBox::currentIndexChanged, foo, static_cast<void (Foo::*)(int)>(&Foo::mySlot));

因为在C/C++中,int从不隐式转换为枚举类型。相反,我认为如果我的信号有一个enum参数,我可以毫无问题地将它连接到带有int参数的插槽。

我知道如何使用lambda函数来解决这个问题:

connect(cb, &QComboBox::currentIndexChanged, [=](int i){ foo.mySlot(static_cast<EnumFoo>(i)); });

如果没有lambda函数,有什么方法可以解决这个问题吗?

编辑:使用我提出的解决方案,我以这种方式连接Foo::mySlot。

LambdaWrapper *lw = new LambdaWrapper;
lw->connect(cb, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [=](int i){ foo.mySlot(static_cast<EnumFoo>(i)); }, foo);

我再也不用担心断开连接的事情了。只需要管理lw的使用寿命。

使用一个中间slot,它接受一个int并直接调用另一个slot函数。

class Foo : public QObject
{
    Q_OBJECT:
public slots:
    void IndexChanged(int intParam);    
    void mySlot(EnumFoo fooType);
};    
void Foo::IndexChanged(int index);
{
    EnumFoo fooType = <static_cast<EnumFoo>(index);
    mySlot(fooType);
}
connect(cb, &QComboBox::currentIndexChanged, foo, &Foo::IndexChanged);

我创建了一个小类来自动化这种断开连接的处理。

lambdawrapper.h

class LambdaWrapper : public QObject {
    Q_OBJECT
public:
    explicit LambdaWrapper(QObject* parent = 0);
    virtual ~LambdaWrapper();
    template<typename Func1, typename Func2>
    void connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender,
                 Func1 signal,
                 Func2 slot,
                 QObject* receiver = 0) {
        connections << QObject::connect(sender, signal, slot);
        helperConnection(receiver);
    }
private slots:
    void disconnectReceiver(QObject* obj);
private:
    void helperConnection(QObject* obj);
    QList<QMetaObject::Connection> connections;
    QObjectList receivers;
};

lambdawrapper.cpp

LambdaWrapper::LambdaWrapper(QObject *parent) : QObject(parent) { }
LambdaWrapper::~LambdaWrapper() {
   for (const QMetaObject::Connection& c : connections)
       QObject::disconnect(c);
}
void LambdaWrapper::disconnectReceiver(QObject *obj) {
    if (receivers.contains(obj)) {
        QList<const QMetaObject::Connection*> toRemove;
        const int n = receivers.size();
        for (int i=0; i<n ; ++i) {
            if (receivers.at(i) == obj) {
                disconnect(connections.at(i));
                toRemove << & connections.at(i);
            }
        }
        receivers.removeAll(obj);
        for (const QMetaObject::Connection* c : toRemove)
            connections.removeAll(*c);
    }
}
void LambdaWrapper::helperConnection(QObject* receiver) {
    receivers << receiver;
    if (receiver) QObject::connect(receiver, &QObject::destroyed, this, &LambdaWrapper::disconnectReceiver);
}

因此,您所要做的就是将这个类实例化到您的主QWidgetQMainWindowQDialog派生类中。

foo.h
....
class Foo : public QMainWindow
....
private:
    LambdaWrapper* lw;
....
foo.cpp
.... 
lw = new LambdaWrapper(this);
QCheckBox *ck = new QCheckBox("check");
lw->connect(ck, &QAbstraceButton::toggled, [=](bool b){ /* do stuff */}, 0);
....

connect成员的第四个参数是可选的,表示在lambda内部调用的某个QObject指针(如果在已删除此对象的情况下调用lambda,则可能会导致程序崩溃)。如果您传递此参数,它被破坏的信号将连接到LambdaWrapper中的一个插槽,该插槽确保在该接收器被破坏时,与该接收器的所有连接都将断开。

它很脏,但解决了我的问题。现在我可以直接连接到lambdas,而不必担心断开它们的连接。

最新更新