Qt - How to get mouse events on Qt3DWindow
Asked Answered
C

2

1

I want to get mouse events (like mouse position) on a Qt3D Window, every time I click inside the window.

I've seen this question (also the same question on this forum) but my Qt3DWindow is not inside any widget, so I don't think I need an EventFilter.

I'm just beggining to learn C++ and Qt, so I'm trying to make the simplest program possible. In the code below (all my program is in this code), I would like to get the mouse position every time I click inside the Qt3D Window, but I can't even get a debug message every time I click.

As far as I understand, the mouseMoveEvent function is only called one time, when the program gets executed. How would I call this function in the main loop, if there is such a thing in Qt?

Do I need to do something like this?

Qt3DInput::QMouseDevice *mouse = new Qt3DInput::QMouseDevice(scene);

But how would I use it?

#include <QGuiApplication>

#include <Qt3DCore/QEntity>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QCameraLens>
#include <Qt3DCore/QTransform>
#include <Qt3DCore/QAspectEngine>

#include <Qt3DInput/QInputAspect>

#include <Qt3DRender/QRenderAspect>
#include <Qt3DExtras/QForwardRenderer>
#include <Qt3DExtras/QPhongMaterial>
#include <Qt3DExtras/QGoochMaterial>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QCuboidMesh>

#include <QMouseEvent>
#include <Qt3DInput/QMouseDevice>
#include <Qt3DInput/QMouseHandler>
#include <Qt3DInput/QMouseEvent>

#include <QDebug>

#include "qt3dwindow.h"

void mouseMoveEvent(Qt3DInput::QMouseEvent *event);

Qt3DCore::QEntity *createScene()
{
    // Root entity
    Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity;

    // Material
    //Qt3DRender::QMaterial *material = new Qt3DExtras::QPhongMaterial(rootEntity);
    Qt3DRender::QMaterial *material = new Qt3DExtras::QGoochMaterial(rootEntity);

    //Cube
    Qt3DCore::QEntity *cubeEntity = new Qt3DCore::QEntity(rootEntity);
    Qt3DExtras::QCuboidMesh *cubeMesh = new Qt3DExtras::QCuboidMesh;

    cubeEntity->addComponent(cubeMesh);
    cubeEntity->addComponent(material);

    return rootEntity;
}

int main(int argc, char* argv[])
{
    QGuiApplication app(argc, argv);
    Qt3DExtras::Qt3DWindow view;

    Qt3DCore::QEntity *scene = createScene();

    // Camera
    Qt3DRender::QCamera *camera = view.camera();
    camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
    camera->setPosition(QVector3D(5.0, 5.0, 5.0f));
    camera->setViewCenter(QVector3D(0, 0, 0));

    Qt3DInput::QMouseEvent *e;

    mouseMoveEvent(e);

    view.setRootEntity(scene);
    view.show();

    return app.exec();
}

void mouseMoveEvent(Qt3DInput::QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        qDebug() << "ok";
    }
}
Comitative answered 19/7, 2018 at 12:26 Comment(0)
E
2

You're doing a couple of things wrong here.

First of all, I would advise you to subclass Qt3DWindow and put the setup code for the scene there. That's the views responsibility after all. The main should stay simple and clean. Of course don't put any important logic in the view, but the setup code should be there (stick to Model-View-Controller).

Next, of course you're not getting any debug message, because you have this check in there

if (event->button() == Qt::LeftButton)

but you create the event without ever setting the button, so event->button() will never be equal to Qt::LeftButton.

I wrote some example code to get you started with the events:

main.cpp:

#include <QApplication>
#include "clickwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ClickWindow clickWindow;
    clickWindow.show();
    return a.exec();
}

clickwindow.h (I left out the header guards to save some space):

#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DCore/QEntity>

class ClickWindow : public Qt3DExtras::Qt3DWindow {
public:
    ClickWindow();
    // Here we say we want to have a custom handling of click events
    void mousePressEvent(QMouseEvent *eventPress) override;

private:
    Qt3DCore::QEntity *createScene();
};

clickwindow.cpp:

