QMouseEvent для одиночного движения на QWidget

Почему QMouseEvent передает несколько событий для одного движения в QWidget?

Я реализую простой эффект перетаскивания, но результат не тот, который я ожидал.

Следующий код переместит виджет в новое место, но мгновенно вернет его в исходное место.

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();
}

И результат debug.txt для одного движения курсора

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)

В результате на некоторое время виджет перемещается на новое место, а затем возвращается на прежнее место.

Внешний вид этой программы будет выглядеть почти так, как будто виджет никогда не перемещается, независимо от того, как вы перетаскиваете виджет.

Моя теория заключается в том, что менеджер событий передает событие, когда вы перемещаете курсор. Но после обработки первого события менеджер передает другое событие, связанное с новым местоположением виджета и текущей позицией курсора. Затем процесс переместит виджет обратно туда, где он был.

Хотя я могу изменить способ получения местоположения курсора из

ev->pos()

в

ev->globalPos()

решить проблему.

Но все же хочу знать, почему менеджер событий так себя ведет.

1 ответ

Вы должны сделать следующее:

  • При событии нажатия мыши сохранить смещение курсора мыши относительно виджета,
  • Переместите свой виджет так, чтобы курсор мыши всегда сохранял исходное ненулевое смещение,
  • Сбросить смещение при отпускании мыши.

Код (черновик) может выглядеть так:

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);
}
Другие вопросы по тегам