【问题标题】:Force QWidget to occupy all available height in QScrollArea强制 QWidget 占据 QScrollArea 中的所有可用高度
【发布时间】:2017-03-20 22:05:44
【问题描述】:

我正在尝试使用 QHBoxLayout 创建 QScrollArea,其中包含一行 od QWidgets。我希望小部件占据所有可用高度并相应调整其大小。

我的小部件是 QLabel,在内部显示按比例缩放的 QPixmap。在我的项目中,这些是照片。我实现了 heightForWidth 和 sizeHint 方法来实现良好的缩放。我仍然无法达到正确的高度。

默认情况下,小部件是缩放的,因此滚动区域不需要滚动条。

不需要滚动条

我使用了滚动小部件的 sizePolicy,但只能强制小部件的大小等于从 sizeHint 返回的最大大小。然后水平滚动处于活动状态。

小部件的最大尺寸超过窗口高度

当我将 sizeHint 更改为返回 QSize(width(), heightForWidth(width())) 时,我观察到 width() 返回最多 640 像素,而且永远不会更多。我不知道这个 640 的限制是从哪里来的。

有没有办法在我的布局中移除 640 像素的宽度限制,或者有没有办法强制小部件放大到窗口高度?

class ImageWidget(QLabel):
    PIXMAP_SIZE = QSize(1000, 500)

    def __init__(self, parent=None):
        super(ImageWidget, self).__init__(parent)

        self._imagePixmap = QtGui.QPixmap(self.PIXMAP_SIZE)
        self._imagePixmap.fill(QtGui.QColor(Qt.green))

        self.setScaledContents(False)
        self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.setStyleSheet("border:1px solid red;")

    def paintEvent(self, event):
        size = self.size()
        point = QPoint(0, 0)
        scaledPix = self._imagePixmap.scaled(size, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        point.setX((size.width() - scaledPix.width())/2)
        point.setY((size.height() - scaledPix.height())/2)
        QtGui.QPainter(self).drawPixmap(point, scaledPix)

    def heightForWidth(self, width):
        return self._imagePixmap.height() * width / self._imagePixmap.width() if self._imagePixmap.width() else 0

    def hasHeightForWidth(self):
        return self._imagePixmap is not None

    def sizeHint(self):
        return QSize(self.width(), self.heightForWidth(self.width()))
        # self.width() has max value of 640 !
        #return self.PIXMAP_SIZE


class App(QMainWindow):
    def __init__(self):
        super(App, self).__init__()

        self.seriesLayout = QHBoxLayout()

        scrollWidget = QWidget()
        scrollWidget.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum)
        scrollWidget.setLayout(self.seriesLayout)

        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        scroll.setWidget(scrollWidget)

        self.setCentralWidget(scroll)

        for col in range(5):
            self.seriesLayout.addWidget(ImageWidget())

【问题讨论】:

  • 您必须自己根据滚动区域客户区的大小调整滚动区域内的小部件的大小。您不需要任何尺寸提示:小部件在所有情况下都具有固定尺寸,垂直尺寸与客户端滚动区域的高度相同,水平尺寸是垂直尺寸的固定比例。 This answer 展示了其中的一些方法。
  • 我最困惑的是布局强制内部小部件的最大尺寸为 640x480。我搜索了 Qt 存储库,发现小部件的默认几何形状正是那个大小。 link。将新几何设置为我的像素图的最大尺寸不会限制小部件。它们仍然溢出窗口的可用高度,因此垂直滚动条可见。我很惊讶默认设置允许窗口的内容增长到窗口的宽度,但是将此行为转换为高度并不是那么简单。
  • 这是意料之中的,因为没有任何东西可以限制内容小部件的大小。 QScrollArea 不做任何事情。您必须自己调整小部件的大小,否则您的任何期望都无法开始。

标签: python c++ qt pyqt qt5


【解决方案1】:

我最终关注了评论并为我的滚动区域添加了自定义调整大小事件。 我使用viewport().height() 作为首选高度并计算所需宽度,询问每个小部件首选widthForHeight()。当然,这仅适用于实现了此方法的自定义小部件,但我确信可以扩展到回退到 sizeHint()

不管怎样,代码如下:

class MyScrollArea(QScrollArea):
    def __init__(self, parent=None):
        super(MyScrollArea, self).__init__(parent)
        self.setWidgetResizable(True)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)

        self.layout = QHBoxLayout()
        self.layout.setSpacing(30)

        scroll = QWidget()
        scroll.setLayout(self.layout)
        self.setWidget(scroll)

    def eventFilter(self, obj, event):
        if obj == self.widget() and event.type() == QEvent.Resize:
            self.widget().resize(self.calcViewportSize())
            return True

        return super(MyScrollArea, self).eventFilter(obj, event)

    def calcViewportSize(self):
        height = self.viewport().height()

        layoutMargins = self.layout.contentsMargins()
        heightForCalc = height - layoutMargins.top() - layoutMargins.bottom()

        width = self.calcWidthForHeight(heightForCalc)
        return QSize(width, height)

    def calcWidthForHeight(self, height):
        sum = 0
        for wgt in range(self.layout.count()):
            sum += self.layout.itemAt(wgt).widget().widthForHeight(height)

        if self.layout.count() > 1:
            sum += self.layout.spacing() * (self.layout.count()-1)
        return sum

class App(QMainWindow):
    def __init__(self):
        super(App, self).__init__()

        self.scroll = MyScrollArea()
        self.setCentralWidget(self.scroll)

        for col in range(3):
            self.scroll.layout.addWidget(ImageWidget())

【讨论】:

    猜你喜欢
    • 2013-01-01
    • 1970-01-01
    • 2022-11-12
    • 1970-01-01
    • 1970-01-01
    • 2015-03-04
    • 2016-12-15
    • 1970-01-01
    • 2018-01-19
    相关资源
    最近更新 更多