Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Adding QGraphicsItem at View Coordinates



  • Hi,

    What's the simplest way to add a QGraphicsItem to a QGraphicsScene so that it's always visible at a fixed, absolute position relative to the view coordinates (such as a FPS counter)?

    I can't understand from the documentation if I'm supposed to juggle stuff around with map from/to scene or if there's a simpler way.

    Thanks.



  • @Strangelove
    You want to show something at, say, the top-left of any view, regardless of where on the scene the view is showing, like say a counter or a logo, right? And probably independent of any zooming? I hope I'm right, but this doesn't sound line a QGraphicsItem to me. That is an object on the scene. If you did that, you'd have to move it as the view moved around the scene? You just have an "artefact" on the view? I assume the would be best implemented via QGraphicsView::drawForeground():

    Draws the foreground of the scene using painter, after the background and all items are drawn. Reimplement this function to provide a custom foreground for this view.

    Actually, if you want it present in any view, same principle but on the scene QGraphicsScene::drawForeground():

    Draws the foreground of the scene using painter, after the background and all items have been drawn. Reimplement this function to provide a custom foreground for the scene.

    But I have feeling you want it on the view....



  • @Strangelove There are several alternative solutions but it will depend on your objective of that item.

    • A possible solution is to do the custom painting (they are not QGraphicsItems) override the drawForeground method.

    • Instead of using a QGraphicsItem you can use a QWidget on top of the viewport(parent):

    FooWidget *widget = new FooWidget(graphicsView->viewport());
    

    OR

    FooWidget *widget = new FooWidget()
    widget->setParent(graphicsView->viewport());
    


  • @JonB said in Adding QGraphicsItem at View Coordinates:

    like say a counter or a logo, right?

    Right.

    I've tried the following but apparently I misunderstood the purpose of drawForeground, as this object is transformed together with the scene like any other object:

        def drawForeground(self, painter: QPainter, rect):
            painter.setPen(QPen(QColor('white')))
            painter.drawEllipse(0, 0, 50, 50)
            return super().drawForeground(painter, rect)
    

    Isn't there a simple way to just draw a static stenciled item using view coordinates?



  • @Strangelove said in Adding QGraphicsItem at View Coordinates:

    as this object is transformed together with the scene like any other object

    I don't know about this, you may have to untransform. But is this an override of QGraphicsView.drawForeground or of QGraphicsScene.drawForeground?

    As a small point, because you want yours on top of anything I would call super().drawForeground(painter, rect) base class before drawing your own stuff rather than afterwards.



  • @JonB said in Adding QGraphicsItem at View Coordinates:

    But is this an override of QGraphicsView.drawForeground or of QGraphicsScene.drawForeground

    It's an override of QGraphicsView. I don't know why it's using scene coordinates.



  • I am still not very familiar with the graphics stuff in Qt but what about just using something like QRec?
    You just put some coordinates to it and then draw it with QPainter.

    QRect::QRect(int x, int y, int width, int height)
    

    https://doc.qt.io/qt-5/qrect.html



  • @LeoC I added the following to the QWidget that contains the QGraphicsView (inside a BoxLayout), but no line was added to the widget:

        def paintEvent(self, event):
            qp = QPainter()
            qp.begin(self)
            pen = QPen()
            pen.setColor('red')
            qp.setPen(pen)
            qp.drawText(20, 15, 'HELLO')
            qp.end()
    

    I tried adding it to the QGraphicsView itself but, as I was expecting, the result was worse (the view stopped working and didn't display anything at all).

    EDIT: The text does get drawn on the part of the widget which is just outside the QGraphicsView (the blue grid in the screenshot below). It doesn't get draw on the view itself (which is what I'm trying to achieve).

    Screenshot 2021-07-29 at 23.59.38.jpg



  • @Strangelove QPainter uses the coordinates of the scene for painting so the QRect (and any geometric elements) you use must be converted to that coordinate system.

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    
    class GraphicsView(QtWidgets.QGraphicsView):
        def drawForeground(self, painter, rect):
            super().drawForeground(painter, rect)
            rect = QtCore.QRect(0, 0, 50, 50)
            point = QtCore.QPoint(20, 15)
    
            painter.setPen(QtGui.QPen(QtGui.QColor("white")))
            painter.drawEllipse(self.mapToScene(rect).boundingRect())
            painter.drawText(self.mapToScene(point), "HELLO")
    
    
    def main():
        import sys
    
        app = QtWidgets.QApplication(sys.argv)
        scene = QtWidgets.QGraphicsScene()
        view = GraphicsView(scene)
        view.show()
        sys.exit(app.exec_())
    
    
    if __name__ == "__main__":
        main()
    

Log in to reply