PDA

View Full Version : Multiple inheritance of QGraphicsView and QGraphicsItem



cookie1909
4th May 2009, 19:00
I'm writing a program that contains a table of buttons, each button has the ability to blink, and if it's clicked, all buttons underneath the clicked button (in the same column) should be moved down.

Initially, I was able to do this if all buttons of one column are in the same QGraphicsView. However one blinking button requires re-painting of the whole view, which results in taking up ~17% CPU. So I changed my code to put one button in a QGraphicsView; then put all the ButtonViews in another QGraphicsView (i.e. the button itself is a graphics item for animation as well as a view). Now one blinking button only takes up 9% CPU, but the animation no longer works.

I'm guessing there must be something wrong with my design, and hoping the Qt experts could help me with a better one.

I've attached the code here. I use Qt 4.5.3 under Unix/X11

wysota
4th May 2009, 19:11
You should surely not inherit from both a widget and a graphics item. In general I think your concept is wrong. You should group items in parent-child relationships instead of creating hierarchies of views. If you want to add widgets to a graphics scene, use QGraphicsScene::addWidget() which uses QGraphicsProxyWidget class.

Please use the attachment feature of the forum instead of inlining large amounts of code into posts.

cookie1909
6th May 2009, 19:47
I changed my code like you suggested to use QGraphicsProxyWidget. However the performance of the blinking button got a little worse.

Attached is a screenshot (buttons.jpg) of what I'm trying to do: a table of QGraphicsProxyWidgets (acting like buttons) and each button can be animated up and down.

Case 1: I have 4 views (1 view for each column), each view contains 8 proxy widgets, only the first button is blinking ==> my CPU consumption is about 17%

Case 2: I have 1 big view, which contains 32 small views (1 view for each proxy widgets, and the view itself is a proxy widget), one blinking button ==> my CPU consumption goes up to 25%

Do I understand this correctly: one blinking button will cause the parent view to be updated, and since this view belongs to another parent view, this makes the big view to be re-painted for every blink?

If so, is there any way to improve the performance, i.e. avoiding repainting everything but only the blinking button?

Thanks a lot for helping.

wysota
6th May 2009, 20:09
You only need one view for everything. Only those items that change will be repainted. If you have buttons that look so custom, consider creating a custom item instead of using widget proxies. If you use stylesheets to get the custom look of the buttons, this is affecting your performance. With what you want to obtain you should have practically no cpu usage.

cookie1909
6th May 2009, 21:08
I do not use any stylesheets, everything is being painted. In order to have animation, I need to use either QGraphicsItem or QGraphicsProxyWidget for my button. I choose proxy widget so that connect() function can be called to do blinking effect when its timer expires.

I will try out putting all buttons in one view and hopefully the performance will be as good as you said :)

Thanks again!

wysota
6th May 2009, 22:01
I choose proxy widget so that connect() function can be called to do blinking effect when its timer expires.
It would be enough to inherit from both QGraphicsItem and QObject. Proxying through a widget just creates overhead.

cookie1909
13th May 2009, 21:47
I'm back with my improved code! :)

Like you suggested, I used one single view and have a table of QGraphicsItems within a scene. One blinking (every 0.5sec) button takes up about 10% CPU usage. Note that this is QtEmbedded I'm using, and we don't have a very powerful CPU, so 10% is acceptable.

Now I put my view into my real program that contains a window with tab widgets and buttons, all static widgets. One blinking button now takes 60% CPU, if I increase the blinking timer to 1sec interval, it drops down to 30%, and of course 0% when there's no blinking.

That tells me whenever the button blinks, more than just the button is being painted. I looked up the desc of QGraphicsItem::update()


As a side effect of the item being repainted, other items that overlap the area rect may also be repainted.

Can "other items that overlap" be the QGraphicsView that it belongs to, as well as the QWidget that the view belongs to? If so, it is redrawing a lot of unneccessary widgets.

Here's how I implement my paint function and blink slot:


QRectF LineButton::boundingRect() const
{
return QRectF(mPicX, mPicY, LINE_BUTTON_WIDTH, LINE_BUTTON_HEIGHT);
}

void LineButton::paint(
QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *)
{
painter->setClipRect(option->exposedRect);
painter->drawPixmap(mPicX, mPicY, *mPic);
}

void LineButton::blink()
{
if (mPic == mPixmapBlack)
mPic = mPixmapYellow;
else
mPic = mPixmapBlack;
QGraphicsItem::update(boundingRect());
}

wysota
14th May 2009, 00:38
Can "other items that overlap" be the QGraphicsView that it belongs to, as well as the QWidget that the view belongs to?
No. Items means graphics items in the view (or scene actually). Remember that if you use an OpenGL viewport for your graphics view then the whole viewport will be redrawn.


Here's how I implement my paint function and blink slot:


QRectF LineButton::boundingRect() const
{
return QRectF(mPicX, mPicY, LINE_BUTTON_WIDTH, LINE_BUTTON_HEIGHT);
}
This is probably wrong. I'd guess it should say QRectF(0,0,LINE_BUTTON_WIDTH, LINE_BUTTON_HEIGHT) and then you should use QGraphicsItem::setPos() to position the button on the scene.


void LineButton::paint(
QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *)
{
painter->setClipRect(option->exposedRect);
painter->drawPixmap(mPicX, mPicY, *mPic);
}
Don't clip the painter. It only slows you down. The architecture already sets up the clipper so you're doubling its work.

cookie1909
14th May 2009, 20:07
I read the Graphics View Framework docs, updated the coordinates like below, but it didn't make any difference in CPU usage :( I'm running out of ideas of what else to try :confused:



LineButton::LineButton(int x, int y)
{
setPos(x, y);
mPixmapBlack = new QPixmap("black.png");
mPixmapYellow = new QPixmap("yellow.png");
mPic = mPixmapBlack;
}

QRectF LineButton::boundingRect() const
{
return QRectF(0, 0, LINE_BUTTON_WIDTH, LINE_BUTTON_HEIGHT);
}

void LineButton::paint(
QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *)
{
painter->drawPixmap(0, 0, *mPic);
}

void LineButton::blink()
{
if (mPic == mPixmapBlack)
mPic = mPixmapYellow;
else
mPic = mPixmapBlack;
QGraphicsItem::update(boundingRect());
}

cookie1909
15th May 2009, 19:02
I finally found out what causes all of the repaintings!!!

I loaded this test program onto a device with a touch screen that is smaller than the Qt Window size. Everytime the button blinks, turned out Qt tries to repaint that button, as well as all other widgets of the window that got cut off by the LCD.

I fixed it so everything fits on the screen, one blinking button takes up 2% CPU, and all buttons blinking takes up 45% CPU, and am quite happy with that result.

Thanks so much again for all the help!