Bismarck Gomes
Bismarck Gomes

Reputation: 25

How to update QScrollArea size when contents change size?

My QScrollArea does not update its size dinamically when I add a new QPushButton inside it. I want to add/remove some QPushButton inside a QScrollArea dinamically, but my QScrollArea does not update its size. I want my QScrollArea has always a minimum possible size.

With this code:

import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QMainWindow,
    QPushButton, QScrollArea, QVBoxLayout)


class MyWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        scroll = QScrollArea(self)
        scroll.setWidgetResizable(True)

        # Contents
        w = QWidget()
        lay = QVBoxLayout(w)
        lay.addWidget(QPushButton('Button'))
        scroll.setWidget(w)

        # Controls
        button_add = QPushButton('Add')
        button_add.clicked.connect(lambda: lay.addWidget(QPushButton('Button')))
        button_del = QPushButton('Del')
        button_del.clicked.connect(lambda: lay.takeAt(0).widget().deleteLater() if lay.count()>0 else None)

        # Main Layout
        vlay = QVBoxLayout()
        vlay.addWidget(scroll)
        vlay.addStretch()
        vlay.addWidget(button_add)
        vlay.addWidget(button_del)

        w = QWidget(self)
        w.setLayout(vlay)
        self.setCentralWidget(w)
        self.resize(200, 300)

if __name__ == '__main__':
    
    app = QApplication([])
    mainWin = MyWindow()
    mainWin.show()
    sys.exit(app.exec_())

I got this view (left) when started and (right) when I add some QPushButtons:

First view After add some QPushButtons

So I have two questions:

  1. How I start my application with QScrollArea with a minimum size?
  2. How QScrollAre can update its size dinamically?

My desirable view is:

Desirable first view Desirable action

And of course, when I add a lot of QPushButtons, a Scrollbar appears.

Upvotes: 0

Views: 1433

Answers (1)

mugiseyebrows
mugiseyebrows

Reputation: 4818

You can set maximum height for scrollarea equals to widget contents (layout size + margins). This should be done after layout completes it's calculations (for example asyncronously with zero-timer).

import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton, QScrollArea, QVBoxLayout

class MyWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        scroll = QScrollArea(self)
        scroll.setWidgetResizable(True)

        # Contents
        w = QWidget()
        lay = QVBoxLayout(w)
        #lay.addWidget(QPushButton('Button'))
        scroll.setWidget(w)

        def updateSize():
            left, top, right, bottom = lay.getContentsMargins()
            hint = lay.sizeHint()
            scroll.setMaximumHeight(hint.height() + top + bottom + 1)

        def addButton():
            lay.addWidget(QPushButton('Button'))
            QTimer.singleShot(0, updateSize)

        def removeButton():
            if lay.count() > 0:
                lay.takeAt(0).widget().deleteLater()
            QTimer.singleShot(0, updateSize)

        addButton()

        # Controls
        button_add = QPushButton('Add')
        button_add.clicked.connect(addButton)
        button_del = QPushButton('Del')
        button_del.clicked.connect(removeButton)

        # Main Layout
        vlay = QVBoxLayout()
        vlay.addWidget(scroll)
        vlay.addStretch(1)
        vlay.addWidget(button_add)
        vlay.addWidget(button_del)
        vlay.setStretch(0,1000)

        w = QWidget(self)
        w.setLayout(vlay)
        self.setCentralWidget(w)
        self.resize(200, 300)

        

if __name__ == '__main__':
    
    app = QApplication([])
    mainWin = MyWindow()
    mainWin.show()
    app.exec()

Upvotes: 2

Related Questions