tmressler
tmressler

Reputation: 45

App size doesn't change when size() is called after hiding widgets

I'm trying to resize my app to a smaller size after a widget is hidden and its sizePolicy is set to Ignored. However, the app will not resize to smaller than it was before, as if the hidden widget is still there taking up space.

I've tried including self.update(), self.updateGeometry(), and app.processEvents() after hiding the second widget/setting its sizePolicy to Ignored and before trying to resize the app, but none of them seem to have any effect.

class App(QWidget):
    def __init__(self):
        super().__init__()

        # basic setup
        self.move(100, 100)

        # main widgets
        self.advanced_metadata = QCheckBox("Advanced", self)
        self.advanced_metadata.stateChanged.connect(self.displayAdvanced)
        self.first_widget = QLabel('first_widget', self)
        self.second_widget = QLabel('second_widget', self)
        self.second_widget.setHidden(True)
        self.second_widget.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)

        # main layout
        self.setLayout(QVBoxLayout())
        self.layout().addWidget(self.advanced_metadata)
        self.layout().addWidget(self.first_widget)
        self.layout().addWidget(self.second_widget)
        self.layout().addStretch()

        self.resizeApp(650, self.sizeHint().height())

        self.show()

    # toggles advanced metadata view
    def displayAdvanced(self):
        if self.advanced_metadata.isChecked():
            self.second_widget.setVisible(True)
            self.second_widget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)

            self.resizeApp(650, self.sizeHint().height())

        else:
            self.second_widget.setHidden(True)
            self.second_widget.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)

            self.resizeApp(650, self.sizeHint().height())

    # resizes main window
    def resizeApp(self, width, height):
        self.resize(width, height)
        print(self.size())

if __name__ == '__main__':
    # accounts for exiting errors in python
    if not QApplication.instance():
        app = QApplication(sys.argv)
    else:
        app = QApplication.instance()
    ex = App()
    app.exec_()

I expect the size of the window when initialized to be 650x112, then when the Advanced checkbox is selected the size should change to 650x149, and lastly, when the Advanced checkbox is unchecked, the size should revert back to 650x112. The last expectation does not occur; instead, the window remains at 650x149.

Upvotes: 2

Views: 93

Answers (1)

eyllanesc
eyllanesc

Reputation: 243887

In Qt many of the tasks are done asynchronously, and that is the case of geometry, so even if you have changed the policies this will not apply immediately until the synchronous part ends. So a workaround is to use QTimer.singleShot(0, ...) to apply the resizing one instant later.

from functools import partial
from PyQt5 import QtCore, QtWidgets


class App(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.move(100, 100)
        # main widgets
        self.advanced_metadata = QtWidgets.QCheckBox(
            "Advanced", stateChanged=self.displayAdvanced
        )
        self.first_widget = QtWidgets.QLabel("first_widget")
        self.second_widget = QtWidgets.QLabel("second_widget")
        self.second_widget.setHidden(True)
        self.second_widget.setSizePolicy(
            QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored
        )
        lay = QtWidgets.QVBoxLayout(self)
        lay.addWidget(self.advanced_metadata)
        lay.addWidget(self.first_widget)
        lay.addWidget(self.second_widget)
        lay.addStretch()

        self.resizeApp(650, self.sizeHint().height())

    # toggles advanced metadata view
    @QtCore.pyqtSlot(int)
    def displayAdvanced(self, state):
        if state == QtCore.Qt.Checked:
            self.second_widget.show()
            self.second_widget.setSizePolicy(
                QtWidgets.QSizePolicy.Preferred,
                QtWidgets.QSizePolicy.Preferred,
            )

        else:
            self.second_widget.hide()
            self.second_widget.setSizePolicy(
                QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored
            )
        wrapper = partial(self.resizeApp, 650, self.sizeHint().height())
        QtCore.QTimer.singleShot(0, wrapper)

    @QtCore.pyqtSlot(int, int)
    def resizeApp(self, width, height):
        self.resize(width, height)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication.instance()
    if app is None:
        app = QtWidgets.QApplication(sys.argv)
    ex = App()
    ex.show()
    sys.exit(app.exec_())

Upvotes: 1

Related Questions