PDA

View Full Version : Avoid flicker during slideshow with QPropertyAnimation



RazZziel
5th March 2012, 09:15
I hace a fullscreen program with several screens, and I want to go for one screen to the next one witht an slideshow animation.

I have a fullscreeen QMainWidget, with a placeholder VerticalLayout, where I put different QWidgets that act as "screens" for my application, one at a time. The user interacts with the current screen, and when it's time to go to the next one, I remove the current page from the vertical layout and place the new page instead.

It works flawlessly, but I want to spice it up with some QPropertyAnimation. I want to transition from one page to the next one with a little slideshow animation. I'm using the "geometry" property, to move both the old page and the new page, right-to-left. This is my code:


if (m_currentPage && animate)
{
// Don't allow the user to interact with the old page as it's being carried away
m_currentPage->setEnabled(false);

QRect a = ui->placeholder->geometry();
QRect b = QRect(a.width(), a.y(), a.width(), a.height());
QRect c = QRect(-a.width(), a.y(), a.width(), a.height());

int animationDuration = 400;

QPropertyAnimation *animation = new QPropertyAnimation(m_currentPage, "geometry", this);
animation->setStartValue(a);
animation->setEndValue(direction == Forward ? c : b);
animation->setDuration(animationDuration);
animation->setEasingCurve(QEasingCurve::OutQuad);
animation->start();
connect(animation, SIGNAL(finished()), m_currentPage, SLOT(deleteLater()));

QPropertyAnimation *animation2 = new QPropertyAnimation(newPage, "geometry", this);
animation2->setStartValue(direction == Forward ? b : c);
animation2->setEndValue(a);
animation2->setDuration(animationDuration);
animation2->setEasingCurve(QEasingCurve::OutQuad);
animation2->start();
connect(animation2, SIGNAL(finished()), newPage, SLOT(setFocus()));
}
else
{
// Remove previous page
clearLayout(ui->placeholder);
}

// Set new page
newPage->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
ui->placeholder->addWidget(newPage);
newPage->setFocus();

connect(newPage,
SIGNAL(setPage(KioskPage*,KioskMainWindow::Directi on)),
SLOT(setPage(KioskPage*,KioskMainWindow::Direction )));
newPage->init();

m_currentPage = newPage;

languageChange();

if (animate)
{
newPage->hide();
QTimer::singleShot(100, newPage, SLOT(show()));
}

It works fairly well, but has one annoying bug: after this code is run, the function quits, the control returns to Qt internals and windows start to be drawn, what I see is, first, the newPage flickers once all over the m_currentPage, just for a fraction of a second, and then it disappears and the animation starts smoothly. I've tried a lot of stuff here, and that ugly QTimer::singleShot(100, newPage, SLOT(show())); is the only thing that appears to at least disguise part of the problem, but not completely. As soon as the show() slot is called, newPage flickers once over m_currentPage, disappears and appears again where it should be according to the QPropertyAnimation.

Does anyone know why this is happening, and what I could do to avoid it?

wysota
5th March 2012, 09:31
Please provide a minimal compilable example reproducing the problem.

RazZziel
6th March 2012, 15:26
Here you have

wysota
6th March 2012, 16:11
In my opinion the problem is with your "placeholder" layout. If you want to animate a page, it can't be part of a layout.


#include <QtGui>

class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = 0) : QWidget(parent) {
QVBoxLayout *l = new QVBoxLayout(this);
placeholder = new QWidget;
placeholder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
l->addWidget(placeholder);
QPushButton *b = new QPushButton;
l->addWidget(b);
b->setText("click");
connect(b, SIGNAL(clicked()), this, SLOT(nextPage()));
current = 0;
}
public slots:
void nextPage() {
QWidget *newPage = new QLabel("page");
newPage->setAutoFillBackground(true);
QStringList c = QColor::colorNames();
QPalette p = newPage->palette();
p.setColor(QPalette::Window, QColor(c.at(qrand() % c.size())));
newPage->setPalette(p);
newPage->setParent(placeholder);
QPropertyAnimation *anim = new QPropertyAnimation(newPage, "geometry", newPage);
QRect start = placeholder->rect();
start.setTopLeft(start.topRight());
newPage->setGeometry(start);
anim->setStartValue(start);
anim->setEndValue(placeholder->rect());
anim->start();

if(current) {
QPropertyAnimation *anim = new QPropertyAnimation(current, "geometry", current);
anim->setStartValue(placeholder->rect());
QRect r = placeholder->rect();
r.translate(-r.width(), 0);
anim->setEndValue(r);
connect(anim, SIGNAL(finished()), current, SLOT(deleteLater()));
anim->start();
}

current = newPage;
current->show();
}
private:
QWidget *placeholder;
QWidget *current;
};

#include "main.moc"

int main(int argc, char **argv) {
QApplication app(argc, argv);
Widget w;
w.show();
return app.exec();
}

RazZziel
6th March 2012, 17:18
This pitiful mortal shall bow before you, oh wise Master Of Zen.