Как сделать высоту виджета фиксированной пропорцией к ее ширине

Я работаю над приложением PyQt5, которое должно иметь баннер сверху. Баннер представляет собой просто широкое изображение, ширина которого всегда должна быть шириной окна, а высота должна быть фиксированной. Другими словами, высота изображения баннера должна зависеть от ширины окна. Виджет под баннером (основное содержимое) должен растягиваться, чтобы заполнить все доступные вертикальные пространства.

Я в основном портировал этот SO ответ на 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) 

(В этом примере я использую это бесплатное изображение баннера, но в этом нет ничего особенного).

Я разместил баннер в коде приложения ниже, где ярлык служит заполнителем основного содержимого:

 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_()) 

Проблема в том, что метка заполняет 100% окна – баннер вообще не отображается.

Я пробовал много разных политик размера и коэффициентов растяжения, а также полностью удалял политики размера, но не нашел, как делать то, что мне нужно. Изображение в баннере должно быть пропорционально масштабировано, чтобы соответствовать ширине окна, а метка должна заполнить оставшееся вертикальное пространство в окне.

Идеи?

    One Solution collect form web for “Как сделать высоту виджета фиксированной пропорцией к ее ширине”

    Комментарий @ hasHeightForWidth() был прав: мне пришлось реализовать hasHeightForWidth() и heightForWidth() в классе Banner.

    Вот модифицированный код, который работает так, как я хочу. Все модификации содержат комментарии в коде.

     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_()) 
      Python - лучший язык программирования в мире.