How to make a widget's height a fixed proportion to its width

爱⌒轻易说出口 提交于 2019-12-24 07:10:41

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!