繁体   English   中英

如何获取给定矩形的 QLabel 字体信息?

[英]How to get QLabel font information for a given rectangle?

我正在尝试获取文本填充整个 QLabel 矩形的QLabel的字体大小。我尝试使用QFontMetrics来获取,但是QFontMetrics无法通过给定的矩形获取字体大小?

这个例子:
调整 window 的大小时,GUI 可能会卡住。

class Label(QLabel):
    def __init__(self):
        super().__init__()
        self.resize(400, 300)
        font = self.calculate_font()
        self.setFont(font)
        self.setText('PASS')

    def calculate_font(self):
        for i in range(400):
            fm = QFontMetrics( QFont('Helvetica', i) )
            fmSize = fm.boundingRect(self.geometry(), Qt.AlignCenter, 'PASS').size()
            print(fm.boundingRect(self.geometry(), Qt.AlignCenter, 'PASS'), self.size())
            #need font height equal label height
            if fmSize.height() > self.size().height():
                return QFont('Helvetica', i)

    def resizeEvent(self, event):
        font = self.calculate_font()
        self.setFont(font)

app = QApplication([])
demo = Label()
demo.show()
app.exec()

不要这样做

您想要实现的目标完全错误的原因有很多。
最重要的是处理文本绘制及其大小不是一件容易的事; 此外,Qt 使用 label 内容来告诉 window 布局它可以具有的大小,它想要的大小,最重要的是,它应该具有的最小大小; 所有这些对 GUI 来说都非常重要,因为它会被考虑在内,以便正确调整界面的所有其他元素的大小。
最后,所有这些因素都基于 label 的文本及其格式,这可能取决于文本内容以及文本可能是“富文本”这一事实(包括多种字体大小和粗细)。

如果您真的了解上面解释的概念,这里有 4 种可能的实现。 它们都部分支持富文本(字体粗细、文本颜色等,但不支持真正的文本对齐)。

标签测试示例

调整字体大小 label

虽然子类化 QLabel 似乎是最简单的方法,但实际上并非如此,因为它是一个比看起来复杂得多的小部件(正如我之前写的,处理文本小部件并非易事)

这种方法最重要的缺点是它非常慢,因为它必须在每次调整大小时仔细计算字体大小。 虽然通过更好的实现可能会实现一些改进,但无论如何我都不建议使用这种方法。

