Search code examples
androidc++qtqt3dqtwidgets

Why does the Qt3D QObjectPicker not work on Android if the Qt3DWindow is embedded inside a QWidget?


I have a Qt app containing a Qt3DWindow as well as multiple QWidgets. To use both, the Qt3DWindow is embedded via QMainWindow::createWindowContainer() which works fine both on Windows and Android. This is not the case for an QObjectPicker attached to a QEntity, the QObjectPicker::clicked event is only rised on Windows, not on Android. However, if i remove the Qt3DWindow from the QMainWindow and use it 'standalone' again, QObjectPicker works as expected on both platforms.

I've tested this usecase with different Qt versions (5.10, 5.12, 5.13 beta) and different tool chains (NDK R14 with GCC, NDK R19 with Clang) without success. In some rare cases I get a QObjectPicker::clicked() event but from a touch event fare away from the screen position of the object.

To reproduce the problem, it's best to extend to "Qt 3D: Simple C++ Example". Add the following includes to main.cpp:

#include <Qt3DRender/QObjectPicker>
#include <Qt3DRender/QPickEvent>
#include <QObject>
#include <QtWidgets/QApplication>
#include <QGuiApplication>
#include <QtWidgets/QMainWindow>

Add the following code at the end of main.cpp/createScene() right before the return statement:

    Qt3DRender::QObjectPicker* picker = new Qt3DRender::QObjectPicker();
    QObject::connect(picker, &Qt3DRender::QObjectPicker::clicked, material, [material](Qt3DRender::QPickEvent *pickEvent){
                qDebug() << "Sphere  clicked";
                static_cast<Qt3DExtras::QPhongMaterial*>(material)->setAmbient(QColor(rand()%255,rand()%255,rand()%255));
            });
    sphereEntity->addComponent(picker);

To compile and deploy for Android, create a AndroidManifest.xml and remove the "density" flag from "android:configChanges". The running app should display the torus and the moving sphere and when touching the sphere the color of both changes randomly.

Next replace the QGuiApplication app(argc, argv); with QApplication app(argc, argv); in main.cpp/main() and append the following code right before the return statement:

    QMainWindow* mainWindow = new QMainWindow();
    mainWindow->resize(800, 600);
    auto centralwidget = new QWidget(mainWindow);
    mainWindow->setCentralWidget(centralwidget);
    auto container = QMainWindow::createWindowContainer(&view,mainWindow->centralWidget());
    mainWindow->show();
    container->resize(mainWindow->centralWidget()->size());

Now the Android app shows the same scene inside a widget, but touching the sphere does not change the color. (On Windows it works in contrast)

Comment out the last three lines makes the example working again:

    //auto container = QMainWindow::createWindowContainer(&view,mainWindow->centralWidget());
    //mainWindow->show();
    //container->resize(mainWindow->centralWidget()->size());

Any ideas whether this is a misconfiguration or a bug?


Solution

  • Thanks to the hint of user3405291 I finally got it working on Android by attaching an event filter to the container widget returned by QMainWindow::createWindowContainer, filtering out QEvent::MouseButtonRelease, call QScreenRayCaster::trigger with the event position as argument and receive the picked objects via QScreenRayCaster::hitsChanged. On Windows, this procedure also works, but only if the event filter is attached to the Qt3DWindow itself, not to the container widget. My assumption is, on Windows mouse events are forwarded from the container widget to the Qt3DWindow and on Android they are consumed by the container widget instead. As QObjectPicker seems to listen (only) on events on the Qt3DWindow it does not work properly on Android and there is no option to manually forward events to it. But this is only a superficial impression and I would be happy about any further clarification.