Trolltech Home | Qt-jambi-interest Home | Recent Threads | All Threads | Author | Date
All threads index page 1

Qt-jambi-interest Archive, January 2007
Neither update() nor repaint() triggers paintEvent in child widgets


Message 1 in thread

Hi!

I've got a problem with a fairly complex tree of widgets: I'm updating the 
state of some custom widgets and need to update the display afterwards. I try 
to do this by calling update() on the parent of all these widgets. However, 
the children do not receive a paint event upon this call. Calling repaint() 
instead won't work, either. When I resize the area afterwards and trigger a 
paint event that way, everything is in order (hiding the window and bringing 
it into the foreground will not work, though). The behaviour seems like 
bitmaps of the rendered child widgets are cached by Qt and used to prevent 
recursion upon redraws. Is there a way I can force a redraw of the parent and 
all the children without having to iterate them all explicitely?

Regards,
Gregor


Message 2 in thread

Gregor Mückl wrote:
> Hi!
> 
> I've got a problem with a fairly complex tree of widgets: I'm updating the 
> state of some custom widgets and need to update the display afterwards. I try 
> to do this by calling update() on the parent of all these widgets. However, 
> the children do not receive a paint event upon this call. Calling repaint() 
> instead won't work, either. 

This is expected behaviour. update() triggers an asynchronous repaint 
and repaint() triggers a synchronous repaint on the widget in question. 
In general you should _always_ use update().

If the parent has transparent child widgets, i.e widgets with 
autoFillBackground set to false (the default), the attribute 
NoBackground or the palette() for QWidget.backgroundRole() is a NoBrush, 
its children _should_ be repainted recursivly. If the child widgets are 
opaque, they will not receive paint events.

> When I resize the area afterwards and trigger a 
> paint event that way, everything is in order (hiding the window and bringing 
> it into the foreground will not work, though). The behaviour seems like 
> bitmaps of the rendered child widgets are cached by Qt and used to prevent 
> recursion upon redraws.

Your observation is correct. Qt uses a doublebuffering technique 
reffered to as a backingstore. Its a pixmap, the size of the toplevel 
widget that all widgets are drawn into. This makes it possible for Qt do 
subwidget-transparency and optimize exposure events.

 > Is there a way I can force a redraw of the parent and
> all the children without having to iterate them all explicitely?

In Qt widgets have the responsibility to repaint themselves when they 
change, so if a child widget change, it calls update() on itself. If the 
parent changes and the child widget is transparent, the parent will 
repaint that child, recursivly. If this does not happen, it sounds like 
a bug and I would like to see your sourcecode if possible ;-)

btw, iterating through all children can be easily done by calling:

for (QWidget w : parent.findChildren(QWidget.class)) {
     w.update();
}

best regards,
Gunnar


Message 3 in thread

On Wednesday 10 January 2007 10:44, Gunnar Sletta wrote:
> In Qt widgets have the responsibility to repaint themselves when they
> change, so if a child widget change, it calls update() on itself. If the
> parent changes and the child widget is transparent, the parent will
> repaint that child, recursivly.

One thing that is easily missed by people not familiar with Qt is that if you 
call update more then once, maybe on a child widget as well as the container 
then Qt will make sure all those repaints are combined into the best possible 
repaint strategy.
The same goes for updates of a specific area of a widget using the 
update(QRect) method. If you call it with overlapping areas there will still 
be only one repaint call.

So, in short, don't feel tempted to make premature optimisations, calling 
update more then once in your is typically not a problem. :)
-- 
 [ signature omitted ] 

Attachment: pgpvQCg3wikXz.pgp
Description: PGP signature


Message 4 in thread

