检测QMainWindow / QDialog(Qt 4.8)的移动结束

3
我正在使用X11上的Qt 4.8.3。
我需要知道当用户结束拖动窗口时,以便读取最终位置,并可能开始动画来调整窗口位置到“允许”的位置。
我注意到每次小移动都会调用QWidget :: moveEvent,但这非常不方便,因为我必须在用户释放鼠标按钮并且移动完全完成时执行位置检查(并可能启动动画)。
这是真正的问题:似乎没有办法检测鼠标释放事件(或获取鼠标按钮状态),当用户单击标题栏时,因为它由操作系统控制而不是Qt。 我还尝试了QWidget :: x11event(XEvent * e)...但事件仅在窗口内部而不是标题栏中收集。
有人知道实现此目的的方法吗?
我怀疑我将不得不自己重新实现标题栏...太糟糕了...

你可能还应该考虑用户使用键盘移动窗口的情况。(至少在Windows中是可以的,我不知道X11。) - user362638
无论以何种方式移动小部件,QMoveEvent都会在每次移动时发布。但是,还有一个QResizeEvent,它可能也会影响窗口中的动画。 - divanov
3个回答

6

虽然这是一个很老的问题,但当你尝试“Qt检测窗口移动事件结束”时,它是第一条搜索结果。因此,我想添加一个与当前(截至本文写作)的Qt 5.12.3版本兼容的解决方案。

您可以设置一个小型状态机,使用 QObject::eventFilter() 来确定顶级窗口位置何时改变。在Qt 5.12.x中,当鼠标按下非客户区域(例如标题栏)时,您将收到一个 QEvent::NonClientAreaMouseButtonPress 事件,在窗口位置改变(如果有)时会接收到后续的 QEvent::Move 事件,最后当鼠标按钮释放时会收到一个 QEvent::NonClientAreaMouseButtonRelease 事件。

知道了这个序列,并使用一个持久的布尔状态标志(user_moved_window)来确定位置是否实际发生变化,您可以在 QObject::eventFilter() 方法内得到以下代码片段:

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
    QEvent::Type event_type = event->type();
    [...]
    else if(event_type == QEvent::NonClientAreaMouseButtonPress)
        user_moved_window = false;
    else if(event_type == QEvent::Move && isVisible())
        user_moved_window = true;
    else if(event_type == QEvent::NonClientAreaMouseButtonRelease)
    {
        if(user_moved_window)
        {
            // do what you need to do to here to respond to
            // the end of the reposition event...

            user_moved_window = false;
        }
    }
    [...]
    return MainWindow::eventFilter(obj, event);
}

根据你的情况,你可能需要添加一些额外的检查--例如,确保事件的obj实际上是主窗口--但此示例适用于我使用Qt 5.12.3的生产代码。


5

我和你遇到了同样的问题。moveEvent会在移动过程中的每个点上触发,Qt没有提供明确的方法来确定移动结束。

但是现在,受到divanov的答案启发,我发现当我们移动对话框后释放鼠标,一个类型为173的事件将始终被触发。这是QEvent::NonClientAreaMouseMove。

所以代码很简单。

首先安装事件过滤器并声明一个成员变量:int nLastEvent;

bool Win::eventFilter(QObject *obj, QEvent *event)
{
    if (nLastEvent == QEvent::Move && event->type() == 173)
    {
        // do what you wanna do here when the mouse is released,
        // like attaching the dialog to the main window
    }
    nLastEvent = event->type();
    return QWidget::eventFilter(obj, event);
}

它简单而高效,是不是!

希望对您也有用。:)


你的例子在我的桌面上无法运行,导致应用程序不具备可移植性。从我的日志中可以看出,没有QEvent::NonClientAreaMouseMove。你使用的是什么发行版和窗口管理器? - divanov
我正在使用Qt 4.8.3和Win7。在你的日志中,释放是自动检测到的。但这并不是Matteo Murgida遇到的情况,尽管你们共享相同的开发环境。然而,我的问题类似,并且已经解决了。我只是提供了一个不同的想法。 :-) - liulios
如果是的话,那我给出的答案有帮助你解决问题吗?它值得点个赞吗? - divanov
是的,但我的声誉还不够...我只是注册了一个账号来分享我的经验。 - liulios

2

让我们考虑以下测试应用程序: main.cpp

#include <QApplication>

#include "win.h"

int main(int argc, char** argv)
{
    QApplication app(argc, argv);

    Win w;
    w.show();

    return app.exec();
}

win.h:

#include <QWidget>
#include <QEvent>
#include <QMoveEvent>
#include <QDebug>

class Win : public QWidget
{
public:
    Win(QWidget *parent = 0) : QWidget(parent) {
        this->installEventFilter(this);
    }

protected:
    bool eventFilter(QObject *obj, QEvent *event) {
        if (event->type() == QEvent::Move) {
            QMoveEvent *moveEvent = static_cast<QMoveEvent*>(event);
            qDebug() << "Move event:" << moveEvent->pos();
        } else {
            qDebug() << "Event type:" << event->type();
        }
        return QWidget::eventFilter(obj, event);
    }
};

这个应用程序只在自身上安装事件过滤器,并使用特殊格式打印所有接收到的事件到控制台,以区分QMoveEvent。

典型日志:

Event type: 203 
Event type: 75 
Move event: QPoint(0,0) 
Event type: 14 
Event type: 17 
Event type: 26 
Event type: 74 
Event type: 77 
Move event: QPoint(66,52) 
Event type: 12 
Event type: 24 
Event type: 99 
Event type: 77 
Event type: 12 
Event type: 10 
Event type: 11 
Move event: QPoint(308,356) 
Event type: 19 
Event type: 25 
Event type: 99 
Event type: 18 
Event type: 27 
Event type: 77 

如您所见,有两个移动事件,一个是应用程序最初创建时,另一个是在我完成窗口移动后。我使用的是Qt 4.8.1和XOrg 7.6进行测试。

检查原始X事件

  1. 运行测试应用程序。
  2. 获取测试应用程序的窗口ID。要执行此操作,请在命令行中执行xwininfo -name WINDOW_NAME,其中WINDOW_NAME是测试应用程序窗口的名称。另一种选择是使用没有参数的xwininfo,然后您必须使用鼠标指针选择测试应用程序窗口。
  3. 运行X事件监视器xev -id 0x2a00002,其中0x2a00002是在上一步中找到的窗口ID。这将打印您的窗口从X服务器接收到的X事件。ConfigureNotifyQMoveEvent的X协议对应项。

1
将您的代码复制并粘贴给我会导致以下结果:移动事件:QPoint(683,135) 移动事件:QPoint(682,133) 移动事件:QPoint(680,131) 移动事件:QPoint(679,130) 移动事件:QPoint(678,128) 移动事件:QPoint(677,128) 移动事件:QPoint(676,128) 移动事件:QPoint(675,128) 移动事件:QPoint(674,128) 移动事件:QPoint(673,128) 移动事件:QPoint(672,128) 移动事件:QPoint(671,128) 移动事件:QPoint(670,128)每个增量移动都会得到通知... Qt4.8.3 XOrg 7.6 - Matteo Murgida
1
有几种可能导致这个问题。您能验证一下 X 事件是否传递到您的应用程序,以确保这不是由于 Qt 版本的问题引起的吗? - divanov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接