[Interest] Adding a C++ wrapper class renders my QML custom type unusable
Elvis Stansvik
elvstone at gmail.com
Wed Aug 31 19:24:33 CEST 2016
Hi Rob,
Looks like a you did a pretty thorough analysis. If I understand you
correctly, I can't really reproduce the problem with a test case I
made.
The test case I made consists of CustomButton.h, CustomButton.cpp,
CustomButton.qml, main.qml and main.cpp as follows:
CustomButton.h:
#pragma once
#include <QQuickItem>
class CustomButton : public QQuickItem
{
Q_OBJECT
public:
CustomButton(QQuickItem *parent = nullptr);
};
CustomButton.cpp:
#include "CustomButton.h"
CustomButton::CustomButton(QQuickItem *parent) : QQuickItem(parent)
{
}
CustomButton.qml:
import QtQuick 2.0
import com.glob.myApp 1.0
CustomButton {
signal clicked()
property string text;
function foo(arg) {
console.info("foo called from " + arg);
}
Rectangle {
anchors.fill: parent
color: "red"
}
MouseArea {
anchors.fill: parent
onClicked: parent.clicked()
}
Text {
anchors.centerIn: parent
text: parent.text
}
}
main.qml:
import QtQuick 2.0
import QtQuick.Window 2.2
Window {
visible: true
CustomButton {
width: 100
height: 30
text: "Click me"
objectName: "button"
onClicked: console.info("button clicked (from QML)");
Component.onCompleted: {
console.log("component completed");
foo("QML");
}
}
}
main.cpp:
#include "CustomButton.h"
#include <QDebug>
#include <QGuiApplication>
#include <QMetaObject>
#include <QQmlApplicationEngine>
#include <QVariant>
class MyClass : public QObject
{
Q_OBJECT
public slots:
void cppSlot() {
qDebug() << "button clicked (from C++)";
}
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
qmlRegisterType<CustomButton>("com.glob.myApp", 1, 0, "CustomButton");
QQmlApplicationEngine engine("main.qml");
QObject *button = engine.rootObjects().first()->findChild<QObject
*>("button");
// Call foo() on button
QVariant returnedValue;
QMetaObject::invokeMethod(button, "foo",
Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, "C++"));
// Listen to the clicked signal of button
MyClass myClass;
QObject::connect(button, SIGNAL(clicked()),
&myClass, SLOT(cppSlot()));
return app.exec();
}
#include "moc_main.cpp"
Now, if I run this test case, I get the following (if I click the button once):
[estan at pyret qmltest]$ ./qmltest
qml: component completed
qml: foo called from QML
qml: foo called from C++
qml: button clicked (from QML)
button clicked (from C++)
[estan at pyret qmltest]$
It would help to see some of your code to see where things go wrong.
Elvis
2016-08-31 8:08 GMT+02:00 Rob Allan <rob_allan at trimble.com>:
> 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:
>
> 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();
> };
>
> Added the necessary qmlRegisterType() call to my application startup code:
>
> qmlRegisterType<CustomButton>("com.glob.myApp", 1, 0, "CustomButton");
>
> Added an import statement to my CustomButton.qml file:
>
> import com.glob.myApp 1.0
>
> 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
>
More information about the Interest
mailing list