I’m working on a GUI where I have a QMainWindow with a layout containing three widgets stacked vertically: a top header, an image, and a bottom header. The top and bottom headers are custom QWidgets with a blue background, and the image is displayed using a QLabel with a QPixmap. The Image is just a generic 400×400 image.
Here’s my code:
from PyQt6.QtWidgets import QMainWindow, QApplication, QLabel, QVBoxLayout, QHBoxLayout, QWidget, QSizePolicy, QSpacerItem
from PyQt6.QtGui import QPixmap, QColor, QPalette
from PyQt6.QtCore import Qt, QSize
class ColorWidget(QWidget):
def __init__(self, color):
super().__init__()
self.setAutoFillBackground(True)
palette = self.palette()
palette.setColor(QPalette.ColorRole.Window, QColor(color))
self.setPalette(palette)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.InitUI()
def InitUI(self):
layout = QVBoxLayout()
layout.setSpacing(0)
# Top Header
top_header = ColorWidget('blue')
top_header_layout = QHBoxLayout()
top_header_layout.addWidget(QLabel(parent=top_header, text='Image name'))
top_header.setLayout(top_header_layout)
layout.addWidget(top_header)
# Image
self.image_label = QLabel()
self.pixmap = QPixmap('Images/Path')
self.image_label.setPixmap(self.pixmap)
self.image_label.setMinimumSize(QSize(100, 100))
self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.image_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
layout.addWidget(self.image_label)
# Bottom Header
bottom_header = ColorWidget('blue')
bottom_header_layout = QHBoxLayout()
bottom_header_layout.addWidget(QLabel(parent=bottom_header, text='Caption'))
bottom_header.setLayout(bottom_header_layout)
layout.addWidget(bottom_header)
central_widget = QWidget()
central_widget.setLayout(layout)
self.setCentralWidget(central_widget)
def resizeEvent(self, event):
super().resizeEvent(event)
self.adjust_image_size()
def adjust_image_size(self):
container_size = self.image_label.parentWidget().size()
pixmap_width = self.pixmap.width()
pixmap_height = self.pixmap.height()
max_width = container_size.width()
max_height = container_size.height()
new_width = min(max_width, pixmap_width)
new_height = min(max_height, pixmap_height)
if new_width / pixmap_width > new_height / pixmap_height:
new_width = new_height * pixmap_width / pixmap_height
else:
new_height = new_width * pixmap_height / pixmap_width
self.image_label.setPixmap(self.pixmap.scaled(int(new_width), int(new_height), Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation))
if __name__ == '__main__':
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
There are three issues that I’m having:
If I expand the window vertically, the top and bottom frames get separated from the image label.
If I expand the window horizontally, the top and bottom frames expand quicker than the image resizes.
If I resize the window to its smallest size possible, the image looks a bit rectangular.
To fix the first issue, I’ve added Spacer Items to the layout to sandwich everything in, which works for the most part, except when I make the window narrow and tall.
To address the second issue, I tried setting a max width of 400 pixels (the width of the photo). This sort of works except if I make the window smaller, the top and bottom frames still stretch further than the image width.
How can I improve this code so that the top and bottom frames are always “glued” to the image and resize as a whole? I want the widgets to resize as if they are one single widget.
After implementing some possible solutions, the widgets are resizing more in unison but still not as if they were the same widget. Below are the edits:
# Add this in Main Window Init method
self.setMaximumWidth(400)
self.setMaximumHeight(400)
# Add this statement before the Top Header
layout.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding))
# Add it again after the bottom header
layout.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding))