Search code examples
pythonqteventspyside6

How to auto-resize a PySide6 app to the minimum size when decreasing a `pixmap`?


I use the following code to set a PySide6 app to the minimal possible size. This works fine when increasing the size of the widgets (set - to + in line 37), but not when decreasing it - in effect, the size of the windows does decrease, but it seems to be one step late.

I found a few workarounds, most notably in Qt Layout, resize to minimum after widget size changes, but none of what I tried seems to be working (and I have met other issues with app.processEvents(), which should be avoided anyway).

Edit: In the new code example below, I think the problem is the width of the QPushButton, which is calculated too late.

enter image description here

Interestingly, this width of the QPushButton is solved by the layout.setSizeConstraint(QLayout.SetFixedSize) workaround, but the window width is not.

enter image description here

app.processEvents() works for this example, but I see bad side effects on other signals when using it.

enter image description here


New code example:

from PySide6.QtCore import Qt
from PySide6.QtGui import QPixmap
from PySide6.QtWidgets import (QApplication, QLabel, QLayout, QMainWindow,
                               QPushButton, QVBoxLayout, QWidget)


class Window(QMainWindow):
    def __init__(self):
        super().__init__()

        self.i = 0

        self.button = QPushButton("push me!")
        self.button.clicked.connect(self.clicked)

        self.label = QLabel()

        layout = QVBoxLayout()
        layout.addWidget(self.button)
        layout.addWidget(self.label)

        # https://stackoverflow.com/a/21458822/880783
        # layout.setSizeConstraint(QLayout.SetFixedSize)  # (ineffective)

        widget = QWidget()
        widget.setLayout(layout)

        self.setCentralWidget(widget)
        self.setWindowFlag(Qt.MSWindowsFixedSizeDialogHint)
        self.clicked()
        self.show()

    def clicked(self):
        npix = 500 - 50 * self.i
        self.label.setPixmap(QPixmap(npix, npix))
        # app.processEvents()  # (effective, but discouraged)
        self.adjustSize()
        self.i += 1


app = QApplication()
win = Window()
app.exec()

Original code example:

import threading
import time

from PySide6.QtCore import Qt
from PySide6.QtGui import QPixmap
from PySide6.QtWidgets import (
    QApplication,
    QHBoxLayout,
    QLabel,
    QLayout,
    QMainWindow,
    QWidget,
)


class Window(QMainWindow):
    def __init__(self):
        super().__init__()

        self.label = QLabel()

        layout = QHBoxLayout()
        layout.addWidget(self.label)

        # https://stackoverflow.com/a/21458822/880783
        # layout.setSizeConstraint(QLayout.SetFixedSize)  # (ineffective)

        widget = QWidget()
        widget.setLayout(layout)

        self.setCentralWidget(widget)
        self.setWindowFlag(Qt.MSWindowsFixedSizeDialogHint)
        self.show()

    def run(self):
        for i in range(10):
            npix = 500 - 50 * i
            self.label.setPixmap(QPixmap(npix, npix))
            # app.processEvents()  # (ineffective)
            self.adjustSize()
            time.sleep(1)


app = QApplication()
threading.Thread(target=Window().run).start()
app.exec()

Solution

  • @musicamante has posted very helpful comments, which I now turn into an answer.

    Basically, this code works great:

    from PySide6.QtCore import QMetaObject, Qt, QTimer, Slot
    from PySide6.QtGui import QPixmap
    from PySide6.QtWidgets import (QApplication, QLabel, QMainWindow, QPushButton,
                                   QVBoxLayout, QWidget)
    
    
    class Window(QMainWindow):
        def __init__(self):
            super().__init__()
    
            self.i = 0
    
            self.button = QPushButton("push me!")
            self.button.clicked.connect(self.clicked)
    
            self.label = QLabel()
    
            layout = QVBoxLayout()
            layout.addWidget(self.button)
            layout.addWidget(self.label)
    
            widget = QWidget()
            widget.setLayout(layout)
    
            self.setCentralWidget(widget)
            self.setWindowFlag(Qt.MSWindowsFixedSizeDialogHint)
            self.clicked()
            self.show()
    
        # @Slot()
        # def adjustSize(self):
        #     super().adjustSize()
    
        def clicked(self):
            npix = 500 - 50 * self.i
            self.label.setPixmap(QPixmap(npix, npix))
            self.i += 1
    
            # # As in https://stackoverflow.com/a/23954088/880783 - does not work
            # QMetaObject.invokeMethod(self, "adjustSize")
    
            # This works!
            QTimer.singleShot(0, self.adjustSize)
    
    
    app = QApplication()
    win = Window()
    app.exec()
    

    As one can see, I have also tried the approach put forward https://stackoverflow.com/a/23954088/880783 - without success, however.