rgov
rgov

Reputation: 4369

Resizing a QWindow to fit contents

The main window of my PyQt5 application is set up with a text label along the top above a custom canvas widget which displays an image:

from PyQt5 import QtCore, QtGui, QtWidgets


class Canvas(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.image = None

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        if self.image:
            qp.drawImage(0, 0, self.image)


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

        self.canvas = Canvas()

        self.label = QtWidgets.QLabel()
        self.label.setText('foobar')
        self.label.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                QtWidgets.QSizePolicy.Fixed)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.canvas)
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)

        content = QtWidgets.QWidget()
        content.setLayout(layout)
        self.setCentralWidget(content)

        self.load_image('a.jpg')

    def load_image(self, filename):
        image = QtGui.QImage(filename)
        self.canvas.image = image
        self.canvas.setFixedSize(image.width(), image.height())
        self.update()

    def keyPressEvent(self, event):
        self.load_image('b.jpg')


if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

This looks like this, which is what I want:

enter image description here

When the canvas changes to display a smaller image, I want to shrink the window to fit accordingly. However, it looks like this:

enter image description here

It seems that the minimum size that I can give the window if I manually drag to resize it is the size that fits the contents, but why isn't it resizing to this automatically?

Upvotes: 1

Views: 843

Answers (1)

eyllanesc
eyllanesc

Reputation: 244311

When a fixed size is set, it is used as sizeHint, and the latter is used by layouts to set the widget size. So the size of the canvas depends on the size of the widget, but you want the opposite. You must scale the image size to the window size:

from PyQt5 import QtCore, QtGui, QtWidgets


class Canvas(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setSizePolicy(
            QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding
        )
        self.image = QtGui.QImage()

    @property
    def image(self):
        return self._image

    @image.setter
    def image(self, image):
        self._image = image
        self.update()

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        if not self.image.isNull():
            image = self.image.scaled(
                self.size(), QtCore.Qt.IgnoreAspectRatio, QtCore.Qt.SmoothTransformation
            )
            qp.drawImage(0, 0, image)


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

        self.canvas = Canvas()

        self.label = QtWidgets.QLabel("foobar")
        self.label.setSizePolicy(
            QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed
        )

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.label)
        layout.addWidget(self.canvas)
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)

        content = QtWidgets.QWidget()
        content.setLayout(layout)
        self.setCentralWidget(content)

        self.load_image("a.jpg")

    def load_image(self, filename):
        image = QtGui.QImage(filename)
        self.canvas.image = image

    def keyPressEvent(self, event):
        self.load_image('b.jpg')
        super().keyPressEvent(event)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

Upvotes: 2

Related Questions