PDA

View Full Version : Speeding up QPaint



georgie
14th May 2006, 03:40
I think my computer swallowed my last posting of the same info... not sure tho...sorry if this is a double post

Still trying to make my nice array of 100 pretty round buttons even nicer :)

due to the fact that I often have to update the state of about 25-30 buttons at a time, the repaints that I am calling are kinda slow....

I thought I could maybe speed up the repaints by not calling

QPainter painter (this);
every single repaint, but instead creating the painter on initialisation and having a QPainter* as a member of the RoundButton class - but there are a couple of qns
1. Will this actually make it that much faster?
2. When I try to do this, it says that a painter can only be created for a QWidget (RoundButton is a derivative of QPushButton) during a paint event, so i can't initialize it during construction

anybody tried anything similar? any success? am I barking up the wrong tree completely?

wysota
15th May 2006, 09:41
1. Will this actually make it that much faster?
No.


2. When I try to do this, it says that a painter can only be created for a QWidget (RoundButton is a derivative of QPushButton) during a paint event, so i can't initialize it during construction
By default, you can't paint on widgets outside paint events in Qt4. You can pass a flag to a widget that you want to paint on it outside the paint event, but it won't help you much here.


am I barking up the wrong tree completely?
Yes, I think so. You'd better focus on optimising the paint event itself. Use pixmaps, redraw only the part of the widget which needs repainting, use faster routines, etc.

georgie
15th May 2006, 10:00
bummer, cause the only reason i ever need to redraw is to change the colour completely, so repainting only a little won't help me much
and it is already a very simple routine which only does the bare minimum so i doubt i can make it much faster

:(

it's just because i always have to do so many at once that it's a prob....oh well....

the only thing i can think of then, is instead of creating a new gradient every time i could draw from one of 3 predefined pixmaps (i want 3 diff colours)....but i don't think this will help either because even when i paint flat colours you can't notice any real speed increase over the gradients....

thanks anyway

wysota
15th May 2006, 10:45
But do you always need to redraw the whole widget? Could you show me your paintEvent routine?

georgie
15th May 2006, 10:56
for a wonderfully wacky and convoluted reason, the attached pic is what my buttons need to look like...the yellow is the selected, the definition of what a "hex" is follows the yellow one - i.e. if i select a diff button, the diff group of buttons becomes the hex centre so the extra circles get removed from one set of buttons and drawn on a diff set



void RoundButton::paintEvent(QPaintEvent* event)
{
QRadialGradient grad(15, 15, 35, 0, 0);
QPainter painter(this);
grad.setColorAt(0.1, Qt::white);
if(this->focus)
grad.setColorAt(0.2, Qt::yellow); //the selected one is yellow
if(this->arr->getHex()) //if the user has selected "display hex" the buttons get linked into
//6's by another circle around the one in the centre of the hex
{
if(this->hexCentre)
{
grad.setColorAt(0.2, Qt::cyan);
QPen pen(Qt::darkRed);
painter.setPen(pen);
painter.drawEllipse(0.5,0.5,29, 29);
pen.setColor(Qt::black);
painter.setPen(pen);
}
}
grad.setColorAt(0.7, Qt::black);

QBrush brush(grad);
painter.setBrush(brush);
painter.drawEllipse(8,8,14,14);
}

thanks for looking at this :)

wysota
15th May 2006, 11:46
Do you need all those buttons as separate buttons? Wouldn't it be easier to have a single widget which handles them all?

I see your paint events shouldn't experience any slowdowns. The reason has to be somewhere else. The only thing you may improve here is not to generate the gradient every repaint -- make it a member of the button and modify it only when it changes.

georgie
15th May 2006, 11:50
to a point, i guess it would be better....but there is quite a bit of associated "internal state" info which goes with each button.....which would get mighty confusing with one big widget :S

thanks for your help though

wysota
15th May 2006, 11:52
to a point, i guess it would be better....but there is quite a bit of associated "internal state" info which goes with each button.....which would get mighty confusing with one big widget :S

Why? You can keep states of each of the buttons in some data structure.

georgie
15th May 2006, 12:00
i never thought about it like that....how would you be able to register a click within a region - like if there was just one big widget but i wanted to click on button number 5, how would i know exactly where the user had clicked? I was using the mousePressEvent of each individual widget....

georgie
15th May 2006, 12:26
keeping the gradient as a "has a" member does speed stuff up quite a bit, but the first time i set the colour at 0.2, this cannot be changed in subsequent repaints (i.e. if i deselect something, it remains its selected colour....e.g. if it was the selected one, but then it becomes only the centre of a hex, instead of going from yellow to cyan it stays yellow for the remainder)

there doesn't seem to be an "unsetColorAt()" function, so i think it is necessary to keep initializing a new grad....which is a bummer :( cause it made it so much better

wysota
15th May 2006, 16:30
i never thought about it like that....how would you be able to register a click within a region - like if there was just one big widget but i wanted to click on button number 5, how would i know exactly where the user had clicked? I was using the mousePressEvent of each individual widget....


void MyWidget::mousePressEvent(QMouseEvent *e){
QPoint clicked = e->pos();
// now calculate which button was clicked (if any)
// for example (simplified problem):
// X X X X X
// X X X X
// X X X X X
// X X X X
// each X is 20x20px
// row:
int row = clicked.y()/20;
// col:
int col = clicked.x()/20;
// check if position occupied and ignore otherwise:
if(row % 2 && !(col%2)) return; // odd numbered row and even numbered column (starting from 0)
if(!(row%2) && col%2) return; // even numbered row and odd numbered column
// now it should be easy to map [row x column] to a button, for example:
QPoint buttoncoords(row/2, col/2);
emit buttonClicked(buttoncoords);
}


keeping the gradient as a "has a" member does speed stuff up quite a bit, but the first time i set the colour at 0.2, this cannot be changed in subsequent repaints (i.e. if i deselect something, it remains its selected colour....e.g. if it was the selected one, but then it becomes only the centre of a hex, instead of going from yellow to cyan it stays yellow for the remainder)

there doesn't seem to be an "unsetColorAt()" function, so i think it is necessary to keep initializing a new grad....which is a bummer cause it made it so much better
So create a new one if it changes. Most of the time it won't change.


void MyButton::paintEvent(QPaintEvent *e){
if(buttonStateChanged){
grad = QRadialGradient (15, 15, 35, 0, 0);
grad.setColorAt(0.1, Qt::white);
grad.setColorAt(0.2, ...);
grad.setColorAt(..., ...);
}
QPainter p(this);
drawButton(&p);
}

or even predefine gradients:

class MyButton : public ... {
//...
private:
static QRadialGradient normalGradient;
static QRadialGradient selectedGradient;
static QRadialGradient someotherGradient;
//...
};
//...
QRadialGradient MyButton::normalGradient = QRadialGradient(15, 15, 35, 0, 0);
//...

georgie
16th May 2006, 01:34
i only ever call repaint if it changes, so this will make no difference....
*BUT* I will definitely try your last idea....it sounds like it will do the trick....thankyou for your help

wysota
16th May 2006, 02:06
i only ever call repaint if it changes, so this will make no difference....

But Qt calls it now and then when it feels like it and defining 1000 gradients, opening 1000 painters, etc. is definitely more expensive than doing it all once.