Reputation: 4082
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
?
Upvotes: 5
Views: 3585
Reputation: 1
Don't use event->pos() in mouse events, use QCursor::pos() intead and check if it changed. Like this:
void MyWidget::mousePressEvent(QMouseEvent *)
{
mPrevPos=QCursor::pos();
mMoving=false;
}
void MyWidget::mouseMoveEvent(QMouseEvent *)
{
auto cursorPos=QCursor::pos();
if(mPressedPos==cursorPos){
return;
}
if(!mMoving
&& (cursorPos-mPrevPos).manhattanLength()>QApplication::startDragDistance()){
mMoving=true;
}
if(mMoving){
auto diff=cursorPos-mPrevPos;
// move something using diff
QCursor::setPos(mPrevPos);
}
}
void MyWidget::mouseReleaseEvent(QMouseEvent *)
{
mMoving=false;
}
void MyWidget::leaveEvent(QEvent *)
{
mMoving=false;
}
Upvotes: 0
Reputation: 27611
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.
Upvotes: 1
Reputation: 1353
I would have a flag to disable the event with will be false by default.
inside the event check if flag is false, then perform the zoom operation, set flag to true and reset cursor.
then the event will be called again and the flag will be true, so you set flag to false and you will be ready to handle the next event.
You just have to make sure you dont have two or more calls to the mouse event firing from the actual mouse before receiving the event from the setCursor call.
Upvotes: 1