[Interest] Adding a C++ wrapper class renders my QML custom type unusable

Sina Dogru sinadooru at gmail.com
Wed Aug 31 23:46:17 CEST 2016


Hello Rob,

As I understand, you would like to define a QML object type
<http://doc.qt.io/qt-5/qtqml-documents-definetypes.html> which root object
is a C++ object, add a
signal to that custom QML type in QML document or defined in C++ and call
an inline JavaScript
function from C++. I am not sure why is your program is not working but
those all are supposed to
be working.

But you can not declare a Q_INVOKABLE function and implement it in QML or
JavaScript file. Because
even if you don't call those functions, MOC generated files will. So the
compiler gives an error. Maybe
you can call your JavaScript implementations from those wrappers but you
must define those functions.

Here is a little example:

// custombutton.h
#include <QQuickItem>


class CustomButton : public QQuickItem
{
    Q_OBJECT
public:
    CustomButton()
    {
        setAcceptedMouseButtons(Qt::AllButtons);
    }

signals:
    void clicked(const QString& text, int eventCode);

protected:
    void mousePressEvent(QMouseEvent* event) override
    {
        QQuickItem::mousePressEvent(event);

        emit clicked("emitted from CustomButton::mousePressEvent",
event->type());

        qDebug("CustomButton::mousePressEvent calls this.foo");
        // method foo is an inline JavaScript function.
        QMetaObject::invokeMethod(this, "foo");
    }
};

// CustomButton.qml
import QtQuick 2.7
import QtQuick.Controls 2.0

import MyApp 1.0 as Cpp


Cpp.CustomButton {
    id: root

    signal qmlclick(string text);

    function foo() {
        qmlclick("emitted from CustomButton.foo");
    }

    Rectangle {
        anchors.fill: parent
        color: "lightblue"

        Label {
            anchors.fill: parent

            text: "Custom Button"

            verticalAlignment: Label.AlignVCenter
            horizontalAlignment: Label.AlignHCenter
            wrapMode: Label.WordWrap
            elide: Label.ElideMiddle
        }
    }
}

// main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
import QtQuick.Controls 2.0


Window {
    visible: true
    width: 200
    height: 200

    title: "MyApp"

    Column {
        anchors.centerIn: parent

        CustomButton {
            id: customButton

            width: 80
            height: 50

            onClicked: console.log("custom button click signal " + text);
            onQmlclick: console.log("custom button qmlclick signal " +
text);
        }

        Button {
            id: qqcButton

            width: 80
            height: 50

            text: "qqcButton"

            onClicked: customButton.clicked("emitted from
qqcButton.onClicked", 2);
        }
    }
}

// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>

#include "custombutton.h"


int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    qmlRegisterType<CustomButton>("MyApp", 1, 0, "CustomButton");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

Hope it helps,
Sina

On Wed, Aug 31, 2016 at 9:08 AM, Rob Allan <rob_allan at trimble.com> wrote:

> I have a custom QML type, CustomButton, defined in CustomButton.qml.
> Initially this was a fairly simple type, with a 'clicked' signal, and a few
> JavaScript functions that set up the button content. I was able to use this
> custom button from other QML files, and from C++ code (using
> QMetaObject::invokeMethod() to invoke its methods), and it all worked
> pretty well.
>
> As this button became more complex, I realised that I really needed a C++
> wrapper class (or backing class?) to deal with some of the additional
> complexity. I've added C++ wrapper classes to other QML types before, with
> varying degrees of success, and I think I understand the basic steps
> involved. I did the following:
>
>    1. Created a minimal wrapper in CustomButton.h and CustomButton.cpp (I
>    made this as minimal as possible to begin with so as not to affect the
>    existing behavior - I thought!):
>
> #include <QQuickItem>
> class CustomButton : public QQuickItem
> {
>     Q_OBJECT
> public:
>     CustomButton();
> };
>
>
>    1. Added the necessary qmlRegisterType() call to my application
>    startup code:
>
> qmlRegisterType<CustomButton>("com.glob.myApp", 1, 0, "CustomButton");
>
>
>    1. Added an import statement to my CustomButton.qml file:
>
> import com.glob.myApp 1.0
>
>    1. Changed the root item in CustomButton.qml from 'Item' to
>    'CustomButton'.
>
> So far all appeared to be OK - the project compiled, and CustomButton.qml
> appeared to be error-free in the QML editor. But then things started going
> downhill.
>
> I noticed that, in another QML file that used CustomButton, it could no
> longer 'see' the signals on my type - a reference to 'onClicked' was
> red-underlined, and hovering over it showed 'Invalid property name'. At
> runtime, it failed to create the referencing QML object due to this error.
> It looks as if the introduction of the C++ wrapper class has 'hidden' the
> signals defined in the original CustomButton.qml file.
>
> I found I could get past this error by deleting the signal definitions
> from CustomButton.qml, and instead adding them to CustomButton.h:
>
> signals:
>     void clicked(const QString text, int eventCode);
>     etc...
>
> That allowed it to build and run. I'm not sure whether this was correct
> and the signals would now have worked, because I then struck another
> problem - my existing C++ code could no longer invoke the JavaScript
> functions defined in CustomButton.qml. For example, when I tried to invoke
> a method 'setContent' (which previously worked just fine) I now got this
> runtime error:
>
> QMetaObject::invokeMethod: No such method CustomButton::setContent(
> QVariant,QVariant)
>
> Again, it appears that adding a C++ wrapper class has 'hidden' the
> function definitions in the QML file, that were previously available to the
> rest of the system.
>
> OK, I thought, maybe I have to define these functions on the C++ class in
> some way. I tried declaring an INVOKABLE function in the .h file that
> matched my JavaScript function, and that failed with a link error - the
> compiler wanted me to define an implementation for this function in my CPP
> file - but I don't want to implement it in my CPP file, as the
> implementation exists in the QML file! Just to see where it got me, I tried
> adding an implementation to the CPP file, and inside this function,
> attempted to "invoke" the JavaScript function. That failed, presumably
> because I had now introduced a kind of circularity and was probably
> attempting to invoke the same handler that I was already in!
>
> I also tried declaring these functions as 'slots' on the C++ class, but
> fared no better - the compiler still wanted a CPP implementation, and I
> wasn't sure if I was really meant to add one, and if I did, how I would
> then invoke the JavaScript function from it.
>
> In short - adding a C++ wrapper class (with almost nothing in it) has
> broken a previously functional QML type implementation, by apparently
> 'hiding' signals and functions that exist in the QML.
>
> Can anyone advise what I'm doing wrong here? Or suggest some relevant
> documentation that might give me the answers I need?
>
> Thanks,
> Rob
>
> _______________________________________________
> Interest mailing list
> Interest at qt-project.org
> http://lists.qt-project.org/mailman/listinfo/interest
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20160901/165581c9/attachment.html>


More information about the Interest mailing list