On Wednesday 10 January 2007 10:44, Gunnar Sletta wrote:
> Gregor Mückl wrote:
> > Hi!
> >
> > I've got a problem with a fairly complex tree of widgets: I'm updating
> > the state of some custom widgets and need to update the display
> > afterwards. I try to do this by calling update() on the parent of all
> > these widgets. However, the children do not receive a paint event upon
> > this call. Calling repaint() instead won't work, either.
>
> This is expected behaviour. update() triggers an asynchronous repaint
> and repaint() triggers a synchronous repaint on the widget in question.
> In general you should _always_ use update().
>
> If the parent has transparent child widgets, i.e widgets with
> autoFillBackground set to false (the default), the attribute
> NoBackground or the palette() for QWidget.backgroundRole() is a NoBrush,
> its children _should_ be repainted recursivly. If the child widgets are
> opaque, they will not receive paint events.
>

The child widgets are not explicitly marked as transparent, so the behaviour 
is correct according to your description. So there is no bug in Qt here. It's 
just not the behaviour I hoped to get.

>  > Is there a way I can force a redraw of the parent and
> >
> > all the children without having to iterate them all explicitely?
>
> In Qt widgets have the responsibility to repaint themselves when they
> change, so if a child widget change, it calls update() on itself. If the
> parent changes and the child widget is transparent, the parent will
> repaint that child, recursivly. If this does not happen, it sounds like
> a bug and I would like to see your sourcecode if possible ;-)
>
> btw, iterating through all children can be easily done by calling:
>
> for (QWidget w : parent.findChildren(QWidget.class)) {
>      w.update();
> }
>

That is what I was trying to avoid :-/. But I think I know how I can implement 
something like that in my tree of child widgets.

Anyway, thanks for you help!

Regards,
Gregor


Message 5 in thread

Gregor Mückl wrote:
> On Wednesday 10 January 2007 10:44, Gunnar Sletta wrote:
> 
>>Gregor Mückl wrote:
>>
>>>Hi!
>>>
>>>I've got a problem with a fairly complex tree of widgets: I'm updating
>>>the state of some custom widgets and need to update the display
>>>afterwards. I try to do this by calling update() on the parent of all
>>>these widgets. However, the children do not receive a paint event upon
>>>this call. Calling repaint() instead won't work, either.
>>
>>This is expected behaviour. update() triggers an asynchronous repaint
>>and repaint() triggers a synchronous repaint on the widget in question.
>>In general you should _always_ use update().
>>
>>If the parent has transparent child widgets, i.e widgets with
>>autoFillBackground set to false (the default), the attribute
>>NoBackground or the palette() for QWidget.backgroundRole() is a NoBrush,
>>its children _should_ be repainted recursivly. If the child widgets are
>>opaque, they will not receive paint events.
>>
> 
> 
> The child widgets are not explicitly marked as transparent, so the behaviour 
> is correct according to your description. So there is no bug in Qt here. It's 
> just not the behaviour I hoped to get.

They are transparent by default, so unless you set them explicitly to 
opaque, they should get the paint events. See the attached example:

/****************************************************************************
/****************************************************************************
 **
 ** Copyright (C) 1992-$THISYEAR$ $TROLLTECH$. All rights reserved.
 **
 ** This file is part of $PRODUCT$.
 **
 ** $JAVA_LICENSE$
 **
 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 **
 ****************************************************************************/

package com.trolltech.tests;

import com.trolltech.qt.gui.*;
import com.trolltech.qt.core.*;

public class ChildOpacity {
    public static void main(String args[]) {
        QApplication.initialize(args);

        QWidget widget = new QWidget() {
            { startTimer(1000); }
            protected void timerEvent(QTimerEvent e) {
                QPalette p = palette();
                p.setColor(QPalette.ColorRole.Window, QColor.fromRgba(p.color(QPalette.ColorRole.Window).rgba() ^ 0xff00ff));
                setPalette(p);
            }
        };

        QWidget child = new QWidget(widget) {
            protected void paintEvent(QPaintEvent e) {
                QPainter p = new QPainter(this);
                p.drawRect(rect().adjusted(0, 0, -1, -1));
            }
        };
        child.setGeometry(30, 30, 50, 30);

        widget.show();

        QApplication.exec();
    }
}

Message 6 in thread

