如何使小部件的高度与其宽度成固定比例 [英] How to make a widget's height a fixed proportion to its width

查看:69
本文介绍了如何使小部件的高度与其宽度成固定比例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

我基本上已经将这个答案移植到 PyQt5:

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

问题是,标签填满了窗口的 100% — 横幅根本不可见.

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.

想法?

推荐答案

@kuba-ober 的评论是对的:我必须实现 hasHeightForWidth()heightForWidth()在 Banner 类中.

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

这篇关于如何使小部件的高度与其宽度成固定比例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