Как изменить размер QLabels, чтобы он соответствовал содержимому в QScrollArea

Эта проблема характерна для PyQt5, но ответы C++ Qt5 тоже подходят.

В QScrollArea с фиксированной шириной и переменной высотой у меня есть QVBoxLayout, который содержит QLabels. Эти QLabels имеют setWordWrap(True) и содержат текст, длина которого может превышать фиксированную ширину QScrollArea. Когда текст внутри QLabel переносится на ~4 строки, все работает нормально, но когда QLabel требуется больше, он не может продолжать увеличивать высоту QLabel и обрезает часть текста сверху и снизу.

В этом ответе была предпринята попытка исправить по существу ту же проблему, которая включает установку sizePolicy() по вертикали QLabel на MinimumExpanding, и это технически< /em> сработало, но это заставит QLabels попытаться заполнить всю область просмотра QScrollArea, если область просмотра еще не заполнена QLabels.

Вот как это выглядит в настоящее время без sizePolicy, установленного на MinimumExpanding (обратите внимание на 1-е QLabel):

sizePolicy не задан

Вот как это выглядит, когда MinimumExpanding установлено в качестве политики вертикального размера для QLabels (выглядит великолепно...):

набор sizePolicy

Но это приводит к такому поведению, когда в области прокрутки всего несколько QLabel, что является неприемлемым поведением, поскольку это будет служба «комментариев», где люди могут публиковать свои вопросы в виде простого текста:

неприемлемое поведение

У кого-нибудь есть обходной путь для этой проблемы или что-то подобное?

Для справки, вот часть моего кода:

class NewsList(QtWidgets.QScrollArea):
    def __init__(self, parent=None):
        super(NewsList, self).__init__(parent)
        self.setMaximumWidth(200)
        self.setWidgetResizable(True)

        layout = QtWidgets.QVBoxLayout()
        layout.setContentsMargins(5, 5, 5, 5)
        layout.setSpacing(5)
        layout.setAlignment(QtCore.Qt.AlignTop)
        self.news_widget = QtWidgets.QFrame()
        self.news_widget.setStyleSheet("""
            QFrame {
                background-color: #ffffff;
            }
        """)
        self.news_widget.setLayout(layout)
        self.setWidget(self.news_widget)
        self.fetch_news()

    def fetch_news(self):
        self.append_message('DSADAISH dshadbsasdsadh sd ashd sah dsha dhsa dsa d')
        self.append_message('DSADAISH dshadbsasdsadh sd ashd sah dsha dhsa dsa d')
        self.append_message('DSADAISH dshadbsasdsadh sd ashd sah dsha dhsa dsa d DSADAISH dshadbsasdsadh sd ashd sah dsha dhsa dsa d')
        self.append_message('DSADAISH ')
        self.append_message('DSADAISH ')
        self.append_message('DSADAISH dshadbsasdsadh sd ashd sah dsha dhsa dsa d')
        self.append_message('DSADAISH dshadbsasdsadh sd ashd sah dsha dhsa dsa d')

    def append_message(self, text):
        new_item = QtWidgets.QLabel(text)
        new_item.setWordWrap(True)
        new_item.setStyleSheet("""
            QLabel {
                padding: 4px;
                border: 1px solid black;
                background-color: #ffffff;
            }
        """)

person bhlee    schedule 15.04.2018    source источник
comment
Не могли бы вы объяснить, что не так со вторым изображением?, я не понимаю, когда вы говорите: Но именно в таком поведении, когда в области прокрутки всего несколько QLabels, что является недопустимым поведением, так как это происходит быть службой комментариев, где люди могут оставлять свои вопросы в виде обычного текста:   -  person eyllanesc    schedule 15.04.2018
comment
Два QLabels, которые я показал на изображении в качестве примера, должны занимать минимальную высоту, чтобы вмещался только текст внутри. Однако если на метке установлено значение MinimumExpanding, это увеличивает высоту QLabel до высоты области просмотра. Контейнер, содержащий метки, представляет собой ScrollArea.   -  person bhlee    schedule 15.04.2018
comment
Извините, запутался во втором изображении, думал, что вы имели в виду третье изображение. На втором изображении оно и так выглядит великолепно, но QLabels НЕ занимает ВСЮ область просмотра, что приводит к третьему изображению, что объясняется в комментарии выше.   -  person bhlee    schedule 15.04.2018
comment
Я вас не понимаю, объясните мне по второму изображению, в чем проблема?, я вижу, что метки занимают минимальную высоту, которая показывает только необходимый текст. Или вы имеете в виду, что полоса прокрутки не появляется? Кроме того, я только что попробовал ваш код, и QScrollArea кажется пустым.   -  person eyllanesc    schedule 15.04.2018
comment
Вы можете показать изображение того, как вы хотите, чтобы оно было вторым изображением.   -  person eyllanesc    schedule 15.04.2018
comment
Второе изображение является результатом установки флага MinimumExpanding на QLabels и показывает правильное поведение, когда количество виджетов заполняет QScrollArea, однако, когда QLabels является небольшим числом и только частично заполняет текущую область просмотра, они расширяются, чтобы заполнить данный видовой экран, показанный на изображении 3. Это не то, что я хочу, я хочу, чтобы QLabel сжались и заняли минимальную высоту, необходимую для размещения текста, подобного изображению 2. Кроме того, я отредактировал код, чтобы он теперь работал.   -  person bhlee    schedule 15.04.2018
comment
Теперь я понимаю, вы могли бы исправить свой код, потому что, когда я его выполняю, есть только пустая область QScrollArea.   -  person eyllanesc    schedule 15.04.2018


Ответы (1)


Это можно решить очень просто, используя метод макета addStretch. который содержит новости:

class NewsList(QtWidgets.QScrollArea):
    def __init__(self, parent=None):
        ...    
        layout = QtWidgets.QVBoxLayout()
        layout.setContentsMargins(5, 5, 5, 5)
        layout.setSpacing(5)
        layout.setAlignment(QtCore.Qt.AlignTop)
        # add a stretchable space to the bottom of the layout
        layout.addStretch(1)

    def append_message(self, text):
        ...
        # set the size policy of the label
        new_item.setSizePolicy(
            QtWidgets.QSizePolicy.Preferred,
            QtWidgets.QSizePolicy.MinimumExpanding)
        # insert the label before the spacer        
        layout = self.news_widget.layout()
        layout.insertWidget(layout.count() - 1, new_item)

Прокладка толкает этикетки вверх, что предотвращает их растягивание, чтобы занять доступное пространство. Использование аргумента stretch-factor для addStretch гарантирует, что разделитель всегда будет иметь приоритет над другими элементами макета.

person ekhumoro    schedule 15.04.2018