1

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

Community
  • 1
  • 1
Natile
  • 193
  • 1
  • 6
  • 20
  • remove () after qmlSignal - you are trying to assign properties, not calling function – Hcorg Jul 30 '15 at 17:03
  • @Hcorg I removed it, Now i dont get the error but still the slot not fired. is the connect statement correct? – Natile Jul 30 '15 at 17:10
  • Does this answer your question? [How to bind C++ property to QML property?](https://stackoverflow.com/questions/41591224/how-to-bind-c-property-to-qml-property) – Rinat Veliakhmedov Mar 12 '20 at 13:25

3 Answers3

2

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.

Ariel M.
  • 896
  • 8
  • 24
0

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.

Alexander V
  • 8,351
  • 4
  • 38
  • 47
  • I tried this and get an error : no matching function for call to 'Menu::connect(QQuickItem*, const char*, Menu* const, const char*)' , did you missed something? – Natile Jul 30 '15 at 20:38
  • I briefly checked with my own code similar to yours and the root object is accessed like (QObject*)view->rootObject() and fixed that in my answer. – Alexander V Jul 30 '15 at 21:42
  • Still have some problems : i get now (in runtime) the folowing error : QObject::connect: No such signal Switch_QMLTYPE_4_QML_6::qmlSignal() in ../bwStimulator/menu.cpp:44 . And when I click on the switch I get the error : qrc:/test.qml:13: ReferenceError: qmlSignal is not defined – Natile Aug 02 '15 at 04:57
  • Maybe it makes sense to find Qt example with QML passing signal to C++. I can think of why it does not work for you: either Switch is not a root item so we address the signal for C++ wrong or maybe there is some mess in the project so wrong QML file is attached. – Alexander V Aug 02 '15 at 16:35
  • This is the only one qml file i have, i dont think this is a mess in the project. where can i find a simple tutorial to add qt quick control to qt widget project and get from gim signals? – Natile Aug 02 '15 at 18:39
  • For instance Qt Creator always offers you those samples right away on the Welcome page or maybe in C:\Qt\Examples\Qt-5.5 or what is location in your OS. – Alexander V Aug 02 '15 at 21:56
  • by the way : i dont understand what is " Switch is not a root item " but i tried to replace that to simple button and get the same result – Natile Aug 03 '15 at 05:52
  • Try search 'root' and 'root item' in here: http://doc.qt.io/qt-5/qtqml-cppintegration-interactqmlfromcpp.html – Alexander V Aug 03 '15 at 06:04
  • The answer by Ariel M. is the better approach, as it doesn't require you to mess with QML generated objects on the C++ side. No need for a signal, finding an object by name, connect, etc, just simply calling the slot from the QML signal handler – Kevin Krammer Oct 11 '16 at 08:55
  • @KevinKrammer Good to know! – Alexander V Oct 11 '16 at 19:41
  • But mind that calling the slot from QML is implying UI thread which is not necessarily wanted (the slot object can be on the other thread). – Alexander V Oct 12 '16 at 20:49
0

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>();
        ...
    }
} );
ZolotovPavel
  • 138
  • 6