Fading Effects with Qt 4.1

Материал из Wiki.crossplatform.ru

Перейти к: навигация, поиск
Image:qt-logo_new.png Image:qq-title-article.png
Qt Quarterly | Выпуск 16 | Документация


by Trenton Schulz

« The backing store in Qt 4.1 makes it much easier to implement transparency effects than previous versions of Qt. In this article, we will combine analpha channel with a timer and get some nice results that can make an interface attractive without distracting the user. »

Download source code

We use semi-transparency to implement FaderWidget, a widgetthat we'll put on top of another widget before fading it away. As it fadesaway, the widget underneath it will become visible.

class FaderWidget : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(QColor fadeColor READ fadeColor \
               WRITE setFadeColor)
    Q_PROPERTY(int fadeDuration READ fadeDuration \
               WRITE setFadeDuration)
public:
    FaderWidget(QWidget *parent);
 
    QColor fadeColor() const { return color; }
    void setFadeColor(const QColor &newColor);
    int fadeDuration() const { return duration; }
    void setFadeDuration(int milliseconds);
    void start();
 
protected:
    void paintEvent(QPaintEvent *event);
 
private:
    QTimer *timer;
    int currentAlpha;
    QColor color;
    int duration;
};

The fadeColor property controls the solid color that we startfading from; fadeDuration is the time in milliseconds it takes forthe fade to complete.

FaderWidget::FaderWidget(QWidget *parent)
    : QWidget(parent)
{
    if (parent)
        startColor = parent->palette().window().color();
    else
        startColor = Qt::white;
 
    currentAlpha = 0;
    duration = 333;
 
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()),
            this, SLOT(update()));
 
    setAttribute(Qt::WA_DeleteOnClose);
    resize(parent->size());
}

The constructor initializes the default fade color to the parent's"window" palette entry (the "background" entry in previous Qtversions), typically some shade of gray. The default fade duration is333 milliseconds. This is usually short enough for the animation tofinish without being too distracting.

We enable the Qt::WA_DeleteOnClose attribute because the fader isa short-lived widget that automatically deletes itself once thefading effect is finished. We also create a QTimerto call update() repeatedly. At the end, we resize the widget so thatit is the same size as the parent, effectively covering it completely.

void FaderWidget::start()
{
    currentAlpha = 255;
    timer->start(33);
    show();
}

The start() function initiates the fade. Here we simply set thecurrentAlpha value to 255 (opaque).To obtain a rate of 30 frames per second, we use 33 milliseconds as the timerinterval.

Now, every time the timer emits the timeout() signal, we will geta paint event. The paintEvent() handler looks like this:

void FaderWidget::paintEvent(QPaintEvent * /* event */)
{
    QPainter painter(this);
    QColor semiTransparentColor = startColor;
    semiTransparentColor.setAlpha(currentAlpha);
    painter.fillRect(rect(), semiTransparentColor);
 
    currentAlpha -= 255 * timer->interval() / duration;
    if (currentAlpha <= 0) {
        timer->stop();
        close();
    }
}

We create a new color based on the original color and our current alphavalue. We then fill the widget with this color and decrement thealpha value. The next time we paint, the FaderWidget will beslightly more transparent. When the currentAlpha reaches zero, westop the timer and close the widget since, at this point, thewidget is completely transparent. The widget will be implicitly deletedwhen it is closed because we set theQt::WA_DestroyOnClose attributein the constructor.

Now that we have implemented the FaderWidget, let's quicklymodify the Config Dialog example that is included with Qt (in"examples/dialogs/configdialog") to fade between its pages. Wesimply need to add a new private slot and a QPointer to the ConfigDialog class:

private slots:
    void fadeInWidget(int index);
private:
    QPointer<FaderWidget> faderWidget;

In the constructor, we connect QStackedWidget'scurrentChanged() signal toour new fadeInWidget() slot:

  connect(pagesWidget, SIGNAL(currentChanged(int)),
            this, SLOT(fadeInWidget(int)));

Here's the slot:

void ConfigDialog::fadeInWidget(int index)
{
    if (faderWidget)
        faderWidget->close();
    faderWidget = new FaderWidget(
                              pagesWidget->widget(index));
    faderWidget->start();
}

First, we check if we have a FaderWidget. This is where a QPointer comes in handy since, unlike a standardpointer, it is automatically reset to zero when the object it points to isdeleted. If there is already a FaderWidget, we close it since wedon't need to have multiple fades happening at the same time.

In any case, we create a new FaderWidget with the widget to berevealed as its parent, and call start on the FaderWidget. The restjust fades away, as you will see when you run the example.