I'm working on this open source project, which basically is a live QML interpreter. Since a user's main activity would be to write QML code, I'd like to embed all logs and warnings and such in the application to provide some basic IDE-like functionality. First, I installed a custom message handler via qInstallMessageHandler which receives all runtime messages from QML:
auto Instance::HandleMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg) -> void {
std::cout << "handle message: " << msg.toStdString() << std::endl;
Logger::GetInstance()->addEntry(msg);
}
The Logger's addEntry method simply appends the message to a QStringList property and emits the corresponding changed()-signal. On the QML side, I feed that property to a ListView:
ListView {
anchors.fill: parent;
model: App.logger.entries;
delegate: Text {
text: modelData;
}
}
This works. But the problem appears in combination with dynamic QML object instantiation. The 'magic' part of my application is a call to Qt.createQmlObject which expects some QML source as string and returns the newly created object:
function compile() {
try {
qmlObject = Qt.createQmlObject(<qml_source>, container, "root");
} catch (exc) {
// handle *compile-time* errors
}
}
If 'compilation' fails, control immediately shifts to the catch clause and no new QML object will be created. But if I try to create an object from the following source, the application crashes:
function compile() {
try {
qmlObject = Qt.createQmlObject("import QtQuick 2.6\nItem { anchors.fill: parent; Rectangle { width: parent.width; height: a.height; } }", container, "root");
} catch (exc) {
// handle *compile-time* errors
}
}
The above source is valid, but generates a runtime error. For some reason, the HandleMessage function gets called twice in this specific scenario:
handle message: [...]App/root:2: ReferenceError: a is not defined
handle message: [...]App/root:2: ReferenceError: a is not defined
And creates a binding loop:
handle message: [...]/App/containerview.qml:76:9: QML Repeater: Binding loop detected for property "model"
Which is also processed by the custom message handler... You see where this is going: the application gets stuck in a loop and finally crashes.
My observations so far:
Not forwarding incoming messages to the QStringList property (just cout'ing them) results in only one single invocation of the custom message handler.
Creating the same error after construction time causes no trouble:
qmlObject = Qt.createQmlObject("import QtQuick 2.6\nimport QtQuick.Controls 1.4\nItem { anchors.fill: parent; Rectangle { id: rect; width: parent.width; } Button { anchors.centerIn: parent; text: \"do something stupid\"; onClicked: { rect.height = a.height; } } }", container, "root");Not using a list-based property (for example storing all messages in a single QString object) also solves the problem but has some obvious disadvantages.
And finally the question: Can someone explain why this happens and how to solve this problem?