pythonpyqtpyqt5qstackedwidget

How to set QStackedWidget size to child widgets minimum size?


Unable to get the QLabel in this example to be of the minimum size to contain its text. I need the layout and stacked widget to then size themselves to the minimum required to fit the label.

I have used code from https://www.tutorialspoint.com/pyqt/pyqt_qstackedwidget.htm to demonstrate my issue.

Setting the size policies seems to work when the application starts, but increasing the application width eventually causes the label to expand after the list reaches a certain width.

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *


class stackedExample(QWidget):

    def __init__(self):
        super(stackedExample, self).__init__()
        self.rightlist = QListWidget()
        self.rightlist.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)

        self.rightlist.insertItem(0, 'Contact')

        self.stack1 = QWidget()
        self.stack1.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Minimum)

        self.stack1UI()

        self.Stack = QStackedWidget(self)
        self.Stack.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Minimum)
        self.Stack.addWidget(self.stack1)

        hbox = QHBoxLayout(self)
        hbox.addWidget(self.Stack)
        hbox.addWidget(self.rightlist)


        self.setLayout(hbox)
        self.rightlist.currentRowChanged.connect(self.display)
        self.setGeometry(300, 50, 10, 10)
        self.setWindowTitle('StackedWidget demo')
        self.show()


    def stack1UI(self):
        layout = QVBoxLayout()
        label = QLabel("Hello World")
        label.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Minimum)
        label.setStyleSheet("QLabel { background-color : red; color : blue; }")
        layout.addWidget(label)
        self.stack1.setLayout(layout)


    def display(self, i):
        self.Stack.setCurrentIndex(i)

def main():
    app = QApplication(sys.argv)
    ex = stackedExample()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

Solution

  • tl;dr

    Remove the size policy settings for everything but the QStackedWidget only (for which you'll have to set the horizontal policy to Maximum), and everything should be fine.

    Explanation

    I have to admit: I always felt that QSizePolicy enum names are confusing (I know that I'm not the only one), so I sympathize with your doubts.

    Setting the stretch resolves the issue only partially, because sometime you might need or want to manually set the stretches, and that will possibly mess around with some size policies.

    The problem is that you're setting the size policy to "Minimum", which, as the QSizePolicy documentation explains, says that:

    The sizeHint() is minimal, and sufficient. The widget can be expanded [...]

    And that's because Minimum uses the GrowFlag.
    This means that, if the layout "thinks" that there's some available space for a widget, it will let it expand: Minimum does not mean that the widget will use it's minimal size (or, better, its minimumSizeHint()), but that it will use the sizeHint() as a minimum size for the layout, while it keeping its capacity to expand; if there's available space, it will use it.

    What you actually need is to set the horizontal policy to Maximum instead, and, specifically, to the Stack object only (the QStackWidget, not the QWidget container, nor the QLabel).
    That's because Maximum actually uses the ShrinkFlag (again, from the QSizePolicy docs):

    The sizeHint() is a maximum. The widget can be shrunk any amount without detriment if other widgets need the space (e.g. a separator line). It cannot be larger than the size provided by sizeHint().

    That said, be aware that there are known issues with QLabels in certain cases, specifically if the label has word wrapping.