Qt - How to get mouse events on Qt3DWindow

2019-03-03 02:14发布

问题:

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";
    }
}

回答1:

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.



回答2:

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().



标签: c++ qt qt3d