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

22,352

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_())
Share:
22,352
Anti Earth
Author by

Anti Earth

Maths is perfect; I am not.

Updated on July 09, 2022

Comments

  • Anti Earth
    Anti Earth almost 2 years

    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

  • ekhumoro
    ekhumoro over 12 years
    @AntiEarth. The bearings can be negative, so if they are taken into account for the purpose of resizing, the label text may be cropped at the edges. Only the boundingRect is guaranteed to always give the full pixel width of the text.
  • Anti Earth
    Anti Earth over 12 years
    It doesn't take subscripting into account, does it? Just point size?
  • ekhumoro
    ekhumoro over 12 years
    @AntiEarth. Please, see my updated answer. The subscripts and point-size don't seem to be important factors for me - but things may work differently on Windows.
  • Anti Earth
    Anti Earth over 12 years
    Well what looks like subscript '2' (for example) in a Qlabel is actually '<sub>2</sub>', which is what's returned by Qlabel.text. If I were to remove the HTML and treat the characters normally, the width would definitely be off since subscript is about half the standard char width. Exact spacing is very important in my code!
  • ekhumoro
    ekhumoro over 12 years
    @AntiEarth. What's wrong with using unicode characters? And, more to the point, what's wrong with allowing the label to set it's own, preferred width within an appropriate layout?
  • Anti Earth
    Anti Earth over 12 years
    I'm afraid I don't understand you. Could you link me to some info? :S
  • ekhumoro
    ekhumoro over 12 years
    @AntiEarth. By default, labels will automatically resize themselves to fit their own contents. One of the main reasons they do this is so that users don't have to mess around trying to calculate the dimensions of the contents in order to set a fixed width. I am pretty certain there are much simpler ways to solve all the layout issues you are having. But as I commented in one of your previous questions: the most practical way to achieve that is by posting some real, testable code (or a ui file) that demonstrates the issues.
  • Anti Earth
    Anti Earth over 12 years
    Solved using the .adjustSize() after your comment. Thanks!