【问题标题】:QObject::connect: Cannot queue arguments of type 'QModbusDevice::State'QObject::connect: 不能对类型为“QModbusDevice::State”的参数进行排队
【发布时间】:2016-11-29 17:52:00
【问题描述】:

我正在开发一个多线程应用程序,我需要通过 modbus 实例化 n 个设备。 所以我创建了一个控制器(ServiceSM)来实例化 N 个线程(ServiceSlots)。

设备多种多样,因此我必须为每种类型的设备创建“驱动程序”,其中一个驱动程序使用 QModbusClient 类,因此我创建了一个控制器来管理设备类型。

schema

为了测试状态机的运行和与设备的连接,我做了一个示例代码在图形界面中运行。

为了便于理解,我删除了一些无关紧要的sn-ps代码


在 MD4040driver 类中 当我的代码运行此部分时,会出现以下消息。 如果我在图形界面中实例化 DeviceDriver 类,它可以完美运行,当我在线程中实例化它时会出现问题。

打电话时

modbusDevice->connectDevice()

MD4040drive::sm_conn() - 尝试连接 - 这是我的消息 错误:

QObject::connect: 不能对类型的参数进行排队 'QModbusDevice::State'(确保 'QModbusDevice::State' 已注册 使用 qRegisterMetaType()。)

QObject:无法为不同的父级创建子级 线。 (Parent是QTcpSocket(0x24a6ce8),parent的线程是 ServiceSlots(0xea66488),当前线程为QThread(0x2418a78)

QObject:无法为不同的父级创建子级 线。 (Parent是QTcpSocket(0x24a6ce8),parent的线程是 ServiceSlots(0xea66488),当前线程为QThread(0x2418a78)

void MD4040drive::sm_conn()
{

    if (modbusDevice->state() != QModbusDevice::ConnectedState) {
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, this->cfg.modbus.porta );
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, this->cfg.modbus.ip);
        modbusDevice->setTimeout( this->cfg.modbus.timeout );
        modbusDevice->setNumberOfRetries(this->cfg.modbus.retries);
        qDebug() << "MD4040drive::sm_conn() - try connect";
        if (!modbusDevice->connectDevice()) {
            qDebug()  << "Erro: " << modbusDevice->errorString();
        } else {
            qDebug()  << "Aguardando conexão...";
        }
    }
    else{
        //already connected...
        this->getDados_NO_TH();
    }
}

休息一下我的代码(部分)


devicedriverviewgui.h devicedriverviewgui.cpp

class DeviceDriverViewGUI : public QDialog
{
    Q_OBJECT
public:
    explicit DeviceDriverViewGUI(QWidget *parent = 0);
    ~DeviceDriverViewGUI();
private slots:
    void on_pbTry_clicked();

private:
    Ui::DeviceDriverViewGUI *ui;
    ServiceSlots *serviceSlot;

};

void DeviceDriverViewGUI::on_pbTry_clicked()
{
    Equip equip_try = Equip();

    serviceSlot = new ServiceSlots();        
    serviceSlot->setEquipamento(equip_try);
    serviceSlot->start();
}

serviceslots.h 服务插槽.cpp

class ServiceSlots : public QThread
{
     Q_OBJECT
public:
    ServiceSlots();
    void run();
private:
    QTimer *timer;
    DeviceDriver *device;

private slots:
    void sm_getData();
    void device_response(bool boEnd);
};

void ServiceSlots::run()
{
    int e;
    eventLoop = new QEventLoop();
    timer = new QTimer();
    connect(timer, SIGNAL(timeout()),this, SLOT(sm_controler()));
    timer->start(TICK_SM_SLOT);
    this->device = new DeviceDriver();
    e = eventLoop->exec();
    qDebug() << "Exit loop"<< e;
}

void ServiceSlots::sm_controler()
{
    if(this->idleState){;}
    else{
        this->sm_getData();
        this->idleState = true;
    }
}

