Rendering animations on Qt/E using timers
-
Hi! I need to create an animation for which I thought it might be simpler using timer instead of the animation framework. I'm using a QGraphicsView with a OpenGL viewport and full screen viewport update. So, I subclassed QObject and I ran startTimer(). In my timerEvent(...) method I simply compute the new positions of some items and I call the setPos() on each of those objects.
What I'm noticing is that the timerEvent code is taking only a couple of ms (does a setPos triggers a redraw of the entire screen?), the average frame rate is constant but the interval between two calls of my timerEvent method is not. I noticed it is always within a couple of ms from the chosen value, but in some cases it is 3 or 4 times higher. This creates really an horrible effect, so it is a problem.
Does anybody know why this is happening and if there is any way to avoid it?
Thanks! -
does a setPos triggers a redraw of the entire screen?
Yes, you've set "QGraphicsView::FullViewportUpdate":http://doc.qt.nokia.com/latest/qgraphicsview.html#ViewportUpdateMode-enum , so item->setPos() will update all viewport.
Can you give us some snippets if code? -
Ok, I was suspecting that. In this case that is not what I had in mind. I just wanted to set the new positions so that at specified intervals the interface was updated. But what should I use to set the position to avoid updates?
Yes, this is my timerEvent method implementation:
@void CircularList::timerEvent(QTimerEvent* event)
{
Q_UNUSED(event);// I have to compute position according to time.
QPointF pos;
qint64 currentTimestamp = QDateTime::currentMSecsSinceEpoch();
for (int i = 0; i < fullList.size(); i++) {
computeNewPosition(fullList[i], currentTimestamp, startPositions[i], pos);
fullList[i]->setPos(pos);
}
}@fullList is a QList of items, which are subclasses of QObject and QGraphicsPixmapItem. Those are the objects I want to move.
computeNewPosition is passed the item to move, the timestamp of the update event, the initial position and sets pos as the new position. I tried to do this some other ways but the same behavior is shown.@inline
void CircularList::computeNewPosition(ListItem* item,
qint64& currentTimestamp,
QPointF& startPos,
QPointF& pos)
{
int direction = (currentDirection == DIRECTION_LEFT) ? -1 : 1;
qint64 elapsedMs = currentTimestamp - timestampStarted;
qreal shift = directionCRUISING_SPEEDelapsedMs/1000.0;
qreal xpos = startPos.x() + shift;
pos.setY(item->pos().y());
pos.setX(xpos);
}@I start the timer when the user presses a button, then I ignore whatever repetition arrives and I kill the timer when the user releases.
I know this is not the most obvious way to do this, but it was only the last attempt.Is it normal that there are such irregular calls of the timerEvent method? Can I setPos without triggering an update?
Thanks! -
Can I setPos without triggering an update?
As I now, no (but I could be wrong).
Is it normal that there are such irregular calls of the timerEvent method?
Can you post the code where you set and delete timer? -
I've been explained that setPos should anyway trigger a paintEvent but not immediately, but in the following paintEvent. So maybe that doesn't happen immediately.
As for the timer this is where I set it up:
@void CircularList::startSliding(Direction direction)
{
// Immediately mark as animation flowing. Do nothing if an animation is
// currently flowing.
if (isCurrentlyAnimated)
return;
isCurrentlyAnimated = true;// ...
// Start the timer to proceed with the animation.
currentDirection = direction;
timestampStarted = QDateTime::currentMSecsSinceEpoch();
currentTimerId = startTimer(REFRESH_PERIOD);for (int i = 0; i < fullList.size(); i++) {
startPositions.append(fullList[i]->pos());
}
}@The only interesting thing is startTimer I suppose. I tried many refresh rate values to see the difference. This function is invoked when there is a pressEvent.
When the release is called, I simply stop the timer.@void CircularList::stopSliding()
{
killTimer(currentTimerId);
isCurrentlyAnimated = false;
startPositions.clear();
}@Any idea why I have those delays? My code is almost all here. Is there any way to check if the delay is caused by the CPU being busy doing something?
Thanks! -
Sorry, but I haven't any thoughts about the delays, because I've made simple example and all works good.
Maybe it helps you, "here is":http://paste.kde.org/141092/ the cpp of my GraphicsView. -
And you want to see hole test project, "here it is":https://github.com/rokemoon/TestTimerEvent
-
Ok, thanks. I'm testing this and seems to be ok. Problem is it is exactly what I do. Only things I can think is it is taking too long to paint, and therefore when the timer should be triggered it is busy drawing. May this be realistic?
Any way I can find out what it is busy doing when timer is triggered?
Thanks for your help! -
[quote author="Luc4" date="1320273190"]Only things I can think is it is taking too long to paint, and therefore when the timer should be triggered it is busy drawing. May this be realistic?
[/quote]
Yes, it's realy so. I've tested it, and have the same situation as you describe.
Timer has this periodic (I've set 25 ms):
25
56
80
105
129
153
177 .... and so on.
So, try to cache some vars, if you calculate them in paintEvent(), calculate them beforre paint and recalculate when you have some changes. Also you can cache whole painting, if you have some static picture, paint it in some image and then draw only image.
And you can try to look this presentation from DevDays 2010 about Graphics Framework.
Here they are: "1":http://developer.qt.nokia.com/videos/watch/performance_do_graphics_the_right_way "2":http://developer.qt.nokia.com/videos/watch/qt_graphicsview_in_depth -
Thanks! I'll read those. Anyway, I suppose my problem is due to the fact that simply rendering is taking too much time. Thanks!