簡體   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