Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets > [Qt] замирает обработка событий при перетаскивании


Автор: iigor 23.1.2010, 13:29
Добрый день, всем!

Наткнулся на такую особенность. При перетаскивании окошка(или изменении размеров) возврат управления processEvents происходит после завершения перетаскивания.

В коде ниже можно наблюдать, как в консоле останавливается вывод циферок, когда окошко перетаскиваешь.

Код

#include <qapplication>
#include <qcoreapplication>
#include <qlabel>
#include <qtgui>
#include <qeventloop>

class MyDlg : public QMainWindow
{
    public:
    MyDlg()
    {
        this->resize(500,200);
        this->setWindowTitle("CRASH TEST");
    };
};


int main(int argc, char *argv[])
{
    QApplication app(argc, argv );
    MyDlg *mywnd = new MyDlg ();

    mywnd->show();

    int i=0;
    
    printf("it %d\n",i);
    for (i=0;i<100000000; i++) 
    {
        app.processEvents(QEventLoop::AllEvents);
        printf("it %d\n",i);
    }
    
    delete mywnd;
    return 0;

}




При этом ничего подобного не происходит, скажем, в консоле (cmd) или плеере lightalloy.   

Автор: SABROG 23.1.2010, 14:15
По-моему это стандартное поведение всех программ. Если передвигать окно, просто кликнуть на бордюр окна для изменения размера и держать мышку зажатой или открыть системное меню заголовка - все события останавливаются: таймеры, анимация, сообщения из сокетов и т.п. Другое дело, если это не стандартное окно, как у winamp или lightalloy - окно со шкуркой. Эти окна не рисуют пунктирный прямоугольник при перетаскивании, они двигаются вслед за мышкой, т.к. обрабатывают своё перемещение сами, а не полагаются на ОС. Поэтому в таких программах цикл событий как работал так и работает. Поэкспериментируй с флагом Qt::FramelessWindowHint, из-за отсутствия заголовка в подобных окнах тебе придется реализовывать перемещение и изменение размеров окна самому, что и сделано в программах типа LA.

Автор: iigor 23.1.2010, 15:55
Нее. тут другое.

Если воткнуть таймер, то события от этого таймера обрабатываются.
Видимо, когда перетаскиваешь окошко - начинают генерироваться много-много сообщений с новыми координатами, а функция processEvents возвращает управление, когда нет больше сообщений.

Есть спец. опция 

Цитата

void QEventLoop::processEvents ( ProcessEventsFlags flags, int maxTime )

Process pending events that match flags for a maximum of maxTime milliseconds, or until there are no more events to process, whichever is shorter. This function is especially useful if you have a long running operation and want to show its progress without allowing user input, i.e. by using the ExcludeUserInputEvents flag.

Notes:

This function does not process events continuously; it returns after all available events are processed.
Specifying the WaitForMoreEvents flag makes no sense and will be ignored.


Но она не работает как ожидается. Опять же, при перетаскивании, выхода из неё не будет пока кнопку не отпустишь (==не закончится поток сообщений)

Автор: SABROG 23.1.2010, 16:27
Тут всего одного события хватает, чтобы processEvents() не вернулся - вызвать системное меню на заголовке окна и не закрывать его. События выполняются последовательно, если какой-то метод реагирует на событие и долго не возвращает управления, то остальные события будут ждать. Аналогично и обработчики таймера будут стоять, это всего лишь события.

Автор: iigor 23.1.2010, 16:55
crashtest.cpp
Код

#include <qapplication>
#include <qcoreapplication>
#include <qlabel>
#include <qtgui>
#include <qeventloop>
#include <qtimer>
#include "crashtest.h"



MyDlg::MyDlg()
{
    this->resize(500,200);
    this->setWindowTitle("CRASH TEST");
    
    this->i = 0;
    
    this->qtimer = new QTimer();
    
    this->qtimer->setInterval(0);

    this->connect(this->qtimer, SIGNAL(timeout()), this, SLOT(timeout()));
    this->qtimer->start();
}


MyDlg::~MyDlg()
{
    delete this->qtimer;
};

void MyDlg::timeout()
{
    printf("itS %d\n",this->i);
    this->i ++;
    
};



crashtest.h
Код

#include <qapplication>
#include <qcoreapplication>
#include <qlabel>
#include <qtgui>
#include <qeventloop>
#include <qtimer>
#include "crashtest.h"



MyDlg::MyDlg()
{
    this->resize(500,200);
    this->setWindowTitle("CRASH TEST");
    
    this->i = 0;
    
    this->qtimer = new QTimer();
    
    this->qtimer->setInterval(100);

    this->connect(this->qtimer, SIGNAL(timeout()), this, SLOT(timeout()));
    this->qtimer->start();
}


MyDlg::~MyDlg()
{
    delete this->qtimer;
};

void MyDlg::timeout()
{
    printf("itS %d\n",this->i);
    this->i ++;
    
};


main.cpp
Код

#include <qapplication>
#include <qcoreapplication>
#include <qlabel>
#include <qtgui>
#include <qeventloop>
#include <qtimer>
#include "crashtest.h"



