Automatically resizing label text in Qt - strange behaviour
reclosedev's answer gave me the key clue of using the Ignored
size policy, but there were still a few details to iron out. Here's an example that calculates the font size that will fit in the label's current size.
from PySide2.QtGui import QResizeEvent, QFontMetrics, Qt
from PySide2.QtWidgets import QLabel
class ScaledLabel(QLabel):
def resizeEvent(self, event: QResizeEvent):
# This flag is used for pixmaps, but I thought it might be useful to
# disable font scaling. Remove the check if you don't like it.
if not self.hasScaledContents():
return
target_rect = self.contentsRect()
text = self.text()
# Use binary search to efficiently find the biggest font that will fit.
max_size = self.height()
min_size = 1
font = self.font()
while 1 < max_size - min_size:
new_size = (min_size + max_size) // 2
font.setPointSize(new_size)
metrics = QFontMetrics(font)
# Be careful which overload of boundingRect() you call.
rect = metrics.boundingRect(target_rect, Qt.AlignLeft, text)
if (rect.width() > target_rect.width() or
rect.height() > target_rect.height()):
max_size = new_size
else:
min_size = new_size
font.setPointSize(min_size)
self.setFont(font)
Unfortunately, there are a few properties required to make this work when you use the scaled label. Either be sure to always set them, or override __init__()
to make the defaults useful. Here's a working example that sets them:
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QSizePolicy, QLabel
from scaled_label import ScaledLabel
def main():
app = QApplication()
widget = QWidget()
label1 = ScaledLabel('Lorem ipsum')
label2 = ScaledLabel('Lorem ipsum')
# Any policy other than Ignored will fight you when you resize.
size_policy = QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
label1.setSizePolicy(size_policy)
label2.setSizePolicy(size_policy)
# If you check this flag, don't forget to set it.
label1.setScaledContents(True)
label2.setScaledContents(True)
# "Ignored" policy means you have to define your own minimum size.
label1.setMinimumSize(200, 40)
label2.setMinimumSize(50, 10)
# Standard label attributes still work.
label1.setAlignment(Qt.AlignBottom)
label2.setAlignment(Qt.AlignTop)
# Tell the layout to scale the two fields at different sizes.
layout = QVBoxLayout(widget)
layout.addWidget(label1)
layout.addWidget(label2)
layout.setStretch(0, 4)
layout.setStretch(1, 1)
widget.show()
exit(app.exec_())
main()
I think problem with resizing caused by SizePolicy
. Try to set label
's size policy to Ignored
it should help.
label.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
Is this the correct approach to achieve this effect?
Probably yes, quick search in documentation gave no better solutions. But I would create subclass of QLabel
, and do policy setup and resizing there. Example:
class StretchedLabel(QLabel):
def __init__(self, *args, **kwargs):
QLabel.__init__(self, *args, **kwargs)
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
def resizeEvent(self, evt):
font = self.font()
font.setPixelSize(self.height() * 0.8)
self.setFont(font)
In case you need to fit text not only by height, but by width too, some aditional code required.