#include "clickwindow.h"
#include <QDebug>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QMaterial>
#include <Qt3DExtras/QGoochMaterial>
#include <Qt3DExtras/QCuboidMesh>

ClickWindow::ClickWindow() : Qt3DExtras::Qt3DWindow() {
    // You could also create a dedicated setup method
    setRootEntity(createScene());
    Qt3DRender::QCamera *camera = this->camera();
    camera->lens()->setPerspectiveProjection(45.0f, 16.0f/9.0f, 0.1f, 1000.0f);
    camera->setPosition(QVector3D(5.0, 5.0, 5.0f));
    camera->setViewCenter(QVector3D(0, 0, 0));
}

void ClickWindow::mousePressEvent(QMouseEvent *eventPress) {
    // No need to pass it on to the parent! It will receive it
    // anyway. You can stop event propagation by calling
    // eventPress->accept();
    // This is where the click is received
    qDebug() << "Click!";
}


Qt3DCore::QEntity* ClickWindow::createScene() {
    // Root entity
    Qt3DCore::QEntity *rootEntity = new Qt3DCore::QEntity;

    // Material
    //Qt3DRender::QMaterial *material = new Qt3DExtras::QPhongMaterial(rootEntity);
    Qt3DRender::QMaterial *material = new Qt3DExtras::QGoochMaterial(rootEntity);

    //Cube
    Qt3DCore::QEntity *cubeEntity = new Qt3DCore::QEntity(rootEntity);
    Qt3DExtras::QCuboidMesh *cubeMesh = new Qt3DExtras::QCuboidMesh;

    cubeEntity->addComponent(cubeMesh);
    cubeEntity->addComponent(material);

    return rootEntity;
}

You could also handle clicks using the Qt3D classes and follow this example here, but to me, depending on your application, this seems a bit over the top. If you simply want to receive the 2D screen coordinates I'd prefer the solution above. If you need custom inputs for e.g. clicking on an object I answered a question regarding this here or if you simply need the 3D coordinates clicked on an object you can use the QObjectPicker class.

If you encounter any QML code remember that QML code simply instantiates C++ classes, i.e. you can transfer the examples, sometimes with small minor changes in naming, etc.

Evolutionary answered 21/7, 2018 at 8:46 Comment(2)
Thanks, it worked! But how do I use any of the QMouseEvent functions, like button() or pos()? I've tried qDebug() << eventPress->pos(); and it does not work.Comitative
It works for me. You have to add the line #include <QMouseEvent>, otherwise the class is not complete because it's only included by the parent class.Evolutionary
A
0

Normally the "mouseMoveEvent" is bind with a Qwidget.

This is the source code of Qt3DInput::QmouseEvent:

class QT3DINPUTSHARED_EXPORT QMouseEvent : public QObject {
    Q_OBJECT 
private:
    QT_PREPEND_NAMESPACE(QMouseEvent) m_event;  
};

m_event has already contained the information of your mouse and it should be automatically processed.

Mouse events occur when a mouse button is pressed or released inside a widget, or when the mouse cursor is moved.

So, I don't think you need to create a new Qt3DInput::QMouseDevice. And you shouldn't do Qt3DInput::QMouseEvent *e; since the widget will get it automatically.

I don't know how to use mouseEvent without overriding it inside of a Qwidget, and Qt3DExtras::Qt3DWindow view should contain a default mouseMoveEvent(). If you just want to print out an "ok", I suggest you don't write anything about mouseMoveEvent(). If you hope something happens when your mouse is moving, you should override view's mouseMoveEvent().

Augmenter answered 20/7, 2018 at 4:11 Comment(2)
I think I understand what you said, but I still don't know how and where I can get something to happen when I have a mouse event. The "ok" message to debug is just to see if it works, but I plan to implement actual functions and actions in the future.Comitative
Because each widget has their own mouseMoveEvent(). So you shouldn't write it outside the widget class. You should override mouseMoveEvent inside the widget. Try to create a class whose parent is Qt3DWindow. And Implement mouseMoveEvent() inside it.Augmenter

© 2022 - 2024 — McMap. All rights reserved.