class ResizeFontLabel(QLabel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.paintFont = self.font()

    def updateFont(self):
        doc = QTextDocument()
        if self.textFormat() == Qt.RichText or self.textFormat() == Qt.AutoText and Qt.mightBeRichText(self.text()):
            doc.setHtml(self.text())
        else:
            doc.setPlainText(self.text())
        frame = doc.rootFrame().frameFormat()
        frame.setMargin(0)
        doc.rootFrame().setFrameFormat(frame)
        doc.setDefaultFont(self.paintFont)

        width = self.width()
        height = self.height()

        if doc.size().width() > width or doc.size().height() > height:
            while doc.size().width() > width or doc.size().height() > height:
                self.paintFont.setPointSizeF(self.paintFont.pointSizeF() - .1)
                doc.setDefaultFont(self.paintFont)
        elif doc.size().width() < width and doc.size().height() < height:
            while doc.size().width() < width and doc.size().height() < height:
                self.paintFont.setPointSizeF(self.paintFont.pointSizeF() + .1)
                doc.setDefaultFont(self.paintFont)

    def resizeEvent(self, event):
        self.updateFont()

    def paintEvent(self, event):
        doc = QTextDocument()
        if self.textFormat() == Qt.RichText or self.textFormat() == Qt.AutoText and Qt.mightBeRichText(self.text()):
            doc.setHtml(self.text())
        else:
            doc.setPlainText(self.text())
        frame = doc.rootFrame().frameFormat()
        frame.setMargin(0)
        doc.rootFrame().setFrameFormat(frame)
        doc.setDefaultFont(self.paintFont)
        qp = QPainter(self)
        doc.drawContents(qp, QRectF(self.rect()))

QLabel 在内部使用 QTextDocument 进行绘制和调整大小。 setMargin的原因是因为 QTextDocument 默认有一些边距,而标签中没有使用。

笔记:

  • 请注意updateFont()方法中的不同or/and 他们的逻辑很重要
  • 这种方法很
  • 它不支持文本 alignment(至少在这个基本实现中)

基于绘画的字体label

该方法是上述方法的简化。 它不计算字体大小,而只是绘制缩放到大小的内容。

class PaintLabel(QLabel):
    def paintEvent(self, event):
        doc = QTextDocument()
        if self.textFormat() == Qt.RichText or self.textFormat() == Qt.AutoText and Qt.mightBeRichText(self.text()):
            doc.setHtml(self.text())
        else:
            doc.setPlainText(self.text())
        frame = doc.rootFrame().frameFormat()
        frame.setMargin(0)
        doc.rootFrame().setFrameFormat(frame)
        scale = min(self.width() / doc.size().width(), self.height() / doc.size().height())
        qp = QPainter(self)
        qp.scale(scale, scale)
        doc.drawContents(qp, QRectF(self.rect()))

笔记:

  • 它比第一种方法快
  • 它不支持 alignment
  • 由于缩放没有考虑字体大小,因此文本不会尽可能大(这是因为 QTextDocument 可以有多个“文本块”,并且在每个 paintEvent 中计算它们会使这个真的很复杂很慢

图形查看 label

这是一种完全不同的方法,因为它使用Graphics View Framework 诀窍是在场景中使用单个QGraphicsTextItem ,并让视图负责调整大小/对齐。

class GraphicsLabel(QGraphicsView):
    def __init__(self, text=''):
        super().__init__()
        # graphics view usually have a background and a frame around them,
        # let's remove both of them
        self.setFrameShape(0)
        self.setStyleSheet('background: transparent;')
        # as most QAbstractScrollAreas, views have a default minimum size hint
        # that makes them "bigger" whenever they're shown; let's ignore that
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        scene = QGraphicsScene(self)
        self.setScene(scene)
        self.textItem = scene.addText('')
        self.setText(text)

    def minimumSizeHint(self):
        # this is related to the minimum size hint specified above
        font = self.font()
        font.setPointSize(1)
        return QFontMetrics(font).boundingRect(self.textItem.toPlainText()).size()

    def setText(self, text):
        font = self.font()
        font.setPointSize(1)
        self.setMinimumSize(QFontMetrics(font).boundingRect(text).size())
        if Qt.mightBeRichText(text):
            self.textItem.setHtml(text)
        else:
            self.textItem.setPlainText(text)
        doc = self.textItem.document()
        frame = self.textItem.document().rootFrame().frameFormat()
        if frame.margin():
            frame.setMargin(0)
            doc.rootFrame().setFrameFormat(frame)
            self.textItem.setDocument(doc)

    def text(self):
        # provide a basic interface similar to a QLabel
        return self.textItem.toPlainText()

    def resizeEvent(self, event):
        # the base class implementation is always required for QAbstractScrollArea
        # descendants; then we resize its contents to fit its size.
        super().resizeEvent(event)
        self.fitInView(self.textItem, Qt.KeepAspectRatio)

笔记:

  • 确实支持 alignment
  • 没有“最佳”最小尺寸(文本的可读性取决于字体,我们对此无能为力)
  • 没有“最佳”大小提示,因此将调整小部件的大小而不会对其文本内容产生任何“抱怨”:如果 label 的文本很长,那么该文本将非常非常小。
  • 不支持基本的 QWidget 样式(通过QWidget.setStyleSheet

图形视图 QLabel 小部件

此方法与上述方法非常相似,但不是创建简单的“文本项”,而是在图形场景中添加了一个实际的 QLabel。

class GraphicsLabelWidget(QGraphicsView):
    def __init__(self, text=''):
        super().__init__()
        self.setFrameShape(0)
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
        self.setStyleSheet('background: transparent;')
        scene = QGraphicsScene(self)
        self.setScene(scene)
        self.label = QLabel(text)
        self.labelItem = scene.addWidget(self.label)
        self.label.setStyleSheet(self.styleSheet())
        self.setText(text)

    def minimumSizeHint(self):
        font = self.font()
        font.setPointSize(1)
        doc = QTextDocument()
        if Qt.mightBeRichText(self.label.text()):
            doc.setHtml(self.label.text())
        else:
            doc.setPlainText(self.label.text())
        return QFontMetrics(font).boundingRect(self.label.text()).size()

    def setText(self, text):
        font = self.font()
        font.setPointSize(1)
        self.setMinimumSize(QFontMetrics(font).boundingRect(text).size())
        self.label.setText(text)

    def text(self):
        return self.label.toPlainText()

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.fitInView(self.labelItem, Qt.KeepAspectRatio)

笔记:

  • 比基本的addText图形实现略好; 同时,它也稍微慢了一点
  • 更好地支持样式表(但它们应该应用于实际的子标签)

测试示例:

仅为商品提供(添加上面的类以查看它的实际效果)。

class Demo(QWidget):
    def __init__(self):
        super().__init__()
        layout = QVBoxLayout(self)

        testText = 'PASS <b>bold</b><br/><i>new italic line</i><br/>{}'

        resizeLabel = ResizeFontLabel(testText.format('resize mode'))
        layout.addWidget(resizeLabel)
        resizeLabel.setAlignment(Qt.AlignRight|Qt.AlignBottom)

        paintLabel = PaintLabel(testText.format('paint mode'))
        layout.addWidget(paintLabel)
        paintLabel.setAlignment(Qt.AlignRight|Qt.AlignBottom)

        graphicsLabel = GraphicsLabel(testText.format('graphicsview mode'))
        layout.addWidget(graphicsLabel)
        graphicsLabel.setAlignment(Qt.AlignRight|Qt.AlignBottom)

        graphicsLabelWidget = GraphicsLabelWidget(testText.format('graphicsview mode'))
        layout.addWidget(graphicsLabelWidget)
        graphicsLabelWidget.setAlignment(Qt.AlignRight|Qt.AlignBottom)
        graphicsLabelWidget.label.setStyleSheet('QLabel {background: green;}')


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    demo = Demo()
    demo.show()
    app.exec()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM