【问题标题】:QFontMetrics leave extra space between linesQFontMetrics 在行之间留出额外的空间
【发布时间】:2022-11-05 06:54:05
【问题描述】:

我正在尝试使用 PySide6 的 QPainterQFontMetrics 绘制多段文本。我想以与将它们全部绘制在一个文本块中相同的间距来绘制它们,但是行间距不太正确。

在下面的示例中,字体度量表明字体的行距为 17。当我测量单行文本时,边界矩形确实是 17 像素高。但是,当我测量两行文本时,边界矩形是 35 像素高,而不是 34 像素。多余的像素是从哪里来的,我可以在字体的某些属性或字体度量上看到它吗?

from PySide6.QtGui import QFont, QFontMetrics
from PySide6.QtWidgets import QApplication

app = QApplication()
font = QFont()
metrics = QFontMetrics(font)
print(metrics.lineSpacing())  # 17
print(metrics.boundingRect(0, 0, 100, 100, 0, 'A').height())  # 17
print(metrics.boundingRect(0, 0, 100, 100, 0, 'A\nB').height())  # 35 != 17 * 2
print(metrics.leading())  # 0
print(metrics.ascent())  # 14
print(metrics.descent())  # 3

顺便说一句,它并不总是一个额外的像素。如果我把字体变大,额外的空间就会增加。

更新

我以为我已经通过从QFontMetrics 切换到QFontMetricsFmusicamante's suggestion 解决了这个问题,但还是有区别的。

from PySide6.QtCore import QRectF
from PySide6.QtGui import QFont, QFontMetricsF
from PySide6.QtWidgets import QApplication

app = QApplication()
font = QFont()
metrics = QFontMetricsF(font)
print(metrics.height())  # 16.8125
print(metrics.boundingRect(QRectF(0, 0, 100, 100),
                           0,
                           'A').getCoords())  # (0.0, 0.0, 9.9375, 16.8125)
print(metrics.boundingRect(QRectF(0, 0, 100, 100),
                           0,
                           'A\nB').getCoords())  # (0.0, 0.0, 9.9375, 34.8125)
# Note the height of that rect doesn't match the next calculation.
print(metrics.height() + metrics.lineSpacing())  # 34.046875

# I can't see any combination of these numbers that makes 34.8125
print(metrics.lineSpacing())  # 17.234375
print(metrics.leading())  # 0.421875
print(metrics.ascent())  # 13.984375
print(metrics.descent())  # 2.828125

【问题讨论】:

    标签: python fonts pyside6


    【解决方案1】:

    首先,字体度量的边界矩形的高度不取决于使用的字符,而是取决于字体规范。

    两行文本没有单行边界矩形的height() 的双倍:相反,您必须考虑lineSpacing()

    在实践中,边界矩形的高度通常是以下各项的总和:

    • height() 乘以行数;
    • leading() 乘以行间空格数(又名:行数 - 1);

    或者,类似地,总和:

    请注意,显然,行数取决于输入文本和给定的选项,例如,如果启用了自动换行并且任何源行不适合给定的源矩形。

    还要考虑大多数字体是矢量的,这意味着它们的坐标和度量是成比例的并且是浮点值。相反,QFontMetrics 出于简单和优化的原因使用整数值,因此在点大小不给出舍入值的情况下,您可能会得到由舍入引起的不一致结果:非整数通常是“下限”(如 int()在python中)。
    在您的情况下,leading 可能大于 0(但仍小于 1),因此您没有得到上述高度的正确总和。

    具体来说,QFontMetrics.boundingRect() 返回由计算的格式化文本的QRectF.toAlignedRect() 生成的 QRect,它始终是“完全包含此矩形的最小可能整数矩形”。

    如果需要获取精确坐标,则需要使用QFontMetricsF,它是默认基本QFontMetrics 的浮点对应物。

    也就是说,如果您打算使用 QPainter 绘制格式化文本,那么请考虑使用 QTextDocument 或至少 QTextLayout,这与标准 Qt 文本绘制一致,并且通常更快、更可靠和“更简单”(好吧,一次你就知道了)。虽然它可能看起来比要求的有点复杂,但它实际上是 Qt 在调用 boundingRect() 时所做的,所以如果你需要自定义绘画,QTextLayout 选项实际上更好,特别是如果你可以将它与一些智能缓存结合使用(参见 QPicture)避免常见的python瓶颈。

    【讨论】: