Qt - 如何在QLabel中获取字符串的像素长度?

20

我有一个固定宽度的QLabel。
我需要(定期)检查整个字符串是否适合当前宽度的QLabel,以便我可以相应地调整其大小。

为此,我需要获取字符串的“像素长度”。
(显示字符串所需的水平像素总数)。
应该注意的是QLabel的点大小永远不会改变。

字符串'像素宽度'示例

我不能简单地检查存在的字符数量,因为某些字符是下标/上标,对整个字符串的宽度产生不同的贡献。
(也就是说,像素宽度和字符数量之间没有简单的关系)

是否有抽象的、超级方便的函数可以实现这一点?

规格:
Python 2.7.1
PyQt4
Windows 7

1个回答

35

要获取文本的精确像素宽度,必须使用 QFontMetrics.boundingRect

不要使用 QFontMetrics.width,因为它考虑了字符的左右间距。这通常(但并非总是)会导致结果比完整像素宽度多几个像素或少几个像素。

因此,要计算标签文本的像素宽度,请使用以下代码:

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

编辑

有三种不同的QFontMetrics方法可用于计算字符串的“宽度”:size()width()boundingRect()

然而,尽管它们都会给出稍微不同的结果,但没有一个在所有情况下似乎能够一致地返回精确的像素宽度。哪个方法最好主要取决于当前使用的字体系列以及字符串开头和结尾的特定字符。

我添加了下面的脚本来测试这三种方法。对我而言,boundingRect方法提供了最一致的结果。另外两种方法往往要么略微过宽,要么在使用衬线字体时剪切第二个文本样本(这是在Linux上使用PyQt 4.9和Qt 4.8时的情况)。

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_())

1
@AntiEarth。轴承可以是负数,因此如果考虑到它们来调整大小,则标签文本可能会在边缘被裁剪。只有boundingRect保证始终给出文本的完整像素宽度。 - ekhumoro
它没有考虑下标,只考虑了字号大小,是吗? - Anti Earth
@AntiEarth。使用unicode字符有什么问题吗?更重要的是,在适当的布局中,允许标签设置自己首选的宽度有什么问题吗? - ekhumoro
2
@AntiEarth。默认情况下,标签将自动调整大小以适应其内容。它们这样做的主要原因之一是为了让用户不必费力地计算内容的尺寸以设置固定宽度。我相信有更简单的方法来解决您遇到的所有布局问题。但正如我在您之前的某个问题中所评论的那样:实现这一点最实用的方法是发布一些真实的、可测试的代码(或ui文件),以演示问题。 - ekhumoro
1
在你的评论后使用 .adjustSize() 解决了。谢谢! - Anti Earth
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接