Problem:
I have a QQuickWidget inside a QWidget, and I’m adding this widget to a QMdiArea. I am trying to call a QML function (plusBtn())
from C++ using QMetaObject::invokeMethod
. However, my application crashes when I try to invoke the method. This crash happens only when the window is added to the QMdiArea. When I remove the widget from QMdiArea, everything works fine.
Here is my simplified code:
QML (Map.qml):
import QtQuick
import QtLocation
import QtPositioning
Rectangle {
id: rectangle
Plugin {
id: mapPlugin
name: "osm"
}
Map {
id: mapView
anchors.fill: parent
plugin: mapPlugin
zoomLevel: 15
minimumZoomLevel: 2
activeMapType: supportedMapTypes[0]
}
function plusBtn() {
if (mapView.zoomLevel < mapView.maximumZoomLevel) {
mapView.zoomLevel = Math.round(mapView.zoomLevel + 1);
}
}
}
C++ (MainWindow Implementation):
#include <QDebug>
#include <QMainWindow>
#include <QPushButton>
#include <QQmlEngine>
#include <QQuickItem>
#include <QQuickWidget>
#include <QVBoxLayout>
#include <QWidget>
#include <QApplication>
#include <QMDIArea>
class QmlWindow : public QWidget
{
public:
QmlWindow(QWidget* parent = nullptr)
: QWidget(parent)
{
QVBoxLayout* layout = new QVBoxLayout(this);
// Create and initialize the QQuickWidget
quickWidget = new QQuickWidget(this);
quickWidget->setSource(QUrl(QStringLiteral("qrc:/Map.qml")));
quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
auto rootObj = quickWidget->rootObject();
// Create a button to invoke the QML method
QPushButton* button = new QPushButton("Call QML Method");
connect(button, &QPushButton::pressed, this, [=]() {
if (!rootObj) {
qWarning() << "Root object is null!";
return;
}
QMetaObject::invokeMethod(rootObj, "plusBtn");
});
layout->addWidget(quickWidget);
layout->addWidget(button);
setLayout(layout);
}
private:
QQuickWidget* quickWidget;
};
class MainWindow1 : public QMainWindow
{
public:
MainWindow1(QWidget* parent = nullptr)
: QMainWindow(parent)
{
mdiArea = new QMdiArea(this);
setCentralWidget(mdiArea);
QPushButton* openWindowBtn = new QPushButton("Open QML Window", this);
connect(openWindowBtn, &QPushButton::clicked, this, &MainWindow1::openQmlWindow);
setMenuWidget(openWindowBtn);
}
private:
void openQmlWindow()
{
QmlWindow* window = new QmlWindow();
QMdiSubWindow* subWindow = mdiArea->addSubWindow(window);
window->show();
}
QMdiArea* mdiArea;
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow1 mainWindow;
mainWindow.show();
return app.exec();
}
The crash occurs when I try to invoke plusBtn() after the window is added to the QMdiArea. Sometimes the application crashes with Segmentation Fault, and other times I get the message: QMetaObject::invokeMethod: No such method QQuickPinchHandler::plusBtn()
What I tried: I checked that QQuickWidget is properly initialized, and setSource(QUrl("qrc:/Map.qml")) loads the QML file without issues. The QML file loads correctly, rootObject() is not nullptr, and the method plusBtn() exists in the QML object. The crash occurs only when the widget is added to QMdiArea—if I invoke plusBtn() before adding it, it works fine, but once the widget is inside QMdiArea, calling plusBtn() sometimes causes a segmentation fault.
Debugging Output Before the Crash:
auto rootObj = ui->quickWidget->rootObject();
if (!rootObj) {
qWarning() << "Root object is null!";
return;
}
qDebug() << "Root object type:" << rootObj->metaObject()->className();
for (int i = 0; i < rootObj->metaObject()->methodCount(); ++i) {
qDebug() << "Method:" << rootObj->metaObject()->method(i).methodSignature();
}
Output:
Root object type: Map_QMLTYPE_0
Method: "plusBtn()"
I would appreciate any insights on why this might be happening and how I can resolve it.
Problem:
I have a QQuickWidget inside a QWidget, and I’m adding this widget to a QMdiArea. I am trying to call a QML function (plusBtn())
from C++ using QMetaObject::invokeMethod
. However, my application crashes when I try to invoke the method. This crash happens only when the window is added to the QMdiArea. When I remove the widget from QMdiArea, everything works fine.
Here is my simplified code:
QML (Map.qml):
import QtQuick
import QtLocation
import QtPositioning
Rectangle {
id: rectangle
Plugin {
id: mapPlugin
name: "osm"
}
Map {
id: mapView
anchors.fill: parent
plugin: mapPlugin
zoomLevel: 15
minimumZoomLevel: 2
activeMapType: supportedMapTypes[0]
}
function plusBtn() {
if (mapView.zoomLevel < mapView.maximumZoomLevel) {
mapView.zoomLevel = Math.round(mapView.zoomLevel + 1);
}
}
}
C++ (MainWindow Implementation):
#include <QDebug>
#include <QMainWindow>
#include <QPushButton>
#include <QQmlEngine>
#include <QQuickItem>
#include <QQuickWidget>
#include <QVBoxLayout>
#include <QWidget>
#include <QApplication>
#include <QMDIArea>
class QmlWindow : public QWidget
{
public:
QmlWindow(QWidget* parent = nullptr)
: QWidget(parent)
{
QVBoxLayout* layout = new QVBoxLayout(this);
// Create and initialize the QQuickWidget
quickWidget = new QQuickWidget(this);
quickWidget->setSource(QUrl(QStringLiteral("qrc:/Map.qml")));
quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
auto rootObj = quickWidget->rootObject();
// Create a button to invoke the QML method
QPushButton* button = new QPushButton("Call QML Method");
connect(button, &QPushButton::pressed, this, [=]() {
if (!rootObj) {
qWarning() << "Root object is null!";
return;
}
QMetaObject::invokeMethod(rootObj, "plusBtn");
});
layout->addWidget(quickWidget);
layout->addWidget(button);
setLayout(layout);
}
private:
QQuickWidget* quickWidget;
};
class MainWindow1 : public QMainWindow
{
public:
MainWindow1(QWidget* parent = nullptr)
: QMainWindow(parent)
{
mdiArea = new QMdiArea(this);
setCentralWidget(mdiArea);
QPushButton* openWindowBtn = new QPushButton("Open QML Window", this);
connect(openWindowBtn, &QPushButton::clicked, this, &MainWindow1::openQmlWindow);
setMenuWidget(openWindowBtn);
}
private:
void openQmlWindow()
{
QmlWindow* window = new QmlWindow();
QMdiSubWindow* subWindow = mdiArea->addSubWindow(window);
window->show();
}
QMdiArea* mdiArea;
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow1 mainWindow;
mainWindow.show();
return app.exec();
}
The crash occurs when I try to invoke plusBtn() after the window is added to the QMdiArea. Sometimes the application crashes with Segmentation Fault, and other times I get the message: QMetaObject::invokeMethod: No such method QQuickPinchHandler::plusBtn()
What I tried: I checked that QQuickWidget is properly initialized, and setSource(QUrl("qrc:/Map.qml")) loads the QML file without issues. The QML file loads correctly, rootObject() is not nullptr, and the method plusBtn() exists in the QML object. The crash occurs only when the widget is added to QMdiArea—if I invoke plusBtn() before adding it, it works fine, but once the widget is inside QMdiArea, calling plusBtn() sometimes causes a segmentation fault.
Debugging Output Before the Crash:
auto rootObj = ui->quickWidget->rootObject();
if (!rootObj) {
qWarning() << "Root object is null!";
return;
}
qDebug() << "Root object type:" << rootObj->metaObject()->className();
for (int i = 0; i < rootObj->metaObject()->methodCount(); ++i) {
qDebug() << "Method:" << rootObj->metaObject()->method(i).methodSignature();
}
Output:
Root object type: Map_QMLTYPE_0
Method: "plusBtn()"
I would appreciate any insights on why this might be happening and how I can resolve it.
Share Improve this question asked 12 hours ago i.troyeni.troyen 1011 silver badge8 bronze badges1 Answer
Reset to default 2When connecting your signal:
auto rootObj = quickWidget->rootObject();
// Create a button to invoke the QML method
QPushButton* button = new QPushButton("Call QML Method");
connect(button, &QPushButton::pressed, this, [=]() {
if (!rootObj) {
qWarning() << "Root object is null!";
return;
}
QMetaObject::invokeMethod(rootObj, "plusBtn");
});
you are making a risky assumption that the pointer to root object obtained in your QmlWindow
constructor will stay valid until the button is pressed and your lambda is invoked. Apparently that's not the case. I didn't find any line in documentation that mentions it explicitly, but rootObject
call "Returns the view's root item." and I wouldn't rely on the "view" to never be altered.
Instead obtain the rootObject
ptr directly in your callback, especially considering that you keep QQuickWidget*
as your class field anyway:
QPushButton* button = new QPushButton("Call QML Method");
connect(button, &QPushButton::pressed, this, [this]() {
auto rootObj = quickWidget->rootObject();
if (!rootObj) {
qWarning() << "Root object is null!";
return;
}
QMetaObject::invokeMethod(rootObj, "plusBtn");
});