Search code examples
c++qtmouse-positionqcursor

How to prevent mouseMoveEvent on QCursor::setPos() using Qt?


I am currently developing on an image viewer application. In this application I have a so called "pan-zoom" feature. This means that, when holding a certain mouse button, the user can zoom the image by panning forth and back.

It works fine, but as the feature is used, the mouse (naturally) moves up and down on the screen and will at some point reach the screen borders, which will make it stop. Instead I would like a behaviour where the mouse remains stationary and only the image magnification changes.

I tried to achieve this by invoking QCursor::setPos inside the QWidget::mouseMoveEvent and reset the mouse to the initial position after I have processed the move. It works so far as that the mouse is staying nearly stationary (it's wiggling forth and back). However, this will cause the mouse move event to be called again effectively annulling the adjustment I just made. This will result in a "wiggling" effect. Every adjustment will immediately be reversed.

Here is a code snipped, so you get an idea of what I am doing:

void ImageView::mouseMoveEvent(QMouseEvent *e) {
    //some code
    if (_panZooming) {
        //some code here

        //doesn't work as expected because it invokes this event again
        QCursor::setPos(mapToGlobal(_initialMousePosition.toPoint()));
    }
}

Is there a way to prevent the mouse move event to happen when using QCursor::setPos?


Solution

  • Assuming you're not calling the base class mouseMoveEvent, you should accept the event to mark it as being handled. By default, they're accepted when you re-implement the event, but it's clearer to be explicit. Call e->accept( ).

    It's also recommended that if you handle any of the mouse events, you should handle all, with the possible exception of mouse double click.

    Here's an example of keeping the mouse still, though on OS X there's an occasional flicker which appears to be due to how Qt is handling the events

    class MyWidget : public QWidget
    {
        void mousePressEvent(QMouseEvent* e)
        {
            m_pos = e->globalPos();
            m_lastPos = m_pos;
            QWidget::mousePressEvent(e);
        }
    
        void mouseMoveEvent(QMouseEvent* e)
        {
           // Calculate  relative zoom factor
           // scaled down ( / 10 ) for image zooming
    
            m_zoomFactor += ((float)e->globalPos().y() - m_lastPos.y()) / 10;
    
            QCursor::setPos(m_pos);
            m_lastPos = m_pos;
            e->accept();
    
            qDebug() << m_zoomFactor << endl;
        }
    
        void mouseReleaseEvent(QMouseEvent* e)
        {
            QWidget::mouseReleaseEvent(e);
        }
    
    private:
        QPoint m_pos;
        QPoint m_lastPos;
    
        float m_zoomFactor = 0; // C++ 11 initialisation
    };
    

    If you're not bothered at keeping the mouse stationary, take out the QCursor::setPos call and this will still receive move events when the cursor is outside the widget, whilst the mouse button is held down.

    However, it may be a better user experience hiding the cursor when zooming.