PDA

View Full Version : How and when to repaint a widget ?



yellowmat
31st March 2006, 16:39
Hi !

I wrote a widget class to manage animations. It is possible to set the displaying speed with a timer, and on each timeout I display an image.

When I have to animation object (one uppon the other), the one which is behind the other is not repainted successfully.

Actually my drawing code is in the function displayImage ... but I think it shouldn't be the right way to process.

Something more conform should be to implement an update or repaint call in the function displayImage and then implement the drawing of the image in the function paintEvent but it is flickering.

Here is the code I actually use to, so if someone could help me to find out how to do, it would be great.



// Constructor / Destructor
CAnimation::CAnimation(QWidget* parent/*=0*/, const char* name/*=0*/):
QWidget(parent, name, Qt::WStyle_Customize|Qt::WStyle_NoBorder),
timer(0),
mode(PlayModeForeward),
loopStopIndex(1),
loopCurrentIndex(0),
speed(500),
imageStartIndex(0),
imageStopIndex(0),
imageCurrentIndex(0),
playingStatus(Idle),
displayingStatus(Hidden),
transparency(false)
{
// Create the timer
if( timer == 0 )
timer = new QTimer(this, "timer");

// Connect the timer signal to the animation object slot
connect(timer, SIGNAL(timeout()), SLOT(timerTick()));
}


CAnimation::~CAnimation()
{
// Stop the timer if active
if( timer->isActive() )
timer->stop();

// Delete the timer
if( timer != 0 )
{
delete timer;
timer = 0;
}

// Clear the image names
images.clear();
}


// Slots
int CAnimation::displayImage(const QPixmap& pixmap)
{
if( transparency )
{
if ( pixmap.mask() )
this->setMask( *pixmap.mask() );
}

QRect rect(this->rect());
bitBlt(this, rect.topLeft(), &pixmap);

return 0;
}



void CAnimation::timerTick()
{
switch(mode)
{
case(CAnimation::PlayModeForeward):
{
/************************************************** ************************************************** ****************
Two cases
CASE 1 : if the image current index is not the image stop index then we increment it and display the image.
CASE 2 : if the image current index is the image stop index, we must take care if the sequence must be looped.
************************************************** ************************************************** *****************/

// CASE 1
if( imageCurrentIndex == imageStartIndex )
{
// Notify
emit sequenceStarted();
}

if( imageCurrentIndex < imageStopIndex )
{
// Display the image
this->displayImage(images[imageCurrentIndex]);

// Notify
emit frameDisplayed(imageCurrentIndex);

// Update the state
this->setPlayingStatus(CAnimation::Started);

// Increment the current index
imageCurrentIndex++;
}

// CASE 2
else if( imageCurrentIndex == imageStopIndex )
{
if( loopStopIndex == 0 )
{
// Display the image
this->displayImage(images[imageCurrentIndex]);

// Notify
emit frameDisplayed(imageCurrentIndex);
emit sequenceFinished();

// Finish the animation
this->finish();
}
else if( loopCurrentIndex < loopStopIndex )
{
// Display the image
this->displayImage(images[imageCurrentIndex]);

// Notify
emit frameDisplayed(imageCurrentIndex);
emit sequenceFinished();

// Update the state
this->setPlayingStatus(CAnimation::Started);

// Increment the loop index
loopCurrentIndex++;

// Reset the image current index
imageCurrentIndex = imageStartIndex;
}
else if( loopCurrentIndex == loopStopIndex )
{
// Display the image
this->displayImage(images[imageCurrentIndex]);

// Notify
emit frameDisplayed(imageCurrentIndex);
emit sequenceFinished();

// Finish the animation
this->finish();
}
}
}
break;

case(CAnimation::PlayModeBackward):
{
/************************************************** ************************************************** ****************
Two cases
CASE 1 : if the image current index is not the image stop index then we decrement it and display the image.
CASE 2 : if the image current index is the image stop index, we must take care if the sequence must be looped.
************************************************** ************************************************** *****************/

// CASE 1
if( imageCurrentIndex == imageStartIndex )
{
// Notify
emit sequenceStarted();
}

if( imageCurrentIndex > imageStopIndex )
{
// Display the image
this->displayImage(images[imageCurrentIndex]);

// Notify
emit frameDisplayed(imageCurrentIndex);

// Update the state
this->setPlayingStatus(CAnimation::Started);

// Increment the current index
imageCurrentIndex--;
}

// CASE 2
else if( imageCurrentIndex == imageStopIndex )
{
if( loopStopIndex == 0 )
{
// Display the image
this->displayImage(images[imageCurrentIndex]);

// Notify
emit frameDisplayed(imageCurrentIndex);
emit sequenceFinished();

// Finish the animation
this->finish();
}
else if( loopCurrentIndex < loopStopIndex )
{
// Display the image
this->displayImage(images[imageCurrentIndex]);

// Notify
emit frameDisplayed(imageCurrentIndex);
emit sequenceFinished();

// Update the state
this->setPlayingStatus(CAnimation::Started);

// Increment the loop index
loopCurrentIndex++;

// Reset the image current index
imageCurrentIndex = imageStartIndex;
}
else if( loopCurrentIndex == loopStopIndex )
{
// Display the image
this->displayImage(images[imageCurrentIndex]);

// Notify
emit frameDisplayed(imageCurrentIndex);
emit sequenceFinished();

// Finish the animation
this->finish();
}
}
}
break;

default:
break;
}
}


