How do I resize the contents of a QScrollArea as more widgets are placed inside
Asked Answered
E

4

36

I have a QScrollArea Widget, which starts empty;

empty QScrollArea

It has a vertical layout, with a QGridLayout, and a vertical spacer to keep it at the top, and prevent it from stretching over the whole scroll area;

QScrollArea in Qt Designer

Elsewhere in the program, there is a QTextEdit, which when changed, has its contents scanned for "species" elements, and then they are added to the QGridLayout. Any species elements which have been removed are removed too. This bit works;

QScrollArea with strange scroll bar

I have turned the vertical scrollbar on all the time, so that when it appears it does not sit on top of the other stuff in there. Note that the scroll bar is larger than the scroll box already though, despite not needing to be.

This is the problem. The scroll area seems to be preset, and I cannot change it. If I add more rows to the QGridLayout, the scroll area doesn't increase in size.

Instead, it stays the same size, and squeezes the QGridLayout, making it look ugly (at first);

Squashed QGridLayout

And then after adding even more it becomes unusable;

unusable line edits

Note that again, the scroll bar is still the same size as in previous images. The first two images are from Qt Designer, the subsequent 3 are from the program running.

If I resize the window so that the QScrollArea grows, then I see this:

dumb too small layout

Indicating that there's some layout inside the scroll area that is not resizing properly.

What do I need to do to make the scrollable area of the widget resize dynamically as I add and remove from the QGridLayout?

Enough answered 8/10, 2012 at 12:6 Comment(0)
K
34

The documentation provide an answer :

widgetResizable : bool
This property holds whether the scroll area should resize the view widget. If this property is set to false (the default), the scroll area honors the size of its widget.

Set it to true.

Kelsy answered 8/10, 2012 at 12:26 Comment(5)
Okay, so i have done this, and it almost works fine, but now the scroll area is resized to the same size as the child widget, but the scroll bar is over the top of the children; i.imgur.com/5DYBW.jpg is there a way to prevent this other than changing the width of the widget and adding on the width of the scroll bar?Enough
what about setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ). Didnt try but i will expect it to show the complete width of the widget.Kelsy
if it doesnt work then you need to manually compute the minimum width for your widget.Kelsy
Always off isn't an option, since sometimes there can be enough in there to require the scroll bar - hence using the scroll area. I just set it to always on. It doesn't look too bad.Enough
@Kelsy That option doesn't work. It just hide the horizontal scroll bar and let you traverse through the items using arrow keys.Bone
M
48

If you're coming here from Google and not having luck with the accepted answer, that's because you're missing the other secret invocation: QScrollArea::setWidget. You must create and explicitly identify a single widget which is to be scrolled. It's not enough to just add the item as a child! Adding multiple items directly to the ScrollArea will also not work.

This script demonstrates a simple working example of QScrollArea:

from PySide.QtGui import *

app = QApplication([])

scroll = QScrollArea()
scroll.setWidgetResizable(True) # CRITICAL

inner = QFrame(scroll)
inner.setLayout(QVBoxLayout())

scroll.setWidget(inner) # CRITICAL

for i in range(40):
    b = QPushButton(inner)
    b.setText(str(i))
    inner.layout().addWidget(b)

scroll.show()
app.exec_()
Monger answered 19/10, 2016 at 18:49 Comment(1)
This should be the accepted answer. QScrollArea should be implemented like this. Although I would like to point out that it's not necessary to pass a parent widget when creating the QFrame or the QPushButton, as they are automatically reparented when setWidget and addWidget are called (at least in C++, I don't know if it's different in Python).Cracker
K
34

The documentation provide an answer :

widgetResizable : bool
This property holds whether the scroll area should resize the view widget. If this property is set to false (the default), the scroll area honors the size of its widget.

Set it to true.

Kelsy answered 8/10, 2012 at 12:26 Comment(5)
Okay, so i have done this, and it almost works fine, but now the scroll area is resized to the same size as the child widget, but the scroll bar is over the top of the children; i.imgur.com/5DYBW.jpg is there a way to prevent this other than changing the width of the widget and adding on the width of the scroll bar?Enough
what about setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ). Didnt try but i will expect it to show the complete width of the widget.Kelsy
if it doesnt work then you need to manually compute the minimum width for your widget.Kelsy
Always off isn't an option, since sometimes there can be enough in there to require the scroll bar - hence using the scroll area. I just set it to always on. It doesn't look too bad.Enough
@Kelsy That option doesn't work. It just hide the horizontal scroll bar and let you traverse through the items using arrow keys.Bone
V
0

Why don't you use a QListView for your rows, it will manage all the issues for you? Just make sure that after you add it you click on the Class (top right window of designer) and assign a layout or it wont expand properly.

I use a QLIstWidget inside a QScrollArea to make a scrollable image list

Try this for adding other objects to the list, this is how I add an image to the list.

QImage& qim = myclass.getQTImage();

QImage iconImage = copyImageToSquareRegion(qim, ui->display_image->palette().color(QWidget::backgroundRole()));

QListWidgetItem* pItem = new QListWidgetItem(QIcon(QPixmap::fromImage(iconImage)), NULL);

pItem->setData(Qt::UserRole, "thumb" + QString::number(ui->ImageThumbList->count()));  // probably not necessary for you

QString strTooltip = "a tooltip"

pItem->setToolTip(strTooltip);

ui->ImageThumbList->addItem(pItem);
Valletta answered 30/8, 2016 at 10:34 Comment(0)
C
0

Update on Artfunkel's answer:

Here's a PySide6 demo that uses a "Populate" button to run the for loop adding items to the scroll area. Each button will also delete itself when clicked.

from PySide6.QtWidgets import *

app = QApplication([])

scroll = QScrollArea()
scroll.setWidgetResizable(True) # CRITICAL

inner = QFrame(scroll)
inner.setLayout(QVBoxLayout())

scroll.setWidget(inner)  # CRITICAL


def on_remove_widget(button):
    button.deleteLater()


def populate():
    for i in range(40):
        b = QPushButton(inner)
        b.setText(str(i))
        b.clicked.connect(b.deleteLater)
        inner.layout().addWidget(b)

b = QPushButton(inner)
b.setText("Populate")
b.clicked.connect(populate)
inner.layout().addWidget(b)

scroll.show()
app.exec()
Carabin answered 4/9, 2021 at 18:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.