Qt - How to get the pixel length of a string in a QLabel?

Anti Earth picture Anti Earth · Dec 26, 2011 · Viewed 18.7k times · Source

I have a QLabel of a fixed width.
I need to check (periodically) that the entire string fits inside the QLabel at its current width, so I can resize it appropriately.

To do this, I need to obtain the 'pixel length' of the string.
(The total amount of horizontal pixels required to display the string).
It should be noted that the point size of the QLabel never changes.

Example of 'Pixel Width' of a string

I can not simply check the amount of characters present, since some characters are subscript / superscript and contribute differently to the width of the entire string.
(This is to say there is no simple relationship between pixel width and the amount of characters)

Is there any abstracted, super conveniant function for this?

Specs:
Python 2.7.1
PyQt4
Windows 7

Answer

ekhumoro picture ekhumoro · Dec 26, 2011

To get the precise pixel-width of the text, you must use QFontMetrics.boundingRect.

Do not use QFontMetrics.width, because it takes into account the left and right bearing of the characters. This will often (but not always) lead to results which can be several pixels more or less than the full pixel-width.

So, to calculate the pixel-width of the label text, use something like:

width = label.fontMetrics().boundingRect(label.text()).width()

EDIT

There are three different QFontMetrics methods which can be used to calculate the "width" of a string: size(), width() and boundingRect().

However, although they all give slightly different results, none of them seems to consistently return the exact pixel-width in all circumstances. Which one is best depends mostly on the current font-family in use and on which particular characters are at the beginning and end of the string.

I have added below a script that tests the three methods. For me, the boundingRect method gives the most consistent results. The other two methods tend to be either slightly too wide, or clip the second text sample when a serif font is used (this is with PyQt 4.9 and Qt 4.8 on Linux).

from PyQt4 import QtGui, QtCore

class Window(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.setAutoFillBackground(True)
        self.setBackgroundRole(QtGui.QPalette.Mid)
        self.setLayout(QtGui.QFormLayout(self))
        self.fonts = QtGui.QFontComboBox(self)
        self.fonts.currentFontChanged.connect(self.handleFontChanged)
        self.layout().addRow('font:', self.fonts)
        for text in (
            u'H\u2082SO\u2084 + Be',
            u'jib leaf jib leaf',
            ):
            for label in ('boundingRect', 'width', 'size'):
                field = QtGui.QLabel(text, self)
                field.setStyleSheet('background-color: yellow')
                field.setAlignment(QtCore.Qt.AlignCenter)
                self.layout().addRow(label, field)
        self.handleFontChanged(self.font())

    def handleFontChanged(self, font):
        layout = self.layout()
        font.setPointSize(20)
        metrics = QtGui.QFontMetrics(font)
        for index in range(1, layout.rowCount()):
            field = layout.itemAt(index, QtGui.QFormLayout.FieldRole).widget()
            label = layout.itemAt(index, QtGui.QFormLayout.LabelRole).widget()
            method = label.text().split(' ')[0]
            text = field.text()
            if method == 'width':
                width = metrics.width(text)
            elif method == 'size':
                width = metrics.size(field.alignment(), text).width()
            else:
                width = metrics.boundingRect(text).width()
            field.setFixedWidth(width)
            field.setFont(font)
            label.setText('%s (%d):' % (method, width))

if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())