qt,win. Застывает окошко при перетаскивании.
моделька приложения: Есть timer. Который вызывает timeout каждую секунду, которая что-то пишет в консоль.
моделька приложения: Есть timer. Который вызывает timeout каждую секунду, которая что-то пишет в консоль.в коде у тебя чуть другое. в коде ты сам крутишь цикл сообщений - и есть вероятность, что делаешь это неправильно.
http://forum.vingrad.ru/forum/s/ca68de077284fb6c29cda9924b3a...
ты более правильно задаешь вопрос.
т.е. у тебя перестает вызываться printf, а не приложение замирает?
так это понятно, т.к. processEvents не возвращает управление пока не обработает все сообщения.
можно попробовать использовать processEvents с timeout-ом (например, в 100ms)
кстати здесь: ты более правильно задаешь вопрос.
т.е. у тебя перестает вызываться printf, а не приложение замирает?
так это понятно, т.к. processEvents не возвращает управление пока не обработает все сообщения.
можно попробовать использовать processEvents с timeout-ом (например, в 100ms)
void QCoreApplication::processEvents ( QEventLoop::ProcessEventsFlags flags, int maxtime ) [static]
Данная перегруженная функция-член предоставлена для удобства. Ее поведение аналогично поведению вышеприведенной фунции.
Обрабатывает ожидающие сообщения в течение maxtime миллисекунд или меньше, если не больше событий.
это другое. Это время на обработку всех сообщений. т.е. в очереди 200 сообщений, он обрабатывает либо пока 100мс не пройдёт, либо пока они не закончатся.
т.е. в очереди 200 сообщений, он обрабатывает либо пока 100мс не пройдёта тебе разве не это надо?
есть вероятность, что делаешь это неправильно.
ждал этого
Специально сделал усилие и вспомнил с++ (изначально приложение на python/pyqt)
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(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;
delete mywnd;
return 0;
}
crashtest.h
#include <qapplication>
#include <qcoreapplication>
#include <qlabel>
#include <qtgui>
#include <qeventloop>
#include <qtimer>
class MyDlg : public QMainWindow
{
Q_OBJECT
public:
int i;
QTimer *qtimer;
MyDlg;
~MyDlg;
public slots:
void timeout;
};
И вот так тоже не работало.
Но сейчас скомпилил - и заработало.
если заменить
exec на
for (i=0;i<100000000; i++)
{
app.processEvents(QEventLoop::AllEvents,1000);
printf("it %d\n",i);
}
то тоже сообщения от таймера обрабатываются. Значит проблема не в том, что есть одно слишком большое сообщение, а их просто много и обработчик не успевает разгрести очередь.
а тебе разве не это надо?
сейчас понял, что оно и надо, но почему-то этот таймаут не пашет.
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:
1)This function does not process events continuously; it returns after all available events are processed.
2)Specifying the WaitForMoreEvents flag makes no sense and will be ignored.
второе выделенное говорит о том, что функция не должна работать с новыми сообщениями, которые появлились после её запуска, я правильно понимаю? Тогда это точно баг.
void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int maxtime)
{
[...]
QTime start;
start.start;
[...]
while (data->eventDispatcher->processEvents(flags & ~QEventLoop::WaitForMoreEvents {
if (start.elapsed > maxtime)
break;
[...]
}
}
глянул в исходники, оказалось, что processEvent с maxTime вызывает processEvent, пока не закончится время.с другими флагами вызывается
второе выделенное говорит о том, что функция не должна работать с новыми сообщениями, которые появлились после её запуска, я правильно понимаю? Тогда это точно баг.и похоже как раз с такими, чтобы не было обработки новых сообщений
Он просто снимает флаг WaitForMoreEvents, который(см. ниже код) просто добавляет дополнительных итераций, если не было сообщений. а я изначально этот флаг не ставил.
обработчика выглядит так.
есть цикл do, который выполняется 1 раз, если не стоит WaitForMoreEvents, и может выполняться несколько, если этот флаг стоит.
внутри есть цикл while (!d->interrupt из которого походу и не выходит.
внутри этого цикла есть проверка некой очереди(в которой, видимо лежат эти спец.сообщения)
d->queuedUserInputEvents
и даже есть нет сообщений ( if (!haveMessage - он там что-то ждёт. (check for signalled objects)
всё это вызывает ощущение какого-то виндового костыля, чтобы исправить какой-то другой костыль.
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;
}
Вообще, мне это нужно, чтобы полу
Он просто снимает флаг WaitForMoreEvents, который(см. ниже код) просто добавляет дополнительных итераций, если не было сообщений. а я изначально этот флаг не ставил.угу.
я думал, что может AllEvents в себя включает флаг WaitForMoreEvents, но сейчас посмотрел - нет не включает.
значит ты прав, похоже на багу
Идея была такая, чтобы поместить обработчик событй под некоторый мутекс. Но тогда необходимо, чтобы обработчик очень быстро выполнялся.это обычно делают через invoke (запаковки функции в сообщение и отправке его в гуишный поток) - так подводных камней меньше, но код, конечно, намного более громоздкий получается.
Не сложнее тогда сделать рабочий процес и управляющий gui-процесс. Зато демон теперь будет не зависить от gui и стабильнее работать.
Приложение ниже замирает(не происходит возврат управления из app.processEvents когда перетаскивается окошко/меняются размеры.Может быть, я не прав, тему полностью не читал, но...
При этом, ничего похожего не наблюдается, например, с консолью (cmd).
В винде есть одна очень интересная особенность: при перетаскивании окна запускается обработчик событий, встроенный в недрах виндового GUI. Поэтому когда пишешь GUI/WIN32 программу, основанную на очереди сообщений, ты должен использовать механизм оконных callbacks вместо просмотра сообщений вручную. Иначе ты будешь терять эти сообщения.
или что понимается по callback ?
или что понимается по callback ?Обратный вызов. Оконная процедура, которая вызывается из недр DispatchMessage
что мешает выйти из processEvents и вызвать dispatch при следующем вызове processEvents, а состояние где-нибудь запомнить?
что мешает выйти из processEvents и вызвать dispatch при следующем вызове processEvents, а состояние где-нибудь запомнить?Читай мой пост внимательнее: внутренности WINAPI съедают все сообщения. С точки зрения WINAPI это выглядит примерно так:
1. очередь сообщений в QT вызывает DispatchMessage для начала перетаскивания окна
2. во внутренностях WINAPI запускается своя очередь сообщений, которая работает до тех пор, пока ты не перетащишь окно (тут твоя прога "замирает")
3. после того, как пользователь перестал перетаскивать окно, завершается работа очереди сообщений во внутренностях WINAPI и ты получаешь управление обратно
Таким образом, единственно верный и надёжный способ сделать что-то через очередь WINAPI сообщений - это использовать оконную процедуру, которая вызывается из DispatchMessage в любом случае.
#include <windows.h>
#include <stdio.h>
const char g_szClassName[] = "myWindowClass";
#define IDC_MAIN_EDIT 101
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
int i;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSHCOLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = g_szClassName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc
{
MessageBox(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(
0,
g_szClassName,
"theForger's Tutorial Application",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 480, 320,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
printf("it_b %d\n", i);
DispatchMessage(&Msg);
printf("it_e %d\n", i);
i++;
}
return Msg.wParam;
}
при кликанье по меню есть it_b, но нет it_e.
Т.е. этот баг, который я думал, что в qt, на самом деле глубже, в самой dispatchmessage.Скорее это у тебя баг. От незнания как работает WINAPI, чего там можно делать, а чего нельзя...
Ты считаешь что то поведение, что ты описал - это не баг? (Слово баг не нравится. Костыль тогда.)
Ты считаешь что то поведение, что ты описал - это не баг? (Слово баг не нравится. Костыль тогда.)Это всё - документированное поведение WINAPI. Так что это не баг (неправильное поведение программы).
Ну а костыль-то тут при чём? Так обычно называют проблему, решённую каким-то некрасивым способом.
Так наверняка такое поведение было сделано, чтобы решить какую-то проблему.
Так наверняка такое поведение было сделано, чтобы решить какую-то проблему.Возможно. Но это не кривой способ её решить.
Подумай, что будет с твоей программой, когда ты добавишь в свой GUI модальный диалог?
Оставить комментарий
Phoenix
Приложение ниже замирает(не происходит возврат управления из app.processEvents когда перетаскивается окошко/меняются размеры.При этом, ничего похожего не наблюдается, например, с консолью (cmd).
Может есть какая-нибудь магическая функция?
Похоже проблема в QT.
Такой код тоже самое выдаёт без всякого питона.