PDA

View Full Version : Implementing fade-in / fade-out for a modal QDialog



d_stranz
28th October 2013, 22:30
Here's a summary of today's adventures in Qt5. Maybe this will be of some help to others.

I would like to implement a fade-in / fade-out feature for modal dialogs derived from QDialog. I did this for a QML-based modal Window and liked the effect, using a QML PropertyAnimation on the opacity property.

It should be possible to duplicate this behavior with QDialog using a QPropertyAnimation on the QWidget::windowOpacity property. I display the dialog using QDialog::exec().

I created two QPropertyAnimation instances, one for fade-in, one for fade-out as members of my dialog class.

I implemented the fade-in feature by overriding the showEvent() and starting the fade-in animation there. This works fine - the dialog slowly fades into view as expected.

I had trouble with the fade-out implementation. I tried overriding the hideEvent(), but this is too late. Qt has already hidden the window by the time this event occurs, so the fade out animation does nothing. I've also tried it in the closeEvent(), but this method is not called - hideEvent() is called instead. I assume that this is because my QDialog is not a top-level widget, but a child of the QMainWindow.

The hideEvent() also occurs before QDialog:exec() returns, so it can't be trapped there either without re-implementing that method.

So what I finally hit on was to override the QDialog::done() slot. I also added a new slot, onFadeOutFinished() and connected this to the QPropertyAnimation::finished() signal for the fade-out instance. In my done() slot, I start the fade-out animation, and in the finished() signal handler slot, I call the actual QDialog::done() slot.

The code looks like this:



FaderDialog::FaderDialog( QWidget * parent ) : QDialog( parent ), mResult( 0 )
{
// Create the fade-in / fade-out animators

mpFadeIn = new QPropertyAnimation( this, "windowOpacity" );
mpFadeIn->setDuration( 150 );
mpFadeIn->setStartValue( 0.0 );
mpFadeIn->setEndValue( 1.0 );

mpFadeOut = new QPropertyAnimation( this, "windowOpacity" );
mpFadeOut->setDuration( 500 );
mpFadeOut->setStartValue( 1.0 );
mpFadeOut->setEndValue( 0.0 );

connect( mpFadeOut, SIGNAL( finished() ), this, SLOT( onFadeOutFinished() ) );
}

void FaderDialog::showEvent( QShowEvent * )
{
mpFadeIn->start();
}

void FaderDialog::done( int result )
{
mResult = result; // remember the result in a member variable
mpFadeOut->start();
}

void FaderDialog::onFadeOutFinished()
{
QDialog::done( mResult ); // now call the real done() slot
}


There seems to be one glitch - the whole application "blinks" just as the fade-out animation starts. It seems that changing the opacity of the dialog causes a paint event in the parent. I will look into ways to counteract that. I think it is possible - if I do the same thing with a QML dialog, I don't think I see a blink.

anda_skoa
29th October 2013, 07:52
Not related to you blink problem, but one other thing you could try for the start of hide animation is overwriting setVisible()

Cheers,
_

d_stranz
29th October 2013, 22:31
Not related to you blink problem, but one other thing you could try for the start of hide animation is overwriting setVisible()


Yes, I thought about that also. In the case of a non-modal dialog, that's probably where it would have to be done, but I can't be sure (without looking at the source code) that calling hide() might be an end-run around setVisible( false ), so it might have to be implemented in both places.

Since a modal dialog vectors everything through the done() slot this seemed like a good place to intervene.

Still don't know about the blink problem - I'm guessing that the window system manages things in the case of simply changing the visibility, but changing some other window property triggers the repaint. I won't worry about it until it becomes more annoying.

stampede
30th October 2013, 07:11
I can't be sure (without looking at the source code) that calling hide() might be an end-run around setVisible( false )
From the docs:
Hides the widget. This function is equivalent to setVisible(false).
sources:

void QWidget::hide()
{
setVisible(false);
}

void QWidget::show()
{
bool isPopup = data->window_flags & Qt::Popup & ~Qt::Window;
if (isWindow() && !isPopup && qApp->styleHints()->showIsFullScreen())
showFullScreen();
else
setVisible(true);
}

void QWidget::setHidden(bool hidden)
{
setVisible(!hidden);
}

// sorry i forgot this one:
void QWidget::showFullScreen()
{
...
...
setVisible(true);
...
}