问题
I'm working on a PyQt5 application that needs to have a banner along the top. The banner is just a wide image, whose width should always be the width of the window, and whose height should be a fixed proportion. In other words, the banner image's height should depend on the width of the window. The widget beneath the banner (the main content) should stretch to fill all available vertical space.
I've basically ported this SO answer to PyQt5:
class Banner(QWidget):
def __init__(self, parent):
super(Banner, self).__init__(parent)
self.setContentsMargins(0, 0, 0, 0)
pixmap = QPixmap('banner-1071797_960_720.jpg') # see note below
self._label = QLabel(self)
self._label.setPixmap(pixmap)
self._label.setScaledContents(True)
self._label.setFixedSize(0, 0)
self._label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self._resizeImage()
def resizeEvent(self, event):
super(Banner, self).resizeEvent(event)
self._resizeImage()
def _resizeImage(self):
pixSize = self._label.pixmap().size()
pixSize.scale(self.size(), Qt.KeepAspectRatio)
self._label.setFixedSize(pixSize)
(For this example, I'm using this free banner image, but there's nothing special about it.)
I've put the banner in the application code below, where a label serves as a placeholder for the main content:
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = QWidget()
widget.setContentsMargins(0, 0, 0, 0)
layout = QVBoxLayout(widget)
banner = Banner(widget)
bannerSizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
bannerSizePolicy.setHeightForWidth(True)
banner.setSizePolicy(bannerSizePolicy)
layout.addWidget(banner)
label = QLabel('There should be a banner above')
label.setStyleSheet('QLabel { background-color: grey; color: white; }');
layout.addWidget(label)
layout.setStretch(0, 1)
widget.resize(320, 200)
widget.move(320, 200)
widget.setWindowTitle('Banner Tester')
widget.show()
sys.exit(app.exec_())
The problem is, the label fills 100% of the window—the banner isn't visible at all.
I've tried many different size policies and stretch factors, and removing size policies altogether, but haven't found how to do what I need. The image in the banner should be proportionately scaled to fit the window's width, and the label should fill the remaining vertical space in the window.
Ideas?
回答1:
@kuba-ober's comment was right: I had to implement hasHeightForWidth()
and heightForWidth()
in the Banner class.
Here's the modified code, which works the way I want. All the modifications have comments within the code.
class Banner(QWidget):
def __init__(self, parent):
super(Banner, self).__init__(parent)
self.setContentsMargins(0, 0, 0, 0)
pixmap = QPixmap('banner-1071797_960_720.jpg')
# First, we note the correct proportion for the pixmap
pixmapSize = pixmap.size()
self._heightForWidthFactor = 1.0 * pixmapSize.height() / pixmapSize.width()
self._label = QLabel('pixmap', self)
self._label.setPixmap(pixmap)
self._label.setScaledContents(True)
self._label.setFixedSize(0, 0)
self._label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self._resizeImage(self.size())
def hasHeightForWidth(self):
# This tells the layout manager that the banner's height does depend on its width
return True
def heightForWidth(self, width):
# This tells the layout manager what the preferred and minimum height are, for a given width
return math.ceil(width * self._heightForWidthFactor)
def resizeEvent(self, event):
super(Banner, self).resizeEvent(event)
# For efficiency, we pass the size from the event to _resizeImage()
self._resizeImage(event.size())
def _resizeImage(self, size):
# Since we're keeping _heightForWidthFactor, we can code a more efficient implementation of this, too
width = size.width()
height = self.heightForWidth(width)
self._label.setFixedSize(width, height)
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = QWidget()
widget.setContentsMargins(0, 0, 0, 0)
layout = QVBoxLayout(widget)
banner = Banner(widget)
# Turns out we don't need the bannerSizePolicy now
# bannerSizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
# bannerSizePolicy.setHeightForWidth(True)
# banner.setSizePolicy(bannerSizePolicy)
layout.addWidget(banner)
label = QLabel('There should be a banner above')
label.setStyleSheet("QLabel { background-color: grey; color: white; }");
layout.addWidget(label)
layout.setStretch(1, 1)
widget.resize(320, 200)
widget.move(320, 200)
widget.setWindowTitle('Banner Tester')
widget.show()
sys.exit(app.exec_())
来源:https://stackoverflow.com/questions/37399515/how-to-make-a-widgets-height-a-fixed-proportion-to-its-width