Автоматическое изменение размера текста метки в Qt — странное поведение

В Qt у меня есть составной виджет, состоящий из нескольких QLabels, расположенных внутри QBoxLayouts. Когда размер виджета изменяется, я хочу, чтобы текст метки масштабировался, чтобы заполнить область метки, и я реализовал изменение размера текста в resizeEvent.

Это работает, но, похоже, происходит какая-то петля обратной связи. Составной виджет размещается в главном окне внутри QBoxLayout вместе с некоторыми другими виджетами. Когда главное окно уменьшается, составной виджет сначала сохраняет свой размер, а затем изменяет размер до нужного размера в несколько шагов (около 10-15). Если высота текста установлена ​​более чем в 0,8 раза от высоты метки, то при изменении размера текста и содержащего виджет увеличивается с каждым шагом, пока в конечном итоге приложение не выйдет из строя.

Это правильный подход для достижения такого эффекта? Если да, то в чем может быть проблема с изменением размера?

Ниже приведен код resizeEvent.

def resizeEvent(self, evt):
        print("resizeEvent", evt.size().width(), evt.size().height())
        QFrame.resizeEvent(self, evt)

        dataLabels = self.dataPanels.values()

        for label in dataLabels:            
            font = label.font()
            h = label.height()
            h2 = h * 0.8
            font.setPixelSize(h2)
            label.setFont(font)

(с использованием PyQt4 4.8, Qt 4.7.4, Win 7 и OSX 10.6)


person glennr    schedule 09.01.2012    source источник


Ответы (2)


Я думаю, что проблема с изменением размера вызвана SizePolicy. Попробуйте установить политику размера label на Ignored, это должно помочь.

label.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)

Это правильный подход для достижения такого эффекта?

Наверное да, беглый поиск в документации не дал лучших решений. Но я бы создал подкласс QLabel и выполнил бы настройку политики и изменение размера там. Пример:

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)

В случае, если вам нужно подогнать текст не только по высоте, но и по ширине, потребуется дополнительный код.

person reclosedev    schedule 10.01.2012
comment
Это не работает для меня. Анимация размера моего виджета метки работает нормально, но как только я добавляю setFont в resizeEvent, анимация ведет себя странно. Похоже, что когда шрифт установлен, он каким-то образом обновляет размер метки, поэтому он борется с анимацией изменения размера. - person Michael B; 25.01.2019

ответ reclosedev дал мне ключ к использованию Ignored политика размера, но оставалось еще несколько деталей, которые нужно было сгладить. Вот пример, который вычисляет размер шрифта, который будет соответствовать текущему размеру метки.

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)

К сожалению, есть несколько свойств, необходимых для того, чтобы это работало, когда вы используете масштабированную метку. Либо обязательно устанавливайте их всегда, либо переопределите __init__(), чтобы сделать значения по умолчанию полезными. Вот рабочий пример, который устанавливает их:

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()
person Don Kirkby    schedule 12.10.2020