Search code examples
pythonpyqt5qgraphicstextitem

How to get PyQt QGraphicsTextItem opaque area


self.boundingRect() give me dashed line dimensions shown in image. i need to get opaque area (where actual text is drawn) I tried getting

self.opaqueArea()

but it returns QPainterPath object, which seems pretty complicated and apparently doesn't have width or height functions etc as its representation of complicated path objects. Q. Is there an easier way to calculate that. any ideas.. thanks in advance easies way to get blue area rect

print ("TEXT BOUNDING RECT:",self.boundingRect())
print ("TEXT OPAQUE AREA  :",self.document().size())
print ("TEXT OPAQUE AREA BR size  :",self.opaqueArea().boundingRect().size())

output:

TEXT BOUNDING RECT: PyQt5.QtCore.QRectF(0.0, 0.0, 590.0, 56.0)
TEXT OPAQUE AREA  : PyQt5.QtCore.QSizeF(590.0, 56.0)
TEXT OPAQUE AREA BR size  : PyQt5.QtCore.QSizeF()

Solution

  • QGraphicsTextItem internally uses a QTextDocument to manage the text. You can access this through .document(). The text document has a size property, which returns a QPointF.

     self.document().size()
    

    Unfortunately all the methods for this document return a standard rect of the bounding box. However, it is possible to make the text box re-adjust itself to the size of the text it contains. It doesn't quite get down to pixel level (due to line height padding) but it's close.

    import sys
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    from PyQt5.QtWidgets import *
    
    
    class MainWindow(QMainWindow):
    
        def __init__(self, *args, **kwargs):
            super(MainWindow, self).__init__(*args, **kwargs)
    
            self.view = QGraphicsView()
    
            scene = QGraphicsScene()
    
            t = QGraphicsTextItem("Sample\n Text")
            font = t.font()
            font.setPointSize(40)
            font.setWeight(600)
            t.setFont(font)
            t.setTextWidth(400)
            scene.addItem(t)
    
            rect = t.boundingRect()
            r = QGraphicsRectItem(rect)
            r.setPen(QColor('red'))
            scene.addItem(r)
    
            d = t.document()
            d.setDocumentMargin(0)
            d.adjustSize()
            rect = t.boundingRect()
    
            r = QGraphicsRectItem(rect)
            r.setPen(QColor('blue'))
            scene.addItem(r)
    
    
            self.view.setScene(scene)
    
            self.setCentralWidget(self.view)
    
    
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = MainWindow()
        w.show()
        app.exec_()
    

    The above will give the following result —

    Image showing the original bounding box and the cropped one.

    The only other idea I had was to get a QPainterPath from the text document and calculate that QRect, but it doesn't appear to be easily accessible. One alternative would be to paint it to a QPainter and crop it there — but there is no built in cropping functionality in Qt.