Lé Code
Qml
I'll assume that the qml code given actually belongs to MyItem.qml
instead of main.qml
.
Your Qml file generated an compile-time error. Functions should be placed inside an object, like so
// MyItem.qml
import QtQuick 2.0
Text {
id: textbox
text: "nothing"
function myQmlFunction(msg) {
console.log("Got message:", msg)
textbox.text = msg
return "some return value"
}
}
I'm not sure how you were able to compile your project without generating an error, but I'm guessing either
- Your QtCreator/Qt version is not the same as mine (highly unlikely the cause); or
- You were try to making your code minimal and originally had a parent.
I'm sure you have a sufficient understanding about Qml so I'm not going to go deep into this.
C++
On the C++ side, I had to fiddle around with debug output to see what's wrong. Here's my main.cpp
:
// main.cpp
#include <QApplication>
#include <QDebug>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QQuickItem>
int main(int argc, char *argv[])
{
QApplication app(argc, argv); // Qt requires an instance of QApplication
QQmlEngine *engine = new QQmlEngine;
QString projectPath = "/Users/user/full/path/to/your/project"; // I'm on a Mac, but replace
// with the appropriate path to your project
// QQmlComponent component(engine, "MyItem.qml"); // this didn't work for me and
// set component.status() to QQmlComponent::Error
QQmlComponent component(engine, projectPath + "/qml/MyItem.qml"); // use full path
qDebug() << "Status:" << component.status();
if (component.status() == QQmlComponent::Error)
qDebug() << "Errors:" << component.errors();
else if (component.status() != QQmlComponent::Ready)
{
qDebug() << "Component is not ready!";
return 0;
}
QObject *object = component.create();
if (!object) { qDebug() << "Object creation failed!"; return 0; }
QQuickItem *item = qobject_cast<QQuickItem*>(object); // adding this didn't change much
// but this could be crucial
QVariant returnedValue;
QVariant msg = "Hello from C++";
bool success = QMetaObject::invokeMethod(item, "myQmlFunction", // replace `object` with `item`
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, msg));
if (success)
qDebug() << "QML function returned:" << returnedValue.toString();
else
qDebug() << "QMetaObject::invokeMethod returned false";
delete object;
return 0;
}
Output
The output I received on a successful build, with a successful object creation was
Status: QQmlComponent::Status(Ready)
Object: MyItem_QMLTYPE_0(0x7f8d4ae8b640)
qml: Got message: Hello from C++
QML function returned: "some return value"
I haven't yet checked whether the text changed in your Qml textbox
. (Didn't bother to. It'll require more changes to the C++ code and this answer is already long enough. I was also confident that nothing'll go wrong, so ¯\_(ツ)_/¯).
Lé Non-Code
What if I don't want to use a raw file path?
If you're meh about using a raw file path (e.g. /Users/whoami/ugly/looking/path
) in
QString projectPath = "/Users/user/full/path/to/your/project";
You can add this to your .pro
file:
DEFINES += SOURCE_PATH=$$PWD
and set projectPath
to
QString projectPath = QT_STRINGIFY(SOURCE_PATH);
This idea was borrowed from a forum thread.
Assumptions
Throughout my answer, I have assumed that your project hierarchy resembles
/./
|- myProject.pro
|- main.cpp
|- qml/
|- MyItem.qml
The essential thing is that you use your full path to your qml item. If you do find another to reference it (maybe using QUrl
?) then do post a comment about it.
Further Reading
Check out the details section of the QQmlComponent
class and QQmlComponent::create
member function. Reading these led me to know which values to debug and what to look out for.