如何使用 Qt 防止 QCursor::setPos() 上的 mouseMoveEvent?

Posted

技术标签:

【中文标题】如何使用 Qt 防止 QCursor::setPos() 上的 mouseMoveEvent?【英文标题】:How to prevent mouseMoveEvent on QCursor::setPos() using Qt? 【发布时间】:2015-11-11 14:09:46 【问题描述】:

我目前正在开发图像查看器应用程序。在这个应用程序中,我有一个所谓的“平移缩放”功能。这意味着,当按住某个鼠标按钮时,用户可以通过前后平移来缩放图像。

它工作正常,但是当使用该功能时,鼠标(自然地)在屏幕上上下移动,并且会在某个点到达屏幕边界,这将使其停止。相反,我想要一种鼠标保持静止并且只有图像放大率发生变化的行为。

我试图通过在QWidget::mouseMoveEvent 中调用QCursor::setPos 来实现这一点,并在我处理完移动后将鼠标重置到初始位置。只要鼠标几乎保持静止(它来回摆动),它就可以工作。但是,这将导致再次调用鼠标移动事件,从而有效地取消我刚刚所做的调整。这将导致“摆动”效果。每次调整都会立即撤销。

这是一段代码,因此您可以了解我在做什么:

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

有没有办法在使用QCursor::setPos时防止鼠标移动事件发生?

【问题讨论】:

QCursor::setPos() 将导致mouseMoveEvent(),原因。也许你想检查光标是否到达屏幕的边缘,例如,如果光标在屏幕的上边缘,无论光标是否移动,都保持放大/缩小。 那么我该如何获得增量呢?增量是通过鼠标在初始位置的 y 坐标与当前位置的差来计算的。如果用户继续向上移动,但鼠标因为在屏幕边缘而不再移动,则 y 坐标不会再有任何差异。 您可以获取桌面小部件光标位置并隐藏实际光标以获得更好的用户体验并在鼠标释放时重置鼠标位置 【参考方案1】:

假设您没有调用基类mouseMoveEvent,您应该accept the event 将其标记为正在处理。默认情况下,当您重新实现事件时会接受它们,但更明确的是更清楚。致电e->accept( )

还建议如果您处理任何鼠标事件,you should handle all,鼠标双击可能例外。

这是一个保持鼠标不动的示例,尽管在 OS X 上偶尔会出现闪烁,这似乎是由于 Qt 处理事件的方式

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
;

如果您不介意保持鼠标不动,请取出 QCursor::setPos 调用,当光标在小部件之外时,它仍会收到移动事件,同时按住鼠标按钮。

不过,缩放时hiding the cursor 的用户体验可能会更好。

【讨论】:

嗯..这并没有真正改变任何东西。我也不明白为什么应该这样做。我的意思是,光标是由操作系统移动的。我的程序只是对该移动事件做出反应。接受事件不会阻止鼠标移动。 无论如何我都想隐藏光标。我的问题也不是鼠标没有保持静止,实际上它是(那部分有效)。我也知道,当我在小部件之外时,我仍然会收到鼠标移动事件。我要防止的问题是当我到达屏幕边缘时会发生什么。然后鼠标将不再移动,因此鼠标位置的 y 坐标将不再变化。由于我计算了鼠标移动事件中的缩放增量超出了那个 y 坐标,因此不会发生进一步的放大,因为 y 坐标保持不变。 现在,我试图通过在每次 moues 移动事件后将鼠标重置到初始位置来规避这个问题。然而,这被处理为好像用户将鼠标移回,并触发一个新的鼠标移动事件,有效地取消最后一次移动。这是我必须以某种方式防止的。 如果将光标保持在同一位置,但将其隐藏,则光标不会超出窗口边框。所以有什么问题?它是否计算用于缩放的增量,因为调用 QCursor::setPos() 正在创建另一个移动事件? 是的。基本上是这样的: 鼠标移动 +5px > 鼠标移动事件发生并且缩放增加了相当于 +5px 的值 > 现在光标比最初高 5px > 调用 QCursor::setPos(initialPos) > 光标移动到初始位置,即 -5px > 鼠标移动事件发生,缩放值减少相当于那些 -5px > 一切都在重新开始。【参考方案2】:

我会有一个标志来禁用该事件,默认情况下为 false。

在事件内部检查flag是否为false,然后执行缩放操作,设置flag为true并重置光标。

然后将再次调用该事件并且标志将为真,因此您将标志设置为假,您将准备好处理下一个事件。

您只需确保在从 setCursor 调用接收事件之前,您没有两次或多次调用从实际鼠标触发的鼠标事件。

【讨论】:

这可能会起作用,但不知何故,我想要一个更...“整洁”的解决方案。这样我可能会错过其他鼠标移动事件。【参考方案3】:

不要在鼠标事件中使用 event->pos(),而是使用 QCursor::pos() 并检查它是否改变了。像这样:

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;

【讨论】:

以上是关于如何使用 Qt 防止 QCursor::setPos() 上的 mouseMoveEvent?的主要内容,如果未能解决你的问题,请参考以下文章

如何防止用户使用 Python 和 Qt 在 QtableWidget 中移动列的大小?

如何防止 Qt 对 QListView 中的图标进行 alpha 混合选择?

Qt Tooltip如何防止文本在内置延迟后消失?

当我使用Qt Android Extras C ++ Classes时,如何防止Windows上的错误

如何防止 Android 设备从 Qt 应用程序进入睡眠状态

如何防止在 Qt 中的 deleteChildren() 期间删除孩子?