【问题标题】:Vector of Signals/Slots in Qt?Qt中的信号/槽向量?
【发布时间】:2021-05-25 15:13:29
【问题描述】:

首先了解一下我为什么要这样做的背景:我有一个类,比如 MasterClass,它是 QWidget 的子类,它创建了多个新的QObject 派生类对象,比如 SlaveClass,这在运行时动态发生。使用moveToThread 将新的SlaveClass 移动到新的QThread,因为它必须进行长时间运行的计算,并且我不希望我的GUI 冻结。由于 SlaveClass 在不同的线程中,我调用 SlaveClass 中的任何方法的正确方法是使用 Qt 的 Signal/Slot 机制以及Qt::QueuedConnection。 SlaveClass 有许多 signalsslots 我想连接到我的 MasterClass。我要做的是在我的 MasterClass 中创建 signalsslotsQVector,以便我可以动态添加和删除它以及 connect 和 @ 987654328@ 这些到我的 SlaveClass 取决于 SlaveClass 是被创建还是销毁。

为了验证我的理论,我创建了一个 MasterClass 并尝试在其中创建 signalsslots 的向量并尝试连接 signalsslots 看看它是否有效:

第一次尝试[不起作用]:

大师班.h

#include <QWidget>
#include <QVector>
#include <QDebug>
#include <functional>

Class MasterClass : public QWidget
{
    Q_OBJECT

public:
    MasterClass(QWidget *parent = nullptr);
    ~MasterClass();

signals:
    void sig_test(int testID);
    QVector<std::function<void(int)>> vSig;

private slots:
    void slt_test(int testID);
    QVector<std::function<void(int)>> vSlt;
};

masterclass.cpp

#include "masterclass.h"

MasterClass::MasterClass(QWidget *parent)
    : QWidget(parent)
{
    vSig << [this](int id) -> void{return this->sig_test(id); };
    vSig << [this](int id) -> void{return this->sig_test(id); };
    vSig << [this](int id) -> void{return this->sig_test(id); };

    vSlt << [this](int id) -> void{return this->slt_test(id); };
    vSlt << [this](int id) -> void{return this->slt_test(id); };
    vSlt << [this](int id) -> void{return this->slt_test(id); };

    connect(this, &MasterClass::vSig[0], this, &MasterClass::vSlt[0]);
    connect(this, &MasterClass::vSig[1], this, &MasterClass::vSlt[1]);
    connect(this, &MasterClass::vSig[2], this, &MasterClass::vSlt[2]);

    emit vSig[0](78);
    emit vSig[1](68);
    emit vSig[2](58);
}

void MasterClass::slt_test(int testID)
{
    qDebug() << "\n" << testID << "\n";
}

这给出了 2 个错误:

  1. E0304:没有重载函数“MasterClass::connect”的实例与参数列表匹配
  2. MSB4181:“QtRunWork”任务返回 false,但未记录错误。

错误编号。上面的2是因为我在signals:下声明QVector&lt;std::function&lt;void(int)&gt;&gt; vSig;,在private slots:下声明QVector&lt;std::function&lt;void(int)&gt;&gt; vSlt;。只要我将上述两个向量移动到private: 下,此错误就会消失。

第二次尝试[有效,但只是一种]:

大师班.h

.
.
.

signals:
    void sig_test(int testID);

private slots:
    void slt_test(int testID);

private:
    QVector<std::function<void(int)>> vSig;
    QVector<std::function<void(int)>> vSlt;
};

masterclass.cpp

.
.
.
    vSig << [this](int id) -> void{return this->sig_test(id); };
    vSig << [this](int id) -> void{return this->sig_test(id + 1); }; // Notice change in argument
    vSig << [this](int id) -> void{return this->sig_test(id + 2); }; // Notice change in argument

    vSlt << [this](int id) -> void{return this->slt_test(id); };
    vSlt << [this](int id) -> void{return this->slt_test(id + 10); }; // Notice change in argument
    vSlt << [this](int id) -> void{return this->slt_test(id + 20); }; // Notice change in argument

    connect(this, &MasterClass::sig_test, this, &MasterClass::slt_test);

    emit vSig[0](78);
    emit vSig[1](68);
    emit vSig[2](58);
}

void MasterClass::slt_test(int testID)
{
    qDebug() << "\n" << testID << "\n";
}

这工作并打印: 78 69 60

这里有两点需要注意:

  1. 如果我删除 connect(this, &amp;MasterClass::sig_test, this, &amp;MasterClass::slt_test); 行,它不会打印任何内容。
  2. 注意创建vSigvSlt 向量时参数的变化。从输出中,我可以看到正确的 vSig 正在被调用,因为它将第二个 vSig 函数的参数加 1,并将第三个 vSig 函数的参数加 2。但是,它不会将 10 添加到第二个 vSlt 函数的参数或 20 到第三个 vSlt 函数的参数,这让我想知道它是在调用正确的 vSlt 函数还是只是在调用slt_test() 函数?

有什么办法可以改善吗?

当动态创建SlaveClass 对象时,我必须做这样的事情来连接到它的插槽connect(MasterClassPointer, &amp;MasterClass::vSig[id], SlaveClassPointer, &amp;SlaveClass::slotName);。有可能吗?

附:我知道我可以使用QMetaObject::invokeMethod() 函数,但我想尽可能避免使用它。

【问题讨论】:

  • 我认为它显然没有调用您的 vSlt 插槽。为什么会呢?您永远不会将任何东西连接到这些插槽。
  • 谢谢,我明白了,我也这么认为,但是为什么要将vSig 信号连接到slt_test 插槽?我也从未建立过这种联系。只有sig_test 连接到slt_test
  • vSig 只是一个函数向量。这些函数只需调用sig_test。由于sig_test 连接到slt_test,调用vSig 到达slt_test
  • @JarMan 谢谢,有道理:)

标签: c++ multithreading qt signals-slots


【解决方案1】:

你可以有一个槽向量(一个 lambda 函数可以是一个槽),但你不能真的有一个信号向量。但是,您可以将信号连接到根据 ID 分派到正确插槽的东西:

// MasterClass.h
class MasterClass : public QObject {
// ... class stuff ...

signals:
  void masterSignal(int id);

private:
std::unordered_map<int, SlaveClass*> m_slaveMap;

}

// MasterClass.cpp ...
connect(this, &MasterClass::masterSignal, slavePtr,
  [](int id) { m_slaveMap[id]->slot(); });

另一种选择是在从属槽上过滤掉具有错误 ID 的调用:

int slaveID = /*something*/
connect(masterPtr, &MasterClass::masterSignal, slavePtr,
  [myID = slaveID, slavePtr] (int id) { if (myID == id) slavePtr->slot(); });

【讨论】:

  • 谢谢!过滤不是一个好主意,因为它会创建发送到插槽的数据副本,因为插槽位于不同的线程中。我将尝试使用 slavemap 的第一种方法。
猜你喜欢
  • 2017-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-11
  • 2021-02-12
相关资源
最近更新 更多