0

Why QMouseEvent passing multiple events for single movement on QWidget?

I'm implementing simple dragging effect, but the result is not what I expected.

The following code will move the widget to new location but instantly move it back to the original location.

customwidget.h

#ifndef CUSTOMWIDGET_H
#define CUSTOMWIDGET_H

#include <QWidget>
#include <fstream>

class CustomWidget : public QWidget
{
    Q_OBJECT
public:
    explicit CustomWidget(QWidget *parent = nullptr);
    ~CustomWidget();

protected:
    // define the painting agorithm to see the area of this widget
    void paintEvent(QPaintEvent* ev);

    // handle the pressing event to initialize the dragging algorithm
    // and to track the start of moving event
    void mousePressEvent(QMouseEvent* ev);

    // implement the dragging algorithm
    void mouseMoveEvent(QMouseEvent* ev);

    // handle the releasing event to track the end of moving event
    void mouseReleaseEvent(QMouseEvent* ev);

private:
    std::ofstream fout; // open file "debug.txt"
    QPoint prev; // to save the previous point of cursor.
};

#endif // CUSTOMWIDGET_H

customwidget.cpp

#include "customwidget.h"
#include <QMouseEvent>
#include <QPaintEvent>
#include <QPainter>
#include <QBrush>

CustomWidget::CustomWidget(QWidget *parent) : QWidget(parent)
{
    // open file for output
    fout.open("debug.txt");

    // set the widget size and position
    setGeometry(0, 0, 100, 100);
}

CustomWidget::~CustomWidget()
{
    // close file when program ended
    fout.close();
}

void CustomWidget::paintEvent(QPaintEvent *ev)
{
    // draw the area with blue color
    QPainter painter(this);
    QBrush brush(Qt::GlobalColor::blue);
    painter.setBrush(brush);
    painter.setBackground(brush);
    painter.drawRect(ev->rect());
}

void CustomWidget::mousePressEvent(QMouseEvent *ev)
{
    ev->accept();

    // debug output
    fout << "pressed at (" << ev->x() << ',' << ev->y() << ')' << std::endl;

    // initialize the dragging start point
    prev = ev->pos();
}

void CustomWidget::mouseMoveEvent(QMouseEvent *ev)
{
    ev->accept();

    // get the cursor position of this event
    const QPoint& pos = ev->pos();

    // debug output
    fout << "moved from (" << prev.x() << ',' << prev.y() << ") to ("
         << pos.x() << ',' << pos.y() << ')' << std::endl;

    // calculate the cursor movement
    int dx = pos.x() - prev.x();
    int dy = pos.y() - prev.y();

    // move the widget position to match the direction of the cursor.
    move(geometry().x() + dx, geometry().y() + dy);

    // update the cursor position for the next event
    prev = pos;
}

void CustomWidget::mouseReleaseEvent(QMouseEvent *ev)
{
    ev->accept();
    fout << "released at (" << ev->x() << ',' << ev->y() << ')' << std::endl;
}

main.cpp

#include "customwidget.h"
#include <QApplication>
#include <QMainWindow>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // generate simple main window.
    QMainWindow w;

    // set the size of the window.
    w.setGeometry(0, 0, 800, 800);

    // generate the CustomWidget
    CustomWidget *widget = new CustomWidget(&w);

    // display the window containing the widget
    w.show();

    return a.exec();
}

And the result of debug.txt for one single movement of cursor is

CustomWidget pressed at (79,83)
CustomWidget moved from (79,83) to (79,83)
CustomWidget moved from (79,83) to (80,83)
CustomWidget moved from (80,83) to (79,83)
CustomWidget released at (80,83)

The result is moving the widget to new location for a little time then move it back to its original location.

The look of this program will almost looked like the widget is never being moved no mater how you dragging the widget.

My theory is the event manager pass the event when you moved the cursor. But after the first event is processed, the manager passes another event related to the new location of the widget and the cursor current position. Then the process will move the widget back to where it was.

Although I can change the method of getting location of cursor from

ev->pos()

to

ev->globalPos()

to solve the problem.

But still want to know why the event manager act like that.

葉翔恩
  • 11
  • 5

1 Answers1

0

You have to do the following:

  • On mouse press event store the mouse cursor offset relative to the widget,
  • Move your widget so that the mouse cursor will always preserve the initial non zero offset,
  • Reset the offset on mouse release event.

The code (draft) might look like:

void CustomWidget::mousePressEvent(QMouseEvent* event)
{
    // m_offset is a member variable of CustomWidget
    m_offset = event->globalPos() - pos();
    QWidget::mousePressEvent(event);
}

void CustomWidget::mouseMoveEvent(QMouseEvent* event)
{
    if (!m_offset.isNull()) {
        move(event->globalPos() - m_offset);
    }
    QWidget::mouseMoveEvent(event);
}

void CustomWidget::mouseReleaseEvent(QMouseEvent* event)
{
    // Reset the offset value to prevent the movement.
    m_offset = QPoint();
    QWidget::mouseReleaseEvent(event);
}
vahancho
  • 20,808
  • 3
  • 47
  • 55
  • That's the solution i mentioned inside the post. I want an explanation why the event manager passing the same cursor position multiple time. – 葉翔恩 Oct 22 '18 at 08:22