| Trolltech Home | Qt-jambi-interest Home | Recent Threads | All Threads | Author | Date | |
| All threads index page 1 | |
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
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
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
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
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();
}
}
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();
}
}
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
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! :-(]