Get signal from qml into c++ class in Qt

蓝咒 提交于 2020-06-13 06:10:11

问题


I try to figure out how to get signal from QML code and connect it to slot located in C++ class.

I take code from this answer and the control shown on the screen but I can't get the signal.

Here is the relevant code:

test.qml:

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1

Switch{
    id: swt;
    checked:true;
    onCheckedChanged: swt.qmlSignal();
}

menu.cpp:

Menu::Menu(QWidget *parent) :
QWidget(parent),
        ui(new Ui::Menu)
{
    ui->setupUi(this);
    QQuickView *view = new QQuickView();
    QWidget *container = QWidget::createWindowContainer(view, this);

    container->setMaximumSize(50, 20);

    QObject::connect(container, SIGNAL(qmlSignal()),
                     this, SLOT(receiveFromQml()));

    view->setSource(QUrl("qrc:/test.qml")); 
    ui->verticalLayout->addWidget(container);



}
void Menu::receiveFromQml() 
{
    qDebug() << "Called the C++ slot with message:" ;
}

I've looked at the examples here but I can't make it to work.

This is the error I get :

qrc:/test.qml:10: TypeError: Property 'qmlSignal' of object Switch_QMLTYPE_4(0x291ac70) is not a function


回答1:


Simple, mark it as slot

Seems you have a C++ Object and you need to call a slot, so mark the function as a slot, then it is exposed automatically. No need for weird connect() in C++.

// Seems Switch is a QML Item
Switch{
 id: swt;
 checked:true;
 onCheckedChanged: myCppObj.slot(); // calls the object's slot 
} 

There are other ways to do it, but it seems this covers your case. If not, please elaborate and we'll refine it.




回答2:


I hope that your example is sufficient and that switch item is the root one as well. This is usually solved like:

Switch {
    id: swt

    signal gmlSignal

    checked:true;
    onCheckedChanged: { 
        qmlSignal();
    }
}

The Qt article with details. Also you can have qualifier swt when needed so swt.qmlSignal() from the other context within that file.

Also, C++ part needs to be fixed as well:

Menu::Menu(QWidget *parent) :
QWidget(parent),
ui(new Ui::Menu)
{
   ui->setupUi(this);
   QQuickView *view = new QQuickView();
   QWidget *container = QWidget::createWindowContainer(view, this);

   container->setMaximumSize(50, 20);

   view->setSource(QUrl("qrc:/test.qml")); 
   ui->verticalLayout->addWidget(container);

   QObject::connect((QObject*)view->rootObject(), SIGNAL(qmlSignal()),
                     this, SLOT(receiveFromQml()));
}

I did changes by looking at own code that emits similar signal and did not actually try but you need to connect to signal exposed from the rootObject of QML view and not widget container of it.




回答3:


You can not create an additional signal in qml, but use the standard properties of objects.

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1

Switch{
    id: swt;
    checked:true;
}

C++:

class MyHandler : public QObject
{
    Q_OBJECT
    public:
        MyHandler( const QObject& object, const QQmlProperty& qmlProperty );
    private slots:
        void handleNotify();
    private:
        const QQmlProperty& m_qmlProperty;
};

MyHandler::MyHandler( const QObject& object, const QQmlProperty& qmlProperty ) :
    QObject(),
    m_qmlProperty( qmlProperty )
{
    static const QMetaMethod metaSlot = this->metaObject()->method( this->metaObject()->indexOfSlot( "handleNotify" ) );
    if( metaSlot.isValid() )
    {
        const QMetaMethod metaQmlPropSignal = qmlProperty.property().notifySignal();
        if( metaQmlPropSignal.isValid() )
            QObject::connect( &object, metaQmlPropSignal, this, metaSlot );
    }
}

MyHandler::handleNotify()
{
    if( m_qmlProperty.isValid() )
    {
        const int qmlPropertyValue = m_qmlProperty.read().value<bool>();
        ...
    }
}

using:

QQuickView* view = ...;
QObject* quickItem = view->rootObject();
const QQmlProperty* qmlProperty = new QQmlProperty( quickItem, "checked" );
MyHandler* myHandler = new MyHandler( *quickItem, *qmlProperty );

Thus, the method MyHandler::handleNotify will be called when the property checked changes (if it exists for the object quickItem).

P.S. You can also connect a qml property with a signal.

class QmlPropertyWrapper : public QObject
{
    Q_OBJECT
    public:
        QmlPropertyWrapper( const QObject& object, const QQmlProperty& qmlProperty );
    signals:
        void triggered();
};

QmlPropertyWrapper::QmlPropertyWrapper( const QObject& object, const QQmlProperty& qmlProperty ) :
    QObject()
{
    static const QMetaMethod metaSignal = QMetaMethod::fromSignal( &QmlPropertyWrapper::triggered );
    if( metaSignal.isValid() )
    {
        const QMetaMethod metaQmlPropSignal = qmlProperty.property().notifySignal();
        if( metaQmlPropSignal.isValid() )
            QObject::connect( &object, metaQmlPropSignal, this, metaSignal );
    }
}

using

QQuickView* view = ...;
QObject* quickItem = view->rootObject();
const QQmlProperty* qmlProperty = new QQmlProperty( quickItem, "checked" );
QmlPropertyWrapper* qmlPropertyWrapper = new QmlPropertyWrapper( *quickItem, *qmlProperty );
QObject::connect( qmlPropertyWrapper, &QmlPropertyWrapper::triggered, [ qmlProperty ]()
{
    if( m_qmlProperty->isValid() )
    {
        const int qmlPropertyValue = m_qmlProperty->read().value<bool>();
        ...
    }
} );


来源:https://stackoverflow.com/questions/31728098/get-signal-from-qml-into-c-class-in-qt

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!