Fade-Effects: a Blast from the Past

Published Tuesday August 21st, 2007
4 Comments on Fade-Effects: a Blast from the Past
Posted in Qt

I must admit, I’m not the best blog writer out there. I always find it fun to read the new entries on labs.trolltech.com than write a new one myself. I enjoy writing articles for Qt Quarterly more, then I have something that is crunchy and I can put it on a shelf and collect space. I suppose I could invest in a printer for the blog, but that seems like more work. Anyway, I found a topic here that follows up on a previous Qt Quarterly article of mine and updates it to more modern Qt technologies, all in a short enough space for a blog entry (I hope).

A couple years ago, I quickly wrote the article, Fading Effects with Qt 4.1. It outlined a fader widget which you could drop on top of another widget (like a QTabWidget or a QStackedWidget) and have a fade transition. It was a quick job that showed off the neat effects you can do when you have a backing store. Since I wrote that article, there were some things added to Qt 4.2 that make the thing even easier to use. So, I thought I would point them out in case you missed them. Download the source code from the original article and you can make changes at home.

Here’s the header:
class FaderWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(QBrush fadeBrush READ fadeBrush WRITE setFadeBrush)
Q_PROPERTY(int fadeDuration READ fadeDuration WRITE setFadeDuration)
public:

FaderWidget(QWidget *parent);

QBrush fadeBrush() const { return startBrush; }
void setFadeBrush(const QBrush &newColor) { startBrush = newColor; }

int fadeDuration() const { return timeLine->duration(); }
void setFadeDuration(int milliseconds) { timeLine->setDuration(milliseconds); }

void start();

protected:
void paintEvent(QPaintEvent *event);

private:
QTimeLine *timeLine;
QBrush startBrush;
};

You’ll notice that we’ve eliminated some of the now needless variables and changed our QColor to a QBrush and our QTimer to a QTimeLine.

Now the changed parts of the source file, first the constructor:

FaderWidget::FaderWidget(QWidget *parent)
: QWidget(parent)
{
if (parent)
startBrush = parent->palette().window();
else
startBrush = Qt::white;

timeLine = new QTimeLine(333, this);
timeLine->setFrameRange(1000, 0);
connect(timeLine, SIGNAL(frameChanged(int)), this, SLOT(update()));

setAttribute(Qt::WA_DeleteOnClose);
resize(parent->size());
}

Here, we create our QTimeLine. QTimeLine is a class introduced in 4.2 that will take a time and a set number of frames and dish them out in regular intervals over that duration to make them appear animated. It helps keep animation constant, dropping frames if things end up getting behind and trying to ensure that an animation only lasts for the time alloted to it.
We set the time to be about a third of a second and give it a thousand frames to draw, starting at 1000 and ending at 0. Obviously, it won’t be drawing all of them in this time frame. We tell the timeline’s frameChanged() signal to trigger an update on the fader widget.

We also try to get the brush for the parent widget’s window role. This is a change from before where we only got the color and not the brush, since we couldn’t trivially alpha blend the an arbitrary pixmap in Qt 4.1.


void FaderWidget::start()
{
timeLine->start();
show();
}

Start remains similar to before, except it only needs to start the timeline.


void FaderWidget::paintEvent(QPaintEvent * /* event */)
{
QPainter painter(this);
qreal frame = timeLine->currentFrame();
painter.setOpacity(frame / 1000.);
painter.fillRect(rect(), startBrush);

if (frame < = 0.) close(); }

Our paint event has probably changed the most (for the better). Instead of constantly calculating the new alpha color, we simply take the current frame of the timeline and divide it by our total frames, and use that as the opacity value for our QPainter. QPainter::setOpacity() was introduced in Qt 4.2 and sets the opacity for all drawing operations done with that painter. It’s easier than ever to get alpha blending effects with QPainter::setOpacity(). Then we simply fill the entire widget with the brush we got at the beginning.

That’s it! Feel free to make these changes to the code and see it in action. It will look much better than the previous version on platforms that have a pixmap for their window brush (like Mac OS X, XP, and Vista). I included a screen shot of a fade in action here below:

FaderWidget in action

Anyway, this is just an example with what you can do with Qt 4.2, not to mention 4.3. So, take a look at the classes like QTimeLine and new functions like QPainter::setOpacity(), see where you can use them in your existing apps to improve the user experience and make your maintenance easier.

Enjoy!

Do you like this? Share it
Share on LinkedInGoogle+Share on FacebookTweet about this on Twitter

Posted in Qt

4 comments

Impressive!
It was already quite simple to do that effect with Qt.
Now it’s even simpler 🙂

How hard would it be to blend both old and new widgets?
Instead of just removing abruptly the old widget and making the new one appear from grat to plain.

trenton says:

Well, without really thinking too much about it, I’d stab at it this way:

You could have another fader widget that takes a pixmap as input. The pixmap would of course be generated from QPixmap::grabWidget() with the widget you just changed from and instead of doing a fillRect() in the paint event, it just draws the pixmap. Then put that fader on top of the old fader. As the faderwidget with the pixmap of the grabbed widget fades out, the other one fades in.

I haven’t tried this, but at least conceptually it would work. Not sure how well it would be performance-wise, but you are wanting some glitzy stuff for that third of a second, right 🙂

Adam Higerd says:

Sebastien, I’ve done a crossfade effect before. It’s really not all that difficult. The concept is similar to this, but instead of stacking two fader widgets like trenton suggests, you grabWidget the first widget into a pixmap and draw it on top of the second widget, with the painter’s opacity set to some decreasing fraction.

If you cache the grabbed QPixmap, the performance is actually quite respectable.

M. Chiadmi says:

Thank you very much for your post. It’s good to see code get simpler!

Although I’m aware that your code is purely meant to illustrate a point, I would simply like to mention that, in the constructor of your class, there might be a problem if no parent is specified (=NULL). When you call “resize(parent->size());”, there should be an if statement to verify that there is indeed a parent. Otherwise, a segmentation will occur.

Thank you once again and I look forward to reading your next article in the Qt Quaterly (and hopefully another blog post!).

PS: This post is relatively old. I hope that you will read my reply even though it does not add anything to your point.

Commenting closed.

Get started today with Qt Download now