Обнаружить конец движения QMainWindow/QDialog (Qt 4.8)

Я использую Qt 4.8.3 на X11.

Мне нужно знать, когда пользователь заканчивает перетаскивать окно по экрану, чтобы прочитать окончательную позицию и, в конечном итоге, запустить анимацию, чтобы настроить положение окна на «разрешенное».

Я заметил, что QWidget::moveEvent вызывается для каждого небольшого движения, но это очень неудобно, потому что я должен выполнять проверку положения (и в конечном итоге запускать анимацию) только тогда, когда пользователь отпускает кнопку мыши и движение полностью завершено.

Это настоящая проблема: кажется, что невозможно обнаружить событие отпускания мыши (или получить статус кнопок мыши), когда пользователь щелкает строку заголовка, поскольку это контролируется ОС, а не Qt. Я также пробовал с QWidget::x11event(XEvent* e)… но события собираются только внутри окна, а не в строке заголовка.

Кто-нибудь знает способ добиться этого?

Я подозреваю, что мне придется самому переделать заголовок… жаль…


person Matteo Murgida    schedule 18.10.2012    source источник
comment
Возможно, вам следует также рассмотреть ситуацию, когда пользователь перемещает окно с помощью клавиатуры. (По крайней мере, в Windows это возможно, насчет X11 не знаю.)   -  person    schedule 18.10.2012
comment
QMoveEvent отправляется каждый раз, когда виджет перемещается, независимо от того, каким образом это перемещение было достигнуто. Однако есть QResizeEvent, который потенциально также влияет на анимацию в окне.   -  person divanov    schedule 18.10.2012


Ответы (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);
}

Это просто и достаточно эффективно, не так ли!

Надеюсь, это будет полезно и для вас. :)

person liulios    schedule 31.10.2012
comment
Ваш пример не будет работать на моем рабочем столе, что сделает приложение не переносимым. Как вы можете видеть из моего журнала, QEvent::NonClientAreaMouseMove отсутствует. Какой дистрибутив и оконный менеджер вы используете? - person divanov; 02.11.2012
comment
Я использую Qt 4.8.3 и Win7. В вашем журнале освобождение определяется автоматически. Но это не та ситуация, с которой столкнулся Маттео Мургида, хотя у вас одна и та же среда разработки. Однако моя проблема аналогична и решена. Я просто предлагаю другую идею. :-) - person liulios; 05.11.2012
comment
Если да, то мой ответ помог вам решить вашу проблему. Заслуживает ли это положительного голоса? - person divanov; 08.11.2012
comment
Да, но моей репутации недостаточно... Я только что зарегистрировался, чтобы поделиться своим опытом. - person liulios; 13.11.2012

Понимая, что это очень старый вопрос, это первый ответ, который возникает, когда вы пытаетесь «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.

person b0bh00d    schedule 30.04.2019

Рассмотрим следующее тестовое приложение: main.cpp

#include <QApplication>

#include "win.h"

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

    Win w;
    w.show();

    return app.exec();
}

победа.ч:

#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. Получить идентификатор окна тестового приложения. Для этого выполните в командной строке xwininfo -name WINDOW_NAME, где WINDOW_NAME — имя окна тестового приложения. Другой вариант — использовать xwininfo без параметров, тогда вам нужно выбрать окно тестового приложения указателем мыши.
  3. Запустите монитор событий X xev -id 0x2a00002, где 0x2a00002 — идентификатор окна, найденный на предыдущем шаге. Это напечатает X событий, которые ваше окно получает от X-сервера. ConfigureNotify является аналогом QMoveEvent протокола X.
person divanov    schedule 18.10.2012
comment
Копирование и вставка вашего кода для меня приводит к следующему: Move event: QPoint(683,135) Move event: QPoint(682,133) Move event: QPoint(680,131) Move event: QPoint(679,130) Move event: QPoint(678,128) Move event: QPoint(677,128) Move event: QPoint(676,128) Move event: QPoint(675,128) Move event: QPoint(674,128) Move event: QPoint(673,128) Move event: QPoint(672,128) Move event: QPoint(671,128) Move event: QPoint(670,128) Каждое дельта-движение получает уведомление.... Qt4.8.3 XOrg 7.6 - person Matteo Murgida; 19.10.2012
comment
Есть несколько вариантов того, что может быть причиной этого. Не могли бы вы проверить события X, поступающие в ваше приложение, чтобы убедиться, что это не проблема вашей версии Qt. - person divanov; 19.10.2012