OK, this turned out to be trickier than anticipated, but it is working.
Very nice that the foreground drawing is in scene coordinates so I don't have to worry about transformations at that point.
This bit turned out to be not as true as I would have liked. I could not get the QGraphicsSimpleTextItem instances representing the labels to draw by simply using the QGraphicsSimpleTextItem:: paint() method. For whatever reason, nothing appeared. I verified that other types of QGraphicsItem could be rendered this way (like a QGraphicsRectItem with position and size in scene coordinates). So what I ended up implementing was something like the following:
// MyGraphicsView inherits from QGraphicsView
// mForegroundItems is a vector of QGraphicsItem pointers passed into the view; in the case of text items (labels)
// these have been filtered to eliminate items that collide *in this view*. The text items have ItemIgnoresTransformations set.
void MyGraphicsView
::drawForeground( QPainter * pPainter,
const QRectF & rect
) {
std
::vector<
QGraphicsItem * >
::iterator it
= mForegroundItems.
begin();
std
::vector<
QGraphicsItem * >
::iterator eIt
= mForegroundItems.
end();
while ( it != eIt )
{
if ( pItem )
{
{
QPointF pos
= mapFromScene
( pText
->pos
() );
pPainter->save();
pPainter->resetTransform();
pPainter->setFont( pText->font() );
if ( pText->pen() != Qt::NoPen )
pPainter->setPen( pText->pen() );
pPainter->setBrush( pText->brush() );
pPainter->drawText( pos, pText->text() );
pPainter->restore();
}
else
pItem->paint( pPainter, &option, this );
}
}
}
// MyGraphicsView inherits from QGraphicsView
// mForegroundItems is a vector of QGraphicsItem pointers passed into the view; in the case of text items (labels)
// these have been filtered to eliminate items that collide *in this view*. The text items have ItemIgnoresTransformations set.
void MyGraphicsView::drawForeground( QPainter * pPainter, const QRectF & rect )
{
QGraphicsView::drawForeground( pPainter, rect );
QStyleOptionGraphicsItem option;
std::vector< QGraphicsItem * >::iterator it = mForegroundItems.begin();
std::vector< QGraphicsItem * >::iterator eIt = mForegroundItems.end();
while ( it != eIt )
{
QGraphicsItem * pItem = *it++;
if ( pItem )
{
if ( pItem->type() == QGraphicsSimpleTextItem::Type )
{
QGraphicsSimpleTextItem * pText = qgraphicsitem_cast< QGraphicsSimpleTextItem *>( pItem );
QPointF pos = mapFromScene( pText->pos() );
pPainter->save();
pPainter->resetTransform();
pPainter->setFont( pText->font() );
if ( pText->pen() != Qt::NoPen )
pPainter->setPen( pText->pen() );
pPainter->setBrush( pText->brush() );
pPainter->drawText( pos, pText->text() );
pPainter->restore();
}
else
pItem->paint( pPainter, &option, this );
}
}
}
To copy to clipboard, switch view to plain text mode
Basically, the text had to be painted as ordinary text at the correct pixel position as mapped from the scene. Prior to painting, it was necessary to reset the painter's transform (since it comes into this method with the scene transformation already applied), then restore it before exiting.
Note that this solution is specific to my needs: labels are a fixed pixel size and do not scale with the scene scaling, and labels are always drawn in horizontal orientation.
If there's a better way to do this, I'd be interested to know. In particular, I don't understand why I can't draw text items directly using the paint() call. Possibly it is because the items are not children of the scene (i.e. QGraphicsScene::addItem() is not called, and the items have no QGraphicsItem parent), but on the other hand, the QGraphicsRect item I drew in testing this method wasn't either, but did not have the ItemIgnoresTranformations flag set.
Whatever, this suggestion of using drawForeground() has turned out to be very useful. I have also implemented an override of drawBackground() for this view class, which will allow me to display Qwt "spectrogram" style images as a background pixmap, overlaid with QGraphicsItem things in the scene to allow for marking and selecting "features" in the image.
Bookmarks