int main(int argc, char *argv[])
{
    QApplication app(argc, argv );
    MyDlg *mywnd = new MyDlg ();


    mywnd->show();

    int i=0;
    
    printf("it %d\n",i);
    //app.exec();
    for (i=0;i<100000000; i++) 
    {
        app.processEvents(QEventLoop::AllEvents,10);
        printf("it %d\n",i);
    }

    
    delete mywnd;
    return 0;

}




данное приложение выводит в консоль сообщения вида "it 123" из main и "itS 456" благодаря таймеру.
Если же нажать на системное меню - тогда сообщения "it 123" не появляются, но сообщения "itS 456"  по прежнему выводятся, т.е. сообщения от таймера обрабатываются => цикл обработки не остановился. Или вы не про это?


Автор: SABROG 23.1.2010, 19:25
Тогда я не понимаю какие еще эвенты могут быть в очереди при открытом системном меню, что невозможно вернуться из processEvents().

Автор: iigor 23.1.2010, 19:36
там QT что-то мутит smile
Он запоминает некоторые состояния. И ждёт некоторых событий. (Я не очень силён в winapi, так что глубоко не понимаю этот код.)

см. около  if (!haveMessage)

Код

bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherWin32);

    if (!d->internalHwnd)
        createInternalHwnd();

    d->interrupt = false;
    emit awake();

    bool canWait;
    bool retVal = false;
    bool seenWM_QT_SENDPOSTEDEVENTS = false;
    bool needWM_QT_SENDPOSTEDEVENTS = false;
    do {
        DWORD waitRet = 0;
        HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
        QVarLengthArray<MSG> processedTimers;
        while (!d->interrupt) {
            DWORD nCount = d->winEventNotifierList.count();
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);

            MSG msg;
            bool haveMessage;

            if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
                // process queued user input events
                haveMessage = true;
                msg = d->queuedUserInputEvents.takeFirst();
            } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
                // process queued socket events
                haveMessage = true;
                msg = d->queuedSocketEvents.takeFirst();
            } else {
                haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
                if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents)
                    && ((msg.message >= WM_KEYFIRST
                         && msg.message <= WM_KEYLAST)
                        || (msg.message >= WM_MOUSEFIRST
                            && msg.message <= WM_MOUSELAST)
                        || msg.message == WM_MOUSEWHEEL
                        || msg.message == WM_MOUSEHWHEEL
                        || msg.message == WM_CLOSE)) {
                    // queue user input events for later processing
                    haveMessage = false;
                    d->queuedUserInputEvents.append(msg);
                }
                if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
                    && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
                    // queue socket events for later processing
                    haveMessage = false;
                    d->queuedSocketEvents.append(msg);
                }
            }

            if (!haveMessage) {
                // no message - check for signalled objects
                for (int i=0; i<(int)nCount; i++)
                    pHandles[i] = d->winEventNotifierList.at(i)->handle();
                waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);
                if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {
                    // a new message has arrived, process it
                    continue;
                }
            }
            if (haveMessage) {
#ifdef Q_OS_WINCE
                // WinCE doesn't support hooks at all, so we have to call this by hand :(
                (void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg);
#endif

                if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
                    if (seenWM_QT_SENDPOSTEDEVENTS) {
                        needWM_QT_SENDPOSTEDEVENTS = true;
                        continue;
                    }
                    seenWM_QT_SENDPOSTEDEVENTS = true;
                } else if (msg.message == WM_TIMER) {
                    // avoid live-lock by keeping track of the timers we've already sent
                    bool found = false;
                    for (int i = 0; !found && i < processedTimers.count(); ++i) {
                        const MSG processed = processedTimers.constData()[i];
                        found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
                    }
                    if (found)
                        continue;
                    processedTimers.append(msg);
                } else if (msg.message == WM_QUIT) {
                    if (QCoreApplication::instance())
                        QCoreApplication::instance()->quit();
                    return false;
                }

                if (!filterEvent(&msg)) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            } else if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
            } else {
                // nothing todo so break
                break;
            }
            retVal = true;
        }

        // still nothing - wait for message or signalled objects
        canWait = (!retVal
                   && !d->interrupt
                   && (flags & QEventLoop::WaitForMoreEvents));
        if (canWait) {
            DWORD nCount = d->winEventNotifierList.count();
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
            for (int i=0; i<(int)nCount; i++)
                pHandles[i] = d->winEventNotifierList.at(i)->handle();

            emit aboutToBlock();
            waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
            emit awake();
            if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
                retVal = true;
            }
        }
    } while (canWait);

    if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) {
        // when called "manually", always send posted events
        QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
    }

    if (needWM_QT_SENDPOSTEDEVENTS)
        PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0);

    return retVal;
}

Автор: iigor 24.1.2010, 20:12
Как мне объяснили, тот "баг", который я думал в qt, оказался в DispatchMessage().   т.е. внутри неё запускается некий дополнительный цикл и qt умеет с ним работать, поэтому нормально отрабатываются сообщения таймера.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)