4

I read that "preferred policy is very flexible policy and the size of widget can be smaller and larger than sizeHint()."
But while I increased or decreased the size of windows, I couldn't find any differences between them.

< QSizePolicy.Minimum >

from PySide2 import QtWidgets

app = QtWidgets.QApplication([])

window = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
vMinimumButton = QtWidgets.QPushButton("h_Minimum, v_Fixed")
hMinimumButton = QtWidgets.QPushButton("h_Fixed, v_Minimum")
bMinimumButton = QtWidgets.QPushButton("h_Minimum, v_Minimum")

vMinimumButton.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
hMinimumButton.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum)
bMinimumButton.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)

layout.addWidget(vMinimumButton)
layout.addWidget(hMinimumButton)
layout.addWidget(bMinimumButton)
window.setLayout(layout)
window.show()

app.exec_()

< QSizePolicy.Preferred >

from PySide2 import QtWidgets

app = QtWidgets.QApplication([])

window = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
vPreferredButton = QtWidgets.QPushButton("h_Preferred, v_Fixed")
hPreferredButton = QtWidgets.QPushButton("h_Fixed, v_Preferred")
bPreferredButton = QtWidgets.QPushButton("h_Preferred, v_Preferred")

vPreferredButton.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
hPreferredButton.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
bPreferredButton.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)

layout.addWidget(vPreferredButton)
layout.addWidget(hPreferredButton)
layout.addWidget(bPreferredButton)
window.setLayout(layout)
window.show()

app.exec_()
fringetos
  • 73
  • 1
  • 5

2 Answers2

5

This is covered in the policy section of the Qt documentation.

The "preferred" policy allows both shrinking and growing of the control from the hinted size. The "minimum" policy allows only growing.

The latter should never let you shrink below the hinted size.

These policies are different combinations of the policy flags, detailed here:

  • QSizePolicy::GrowFlag = 1 : The widget can grow beyond its size hint if necessary.
  • QSizePolicy::ExpandFlag = 2 : The widget should get as much space as possible.
  • QSizePolicy::ShrinkFlag = 4 : The widget can shrink below its size hint if necessary.
  • QSizePolicy::IgnoreFlag = 8 : The widget's size hint is ignored. The widget will get as much space as possible.
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
3

I'd like to add some insight other than what provided by the succint but complete answer given by paxdiablo.

