强制QWidget占用QScrollArea中的所有可用高度

我正在尝试用QHBoxLayout创建QScrollArea,里面包含一行od QWidgets . 我希望小部件占据所有可用高度并相应地调整它的大小 .

我的小部件是QLabel,里面显示缩放尺寸的QPixmaps . 在我的项目中,这些是照片 . 我实现了heightForWidth和sizeHint方法来实现良好的缩放 . 我仍然无法达到正确的高度 .

默认情况下,窗口小部件会缩放,因此scrollarea不需要滚动条 .

不需要滚动条

我使用了scroll小部件的sizePolicy,但只能强制小部件的大小等于sizeHint返回的最大大小 . 然后水平滚动激活 .

窗口小部件的最大大小超出窗口高度

当我更改sizeHint以返回QSize(width(),heightForWidth(width()))时,我发现width()最多返回640像素,而且不会更多 . 我无法弄清楚640这个限制的来源 .

有没有办法在我的布局中删除640px宽度限制或有没有办法强制小部件扩展到窗口高度?

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

回答(1)

2 years ago

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

Only horizontal scroll active. Widgets adjusted.

无论如何,这是代码:

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