如何使小部件的高度与其宽度

时间:2016-05-23 20:00:08

标签: python qt qt5 pyqt5

我正在开发一个需要在顶部有横幅的PyQt5应用程序。横幅只是一个宽图像,其宽度应始终为窗口的宽度,其高度应为固定比例。换句话说,横幅图像的高度应取决于窗口的宽度。横幅下方的小部件(主要内容)应该拉伸以填充所有可用的垂直空间。

我基本上将this SO answer移植到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)

(对于这个例子,我使用this免费横幅图片,但没有什么特别的。)

我已将横幅放在下面的应用程序代码中,其中标签用作主要内容的占位符:

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%的窗口 - 横幅根本不可见。

我已经尝试了许多不同规模的政策和延伸因素,并完全取消了尺寸政策,但却找不到如何做我需要的事情。横幅中的图像应按比例缩放以适合窗口的宽度,标签应填充窗口中剩余的垂直空间。

想法?

1 个答案:

答案 0 :(得分:0)

@ kuba-ober的评论是正确的:我必须在Banner课程中实施hasHeightForWidth()heightForWidth()

这是修改后的代码,它按我想要的方式工作。所有修改都在代码中有注释。

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