QMouseEvent 用于 QWidget 上的单次移动

Posted

技术标签:

【中文标题】QMouseEvent 用于 QWidget 上的单次移动【英文标题】:QMouseEvent for single movement on QWidget 【发布时间】:2018-10-22 07:57:56 【问题描述】:

为什么 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);

【讨论】:

这就是我在帖子中提到的解决方案。我想要解释为什么事件管理器多次传递相同的光标位置。

以上是关于QMouseEvent 用于 QWidget 上的单次移动的主要内容,如果未能解决你的问题,请参考以下文章

QT之鼠标事件

qt中mpv的鼠标点击

qt 界面去掉系统边框

绘图,不规则窗口,蝴蝶飞舞

QT:使用 QMouseEvent 从代码中选择多个 QTableWidgetItem

如何使用 QMouseEvent 在 QChart 中显示鼠标位置?