[英]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 种可能的实现。 它们都部分支持富文本(字体粗细、文本颜色等,但不支持真正的文本对齐)。
虽然子类化 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
。 他们的逻辑很重要该方法是上述方法的简化。 它不计算字体大小,而只是绘制缩放到大小的内容。
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()))
笔记:
这是一种完全不同的方法,因为它使用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)
笔记:
QWidget.setStyleSheet
)此方法与上述方法非常相似,但不是创建简单的“文本项”,而是在图形场景中添加了一个实际的 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.