PDA

View Full Version : Drawing over content widgets? (overlay)



sertrem
16th January 2006, 22:54
I do have a widget that is composed of some other widgets, and I want to draw something over it, how can I do that?

At first I thought that Just drawing on the paintEvent(...) should be enought, but it gets drawn under the contained widgets. Then I though about forcing to draw the children first, then my paint event... but no luck again. Basic example code looks like this:



class PaintWidget : public QWidget{
Q_OBJECT
public:
PaintWidget(QWidget *parent){
b=new MyPushButton("Hello world",this);
}

protected:
virtual void paintEvent(QPaintEvent *event){
b->repaint();
QPainter p(this);
p.setPen(QColor("red"));
p.drawLine(0,0,width(),height());
event->ignore();
}
MyPushButton *b;
};


Then I thought also to force by hand redrawing calling the paintEvent on the children first, and hiding the widget... But a "QPainter::begin: Widget painting can only begin as a result of a paintEvent" message tells me I can not do it in this way.

Anybody any idea?

(looking at the designer source code I dont get any clear idea on how to do it)

At http://www.monasteriomono.org/.../PaintWidget.tgz I put the full example.

thanks!

sertrem
17th January 2006, 23:01
My own repy, just for the forum annals, of why this seems to be impossible to achieve fully, but possible partially. And finally a small chance to make it possible.

I achieved at most to be able to draw under the widget. That is accomplished just by setting the Qt::WA_ContentsPropagated on the parent children. This way it does not redraw it's background, and uses the one the parent left.

I managed to make sure that the order everything is drawn is right, to draw something inside the widget I want to draw over, and then draw something over. My results are: Qt (at least over X11), in the way it manages QPainters clips that region out. The only widget allowed to draw there is to use that QWidget's QPainter object.

So with that thing in mind, it is possible, with some Flag black magic, draw over the widget:

1. Reimplement the widget you want to draw over, and do a wrapper method to avoid the protect clause of the paintEvent() method.
2. Set the children Qt::WA_PaintOutsidePaintEvent to true, so he paintEvent can be called by your own code not coming from the appropiate event.
3. On you parent widget or at the new widget, disable updates (setUpdatesEnabled(false))
4. On your parent widget draw your thing, then call the wrapper for paintEvent(...) at your childs, and finally use your child QPainter and draw whatever you want to draw with your childs QPainter. I repeat, you have to use your own QPainter, and the your child one. At least two QPainters.

As you can see the solution is ugly. It achieves the goal to draw over a child widget, but the drawbacks are: you have to make a new class with a few "tricks" to allow painting outside the class, and you have to be carefull to draw everything at least in the proper QPainters.

For this two things... I'm still waiting for the Qt4 QCanvas. I can afford the reimplement thing as I want to draw over my own implemented widgets, but I can not afford to draw everything at least two times.

To me it seems there should still be some way, as the first aproach (draw under the widgets) works without the childs QCanvas, but if you enable painting, the children is drawn after the parent (to correctly use the background, I guess), and if you disable updates on child (second aproach, custom child's public paintEvent), the parent gets that region clipped.

Somebody know how to avoid this?

Mandatory source code:



class MyPushButton:public QPushButton{
public:
MyPushButton(const char *txt,QWidget *parent):
QPushButton(txt,parent){
setAttribute(Qt::WA_PaintOutsidePaintEvent,true );
setUpdatesEnabled(false);
};
void _paintEvent(QPaintEvent *e){ this->paintEvent(e); };
protected:
};

class PaintWidget:public QWidget{
Q_OBJECT
public:
PaintWidget(QWidget *parent){
b=new MyPushButton("Hello world",this);
}
protected:
virtual void paintEvent(QPaintEvent *event){
b->_paintEvent(event);
QPainter p(this);
p.setPen(QColor("red"));
p.drawLine(0,0,width(),height());
QPainter q(b);
q.setPen(QColor("red"));
q.drawLine(0,0,width(),height());
}
MyPushButton *b;
};


Thanks for reading.

http://monasteriomono.org

elcuco
17th January 2006, 23:18
similar question:

lets assume my window contains some internal layout.

now, at some point, I want to display a widget (in my case a progress bar) floating at the bottom left, which will be hidden at a specific time. Just imagine the progress bar yousee on firefox when posting, 30 pixels up, just floating.

how can I implement such thing?