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";
}
}
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.
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()
.