On Thursday 11 January 2007 13:10, you wrote:
> Gregor Mückl wrote:
> > On Wednesday 10 January 2007 10:44, Gunnar Sletta wrote:
> >>If the parent has transparent child widgets, i.e widgets with
> >>autoFillBackground set to false (the default), the attribute
> >>NoBackground or the palette() for QWidget.backgroundRole() is a NoBrush,
> >>its children _should_ be repainted recursivly. If the child widgets are
> >>opaque, they will not receive paint events.
> >
> > The child widgets are not explicitly marked as transparent, so the
> > behaviour is correct according to your description. So there is no bug in
> > Qt here. It's just not the behaviour I hoped to get.
>
> They are transparent by default, so unless you set them explicitly to
> opaque, they should get the paint events. See the attached example:

The example code you attached seems to trigger the repaint through a palette 
update. I've modified the code (see attached source) to have the timerEvent() 
function call update() on the parent widget instead. As you should be able to 
see in the console the child widget does not get a paintEvent() call 
following the timerEvent(). This is the behaviour I get in my app. Is this 
the intended behaviour? If so, then everything is in order.

Regards,
Gregor
/****************************************************************************
/****************************************************************************
 **
 ** Copyright (C) 1992-$THISYEAR$ $TROLLTECH$. All rights reserved.
 **
 ** This file is part of $PRODUCT$.
 **
 ** $JAVA_LICENSE$
 **
 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 **
 ****************************************************************************/

package com.trolltech.tests;

import com.trolltech.qt.gui.*;
import com.trolltech.qt.core.*;

public class NoChildPaintEvent {
    public static void main(String args[]) {
        QApplication.initialize(args);

        QWidget widget = new QWidget() {
            { startTimer(1000); }
            protected void timerEvent(QTimerEvent e) {
            	System.out.println("widget timerEvent");
            	update();
            }
        };
        
        QWidget child = new QScrollArea(widget) {
            protected void paintEvent(QPaintEvent e) {
            	System.out.println("child paintEvent");
                QPainter p = new QPainter(this);
                p.drawRect(rect().adjusted(0, 0, -1, -1));
            }
        };
        child.setGeometry(30, 30, 50, 30);

        widget.show();

        QApplication.exec();
    }
}

Message 7 in thread

Gregor Mückl wrote:

> The example code you attached seems to trigger the repaint through a palette 
> update. I've modified the code (see attached source) to have the timerEvent() 
> function call update() on the parent widget instead. As you should be able to 
> see in the console the child widget does not get a paintEvent() call 
> following the timerEvent(). This is the behaviour I get in my app. Is this 
> the intended behaviour? If so, then everything is in order.

You also changed the widget to QScrollArea... Took me a while to spot 
that  ;-)

The QScrollArea's viewport has autoFillBackground set to true for so 
that we can do hardware bitblts for scrolling. You can disable this by 
calling:

scrollArea.viewport().setAutoFillBackground(false);

This will trigger paint events in your subwidgets.

best regards,
Gunnar


Message 8 in thread

On Thursday 11 January 2007 15:30, you wrote:
> Gregor Mückl wrote:
> > The example code you attached seems to trigger the repaint through a
> > palette update. I've modified the code (see attached source) to have the
> > timerEvent() function call update() on the parent widget instead. As you
> > should be able to see in the console the child widget does not get a
> > paintEvent() call following the timerEvent(). This is the behaviour I get
> > in my app. Is this the intended behaviour? If so, then everything is in
> > order.
>
> You also changed the widget to QScrollArea... Took me a while to spot
> that ;-)
>

Ups, I thought I had reverted that change - sorry! I was playing around with 
this because I already suspected that there was something going on with the 
QScrollArea. It makes sense.

> The QScrollArea's viewport has autoFillBackground set to true for so
> that we can do hardware bitblts for scrolling. You can disable this by
> calling:
>
> scrollArea.viewport().setAutoFillBackground(false);
>
> This will trigger paint events in your subwidgets.
>

I think I will do this. Thank you very much!

Regards,
Gregor

[PS: Gunnar, I hope you don't mind getting this mail twice. I keep answering 
without checking where the mail will be sent - sorry! :-(]