QGroupBox, QScrollArea, QFormLayout
-
//Create container for errors panel mpobjErrors = new QWidget(this); mpvbloErrors = new QVBoxLayout; mpobjErrors->setLayout(mpvbloErrors); mpsaErrors = new QScrollArea; int intFixedHeight(fontMetrics().height() * MainWindow::mscuintErrorRows); mpsaErrors->setFixedHeight(intFixedHeight); //Create a widget that contains the scrollable errors form QWidget* pobjFormErrorsContr(new QWidget); mpsaErrors->setWidget(pobjFormErrorsContr); mpfrmloErrors = new QFormLayout; pobjFormErrorsContr->setLayout(mpfrmloErrors); mpvbloErrors->addWidget(pobjFormErrorsContr, 1, Qt::AlignHCenter); //Create a push button for acknowledging and clearing errors panel QPushButton* pbtnAck(new QPushButton(tr("&Clear Errors"))); mpvbloErrors->addWidget(pbtnAck, 1, Qt::AlignHCenter); QObject::connect(pbtnAck, &QPushButton::clicked, [this, pbtnAck] { mpobjErrors->setVisible(false); } ); //Default to hidden until errors are available to display mpobjErrors->setVisible(false);
From the above the structure should be:
QWidget
-- QVBoxLayout
----- QScrollArea, with fixed height to show 3 rows
------- QWidget
---------- QFormLayout
------ QPushButtonThe image of this output:
The form layout needs to be the full width of the QVBoxLayout and should be just 3 rows as the image shows it isn't. -
//Create container for errors panel mpobjErrors = new QWidget(this); mpvbloErrors = new QVBoxLayout; mpobjErrors->setLayout(mpvbloErrors); mpsaErrors = new QScrollArea; int intFixedHeight(fontMetrics().height() * MainWindow::mscuintErrorRows); mpsaErrors->setFixedHeight(intFixedHeight); //Create a widget that contains the scrollable errors form QWidget* pobjFormErrorsContr(new QWidget); mpsaErrors->setWidget(pobjFormErrorsContr); mpfrmloErrors = new QFormLayout; pobjFormErrorsContr->setLayout(mpfrmloErrors); mpvbloErrors->addWidget(pobjFormErrorsContr, 1, Qt::AlignHCenter); //Create a push button for acknowledging and clearing errors panel QPushButton* pbtnAck(new QPushButton(tr("&Clear Errors"))); mpvbloErrors->addWidget(pbtnAck, 1, Qt::AlignHCenter); QObject::connect(pbtnAck, &QPushButton::clicked, [this, pbtnAck] { mpobjErrors->setVisible(false); } ); //Default to hidden until errors are available to display mpobjErrors->setVisible(false);
From the above the structure should be:
QWidget
-- QVBoxLayout
----- QScrollArea, with fixed height to show 3 rows
------- QWidget
---------- QFormLayout
------ QPushButtonThe image of this output:
The form layout needs to be the full width of the QVBoxLayout and should be just 3 rows as the image shows it isn't.@SPlatten
Your "diagram" now looks like @SGaist's, so I am happy.I have played with scroll areas all morning for you, and I have had enough! Play with this:
#include "widget.h" #include <QApplication> #include <QScrollArea> #include <QBoxLayout> #include <QPushButton> int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; QVBoxLayout layout0; w.setLayout(&layout0); QScrollArea scrollArea(&w); layout0.addWidget(&scrollArea); QWidget container(&scrollArea); scrollArea.setWidget(&container); QVBoxLayout layout; container.setLayout(&layout); for (int i = 0; i < 10; i++) layout.addWidget(new QPushButton); scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); scrollArea.setFixedHeight(100); scrollArea.setWidgetResizable(true); w.show(); return a.exec(); }
Note the
scrollArea.setWidgetResizable(true);
. Without that I don't actually see any buttons. Is that what you are missing?Don't ask me about Qt layouts/size policies etc., I have always found them about as intuitive as a duck-billed platypus, I play till I get what I want.
-
@SPlatten
Your "diagram" now looks like @SGaist's, so I am happy.I have played with scroll areas all morning for you, and I have had enough! Play with this:
#include "widget.h" #include <QApplication> #include <QScrollArea> #include <QBoxLayout> #include <QPushButton> int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; QVBoxLayout layout0; w.setLayout(&layout0); QScrollArea scrollArea(&w); layout0.addWidget(&scrollArea); QWidget container(&scrollArea); scrollArea.setWidget(&container); QVBoxLayout layout; container.setLayout(&layout); for (int i = 0; i < 10; i++) layout.addWidget(new QPushButton); scrollArea.setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); scrollArea.setFixedHeight(100); scrollArea.setWidgetResizable(true); w.show(); return a.exec(); }
Note the
scrollArea.setWidgetResizable(true);
. Without that I don't actually see any buttons. Is that what you are missing?Don't ask me about Qt layouts/size policies etc., I have always found them about as intuitive as a duck-billed platypus, I play till I get what I want.
@JonB , thank you, however its missing the QFormLayout which is required, see my post before you post.
Edited code, still doesn't work.
//Create container for errors panel mpobjErrors = new QWidget(this); mpvbloErrors = new QVBoxLayout; mpobjErrors->setLayout(mpvbloErrors); //Create scroll area mpsaErrors = new QScrollArea(mpobjErrors); //Create a widget that contains the scrollable errors form QWidget* pobjFormErrorsContr(new QWidget(mpsaErrors)); //Set-up scroll area mpsaErrors->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); int intFixedHeight(fontMetrics().height() * MainWindow::mscuintErrorRows); mpsaErrors->setWidget(pobjFormErrorsContr); mpsaErrors->setFixedHeight(intFixedHeight); mpsaErrors->setWidgetResizable(true); mpfrmloErrors = new QFormLayout(pobjFormErrorsContr); mpvbloErrors->addWidget(pobjFormErrorsContr, 1, Qt::AlignHCenter);//addLayout(mpfrmloErrors, 1);// //Create a push button for acknowledging and clearing errors panel QPushButton* pbtnAck(new QPushButton(tr("&Clear Errors"))); mpvbloErrors->addWidget(pbtnAck, 1, Qt::AlignHCenter); QObject::connect(pbtnAck, &QPushButton::clicked, [this, pbtnAck] { mpobjErrors->setVisible(false); } );
-
@JonB , thank you, however its missing the QFormLayout which is required, see my post before you post.
Edited code, still doesn't work.
//Create container for errors panel mpobjErrors = new QWidget(this); mpvbloErrors = new QVBoxLayout; mpobjErrors->setLayout(mpvbloErrors); //Create scroll area mpsaErrors = new QScrollArea(mpobjErrors); //Create a widget that contains the scrollable errors form QWidget* pobjFormErrorsContr(new QWidget(mpsaErrors)); //Set-up scroll area mpsaErrors->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); int intFixedHeight(fontMetrics().height() * MainWindow::mscuintErrorRows); mpsaErrors->setWidget(pobjFormErrorsContr); mpsaErrors->setFixedHeight(intFixedHeight); mpsaErrors->setWidgetResizable(true); mpfrmloErrors = new QFormLayout(pobjFormErrorsContr); mpvbloErrors->addWidget(pobjFormErrorsContr, 1, Qt::AlignHCenter);//addLayout(mpfrmloErrors, 1);// //Create a push button for acknowledging and clearing errors panel QPushButton* pbtnAck(new QPushButton(tr("&Clear Errors"))); mpvbloErrors->addWidget(pbtnAck, 1, Qt::AlignHCenter); QObject::connect(pbtnAck, &QPushButton::clicked, [this, pbtnAck] { mpobjErrors->setVisible(false); } );
@SPlatten
I found I had to put inscrollArea.setWidgetResizable(true);
to make it respect the height. You are saying it still does not. I assume you have verified the value forfontMetrics().height() * MainWindow::mscuintErrorRows
. You can put aQFormLayout
into my sample code in place ofQVBoxLayout layout;
, I can't see why that would behave any differently vertically. -
@SPlatten
I found I had to put inscrollArea.setWidgetResizable(true);
to make it respect the height. You are saying it still does not. I assume you have verified the value forfontMetrics().height() * MainWindow::mscuintErrorRows
. You can put aQFormLayout
into my sample code in place ofQVBoxLayout layout;
, I can't see why that would behave any differently vertically.@JonB , just to verify:
int intFontHeight(fontMetrics().height()), intFixedHeight(intFontHeight * MainWindow::mscuintErrorRows);
intFontHeight is 13
MainWindow::mscuintErrorRows is 3
intFixedHeight is 39Clearly setFixedHeight on mpsaErrors is not having the desired effect.
-
Assuming margins at set to zero:
pFormLayout=new QFormLayout; pFormLayout->setContentsMargins(0,0,0,0); pFormLayout->setSpacing(0); pFormLayout->setHorizontalSpacing(10);
In the show event you can calculate 3 rows height like that:
void Widget::showEvent(QShowEvent* event) { QWidget::showEvent(event); QLayoutItem * item=pFormLayout-> itemAt(0); int h=item->geometry().height(); pScrollArea->setFixedHeight(h*3+2); }
-
Assuming margins at set to zero:
pFormLayout=new QFormLayout; pFormLayout->setContentsMargins(0,0,0,0); pFormLayout->setSpacing(0); pFormLayout->setHorizontalSpacing(10);
In the show event you can calculate 3 rows height like that:
void Widget::showEvent(QShowEvent* event) { QWidget::showEvent(event); QLayoutItem * item=pFormLayout-> itemAt(0); int h=item->geometry().height(); pScrollArea->setFixedHeight(h*3+2); }
-
@SPlatten
I assumed that the formLayout is not empty, unless the app will crash.If you create one row in the constructor and want an empty form on show up, you can do:
QWidget::showEvent(event);
QLayoutItem* item=pFormLayout-> itemAt(0);
int h=item->geometry().height();
pScrollArea->setFixedHeight(h3+3);
pFormLayout->removeRow(0);[WARNING]
Multi showEvent can occur, you need to add some flag to prevent that:void Widget::showEvent(QShowEvent* event) { static bool firstShow=true; QWidget::showEvent(event); if(firstShow) { QLayoutItem * item=pFormLayout-> itemAt(0); int h=item->geometry().height(); pScrollArea->setFixedHeight(h*3+3); pFormLayout->removeRow(0); firstShow=false; } }
-
@SPlatten
I assumed that the formLayout is not empty, unless the app will crash.If you create one row in the constructor and want an empty form on show up, you can do:
QWidget::showEvent(event);
QLayoutItem* item=pFormLayout-> itemAt(0);
int h=item->geometry().height();
pScrollArea->setFixedHeight(h3+3);
pFormLayout->removeRow(0);[WARNING]
Multi showEvent can occur, you need to add some flag to prevent that:void Widget::showEvent(QShowEvent* event) { static bool firstShow=true; QWidget::showEvent(event); if(firstShow) { QLayoutItem * item=pFormLayout-> itemAt(0); int h=item->geometry().height(); pScrollArea->setFixedHeight(h*3+3); pFormLayout->removeRow(0); firstShow=false; } }
-
@SPlatten Q
When you use font size to set item height, it is better to use some reference chars. That is what I do:
Rect bounding_rect = QFontMetrics( font ).tightBoundingRect( QString( "MWLPGHXYZmwlpghxyz" ) );
use bounding_rect.height();
And item height has to be a bit bigger than this height. -
@SPlatten
I assumed that the formLayout is not empty, unless the app will crash.If you create one row in the constructor and want an empty form on show up, you can do:
QWidget::showEvent(event);
QLayoutItem* item=pFormLayout-> itemAt(0);
int h=item->geometry().height();
pScrollArea->setFixedHeight(h3+3);
pFormLayout->removeRow(0);[WARNING]
Multi showEvent can occur, you need to add some flag to prevent that:void Widget::showEvent(QShowEvent* event) { static bool firstShow=true; QWidget::showEvent(event); if(firstShow) { QLayoutItem * item=pFormLayout-> itemAt(0); int h=item->geometry().height(); pScrollArea->setFixedHeight(h*3+3); pFormLayout->removeRow(0); firstShow=false; } }
-
@mpergand , how is the showEvent connected and to which widget? I assume this is the MainWindow instance that this code is relevant to?
-
@SPlatten
Here Widget is the top level object, but it does not have to be.how is the showEvent connected
It's connected to nothing, it's a virtual method of QWidget.
-
Widget.h
#include <QWidget> class QScrollArea; class QFormLayout; class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = nullptr); private: void showEvent(QShowEvent* event); QScrollArea* pScrollArea; QFormLayout* pFormLayout; };
Widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent) { auto vLayout=new QVBoxLayout; setLayout(vLayout); pScrollArea=new QScrollArea; vLayout->addWidget(pScrollArea); auto sWidget=new QWidget; pScrollArea->setWidget(sWidget); pScrollArea->setWidgetResizable(true); pFormLayout=new QFormLayout; pFormLayout->setContentsMargins(0,0,0,1); pFormLayout->setSpacing(0); pFormLayout->setHorizontalSpacing(10); sWidget->setLayout(pFormLayout); // add one row for calculation in showEvent pFormLayout->addRow("", new QLabel()); vLayout->addWidget(new QPushButton("Clear")); }
-
Widget.h
#include <QWidget> class QScrollArea; class QFormLayout; class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = nullptr); private: void showEvent(QShowEvent* event); QScrollArea* pScrollArea; QFormLayout* pFormLayout; };
Widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent) { auto vLayout=new QVBoxLayout; setLayout(vLayout); pScrollArea=new QScrollArea; vLayout->addWidget(pScrollArea); auto sWidget=new QWidget; pScrollArea->setWidget(sWidget); pScrollArea->setWidgetResizable(true); pFormLayout=new QFormLayout; pFormLayout->setContentsMargins(0,0,0,1); pFormLayout->setSpacing(0); pFormLayout->setHorizontalSpacing(10); sWidget->setLayout(pFormLayout); // add one row for calculation in showEvent pFormLayout->addRow("", new QLabel()); vLayout->addWidget(new QPushButton("Clear")); }
-
Widget.h
#include <QWidget> class QScrollArea; class QFormLayout; class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = nullptr); private: void showEvent(QShowEvent* event); QScrollArea* pScrollArea; QFormLayout* pFormLayout; };
Widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent) { auto vLayout=new QVBoxLayout; setLayout(vLayout); pScrollArea=new QScrollArea; vLayout->addWidget(pScrollArea); auto sWidget=new QWidget; pScrollArea->setWidget(sWidget); pScrollArea->setWidgetResizable(true); pFormLayout=new QFormLayout; pFormLayout->setContentsMargins(0,0,0,1); pFormLayout->setSpacing(0); pFormLayout->setHorizontalSpacing(10); sWidget->setLayout(pFormLayout); // add one row for calculation in showEvent pFormLayout->addRow("", new QLabel()); vLayout->addWidget(new QPushButton("Clear")); }
@mpergand , Thank you, works a treat...now to port to my main application:
ui->setupUi(this); QVBoxLayout* pvbxLayout(new QVBoxLayout); mpsa = new QScrollArea; pvbxLayout->addWidget(mpsa); QWidget* pContainer(new QWidget); mpsa->setWidget(pContainer); int intFixedHeight(fontMetrics().height() * 3); mpsa->setFixedHeight(intFixedHeight); mpsa->setWidgetResizable(true); mpFormLayout = new QFormLayout; mpFormLayout->setContentsMargins(0,0,0,0); mpFormLayout->setSpacing(0); mpFormLayout->setHorizontalSpacing(10); pContainer->setLayout(mpFormLayout); for( int i=1; i<=10; i++ ) { mpFormLayout->addRow(QString::number(i), new QLabel(QString("Hello World: %1").arg(i))); } pvbxLayout->addWidget(new QPushButton("HELLO")); ui->centralwidget->setLayout(pvbxLayout);