First of all, Qt size policy names are, on first sight, counterintuitive.
Understanding them requires careful and patient understanding on the documentation, and possibly some testing. Once you get the gist of it, you will probably realize the logic and assumptions behind that naming, and it actually makes sense (unintuitive doesn't always mean wrong: complex subjects require more awareness in order to avoid wrong choices only based on intuitions).

The important thing to understand is that they always refer to the size hint of the widget, and they indicate how that hint is used as a reference.

Maximum means that the hint will be used as the maximum size of the widget: it cannot be bigger than that size.
Minimum means that the hint is the minimum size, so it cannot be smaller.
Remember that minimum* and maximum* (with * being Size, Width or Height) override those aspects. Expanding means that the hint is the preferred size, and if there's some space left, that widget will try to use it.

Then we have to consider that QWidget also has a minimumSizeHint, which is a recommended minimum size: if it returns a valid size, it can not be smaller (again, unless a minimum size is explicitly given).

We also have to realize that some widgets have peculiar behavior, which also explains why you don't see differences in your code: QPushButton, for instance, returns sizeHint() even for minimumSizeHint(), it's based on its contents, and is only valid for its width; by default, Qt buttons have a fixed height.

As always, experimenting is a perfect way to understand the behavior.
The following is a basic example that better shows how policies work; using a basic QWidget as base allows avoiding unexpected behavior caused by widget-specific hints and policies.

You can use context menus to set the policy, then eventually resize the window in order to see the result. The displayed value shows the current policy and size hint.

from PySide2 import QtCore, QtGui, QtWidgets

policyNames = 'Fixed', 'Minimum', 'Maximum', 'Preferred', 'Expanding'
policyNameDict = {}
policyValueDict = {}
for name in policyNames:
    policy = getattr(QtWidgets.QSizePolicy, name)
    policyNameDict[policy] = name
    policyValueDict[name] = policy

class TestWidget(QtWidgets.QWidget):
    def sizeHint(self):
        return QtCore.QSize(150, 30)

    def minimumSizeHint(self):
        return QtCore.QSize(10, 10)

    def contextMenuEvent(self, event):
        currentPolicy = self.sizePolicy()
        menu = QtWidgets.QMenu()
        group = QtWidgets.QActionGroup(menu, exclusive=True)
        for name in policyNames:
            action = group.addAction(name)
            action.setCheckable(True)
            policy = policyValueDict[name]
            action.setData(policy)
            if policy == currentPolicy.horizontalPolicy():
                action.setChecked(True)
        menu.addActions(group.actions())
        res = menu.exec_(event.globalPos())
        if res:
            # PySide requires reconversion of the data, on PyQt the
            # res.data() alone is enough;
            currentPolicy.setHorizontalPolicy(
                QtWidgets.QSizePolicy.Policy(res.data()))
            self.setSizePolicy(currentPolicy)
            self.update()

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        qp.drawRect(self.rect().adjusted(0, 0, -1, -1))
        text = '{policy}\n{width}x{height}'.format(
            policy=policyNameDict[self.sizePolicy().horizontalPolicy()], 
            width=self.sizeHint().width(), 
            height=self.sizeHint().height(), 
        )
        qp.drawText(self.rect(), QtCore.Qt.AlignCenter, text)


app = QtWidgets.QApplication([])

window = QtWidgets.QWidget()
layout = QtWidgets.QHBoxLayout(window)
for i in range(3):
    layout.addWidget(TestWidget())
window.show()

app.exec_()
musicamante
  • 41,230
  • 6
  • 33
  • 58
  • @fringetos You're welcome. Remember that if an answer solves your issue or clarifies your problem, you can mark it as accepted by clicking the gray tickmark on its left. – musicamante May 03 '21 at 15:43
  • Sure. But first of all, I think there seems to be an error in the code you gave me. It doesn't work. Would you check again and fix it? My terminal says < TypeError: 'PySide2.QtWidgets.QSizePolicy.setHorizontalPolicy' called with wrong argument types: PySide2.QtWidgets.QSizePolicy.setHorizontalPolicy(int) > – fringetos May 03 '21 at 16:40
  • @fringetos sorry, I use PyQt and it doesn't try to reconvert Qt enums unlike PySide, which makes some `int` convertion "on the road" (I suppose that it does that in the `setData()`). You need to ensure that value has the correct signature, so change that line to: `currentPolicy.setHorizontalPolicy(QtWidgets.QSizePolicy.Policy(res.data()))` – musicamante May 03 '21 at 19:53
  • Thank you so much. It works. For more accurate results, it would be better to fix three places in your code as follows. (1) Add: policyNames = 'Fixed', 'Minimum', 'Maximum', 'Preferred', 'Expanding', 'MinimumExpanding', 'Ignored' (2) Add: currentPolicy.setVerticalPolicy(QtWidgets.QSizePolicy.Policy(res.data())) (3) Remove: for i in range(3): – fringetos May 04 '21 at 03:25
  • And (4) more proper size: def sizeHint(self): return QtCore.QSize(500, 300), def minimumSizeHint(self): return QtCore.QSize(200, 100) – fringetos May 04 '21 at 03:34
  • @fringetos I'm aware of those aspects, I didn't include them for simplicity. The for cycle is required to properly show the results compared to *other* widgets with different size policies, which is an important aspect to consider when dealing with "adjusting" policies. – musicamante May 04 '21 at 03:34
  • @fringetos having smaller sizes is important for the purpose of the example, even if they potentially cut out the displayed text (which wouldn't be properly shown anyway if font scaling is applied, and which would have required a much more complex example that would be out of scope for these demonstration purposes). Please consider that an example has to be *concise* and only display the matter at hand; of course I could have added more code in order to make it more compliant/compatible, but if those "features" make the example unnecessarily complex then the answer wouldn't be appropriate. – musicamante May 04 '21 at 03:36
  • When multiple widgets were displayed together, it seemed that minimumSizeHint() was not working due to interference from other widgets. – fringetos May 04 '21 at 03:48
  • That can depend on the policies of all widgets, can you provide a reproducible combination? – musicamante May 04 '21 at 04:09
  • Mmm... well, each seems to have its own pros and cons. Anyway, thank you in many ways. – fringetos May 04 '21 at 05:51