I have a subclass of QGraphicsView that should accept two kinds of mouse events: drag and drop for scrolling and simple clicks for item selection/highlight.
So I use
setDragMode(QGraphicsView::ScrollHandDrag);
to enable scrolling the view with the "Hand". And I have a function like this:
void GraphView::mouseReleaseEvent(QMouseEvent* e)
{
if (e->button() == Qt::LeftButton)
emit leftClicked(mapToScene(e->pos()));
else
emit rightClicked(mapToScene(e->pos()));
QGraphicsView::mouseReleaseEvent(e);
}
which creates signal whenever the user clicks on the scene.
However, the problem is: when I stop dragging and release the mouse button, the mouseReleaseEvent function is called, and if the cursor happens to be over some element of the scene, it will get highlighted.
How can I changed the mouseReleaseEvent function so that the signals are created only if there was no previous drag of the mouse?
If you use mousePress and mouseMove in combination with mouseRelease, then you can determine what mouse action the user just performed.
If you have mousePress then mouseRelease, then it must be a simple click.
If you have mousePress, mouseMove, and then mouseRelease, then it must be a drag.
The Qt documentation contains an example of interpreting combinations of mouse events in action in a scribbling program.
http://doc.qt.io/qt-4.8/qt-widgets-scribble-example.html
You can extend the principle to something like this:
private bool MyWidget::dragging = false;
private bool MyWidget::pressed = false;
void MyWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
pressed=true;
}
QGraphicsView::mousePressEvent(event)
}
void MyWidget::mouseMoveEvent(QMouseEvent *event)
{
if ((event->buttons() & Qt::LeftButton) && pressed)
{
dragging=true;
}
QGraphicsView::mouseMoveEvent(event)
}
void MyWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton && pressed) {
pressed = false;
if (dragging)
{
dragging = false;
}
else
{
// plain mouse click
// do something here
}
}
QGraphicsView::mouseReleaseEvent(event)
}
Note that this does not address edge cases where a user's mouse action is performed only partially inside the widget. I must also admit that I am relatively new to Qt and have not yet used ScrollHandDrag, but this is how one would go about identifying a certain combination of mouse events.
Related
I am trying to implement mouse wheel zoom in/out. It works BUT when I zoom in/out, the image is getting smaller AND scrolls up/down at the same time with zoom function. Look like events exist at the same time and work together.
I cannot find how to disable scrolling with mouse wheel. May be there is a way to make possible scrolling only with mouse cursor (by clicking on scrollbar).
I was overriding the main method of mouse wheel but it was causing the effect I wrote above
void MainWindow::wheelEvent( QWheelEvent* event )
Solved by using event filter. The code below provides zoom in/out with holding ctrl button.
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::GraphicsSceneWheel)
{
ui->GV_image->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
double scaleFactor = 1.15;
bool ok = QApplication::keyboardModifiers() & Qt::ControlModifier;
if (ok)
{
QGraphicsSceneWheelEvent *scrollevent = static_cast<QGraphicsSceneWheelEvent *>(event);
if (scrollevent->delta() > 0)
{
ui->GV_image->scale(scaleFactor, scaleFactor);
}
else
{
ui->GV_image->scale(1/scaleFactor, 1/scaleFactor);
}
}
event->accept();
return true;
}
return false;
}
Put this line into your constructor or other your init function
QGraphicsView *GV_image;
...
ui->GV_image->scene()->installEventFilter(this);
I have to make a virtual keyboard.
So I created few QPushButtons, but if I click on one, and move to release on another, then the other button doesn't receive any mouse event.
I to override enterEvent(QEvent *) and leaveEvent(QEvent *), but without success when I have clicked on another button.
Anyone got an idea?
In QAbstractButton code:
void QAbstractButton::mousePressEvent(QMouseEvent *e)
{
Q_D(QAbstractButton);
if (e->button() != Qt::LeftButton) {
e->ignore();
return;
}
if (hitButton(e->pos())) {
setDown(true);
d->pressed = true;
repaint(); //flush paint event before invoking potentially expensive operation
QApplication::flush();
d->emitPressed();
e->accept();
} else {
e->ignore();
}
}
When mousePressEvent triggers, QAbstractButton calls setDown(true) and QAbstractButtonPrivate ( Q_D(QAbstractButton) ) calls d->emitPressed(). After that, others buttons will not receive any events, they are already handled in the active button.
You can see in mouseReleaseEvent
void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
{
Q_D(QAbstractButton);
d->pressed = false;
if (e->button() != Qt::LeftButton) {
e->ignore();
return;
}
if (!d->down) {
// refresh is required by QMacStyle to resume the default button animation
d->refresh();
e->ignore();
return;
}
if (hitButton(e->pos())) {
d->repeatTimer.stop();
d->click();
e->accept();
} else {
setDown(false);
e->ignore();
}
}
It also checks state d->down. And if mouse hits that button, it will call d->click(); emit click event. If mouse does not hit button, it call setDown(false); to reset state.
I suggest to inherit virtual button from QWidget for easier handle mouse events.
I've got this test piece of code in mainwindow.cpp:
bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseMove)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
qDebug() << QString("Mouse move (%1,%2)").arg(mouseEvent->pos().x()).arg(mouseEvent->pos().y());
}
return false;
}
I just want to get mouse position when clicked, and send the coordinates to another thread with an OpenCV loop that'll pull HSV information and do things accordingly. I'm using mouse over just for testing.
The problem is that I have no idea how to attach this (tracking, clicking) to a QLabel labelKalibracja, one I use to display video frames, instead of the whole window.
ui->labelKalibracja->installEventFilter(this);
is supposed to work, but doesn't, but
qApp->->installEventFilter(this);
Will make the whole window a mouse track zone.
You should check the object of the event filter :
if (qobject_cast<QLabel*>(obj)==ui->labelKalibracja && event->type() == QEvent::MouseMove)
{
...
}
Now you can make sure that the event is for the label. Note that the event filter could be installed on multiple objects and it's your duty to identify the combination of objects and events.
QGraphicsview has a method setDragMode(ScrollHandDrag) to enable panning with the left mouse click. But I wanted to enable panning when the mouse wheel is clicked(the middle button) and created the following custom implementation to pan:
//Within a custom derived class of QGraphicsView
//pan is true when wheel is clicked and false when released
//last Pos is defined somewhere else in the class
void GridView::mouseMoveEvent(QMouseEvent *event){
if(pan){
QPointF currentPos = event->pos();
QPointF relPos = currentPos - lastPos;
lastPos = currentPos;
//this is what creates the panning effect
translate(relPos.x(), relPos.y());
}
QGraphicsView::mouseMoveEvent(event);
}
This works fine for the most part. But for example, if I scale the identity matrix by 1,000,000 this method fails and stops panning (as if the view got stuck). This problem doesn't arise when I use setDragMode()
What would be the proper custom implementation of setDragMode() so it is enabled by the wheel click?
This works for me... it tricks Qt into thinking the user is pressing the left mouse button when he's really pressing the middle one.
void GridView :: mousePressEvent(QMouseEvent * e)
{
if (e->button() == MidButton)
{
QMouseEvent fake(e->type(), e->pos(), LeftButton, LeftButton, e->modifiers());
QGraphicsView::mousePressEvent(&fake);
}
else QGraphicsView::mousePressEvent(e);
}
void GridView :: mouseReleaseEvent(QMouseEvent * e)
{
if (e->button() == MidButton)
{
QMouseEvent fake(e->type(), e->pos(), LeftButton, LeftButton, e->modifiers());
QGraphicsView::mouseReleaseEvent(&fake);
}
else QGraphicsView::mouseReleaseEvent(e);
}
I have problems in PySide while trying to determine which mouse button is pressed in event function. I need it in particular for ignoring mouse move event, because it's doing job on both mouse buttons, left and right.
I want to ignore mouse move event if the right button on scene is pressed. Any help?
All of mouse events have two methods (button and buttons) to determine which of buttons are pressed. But for only move event the documentation says:
Note that the returned value is always Qt::NoButton for mouse move events.
for mouseMoveEvent you should use buttons method.
void mouseMoveEvent(QMouseEvent *e)
{
if(e->buttons() == Qt::RightButton)
qDebug() << "Only right button";
}
In order to ignore move events you need to do this work in eventFilter of course.
QApplication::mouseButtons() will return the status of mouseButton,
so, you can get the status of mouse in KeyPressEvent.
you can check, which mouse button is pressed via Qt::RightButton. Sorry for c++ code, but i hope, you would understand idea anyway:
void mousePressEvent(QMouseEvent *event)
{
if (event->button()==Qt::RightButton){
qDebug() << "right button is pressed
}
}
You could use a boolean:
void mousePressEvent(QMouseEvent *event)
{
if (event->button()==Qt::RightButton){
qDebug() << "right button is pressed
pressed=true; //<-----
}
}
and on mouseMoveEvent
void GLWidget::mouseMoveEvent(QMouseEvent *event)
{
float dx = event->x() - lastPos.x(); // where lastpos is a QPoint member
float dy = event->y() - lastPos.y();
if (dx<0) dx=-dx;
if (dy<0) dy=-dy;
if (event->buttons() & Qt::LeftButton) { //if you have MOVEd
...do something
}
if (event->buttons() & Qt::RightButton) {
if (pressed==true) return;
else{
...do
}
}
}
On mouserelease you have to set pressed=false; ( "pressed" must be a member of the class)
Hope it helps,let me know