Как использовать setSizePolicy для QAbstractScrollArea

#python #qt #layout #pyqt5 #scrollview

#python #qt #макет #pyqt5 #scrollview

Вопрос:

Я пытаюсь использовать свойство setSizePolicy QWidget. Использование этой функции с QWidget или QFrame работает, как и ожидалось. Однако при использовании этой функции с QAbstractScrollArea результат оказывается неожиданным.

Следующий минимальный рабочий пример демонстрирует это поведение:

Слева расположены два Parent виджета, в которых расположены виджеты QAbstractScrollArea, а справа — набор виджетов QFrame. Каждому виджету присваивается индивидуальная высота, и все виджеты указывают в политике размера, который должен быть привязан к возвращаемому размеру sizeHint, который привязан к вышеупомянутой высоте.

 from PyQt5 import QtCore, QtWidgets
import sys


class ASWidget(QtWidgets.QAbstractScrollArea):
    def __init__(self, height, parent=None):
        super().__init__(parent)

        self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)

        self._height = height
        self.setStyleSheet("background: red;")

    def sizeHint(self):
        return QtCore.QSize(100, self._height)


class NonASWidget(QtWidgets.QFrame):
    def __init__(self, height, parent=None):
        super().__init__(parent)

        self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)

        self._height = height
        self.setStyleSheet("background: red;")

    def sizeHint(self):
        return QtCore.QSize(100, self._height)


class ParentWidget(QtWidgets.QWidget):
    def __init__(self, classType, parent=None):
        super().__init__(parent)

        layout = QtWidgets.QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(1)

        sizes = [5, 15, 25, 35, 45, 55, 65]
        for i in sizes:
            layout.addWidget(classType(i))


class Dialog(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()

        w1 = ParentWidget(ASWidget, self)
        w2 = ParentWidget(NonASWidget, self)

        w2.move(110, 0)


def main():
    app = QtWidgets.QApplication(sys.argv)

    dialog = Dialog()
    dialog.show()

    app.exec_()


if __name__ == "__main__":
    main()
  

Результатом приведенного выше кода является этот снимок экрана:

Снимок экрана минимального рабочего примера

Как вы можете видеть, виджеты QAbstractScrollArea слева не используют политику фиксированного размера в отличие от виджетов QFrame справа.

В чем причина этого и как я могу использовать функцию setSizePolicy с виджетами QAbstractScrollArea?

Комментарии:

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

2. @musicamante Я хотел бы перегрузить sizeHint виджета, который наследуется от QAbstractScrollArea , и явно установить размер виджета с его помощью. Использование setFixedHeight работает нормально, но я думаю, что другой подход намного чище (если он будет работать).

3. Я понимаю, что вы хотите сделать, и я не говорил ни о подсказке размера, ни о фиксированном размере, но minimumSizeHint()

Ответ №1:

При создании подклассов виджетов (помимо самого QWidget) важно помнить, что все существующие подклассы Qt также устанавливают некоторые свойства по умолчанию или переопределяют методы, включая абстрактные.

Это minimumSizeHint() рекомендуемый минимальный размер виджета, и макет (почти) всегда будет его учитывать. Следующий абзац важен:

QLayout никогда не изменит размер виджета до размера, меньшего минимального размера, если не установлен параметр MinimumSize() или политика размера не установлена на QSizePolicy::Ignore . Если установлен параметр MinimumSize(), подсказка о минимальном размере будет проигнорирована.

Минимальная подсказка важна, поскольку она действительна только для виджетов, которые находятся внутри макета, и ее также можно использовать как [вложенный] класс «по умолчанию» вместо using minimumSize() , который следует использовать для экземпляров.

В то время как многие виджеты возвращают недопустимую (например, игнорируемую) подсказку о минимальном размере, другие этого не делают, поскольку для их природы важно установить минимальный размер по умолчанию. Это происходит для подклассов QAbstractButton и для всех подклассов QAbstractScrollArea .

Чтобы переопределить это поведение (хотя оно не предлагается при определенном размере), просто перезапишите метод. Обратите внимание, что предпочтительнее иметь sizeHint() возврат minimumSizeHint() , а не наоборот, так что sizeHint() всегда соблюдайте минимальную подсказку, но ее все равно можно переопределить в других подклассах.

 class ASWidget(QtWidgets.QAbstractScrollArea):
    def __init__(self, height, parent=None):
        super().__init__(parent)

        self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)

        self._height = height
        self.setStyleSheet("background: red;")

    def sizeHint(self):
        return self.minimumSizeHint()

    def minimumSizeHint(self):
        return QtCore.QSize(100, self._height)
  

Комментарии:

1. Большое вам спасибо за этот подробный ответ. Это определенно решает мою проблему. Но не было бы более «правильным» перегрузить метод minimumSizeHint для возврата недопустимого минимального размера и использования sizeHint по назначению? Если это предложение имеет какой-либо смысл, не могли бы вы также пролить свет на этот подход?

2. @Woltan Это зависит от того, что вы планируете делать с виджетом, какова его цель и какое содержимое он должен показывать. Учитывая, что это был всего лишь пример, я не знаю, что вы собираетесь делать (например, вы добавили родительские виджеты без основного макета, чего обычно делать не следует), но если вы это сделаете, результатом будет то, что без действительной минимальной подсказки виджеты могут стать слишком маленькими. Для областей прокрутки это не очень хорошая идея, поскольку содержимое станет почти невидимым, а полосы прокрутки непригодными для использования. Для сложных виджетов, подобных этим, вы всегда должны предоставлять действительную минимальную подсказку.

3. @musicamente Вы правы, мой пример не показывает, для чего я намерен использовать виджет. В моем случае виджет не имеет полос прокрутки, но прокручивается «внешне», и для этого достаточно небольшого размера. Из вашего комментария я делаю вывод, что можно перегружать minimumSizeHint , и это то, что я собираюсь сделать сразу после принятия вашего ответа. Еще раз спасибо за помощь!