Unsolved QScrollArea as a transparent wrapper
-
Hi.
I've already asked this question on Stackoverflow, but this place is probably better tailored to the problem.
I want QScrollArea to act as a transparent wrapper for a window's contents, but haven't had a lot of success in doing so.
First, some boring code as a MWE.
qscrollarea_test.pro
:HEADERS = qscrollarea_test.hpp SOURCES = qscrollarea_test.cpp main.cpp CONFIG += qt debug QT += gui widgets
main.cpp
:#include "qscrollarea_test.hpp" int main (int argc, char **argv) { QApplication app (argc, argv); scroll_area_test widget (true); widget.show(); return (app.exec ()); }
qscrollarea_test.hpp
:#pragma once #ifndef QSCROLLAREA_TEST_HPP #define QSCROLLAREA_TEST_HPP #include <QVBoxLayout> #include <QScrollArea> #include <QWidget> #include <QPushButton> #include <QApplication> class scroll_area_test : public QWidget { Q_OBJECT public: scroll_area_test (bool want_scrollarea = true); void scroll_area_test_normal (); void scroll_area_test_qscrollarea (); }; #endif /* !defined (QSCROLLAREA_TEST_HPP) */
qscrollarea_test.cpp
:#include <iostream> #include <QDebug> #include "qscrollarea_test.hpp" scroll_area_test::scroll_area_test (bool want_scrollarea) { if (want_scrollarea) { scroll_area_test::scroll_area_test_qscrollarea (); } else { scroll_area_test::scroll_area_test_normal (); } } void scroll_area_test::scroll_area_test_normal () { QVBoxLayout *top_layout = new QVBoxLayout (this); QWidget *contents = new QWidget (this); top_layout->addWidget (contents); QVBoxLayout* inner_layout = new QVBoxLayout (contents); for (size_t i = 0; i < 25; ++i) { QPushButton *tmp_button = new QPushButton (this); inner_layout->addWidget (tmp_button); } qDebug () << "total size (hint): " << this->size () << " - " << this->sizeHint (); qDebug () << "layout size hint: " << top_layout->sizeHint (); qDebug () << "contents size (hint): " << contents->size () << " - " << contents->sizeHint (); } void scroll_area_test::scroll_area_test_qscrollarea () { QVBoxLayout *top_layout = new QVBoxLayout (); QScrollArea *scrolling_area = new QScrollArea (); scrolling_area->setWidgetResizable (true); scrolling_area->setFocusPolicy (Qt::NoFocus); top_layout->addWidget (scrolling_area); QWidget *contents = new QWidget (); contents->setSizePolicy (QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); QVBoxLayout* inner_layout = new QVBoxLayout (); inner_layout->setSizeConstraint (QLayout::SetMinimumSize); for (size_t i = 0; i < 25; ++i) { QPushButton *tmp_button = new QPushButton (); inner_layout->addWidget (tmp_button); } contents->setLayout (inner_layout); this->setLayout (top_layout); scrolling_area->setWidget (contents); top_layout->addWidget (scrolling_area); //this->resize (contents->size ()); qDebug () << "total size (hint): " << this->size () << " - " << this->sizeHint (); qDebug () << "layout size hint: " << top_layout->sizeHint (); qDebug () << "contents size (hint): " << contents->size () << " - " << contents->sizeHint (); qDebug () << "scroll area size (hint): " << scrolling_area->size () << " - " << scrolling_area->sizeHint (); }
Without QScrollArea as a wrapper, the window is shown as such (and this is actually my expected result):
Also, the size debug output sounds (more or less) reasonable:
total size (hint): QSize(640, 480) - QSize(84, 809) layout size hint: QSize(84, 809) contents size (hint): QSize(100, 30) - QSize(62, 787)
Note how the window's
size()
looks odd (and does not reflect its actual size), butsizeHint()
is saner. The same goes forcontents->size()
andcontents->sizeHint()
.Now, changing to the code where a QScrollArea is used, leads to this "output":
total size (hint): QSize(640, 480) - QSize(92, 430) layout size hint: QSize(92, 430) contents size (hint): QSize(638, 787) - QSize(62, 787) scroll area size (hint): QSize(640, 480) - QSize(64, 408)
This isn't quite what I expected - but why? Given that I have enough screen space, I would have expected the window to be sized and look just like when not using QScrollArea as a wrapper, but this is obviously not true.
I am fully aware that this may contradict using QScrollArea as a wrapper in the first place. The general idea, though, is to keep the window correctly sized even on displays with lower resolutions. Its size should roughly be
min (<contents size (+ margins etc.)>, <usable display size - window position>)
. In the first case, no scrolling will be necessary at all, so users should not even notice the QScrollArea wrapper, while in the second case, the window will use the available size without overflowing display borders, with scrollable contents due to the QScrollArea wrapper. -
Hi and welcome to devnet,
Which version of Qt are you using ?
-
Of course, I should have mentioned that. This is with Qt 5.9.2, but the code can be equally compiled against 4.8.7 and the behavior is exactly the same. I generally test with both. Legacy platforms and such.
-
Try adding
top_layout->setContentsMargins(0, 0, 0 , 0);
I think it should give you the result you are looking for.
-
@SGaist said in QScrollArea as a transparent wrapper:
top_layout->setContentsMargins(0, 0, 0 , 0);
Uhm, no, that doesn't change anything.
It actually can't, either, since it's not the top layout resizing the contents (well, not in this case at least, since it doesn't specify a maximum size), but
QScrollArea
itself.According to the source code,
QScrollArea
overridessizeHint()
and does some bounding calculations - but I can't explain the behavior based upon this, since the bounding merely makes sure that a minimum size is respected. It doesn't constrain to a maximum size.The
sizeHint
returned byQScrollArea
should be at least as big as either thesizeHint
or thesize
returned by its internal widget, but that's clearly not the case, judging from my debug output. It's smaller than that. Well, at least the height is. -
@Ionic
I am only a newbie, not an expert like @SGaist . I have started usingQScrollLayout
s, and find them most confusing compared to what I am used from elsewhere. I probably don't have a clue what I'm talking about here but can't resist sticking my nose in, so having said that, here's what I would look at if I were you (if they're all left-field, don't be surprised!):-
Try altering the order of your adds, e.g. add the scroll area to the layout before you set its content. Any difference?
-
You print out all the sizes & size hints right at the end. Try printing various ones as you go along, before & after adding/setting things. Try to identify exactly which lines of code alter which ones.
-
Try with a simpler content, such as just a single
QWidget::setFixedSize()
. (Probably irrelevant, but just in case.) -
Is the discussion in https://stackoverflow.com/questions/14980620/qt-layout-resize-to-minimum-after-widget-size-changes useful, even if it's not the same as your problem? Need to use
resize()
on the right layout/widget?
-
-
@Ionic there were two
top_layout
in your example IIRC, which one did you modify ?