Expanding multiple ListViews within QScrollArea.
-
I am trying to show several vertically stacked lists within a single QScrollArea. I would like the lists to expand so that they show all of their items (and don't have their own individual scrollbars), and have the QScrollArea scrollbar scroll the entire set.
I've been trying the following:
auto listsContainer = new QScrollArea(this); auto lcLayout = new QVBoxLayout(); for (auto& qa : _qaLists) { // Header for the list auto l = new QLabel(); l->setText(qa.header.c_str()); l->setStyleSheet("QLabel { font-size: 18px }"); lcLayout->addWidget(l); qa.listView->setModel(qa.model); qa.listView->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); lcLayout->addWidget(qa.listView); } listsContainer->setLayout(lcLayout);
However this gives lists that have their header, but then the list itself only shows a couple of items, and has its own scroll bar. Is there any way to tell the lists to expand so that they show all items (which would push lists/headers off the bottom of the container, and would then be scrolled vertically with the QScrollArea scrollbar). See the attached picture for what I'm seeing, vs. how I want it to work.
-
Hi and welcome to devnet,
One thing you could do is subclcass QListView, reimplement sizeHint and use contentsRect + contentMargins to return the adequate size.
-
Thank you for the warm welcome!
I will give that a try (I am pretty new to QT). To clarify- are "contentSize" and "contentMargins" automatically calculated? Or would I need to iterate over the items and calculate those myself?
-
Sorry, there was a typo and the links were missing. Both are functions from QWidget.
@Pl45m4's suggestion is good however it assumes that all items have the same height. Depending on what you put it there it might not be true.
-
@MarkLT1 said in Expanding multiple ListViews within QScrollArea.:
Or would I need to iterate over the items and calculate those myself?
Hi, I think your solution is right here
@Pl45m4 said in Expanding multiple ListViews within QScrollArea.:
@MarkLT1 said in Expanding multiple ListViews within QScrollArea.:
Or would I need to iterate over the items and calculate those myself?
Hi, I think your solution is right here
Thank you for this. I gave this a try in my derived ListView
sizeHint()
function:QSize MyListViewt::sizeHint() const { int rows = model()->rowCount(); if (rows == 0) return QSize(width(), 0); int height = rows * sizeHintForRow(0); return QSize(width(), height); }
And when I set a breakpoint, I see that my new
sizeHint()
is being called and the height is being set to a reasonable number (for a large list, it is 3000+px). However, the rendered lists are still the same height they were before, with scrollbars. Its like the Layout is squashing everything down to fit in the box provided by the parent ScrollArea dimensionsFor your solution, when
sizeHint()
is called,contentsRect()
returns a tiny box (24px wide by 98px tall), and the ListView ends up being extremely small. I am guessing I'm missing some vital step here. -
Here you have a variant in python that works quite well:
from PySide6 import QtWidgets from PySide6 import QtCore app = QtWidgets.QApplication([]) widget = QtWidgets.QWidget() layout = QtWidgets.QVBoxLayout(widget) for i in range(0, 3): list_widget = QtWidgets.QListWidget() list_widget.addItems([f"Test {item}" for item in range(0, 13)]) list_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) list_widget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) list_widget.setFixedSize(list_widget.sizeHintForColumn(0) + 2 * list_widget.frameWidth(), list_widget.sizeHintForRow(0) * list_widget.count() + 2 * list_widget.frameWidth()) layout.addWidget(list_widget) scroll_area = QtWidgets.QScrollArea() scroll_area.setWidget(widget) scroll_area.show() app.exec()
-
Ok, I think I got it working.
The first issue was with:
listsContainer->setLayout(lcLayout);
Rather than adding a layout, I created a container widget, added the layout to that widget, and did
setWidget()
on the scrollarea.Regarding the
sizeHint
,contentsRect
doesn't seem to return the correct value, and all of the rows will be the same size, so I think it should probably work well. I did need to add the margin values to get rid of the scroll bar. So my working code looks like:// Size hint code for MyListView QSize MyListView::sizeHint() const { QMargins margins = contentsMargins(); int rows = model()->rowCount(); if (rows == 0) return QSize(width(), 0); int height = rows * sizeHintForRow(0); return QSize(width(), height + margins.top() + margins.bottom()); }
And the widget setup code:
auto listsContainer = new QScrollArea(this); listsContainer->setWidgetResizable(true); auto listsWidget = new QWidget(); auto lcLayout = new QVBoxLayout(); for (auto& qa : _qaLists) { auto l = new QLabel(); l->setText(qa.header.c_str()); l->setStyleSheet("QLabel { font-size: 18px }"); lcLayout->addWidget(l); // qa.listView is a MyListView object qa.listView->setModel(qa.model); qa.listView->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); lcLayout->addWidget(qa.listView); } listsWidget->setLayout(lcLayout); listsContainer->setWidget(listsWidget);
-
Here you have a variant in python that works quite well:
from PySide6 import QtWidgets from PySide6 import QtCore app = QtWidgets.QApplication([]) widget = QtWidgets.QWidget() layout = QtWidgets.QVBoxLayout(widget) for i in range(0, 3): list_widget = QtWidgets.QListWidget() list_widget.addItems([f"Test {item}" for item in range(0, 13)]) list_widget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) list_widget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) list_widget.setFixedSize(list_widget.sizeHintForColumn(0) + 2 * list_widget.frameWidth(), list_widget.sizeHintForRow(0) * list_widget.count() + 2 * list_widget.frameWidth()) layout.addWidget(list_widget) scroll_area = QtWidgets.QScrollArea() scroll_area.setWidget(widget) scroll_area.show() app.exec()
@SGaist said in Expanding multiple ListViews within QScrollArea.:
Here you have a variant in python that works quite well:
Thank you for this. I'll dig into what you're doing with sizeHints, and see if I can adapt it to my working code above. Would be nice to not be dependent on all list items being the same size.
-