int CAnimation::start()
{
// Check conditions
// ... TO DO

// Set initial values
imageCurrentIndex = imageStartIndex;
loopCurrentIndex = 0;

// Start timer
timer->start(speed);

// Update state
this->setPlayingStatus(CAnimation::Started);

// Notify
emit animationStarted();

return 0;
}


int CAnimation::stop()
{
// Stop the timer
timer->stop();

// Update the state
this->setPlayingStatus(CAnimation::Stopped);

// Notify
emit animationStopped();

return 0;
}


int CAnimation::pause()
{
// Stop the timer
timer->stop();

// Update the state
this->setPlayingStatus(CAnimation::Paused);

// Notify
emit animationPaused();

return 0;
}


int CAnimation::resume()
{
// Start the timer
timer->start(speed);

// Update the state
this->setPlayingStatus(CAnimation::Started);

// Notify
emit animationResumed();

return 0;
}


int CAnimation::finish()
{
// Stop the timer
timer->stop();

// Reset values
imageCurrentIndex = imageStartIndex;
loopCurrentIndex = 0;

// Update the state
this->setPlayingStatus(CAnimation::Finished);

// Notify
emit animationFinished();

return 0;
}



int CAnimation::showAnim()
{
// Show the widget
this->show();

// Update the state
this->setDisplayingStatus(CAnimation::Shown);

// Notify
emit animationShown();

return 0;
}


int CAnimation::hideAnim()
{
// Hide the widget
this->hide();

// Update the state
this->setDisplayingStatus(CAnimation::Hidden);

// Notify
emit animationHidden();

return 0;
}


Thanks in advance.

wysota
1st April 2006, 01:14
Did you try just using QMovie (http://doc.trolltech.com/3.3/qmovie.html)?

yellowmat
3rd April 2006, 08:49
Yes I did it first but it did not worked well ... in fact it did not display correctly big animations (800x640) so that's the reason why I decided to developp my own widget.

wysota
3rd April 2006, 11:09
Maybe it would be better just to subclass it and correct things you don't like instead of doing everything on your own? Your code looks a bit complicated.

yellowmat
3rd April 2006, 13:28
Is it possible to subclass the QMovie class even if it does not have any virtual method ?

wysota
3rd April 2006, 14:33
Sure. Why not? You can subclass and reimplement whatever you want. Just make sure you reference the right class later on. Qt doesn't use QMovie anywhere by itself, so it's just your choice if you write:


QMovie *movie = MyMovie();
or

MyMovie *movie = MyMovie();

The first one will not use your inherited functions which are not virtual, but the second one will, no matter that the base class methods are not virtual -- you are calling the class by name.

yellowmat
3rd April 2006, 15:33
Hmm, to be sure I understood the subclassing concept let me (trying to) explain it with my words :o

Subclassing and inheriting are synonyms. Subclassing consist of inheriting an existing class in order to make a specialization of it. It is up to the developper to reimplement one or more function of the base class. It will be then possible to access methods from base class or inherited class using a cast operator.

If I use your example :


class MyMovie : public QMovie
{
void step() { /* DO NOTHING */ }
};


The previous code make MyMovie subclassing QMovie, and reimplements the step() method which does nothing in the specialized class (I know it's not usefull but it is just an example ;) )

If I do


MyMovie* movie = new MyMovie();
movie->step();

... it will call the step() method reimplemented in MyMovie

and if I then do


QMovie* base = new MyMovie();
movie->step();

... then this is the base class methods which will be called.

That's it ?

Another things, about my CAnimation class. First, I decided to developp it from scratch because the QMovie doesn't not worked well and finally its functionnalities were too poor compared to my needs, so it was faster for me to do it this way. Second, I solved my problem using the paintEvent handler and I have modified my code as follow :
- I have replaced each call to the displayImage with an update call
- I have reimplemented the paintEvent function as follow :

void CAnimation::paintEvent(QPaintEvent* event)
{
if( this->isShown )
{
QPixmap pixmap(images[imageCurrentIndex]);
if( transparency )
{
if ( pixmap.mask() )
this->setMask( *pixmap.mask() );
}

QRect rect(event->rect());
bitBlt(this, rect.topLeft(), &pixmap);
}
}

isShown is just a boolean indicating if the animation is shown or hidden ... and it works nice :)

Thanks for your help.

wysota
3rd April 2006, 17:36
and if I then do


QMovie* base = new MyMovie();
movie->step();

... then this is the base class methods which will be called.

That's it ?

I assume you meant base->step(). No, the compiler will complain there is no step() method defined in QMovie.

Try this:


#include <iostream>

class A {
public:
void func1(){ std::cout << "Class A func1" << std::endl; }
virtual void func2(){ std::cout << "Class A func2" << std:: endl; }
};

class B : public A {
public:
void func1(){ std::cout << "Class B func1" << std::endl; }
void func2(){ std::cout << "Class B func2" << std::endl; }
};

int main(){
A a;
B b;
a.func1();
a.func2();
b.func1();
b.func2();
return 0;
}