PDA

View Full Version : QGraphicsView advantages of drawBackground and drawForeground?



Bla1
22nd July 2017, 00:27
So I'm working on a map widget to place a drone on a map, and I don't understand all the existing code just yet. I'm wondering, as I can do


QPainter(viewport());
viewport()->rect();

to get a painter to draw things to the widget and to get the widget's geometric properties, what is the point of the functions void drawBackground(QPainter * painter, const QRectF & rect) and void drawForeground(QPainter * painter, const QRectF & rect) when one could just create their own functions to deal with specific events instead of 2 functions for all cases? Also, are these 2 functions called when the QGraphicsView recieves a call to update()?

Is the benefit that when one calls drawForeground, it only replaces the items drawn in previous calls to drawForeground? In other words, If I were to draw a physical map of real world terrain in drawBackground, then call drawForeground each time the drone position changes, will it erase the drone drawn from the previous drawForeground before drawing another drone and without affecting the map? If I were to draw a picture from an image file with drawBackground that takes up the entire viewport, will it erase the stuff from the previous call to drawForeground?

d_stranz
23rd July 2017, 18:08
Your lines of code are nonsensical - the first line creates a QPainter instance on the stack but then doesn't assign it to any QPainter variable. The second line likewise reads the viewport rect and does nothing with it.

You also seem to be confusing general QWidget painting with the specialized painting done in QGraphicsView. In both cases, painting occurs via the paintEvent(). In the case of QWidget, that's where it all occurs. In the case of QGraphicsView, the paintEvent() is divided into three stages:

- the widget background is erased (unless autoFillBackground is set to false), followed by calls to
- QGraphicsView::drawBackground() followed by
- QGraphicsScene::render(), which uses the QPainter provided by the QGraphicsView to paint the scene into the viewport, followed by
- QGraphicsView::drawForeground()


In the base class QGraphicsView implementation, the drawBackground() and drawForeground() methods do nothing. They exist so that you can derive from QGraphicsView and draw things under or on top of the scene.

So in your case, you might draw your map in drawBackground(), make your drone an object derived from QGraphicsItem in the scene (which is drawn next), and then draw text which updates the drone's velocity and position in a fixed place in the foreground that doesn't change relative either to the map or the drone's position in the scene.

You also seem to be confused in thinking that the widget window is erased and completely replaced by whatever occurs in drawForeground() and drawBackground(). That's not what happens - the only time the window is erased is at the very beginning of the paint event. After that, everything else is layered on top of the blank window in the order I gave above. Neither the background, scene, or foreground have to fill the whole window with graphics, but the three layers -will- be painted on top of each other so scene elements will cover whatever is under them in the background, and foreground elements will cover whatever is under them in both the scene and background.

Bla1
25th July 2017, 19:45
The QPainter and QRectF provided to drawBackground and drawForeground are the same ones you would get by calling:


QPainter(viewport());
viewport()->rect();


right?
So, for example, if I had another function that was responsible for drawing a diagonal line across the widget, I could do something along the lines of



void drawDiagonalLine(){
QPainter a(viewport());
a.drawLine(0,0,viewport()->rect().x(),viewport()->rect().y());
}


That way, one wouldn't be completely dependent on drawForeground and drawBackground. I could also have special case helper functions that are only occasionally called that don't need the QPainter and QRectF from drawForeground/drawBackground, as I could get them each time.

d_stranz
26th July 2017, 00:35
So, for example, if I had another function that was responsible for drawing a diagonal line across the widget, I could do something along the lines of

No. You cannot do any drawing to the screen in Qt outside of the paintEvent() (or a function called while inside paintEvent()) of the QWidget in which you want to do the drawing (unless you have a custom QPaintEngine, which you will likely never have).


QPainter(viewport());
There is no such QPainter constructor that simply takes a rect as an argument. QPainter always renders its output to a QPaintDevice instance you give it as a constructor argument, and the only place you can construct a QPainter to render to the screen is inside of the QWidget's paintEvent().

You are never dependent on drawForeground() or drawBackground() if all you are drawing to the screen are QGraphicsItems added to a QGraphicsScene. These two functions exist only to provide you a "hook" into QGraphicView's paintEvent() processing so that you can draw things before the QGraphicsScene is rendered (drawBackground()) or after the scene is rendered (drawForeground()). The widget is a sandwich - background bread on the bottom, scene meat in the middle, and foreground bread on the top. If you just have meat, then you can omit the bread.

Everything (background, scene, forground) is redrawn with every paintEvent(). The QPaintEvent::rect() and QPaintEvent::region() methods give you the part of the widget that needs to be repainted so you don't have to repaint the entire thing if your painting is complex. In most cases, most widgets repaint everything all the time because their painting needs are simple.


a.drawLine(0,0,viewport()->rect().x(),viewport()->rect().y());

Even if you could do this, this code would not give you a diagonal line, it would give you a dot in the top left corner. (0, 0) is top left, and x() and y() return the left (0) and top (0) coordinates, respectively.