[SEMI-SOLVED] Update loop in Qt? QTimer no longer working...
-
I'm using Qt on top of a game and I need to call an update function regularly. I once did this by having a QTimer, with 0 in interval, calling an update function similar to what you see below.
@class Window : public QMainWindow
{
Q_OBJECTprivate:
QTimer timer;public:
Window()
{
timer.setInterval(0);
timer.start();
connect(&timer, SIGNAL(timeout()), this, SLOT(update()));
}public slots:
void update()
{
// Update game logic
}
};@I don't remember the exact Qt version I used, but this used to work without any problems.
However, in Qt 4.8.4 and Qt 5, the window "freezes", probably due to QTimer receiving all execution time without the GUI updating in between.
Does anyone know how to solve this?
-
Are you sure you want your interval to be 0? Open Task Manager -- you'll see that one of your CPU cores will be running at 100% which wastes power, generates lots of heat, and shortens your CPU life.
I recommend a longer interval -- 10 ms for example. It will un-freeze your window and protect your CPU.
-
Unfortunately no. If the game logic takes more than 10 ms the window still freezes.
E.g.
@void update()
{
Sleep(11);
}@ -
I managed to come up with a way to bypass the problem. If I give QTimer a new Interval each frame, it will give the window some time to process. Like this.
@void update()
{
Sleep(11);
timer->setInterval(1);
}@However, I would still like to think there is a proper way to solve it.
According to the documentation:
http://qt-project.org/doc/qt-4.8/qtimer.html#timerId
"A QTimer with a timeout interval of 0 will time out as soon as all the events in the window system's event queue have been processed."This appears to no longer be the case. QTimer will timout even if there is other event's to be processed. Should I perhaps report this as a bug?
-
It might not be related, as I am not really familiar with Timers, but I've had problems with long and repeated operations blocked the main event loop.
Had to add this line to unblock the loop:
@qApp->processEvents(QEventLoop::AllEvents);@I think the problem was different however, so this might not do any good.
-
[quote author="Adelost" date="1368112256"]
@void update()
{
Sleep(11);
}@
[/quote]This freezes BOTH the game logic AND the window, because both are running in the same thread. Sleep() freezes the whole thread -- you don't want to call sleep(), as it slows down your entire application.I meant:
@
Window()
{
timer.setInterval(0); // Change this line...
timer.start();
...
}void update()
{
// ... and remove the 2 lines below
Sleep(11);
timer->setInterval(1);
}
@But yes, it sounds like QTimer isn't doing what the documentation says, so it could be a bug. Please file a bug report if you wish, and post a link back here.
Also, if you're interested in learning more robust methods for computation-intensive programs like yours, consider separating the logic from the window, and update the logic in a separate thread. This way, no matter how complex your logic gets, the window will always be responsive.
-
[quote author="JKSH" date="1368146416"]...you don't want to call sleep()[/quote]
Thanks for answering. But I think you misunderstood me. Sleep does not serve any purpose. It was just to illustrate that the game would still freeze if the gamelogic exceeded the given interval. Perhaps I should have been more clear.
Still freezes:
@Window()
{
timer.setInterval(10);
...
}void update()
{
gameLogic();
}void gameLogic()
{
// Fake intensive gamelogic that
// exceeds timer interval
Sleep(11);
}@[quote author="JKSH" date="1368146416"] ...separating the logic from the window, and update the logic in a separate thread[/quote]
This would probably be a good Idea, however I display some elements from the game in the GUI, and I'm a little bit afraid of having to handle mutexes and such. -
[quote author="Guigui" date="1368131452"]...had to add this line to unblock the loop:
@qApp->processEvents(QEventLoop::AllEvents);@
[/quote]Thanks! This is a better solution than the one I came up with. The game no longer freezes even if it exeeds the QTimer interval, and I can now set the interval to anything.
My update loop now looks like this.
@void update()
{
gameLogic();
qApp->processEvents(QEventLoop::AllEvents);
}@