void ServiceSlots::sm_getData()
{
    connect(device,SIGNAL(end(bool)),this,SLOT(device_response(bool)));
    device->requestDeviceDriver(&this->equipamento,&this->next_date);
}

设备驱动程序.h 设备驱动程序.cpp

class DeviceDriver : public QObject
{
    Q_OBJECT
public:
    DeviceDriver();
    void requestDeviceDriver(Equip *equip,QDateTime *date);
private:
    //Drivers de dispositivos..
    MD4040drive *md4040;
private slots:
    //Request data to driver...
    void request();
signals:
    void end(bool boEnd);
};

void DeviceDriver::request()
{
    connect(md4040,SIGNAL(end(bool)),this,SLOT(md4040_end(bool)));
    this->md4040->requestMD4040drive(&this->equip,&this->date);
}

DeviceDriver::DeviceDriver(){
    ----
    md4040 = new MD4040drive();
    ---
}

void DeviceDriver::requestDeviceDriver(Equip *equip, QDateTime *date){
    this->equip = *equip;
    this->date = *date;
    this->request();
}

md4040drive.h md4040drive.cpp

class MD4040drive : public QObject // QObject//public QObject  QRunnable  QThread
{
    Q_OBJECT
public:
    explicit MD4040drive(QObject *parent = 0);
    ~MD4040drive();
    void requestMD4040drive(Equip *equip,QDateTime *date);
private:
    void run();
    QModbusClient *modbusDevice;

private slots:
    void m_conn();
signals:
    void end(bool boRun);

};

MD4040drive::MD4040drive(QObject *parent): QObject(parent)
{
    modbusDevice = new QModbusTcpClient();
    connect(modbusDevice, &QModbusClient::stateChanged,this, &MD4040drive::onStateChanged);
}

void MD4040drive::requestMD4040drive(Equip *equip, QDateTime *date)
{
    this->equip = *equip;
    this->date = *date;
    this->run();
}


void MD4040drive::run()
{
    this->sm_conn();
}

void MD4040drive::sm_conn()
{

    if (modbusDevice->state() != QModbusDevice::ConnectedState) {
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, this->cfg.modbus.porta );
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, this->cfg.modbus.ip);
        modbusDevice->setTimeout( this->cfg.modbus.timeout );
        modbusDevice->setNumberOfRetries(this->cfg.modbus.retries);
        qDebug() << "MD4040drive::sm_conn() - try connect";
        if (!modbusDevice->connectDevice()) {
            qDebug()  << "Erro: " << modbusDevice->errorString();
        } else {
            qDebug()  << "Aguardando conexão...";
        }
    }
    else{
        //already connected...
        this->getDados_NO_TH();
    }
}

