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

问题描述 投票:1回答:3

我在X11上使用Qt 4.8.3。

我需要知道用户何时在屏幕周围拖动窗口结束,这是为了读取最终位置并最终启动动画以将窗口位置调整为“允许”窗口位置。

我注意到QWidget :: moveEvent是为每个小动作调用的,但是这非常不方便,因为我必须仅在用户释放鼠标按钮并且动作完全完成时才执行位置检查(并最终控制动画)。

这是真正的问题:当用户点击标题栏时,似乎无法检测鼠标释放事件(或获取鼠标按钮状态),因为它是由操作系统控制而不是由Qt控制。我也尝试使用QWidget :: x11event(XEvent * e)......但事件只在窗口内收集,而不是标题栏。

有人知道实现这一目标的方法吗?

我怀疑自己必须重新实现标题栏......太糟糕了......

谢谢! ;-)

qt qt4 qdialog qmainwindow qt4.8
3个回答
3
投票

我和你的问题一样。 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);
}

它简单而有效,不是吗!

希望它对你也有用。 :)


1
投票

意识到这是一个非常古老的问题,它是你尝试“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的生产代码。


0
投票

让我们考虑以下测试应用程序: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 

如您所见,在完成窗口移动后,最初创建应用程序时有2个移动事件。我正在使用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协议对应物。
© www.soinside.com 2019 - 2024. All rights reserved.