How to fix: custom QGraphicsItem receiving mousePressEvent coordinates late/laggy?

821 Views Asked by At

I have a "standard" Qt5 QWidgets application, with a MainWindow that includes a QGraphicsView in the mainwindow.ui created in QtCreator. That QGraphicsView has its scene set to a simple subclass of QGraphicsScene, which has a big rectangle in the background that is a subclass of QGraphicsRectItem which reimplements the mousePressEvent() and mouseReleaseEvent() handlers of the QGraphicsRectItem. Running on Ubuntu 18.04, which shouldn't matter, but just incase...

Everything is working, except... the 2nd and later times I press the left (or any) mouse button, the coordinates reported in the mousePressEvent's QGraphicsSceneMouseEvent buttonDownScenePos are "stale" - the same as the previous mouse click, not the new location where the mouse is when the new click happened. The mouseReleaseEvent reports coordinates as expected.

Is there any way to get the buttonDownScenePos of the mousePressEvent to stay current with the actual position of the mouse when clicked, instead of the previous mouse location?

I feel like I have dealt with a similar issue in the past which had something to do with double-click processing, that the event is reported before it knows if a double-click has happened or not. In this instance, double-click events are not important, but it would be nice to be able to respond to the single click as soon as it happens, instead of waiting for the release event.

Relevant code:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

class Board;
class BoardScene;
#include <QMainWindow>
#include <QPointer>
#include "board.h"
#include "boardscene.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{ Q_OBJECT
public:
    explicit  MainWindow(QWidget *parent = nullptr);
             ~MainWindow();
        void  drawBoard();

private:
     Ui::MainWindow *ui;
     QPointer<Board> board;
QPointer<BoardScene> boardScene;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    board      = new Board( this );
    boardScene = new BoardScene( board, this );
    ui->boardView->setScene( boardScene );
    ui->boardView->setDragMode( QGraphicsView::ScrollHandDrag );
    ui->boardView->scale( 40.0, 40.0 );
    drawBoard();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::drawBoard()
{ }

boardscene.h

#ifndef BOARDSCENE_H
#define BOARDSCENE_H

class Board;
#include <QGraphicsScene>
#include "board.h"
#include "boardrect.h"

class BoardScene : public QGraphicsScene
{ Q_OBJECT
public:
            BoardScene( Board *pbp, QObject *parent = nullptr );
      void  drawGrid();

     Board *bp;
    QBrush  backBrush,blackBrush,whiteBrush;
      QPen  linePen;
};

#endif // BOARDSCENE_H

boardscene.cpp

#include "boardscene.h"
#include <QGraphicsLineItem>
#include <QGraphicsRectItem>

BoardScene::BoardScene( Board *pbp, QObject *parent ) : QGraphicsScene ( parent )
{ bp = pbp;
  backBrush  = QBrush( QColor( 224,152, 64 ) );
  blackBrush = QBrush( QColor(   0,  0,  0 ) );
  whiteBrush = QBrush( QColor( 255,255,255 ) );
  linePen    = QPen  ( QColor(   0,  0,  0 ) );
  linePen.setWidth( 0 );
  drawGrid();
}

void BoardScene::drawGrid()
{ QGraphicsLineItem *lip;
  BoardRect *rip;
  setBackgroundBrush( blackBrush );
  rip = new BoardRect( QRectF( -2.0, -2.0, (qreal)(bp->Xsize +3), (qreal)(bp->Ysize + 3) ), nullptr );
  rip->setBrush( backBrush );
  rip->setPen( linePen );
  addItem( rip );

  for ( int x = 0; x < bp->Xsize; x++ )
    { lip = addLine( QLineF( (qreal)x, 0.0, (qreal)x, (qreal)(bp->Ysize - 1) ), linePen );
      lip->setAcceptedMouseButtons( Qt::NoButton );
    }
  for ( int y = 0; y < bp->Ysize; y++ )
    { lip = addLine( QLineF( 0.0, (qreal)y, (qreal)(bp->Xsize - 1), (qreal)y ), linePen );
      lip->setAcceptedMouseButtons( Qt::NoButton );
    }
}

boardrect.h

#ifndef BOARDRECT_H
#define BOARDRECT_H

#include <QGraphicsRectItem>
#include <QGraphicsSceneMouseEvent>


class BoardRect : public QGraphicsRectItem
{
public:
          BoardRect( const QRectF &rect, QGraphicsItem *parent = nullptr );
         ~BoardRect() {}

protected:
    void  mousePressEvent(QGraphicsSceneMouseEvent *event);
    void  mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};

#endif // BOARDRECT_H

boardrect.cpp

#include "boardrect.h"

BoardRect::BoardRect( const QRectF &rect, QGraphicsItem *parent ) : QGraphicsRectItem( rect, parent )
{}

void  BoardRect::mousePressEvent(QGraphicsSceneMouseEvent *event)
{ QString msg = QString("press %1 %2").arg(event->buttonDownScenePos(event->button()).rx())
                                      .arg(event->buttonDownScenePos(event->button()).ry());
  qDebug( qPrintable( msg ) );
  QGraphicsRectItem::mousePressEvent(event);
  event->accept();
}

void  BoardRect::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{ QString msg = QString("release %1 %2").arg(event->buttonDownScenePos(event->button()).rx())
                                        .arg(event->buttonDownScenePos(event->button()).ry());
  qDebug( qPrintable( msg ) );
  QGraphicsRectItem::mousePressEvent(event);
  event->accept();
}

On the first click after running, the reported coordinates agree well with the location on the grid where the mouse was clicked, both for press and release - they both show where the button went down.

However, on the 2nd and later clicks, the mousePressEvent reports the same coordinates as the previous mousePress and Release events, while the mouseReleaseEvent reports the coordinates where the mouse button "went down" in the current event.

One final bit of weirdness: when clicking left, then right, then left again, the coordinate reported by mousePressEvent for the 2nd left click is the previous left click coordinate, skipping over the right click coordinate to go back to where the mouse button went down on the last left click.

Any ideas? Thanks.

2

There are 2 best solutions below

0
On

QGraphicsSceneMouseEvent::buttonDownPos(Qt::MouseButton button)

Returns the mouse cursor position in item coordinates where the specified button was clicked.

It returns the coordinates from the graphics item you are clicking on (as the documentation says). Maybe you just click in the same spot? If you want the scene position, just use mapToScene or QGraphicsSceneMouseEvent::buttonDownScenePos(Qt::MouseButton button) or event->scenePos().

PS. and use QPointF::x() and QPointF::y() instead of rx() and ry(). You don't need a reference and manipulate the position.

0
On

I have an app using the same components: a qgraphicsview and a qgraphicsscene composed by some number of qgraphicsitems. It is a virtual MIDI piano keyboard just in case you want to take a look to the code. In my case, all the qgraphicsitems (the piano keys) have setAcceptedMouseButtons(Qt::NoButton), and the mouse events are handled at the scene level instead of the graphics item. I've never observed a problem like yours.