Qt信号/槽发送完整结构体

6
我试图在两个线程之间通过信号/插槽发送一个结构体,我的信号/插槽已经正确连接,我已经能够发送包含我的数据部分的QString,但现在我需要发送整个结构体,结构体似乎是最明智的选择。然而,当我尝试发送时,信号没有被发送/接收。问题似乎仅限于发送/接收结构体,处理前后我已经尝试了很多方法。
由于我的数据生成得太快并且内存被覆盖或释放(我已经尝试过指针,并且认为引用会受到类似的影响),因此我无法使用诸如这里这里的指针。
我已经将Q_DECLARE_METATYPE添加到我的结构体中。我的结构体目前只是一个小测试(稍后将扩大),并位于其自己的头文件中。
#ifndef RETURNSTRUCT_H
#define RETURNSTRUCT_H

struct Datastruct
{
    int markeridone;
};

Q_DECLARE_METATYPE(Datastruct);

#endif //RETURNSTRUCT_H

我的程序为什么无法发送/接收结构体?非常感谢任何帮助。

我正在使用Windows 7,MinGW 32位,Qt 5.7.0,Qt Creator 4.0.3

4个回答

12

您的调试日志应该警告您 - 您只能发送qt元系统已知的类型。使用Q_REGISTER_METATYPE,您最终会注册与定义所在命名空间相关联的类型。

幸运的是,您可以像这样告诉Qt您的结构:

// after QApplication was instantiated
qRegisterMetaType<Datastruct>("Datastruct");
// but before any class is instantiated that connects signals with this type

它不会尝试通过查看代码来推断命名空间。请确保重新运行qmake(或最好进行清理),否则在使用QtCreator构建时可能会被忽略。

如果您稍后传递模板类以通过信号传递您的类型,请确保也注册它们,因为即使Qt知道QList,它也不知道您类型的QList:

qRegisterMetaType<QList<Datastruct>>("QList<Datastruct>");

另外提醒:如果你使用 #define 定义类别名,请确保将它们注册为它们真实的名称。

#define std::shared_ptr model_ptr
// you can declare your signals like this:
void my_signal(model_ptr<my_model>);
// but have to register the type like this:
qRegisterMetaType<std::shared_ptr<my_model>>("std::shared_ptr<my_model>");

1
为什么你会建议在这里使用#define呢?即使在C语言中也是不必要的(你可以使用typedef)!在C++中,你应该使用类型别名:using model_ptr = std::shared_ptr<model>。只使用别名而不使用基本类型名称也完全可以,只要你在每个地方都保持一致性使用它。 - Kuba hasn't forgotten Monica
我举了一个类似于我曾经遇到的头疼问题的例子,这个例子是从我的脑海中想出来的,如果你已经在使用 #define,那么我并不是在建议你使用 #define。 - markus-nm
注册一个模板类可能不会像原样工作,您可能需要首先使用typedefusing进行别名处理,然后再注册别名。此外,请注意,模板类型末尾的双角括号仅允许在C++11及以上版本中使用。如果您还为其他项目工作,请不要将其变成习惯! - iksemyonov
请参考以下两个链接:https://dev59.com/SYTba4cB1Zd3GeqP3kMJ 和 https://steveire.wordpress.com/2011/03/16/implementing-qvariantqmetatype-features-with-template-tricks/。 - iksemyonov
@markus-nm,你的答案非常好,如果你有时间,我想再问一下,与仅发送QString(或其他更基本的数据)相比,是否可能会有速度惩罚? - Harry de winton
你是否有任何文档说明必须在实例化QApplication之后调用“qRegisterMetaType”?我已经测试过在进入“main()”之前执行该函数。这对我也起作用。另外请注意,“qRegisterMetaType <Datastruct>()”可以替代“qRegisterMetaType <Datastruct>(“Datastruct”)”。 - JojOatXGME

4
在使用宏 Q_DECLARE_METATYPE 声明已知于 QMetaType 的结构体时,可以在一瞬间完成。
struct Datastruct
{
    int markeridone;
};

Q_DECLARE_METATYPE(Datastruct)

您可以通过QVariant发送此结构。这是一种简单而不失优雅的方式。 在您的头文件中声明:
signals:
    void sendDatastruct(QVariant data);

public slots:
    void getDatastruct(QVariant data);

在你的代码中使用信号(Signal):

.....
Datastruct ds;
.....
QVariant data;
data.setValue(ds);
emit sendDatastruct(data);  // now send signal
.....

使用插槽:

void MyObject::getDatastruct(QVariant data)
{
    Datastruct ds = data.value<Datastruct>();
    .....
    // now You can use structure in Your code
}

1

我无法使其正常工作,因为我收到了一个运行时警告:

startsQObject::connect: 没有这样的信号

我阅读了文档,并发现您必须仅通过引用传递const值:

所以你最终会得到:

/* myWorker Class Instance header .h */

struct notifyMsg_t
{
    QString m_TitleSuffix;
    QString m_MessageSuffix;
    int     m_errNumber;
};

Q_DECLARE_METATYPE(notifyMsg_t);

signals:
    void sendDatastruct(const QVariant&);

/* MainWindow Class instance Header .h*/
public slots:
    void getDatastruct(const QVariant& data);

/* MainWindow Implementation .cpp */

connect(myWorker,
        SIGNAL(sendDatastruct(const QVariant&)),
        this,
        SLOT(getDatastruct(const QVariant&))
        );


/* Worker implementation .cpp */

void Worker::notify(QString    TitleSuffix,
                    QString    MessageSuffix,
                    int        errNumber)
{
    notifyMsg_t notifyMsg;
    notifyMsg.m_TitleSuffix = TitleSuffix;
    notifyMsg.m_MessageSuffix = MessageSuffix;
    notifyMsg.m_errNumber = errNumber;


    QVariant data;
    data.setValue(notifyMsg);
    emit wrkrError_dbTaskSig(data);
}

这段代码有效。
请注意,在SIGNAL定义中不需要包含虚拟变量,即:
/* This >> */ (const QVariant&)
/* Not this >> */ const QVariant& data
不要问我为什么。
最好的问候,
罗布

0
如果有人需要完整的工作解决方案(在5.6上测试过):
  1. 添加文件StructSignal.h(或其他名称)

    struct StructSignal { QString data; bool checkBox_data; ... }; 您不需要添加Q_DECLARE_METATYPE(SearchResult);

  2. 在调用类的头文件中

    public slots: ... void getResult(StructSignal*);

  3. 在调用类的cpp文件中

    connect(&emitClass, SIGNAL(resultParameters(StructSignal*)), this, SLOT(getResult(StructSignal*)));

  4. getResult方法

    void callingClass::getResult(StructSignal* ss) {

     qDebug () << "getResult";
    
     qDebug () << "data: " << ss->pattern;
     ...
    

    }

  5. 在具有发射的方法中:

    StructSignal ss; ss.data = ui->data->text(); ss.checkBox_data = ui->checkBox_data->isChecked (); ...

    emit searchParameters(&ss);

  6. 您需要包含信号声明:

    signals: void searchParameters(StructSignal* data);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接