I am trying to use the setSizePolicy property of a QWidget. Using this feature with a QWidget or QFrame works as expected. However, using this feature with a QAbstractScrollArea the result is unexpected.
The following minimum working example demonstrates this behavior:
Arranged in two Parent
widgets are on the left a layout of QAbstractScrollArea widgets and on the right a set of QFrame widgets. Each widget gets assigned an individual height and all widgets specify in the size policy to be fixed to the sizeHint return size, which is fixed to the aforementioned height.
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()
The result of the code above is this screen shot:
As you can see, the QAbstractScrollArea widgets on the left do not make use of the fixed size policy in contrast to the QFrame widgets on the right.
What is the reason behind this and how can I make use of the setSizePolicy feature with QAbstractScrollArea widgets?
When subclassing widgets (besides QWidget itself), it's important to remember that all existing Qt subclasses also set some default properties or reimplement methods, including the abstract ones.
The minimumSizeHint()
is the recommended minimum size of the widget, and the layout will (almost) always respect that. The following paragraph is important:
QLayout will never resize a widget to a size smaller than the minimum size hint unless minimumSize() is set or the size policy is set to QSizePolicy::Ignore. If minimumSize() is set, the minimum size hint will be ignored.
The minimum hint is important as it's valid only for widgets that are inside a layout, and it also can be used as a [sub]class "default" instead of using minimumSize()
, which should be used for instances instead.
While many widgets return an invalid (as in ignored) minimum size hint, others don't, as it's important for their nature to have a default minimum size set. This happens for subclasses of QAbstractButton and for all subclasses of QAbstractScrollArea.
To override this behavior (while not suggested under a certain size), just overwrite the method. Note that it's preferred to have the sizeHint()
return minimumSizeHint()
and not the other way around, so that sizeHint()
always respect the minimum hint, but can still be overridden in other subclasses.
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)