【问题讨论】:

    标签: qt qthread qt5.7


    【解决方案1】:

    有几个问题:

    1. 您需要在main()中拨打qRegisterMetaType&lt;QModbusDevice::State&gt;()

    2. 您需要维护所有可能作为一个组移动到其他线程的对象之间的父子关系。

    3. ServiceSlotsDeviceDriver 类似乎是不必要的。

    4. 无处不在的this-&gt; 是非惯用的 C++。不要写this-&gt;,除非您需要从局部变量中消除成员的歧义。

    5. 如果对象与父对象具有相同的生命周期,则首选按值保存对象。让编译器为你生成内存管理代码!

    6. 利用 C++11。

    首先,让我们有一个帮助器SafeThread 类,它为我们提供了一个随时可以安全销毁的线程:

    class SafeThread : public QThread {
       Q_OBJECT
       using QThread::run;
    public:
       using QThread::QThread;
       ~SafeThread() { quit(); wait(); }
    };
    

    DeviceDriverViewGUI 类可以按值保存驱动器及其线程:

    class DeviceDriverViewGUI : public QDialog
    {
       Q_OBJECT
    public:
       explicit DeviceDriverViewGUI(QWidget *parent = nullptr);
    
    private:
       Ui::DeviceDriverViewGUI ui;
       MD4040drive drive;
       SafeThread driveThread{this};
       Equip equipamento;
       QDateTime nextDate;
    
       Q_SLOT void on_pbTry_clicked();
    };
    

    然后,按钮可以直接连接到驱动器的线程上下文,并在适当的线程中运行requestMD4040drive

    DeviceDriverViewGUI::DeviceDriverViewGUI(QWidget *parent) : QDialog(parent)
    {
       ui.setupUi(this);
       //                                       vvvvvv -- gives the thread context
       connect(ui.pbTry, &QPushButton::clicked, &drive, [this]{
          Q_ASSERT(QThread::currentThread() == drive.thread()); // ensured by the thread context
          drive.requestMD4040drive(&equipamento, nextDate);
       });
       connect(&drive, &MD4040drive::end, this, [this](bool end){
          //...
       });
       drive.moveToThread(&driveThread);
       driveThread.start();
    }
    

    以这种方式完成后,您不需要任何无关的帮助器对象或计时器来对请求进行排队。 Qt 处理所有这些。

    将 Qt 值类传递给函数时,通过 const 引用而不是指针传递它们。 MD4040drive 大致如下所示:

    class MD4040drive : public QObject
    {
       Q_OBJECT
    public:
       explicit MD4040drive(QObject *parent = nullptr);
       void requestMD4040drive(Equip *equip, const QDateTime &date);
          Q_SIGNAL void end(bool boRun);
    private:
       Equip *equip = nullptr;
       QDateTime date;
       QModbusTcpClient modbusDevice{this};
       Cfg cfg;
    
       Q_SLOT void onStateChanged();
       Q_SLOT void m_conn();
       void sm_conn();
       void getDados_NO_TH() {}
    };
    

    实现:

    MD4040drive::MD4040drive(QObject *parent): QObject(parent)
    {
       connect(&modbusDevice, &QModbusClient::stateChanged,this, &MD4040drive::onStateChanged);
    }
    
    void MD4040drive::requestMD4040drive(Equip *equip, const QDateTime &date)
    {
       this->equip = equip;
       this->date = date;
       sm_conn();
    }
    
    void MD4040drive::sm_conn()
    {
    
       if (modbusDevice.state() != QModbusDevice::ConnectedState) {
          modbusDevice.setConnectionParameter(QModbusDevice::NetworkPortParameter, cfg.modbus.porta );
          modbusDevice.setConnectionParameter(QModbusDevice::NetworkAddressParameter, cfg.modbus.ip);
          modbusDevice.setTimeout( this->cfg.modbus.timeout );
          modbusDevice.setNumberOfRetries(this->cfg.modbus.retries);
          qDebug() << "MD4040drive::sm_conn() - try connect";
          if (!modbusDevice.connectDevice()) {
             qDebug()  << "Erro: " << modbusDevice.errorString();
          } else {
             qDebug()  << "Aguardando conexão...";
          }
       }
       else{
          //already connected...
          getDados_NO_TH();
       }
    }
    

    配置类可能如下所示 - 请注意编译器将为您生成必要的构造函数和析构函数:

    struct Cfg {
       struct Modbus {
          int porta = 0;
          QString ip = QStringLiteral("127.0.0.1");
          int timeout = 1000;
          int retries = 2;
       } modbus;
    };
    

    【讨论】:

      【解决方案2】:

      确保使用 qRegisterMetaType() 注册“QModbusDevice::State”

      意味着您需要在连接将在线程之间传递此类参数的信号/插槽之前调用qRegisterMetaType&lt;QModbusDevice::State&gt;();

      和/或在全局范围内添加 Q_DECLARE_METATYPE(QModbusDevice::State) 宏(从未清楚地了解实际需要哪个宏,确定两者都可以......)。

      查看这篇文章了解更多详情:Emitting signals with custom types does not work

      【讨论】:

        猜你喜欢
        • 2013-06-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多