Can anyone help me fix this layout?
-
I have been working on this dialog for a few days:

The class behind this is derived from QWidget, here is the constructor:
DataSets::DataSets(QWidget* pParent) : QWidget(pParent), mpbtnClose(nullptr), mpfileDlg(nullptr), mpJSEngine(nullptr), mplvRecs(nullptr), mpsiModel(nullptr), mpvbxLayout(nullptr), mpTabs(nullptr) { static const char scszCategory[] = "category", scszDataTypeID[] = "dataTypeID", scszInput[] = "input", scszMax[] = "max", scszMin[] = "min", scszOption[] = "jsonOption", scszTag[] = "tag", scszValidate[] = "validate"; //Now query any datatypes specific to this category QSqlQuery query; query.prepare("SELECT" " d.biPK AS dataTypeID" ",c.vcDescription AS category" ",d.vcTag AS tag" ",IF(d.tiType IS NULL,0,d.tiType) AS input" ",d.jsonOption" " FROM" " categories c" " INNER JOIN" " datatypes d" " ON d.biCategory=c.biPK" " ORDER BY" " c.biPK, tiOrder"); query.exec(); //Any error? QSqlError err(query.lastError()); if ( err.type() != QSqlError::NoError ) { return; } QJsonObject objJSONdataTypes; while( query.next() ) { QSqlRecord record(query.record()); QJsonObject objJSONoption; QString strDataTypeID; for( int f=0; f<record.count(); f++ ) { QSqlField field(record.field(f)); if ( field.name().compare(scszDataTypeID) == 0 ) { strDataTypeID = field.value().toString(); continue; } objJSONoption.insert(field.name(), QJsonValue(field.value().toString())); } if ( strDataTypeID.isEmpty() != true && objJSONoption.isEmpty() != true ) { objJSONdataTypes.insert(strDataTypeID, objJSONoption); } } //Create close push button mpbtnClose = new QPushButton(this); mpbtnClose->setText("&Close"); mstkConnections.push(QObject::connect(mpbtnClose, &QPushButton::clicked, this, &DataSets::closeDialog)); //Create list view mplvRecs = new QListView(this); //Create standard item model for listview mpsiModel = new QStandardItemModel(DataSets::mscuint16Rows, DataSets::mscuint16Cols, this); mplvRecs->setModel(mpsiModel); QFontMetrics fntMetrics(mplvRecs->fontMetrics()); int intHeight(fntMetrics.capHeight()); intHeight += mplvRecs->spacing(); mplvRecs->setFixedHeight(intHeight * DataSets::mscuint16Rows); //Create layout mpvbxLayout = new QVBoxLayout(this); mpvbxLayout->addStretch(1); //Create instance of file dialog mpfileDlg = new QFileDialog(this, "Select RDF"); mpfileDlg->setFileMode(QFileDialog::ExistingFile); mpfileDlg->setLabelText(QFileDialog::Accept, tr("Select")); //Create scripting engine for expression evaluation mpJSEngine = new QJSEngine(); //Create tabs widget mpTabs = new QTabWidget(); //Pointer to tab layout QGridLayout* pgrdLayout = nullptr; int intRow; for( QJsonObject::iterator itJSON=objJSONdataTypes.begin(); itJSON!=objJSONdataTypes.end(); itJSON++ ) { QJsonObject objJSON(itJSON.value().toObject()); QJsonObject::iterator itFound = objJSON.find(scszCategory); QString strCategory(itFound.value().toString()); QString strID = itJSON.key(); if ( mmpTabs.contains(strCategory) != true ) { intRow = -1; //Create scrollable area for tab content QScrollArea* psaTab = new QScrollArea(); //Create a widget for holding the rows in the tab QWidget* pPage = new QWidget(); //Create grid layout for page content pgrdLayout = new QGridLayout(); pgrdLayout->setHorizontalSpacing(DataSets::mscuint16HorzSpacing); pgrdLayout->setVerticalSpacing(DataSets::mscuint16VertSpacing); pgrdLayout->setContentsMargins(DataSets::mscuint16Margins, DataSets::mscuint16Margins, DataSets::mscuint16Margins, DataSets::mscuint16Margins); //Set-up page widget pPage->setLayout(pgrdLayout); pPage->setMinimumSize(0, 0); //Set-up scrolling area psaTab->setFrameStyle(QFrame::NoFrame); psaTab->setWidget(pPage); psaTab->setWidgetResizable(true); psaTab->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); psaTab->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); //Add scrollale area to tab map mmpTabs.insert(strCategory, psaTab); //Add scrollable area to tabs area mpTabs->addTab(psaTab, strCategory); } itFound = objJSON.find(scszInput); int intInput(itFound.value().toString().toInt()); itFound = objJSON.find(scszOption); QString strOption(itFound.value().toString()); itFound = objJSON.find(scszTag); QString strTag(itFound.value().toString()); QWidget* pwdgtInput = nullptr; if ( intInput == DataSets::IO_FILE_SELECTOR ) { //Create widget to group file selection controls QWidget* pFSContr = new QWidget(); //Create grid layout for file selection widget QGridLayout* pgrdFSLayout = new QGridLayout(); pgrdFSLayout->setMargin(0); pgrdFSLayout->setSpacing(0); pFSContr->setMinimumSize(0, 0); pFSContr->setLayout(pgrdFSLayout); //Create line edit to display selection result QLineEdit* plnedFS = new QLineEdit(); QFontMetrics fntMetrics(plnedFS->fontMetrics()); quint16 cuint16FixedWidth(fntMetrics.averageCharWidth() * DataSets::mscuint16PathDisplayLength); //Don't allow user to type in the file, safer to use browser to select! plnedFS->setEnabled(false); //Set-up file selection widget plnedFS->setFixedWidth(cuint16FixedWidth); plnedFS->setMaxLength(_MAX_PATH); plnedFS->setAlignment(Qt::AlignRight); //Create button to invoke QFileDialog QPushButton* pbtnBrowse = new QPushButton(); const QString cstrBrowseBtnTxt("..."); pbtnBrowse->setText(cstrBrowseBtnTxt); pbtnBrowse->setFixedSize( (fntMetrics.averageCharWidth() * cstrBrowseBtnTxt.length()) + DataSets::mscuint16ButtonBorder, fntMetrics.capHeight() + DataSets::mscuint16ButtonBorder); //Connect browse button signal mstkConnections.push(QObject::connect(pbtnBrowse, &QPushButton::clicked, [this, pbtnBrowse, plnedFS]() { this->mpfileDlg->show(); })); //Add widgets to the layout pgrdFSLayout->addWidget(plnedFS, 0, 0, 1, 1, Qt::AlignLeft); pgrdFSLayout->addWidget(pbtnBrowse, 0, 1, 1, Qt::AlignLeft); pwdgtInput = pFSContr; //Connect signal for when file selected mstkConnections.push(QObject::connect(mpfileDlg, &QFileDialog::fileSelected, [this, plnedFS]() { QStringList slstFiles(this->mpfileDlg->selectedFiles()); if ( slstFiles.length() > 0 ) { plnedFS->setText(slstFiles[0]); } })); } else if ( intInput == DataSets::IO_LIST ) { QJsonDocument docJSON(QJsonDocument::fromJson(strOption.toLatin1())); QStringList slstOptions(docJSON.toVariant().toStringList()); QComboBox* pcboInput(new QComboBox()); //Insert blank at front of options slstOptions.insert(0, ""); pcboInput->addItems(slstOptions); pwdgtInput = static_cast<QWidget*>(pcboInput); } else if ( intInput == DataSets::IO_NUMERIC_ENTRY ) { //Add custom widget for numeric entry that will provide //integer or floating spinners QJsonDocument docJSON(QJsonDocument::fromJson(strOption.toLatin1())); QJsonObject objJSON(docJSON.object()); QJsonObject::iterator itMax = objJSON.find(scszMax), itMin = objJSON.find(scszMin), itValidate = objJSON.find(scszValidate); QString strValidate; if ( itValidate != objJSON.end() ) { strValidate = itValidate.value().toString(); } bool blnSpinners((itMax != objJSON.end() && itMin != objJSON.end())); if ( blnSpinners == true ) { QString strMax(itMax.value().toString()), strMin(itMin.value().toString()); bool blnDblSpnr = (strMax.indexOf(".") >= 0) || (strMin.indexOf(".") >= 0); if ( blnDblSpnr == true ) { QDoubleSpinBox* pdblspbxInput(new QDoubleSpinBox()); pdblspbxInput->setMaximum(strMax.toDouble()); pdblspbxInput->setMinimum(strMin.toDouble()); pwdgtInput = static_cast<QWidget*>(pdblspbxInput); if ( strValidate.isEmpty() != true ) { mstkConnections.push(QObject::connect(pdblspbxInput, &QAbstractSpinBox::editingFinished, [this, pdblspbxInput, strValidate]() { QString strCurrent(QString::number(pdblspbxInput->value())); this->parseValidationExpression(strCurrent, strValidate, VT_DOUBLE); })); } } else { QSpinBox* pspbxInput(new QSpinBox()); pspbxInput->setMaximum(strMax.toInt()); pspbxInput->setMinimum(strMin.toInt()); pwdgtInput = static_cast<QWidget*>(pspbxInput); if ( strValidate.isEmpty() != true ) { mstkConnections.push(QObject::connect(pspbxInput, &QAbstractSpinBox::editingFinished, [this, pspbxInput, strValidate]() { QString strCurrent(QString::number(pspbxInput->value())); this->parseValidationExpression(strCurrent, strValidate, VT_INTERGER); })); } } } else { QLineEdit* plnEdit(new QLineEdit()); pwdgtInput = static_cast<QWidget*>(plnEdit); if ( strValidate.isEmpty() != true ) { mstkConnections.push(QObject::connect(plnEdit, &QLineEdit::editingFinished, [this, plnEdit, strValidate]() { QString strCurrent(plnEdit->text()); this->parseValidationExpression(strCurrent, strValidate, VT_STRING); })); } } } else if ( intInput == DataSets::IO_TEXT_ENTRY ) { QTextEdit* ptxtedInput(new QTextEdit()); pwdgtInput = static_cast<QWidget*>(ptxtedInput); } if ( pwdgtInput != nullptr && pgrdLayout != nullptr ) { //Add object to map so it can be recalled by name mmpWidgets.insert(strID, pwdgtInput); //Add label to layout QLabel* plblTag = new QLabel(strTag); pgrdLayout->addWidget(plblTag, ++intRow, 0, 1, 1, Qt::AlignRight); //Add the input control pgrdLayout->addWidget(pwdgtInput, intRow, 1, 1, 1, Qt::AlignLeft); } } //Add tabs to layout mpvbxLayout->addWidget(mpTabs); //Add list view to layout mpvbxLayout->addWidget(mplvRecs); //Add close button to layout mpvbxLayout->addWidget(mpbtnClose); //Set-up window title and geometry Qt::WindowFlags wndFlags; wndFlags |= Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::CustomizeWindowHint | Qt::WindowTitleHint; setWindowFlags(wndFlags); setWindowTitle("Datasets"); QRect rctGeom(pParent->geometry()); rctGeom.setHeight(DataSets::mscuint16Height); rctGeom.setWidth(DataSets::mscuint16Width); setGeometry(rctGeom); }The main layout is QVBoxLayout, I add the QTabWidget to this layout then a QListView which I want to display at least 4 rows in, but it doesn't. Then the QPushButton showing Close.
-
Hi,
@SPlatten said in Can anyone help me fix this layout?:
mpvbxLayout->addStretch(1);
That's the thing that is pushing the content downward since it's the first thing you add to your layout.
Next time, please make it minimal, having your parsing code in there just makes it hard to read.
If I may a recommendation: you should keep your layout creation close to where you actually add widgets/layouts/spacer to them. This will make your code easier to follow as well as maintain.
-
I have been working on this dialog for a few days:

The class behind this is derived from QWidget, here is the constructor:
DataSets::DataSets(QWidget* pParent) : QWidget(pParent), mpbtnClose(nullptr), mpfileDlg(nullptr), mpJSEngine(nullptr), mplvRecs(nullptr), mpsiModel(nullptr), mpvbxLayout(nullptr), mpTabs(nullptr) { static const char scszCategory[] = "category", scszDataTypeID[] = "dataTypeID", scszInput[] = "input", scszMax[] = "max", scszMin[] = "min", scszOption[] = "jsonOption", scszTag[] = "tag", scszValidate[] = "validate"; //Now query any datatypes specific to this category QSqlQuery query; query.prepare("SELECT" " d.biPK AS dataTypeID" ",c.vcDescription AS category" ",d.vcTag AS tag" ",IF(d.tiType IS NULL,0,d.tiType) AS input" ",d.jsonOption" " FROM" " categories c" " INNER JOIN" " datatypes d" " ON d.biCategory=c.biPK" " ORDER BY" " c.biPK, tiOrder"); query.exec(); //Any error? QSqlError err(query.lastError()); if ( err.type() != QSqlError::NoError ) { return; } QJsonObject objJSONdataTypes; while( query.next() ) { QSqlRecord record(query.record()); QJsonObject objJSONoption; QString strDataTypeID; for( int f=0; f<record.count(); f++ ) { QSqlField field(record.field(f)); if ( field.name().compare(scszDataTypeID) == 0 ) { strDataTypeID = field.value().toString(); continue; } objJSONoption.insert(field.name(), QJsonValue(field.value().toString())); } if ( strDataTypeID.isEmpty() != true && objJSONoption.isEmpty() != true ) { objJSONdataTypes.insert(strDataTypeID, objJSONoption); } } //Create close push button mpbtnClose = new QPushButton(this); mpbtnClose->setText("&Close"); mstkConnections.push(QObject::connect(mpbtnClose, &QPushButton::clicked, this, &DataSets::closeDialog)); //Create list view mplvRecs = new QListView(this); //Create standard item model for listview mpsiModel = new QStandardItemModel(DataSets::mscuint16Rows, DataSets::mscuint16Cols, this); mplvRecs->setModel(mpsiModel); QFontMetrics fntMetrics(mplvRecs->fontMetrics()); int intHeight(fntMetrics.capHeight()); intHeight += mplvRecs->spacing(); mplvRecs->setFixedHeight(intHeight * DataSets::mscuint16Rows); //Create layout mpvbxLayout = new QVBoxLayout(this); mpvbxLayout->addStretch(1); //Create instance of file dialog mpfileDlg = new QFileDialog(this, "Select RDF"); mpfileDlg->setFileMode(QFileDialog::ExistingFile); mpfileDlg->setLabelText(QFileDialog::Accept, tr("Select")); //Create scripting engine for expression evaluation mpJSEngine = new QJSEngine(); //Create tabs widget mpTabs = new QTabWidget(); //Pointer to tab layout QGridLayout* pgrdLayout = nullptr; int intRow; for( QJsonObject::iterator itJSON=objJSONdataTypes.begin(); itJSON!=objJSONdataTypes.end(); itJSON++ ) { QJsonObject objJSON(itJSON.value().toObject()); QJsonObject::iterator itFound = objJSON.find(scszCategory); QString strCategory(itFound.value().toString()); QString strID = itJSON.key(); if ( mmpTabs.contains(strCategory) != true ) { intRow = -1; //Create scrollable area for tab content QScrollArea* psaTab = new QScrollArea(); //Create a widget for holding the rows in the tab QWidget* pPage = new QWidget(); //Create grid layout for page content pgrdLayout = new QGridLayout(); pgrdLayout->setHorizontalSpacing(DataSets::mscuint16HorzSpacing); pgrdLayout->setVerticalSpacing(DataSets::mscuint16VertSpacing); pgrdLayout->setContentsMargins(DataSets::mscuint16Margins, DataSets::mscuint16Margins, DataSets::mscuint16Margins, DataSets::mscuint16Margins); //Set-up page widget pPage->setLayout(pgrdLayout); pPage->setMinimumSize(0, 0); //Set-up scrolling area psaTab->setFrameStyle(QFrame::NoFrame); psaTab->setWidget(pPage); psaTab->setWidgetResizable(true); psaTab->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); psaTab->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); //Add scrollale area to tab map mmpTabs.insert(strCategory, psaTab); //Add scrollable area to tabs area mpTabs->addTab(psaTab, strCategory); } itFound = objJSON.find(scszInput); int intInput(itFound.value().toString().toInt()); itFound = objJSON.find(scszOption); QString strOption(itFound.value().toString()); itFound = objJSON.find(scszTag); QString strTag(itFound.value().toString()); QWidget* pwdgtInput = nullptr; if ( intInput == DataSets::IO_FILE_SELECTOR ) { //Create widget to group file selection controls QWidget* pFSContr = new QWidget(); //Create grid layout for file selection widget QGridLayout* pgrdFSLayout = new QGridLayout(); pgrdFSLayout->setMargin(0); pgrdFSLayout->setSpacing(0); pFSContr->setMinimumSize(0, 0); pFSContr->setLayout(pgrdFSLayout); //Create line edit to display selection result QLineEdit* plnedFS = new QLineEdit(); QFontMetrics fntMetrics(plnedFS->fontMetrics()); quint16 cuint16FixedWidth(fntMetrics.averageCharWidth() * DataSets::mscuint16PathDisplayLength); //Don't allow user to type in the file, safer to use browser to select! plnedFS->setEnabled(false); //Set-up file selection widget plnedFS->setFixedWidth(cuint16FixedWidth); plnedFS->setMaxLength(_MAX_PATH); plnedFS->setAlignment(Qt::AlignRight); //Create button to invoke QFileDialog QPushButton* pbtnBrowse = new QPushButton(); const QString cstrBrowseBtnTxt("..."); pbtnBrowse->setText(cstrBrowseBtnTxt); pbtnBrowse->setFixedSize( (fntMetrics.averageCharWidth() * cstrBrowseBtnTxt.length()) + DataSets::mscuint16ButtonBorder, fntMetrics.capHeight() + DataSets::mscuint16ButtonBorder); //Connect browse button signal mstkConnections.push(QObject::connect(pbtnBrowse, &QPushButton::clicked, [this, pbtnBrowse, plnedFS]() { this->mpfileDlg->show(); })); //Add widgets to the layout pgrdFSLayout->addWidget(plnedFS, 0, 0, 1, 1, Qt::AlignLeft); pgrdFSLayout->addWidget(pbtnBrowse, 0, 1, 1, Qt::AlignLeft); pwdgtInput = pFSContr; //Connect signal for when file selected mstkConnections.push(QObject::connect(mpfileDlg, &QFileDialog::fileSelected, [this, plnedFS]() { QStringList slstFiles(this->mpfileDlg->selectedFiles()); if ( slstFiles.length() > 0 ) { plnedFS->setText(slstFiles[0]); } })); } else if ( intInput == DataSets::IO_LIST ) { QJsonDocument docJSON(QJsonDocument::fromJson(strOption.toLatin1())); QStringList slstOptions(docJSON.toVariant().toStringList()); QComboBox* pcboInput(new QComboBox()); //Insert blank at front of options slstOptions.insert(0, ""); pcboInput->addItems(slstOptions); pwdgtInput = static_cast<QWidget*>(pcboInput); } else if ( intInput == DataSets::IO_NUMERIC_ENTRY ) { //Add custom widget for numeric entry that will provide //integer or floating spinners QJsonDocument docJSON(QJsonDocument::fromJson(strOption.toLatin1())); QJsonObject objJSON(docJSON.object()); QJsonObject::iterator itMax = objJSON.find(scszMax), itMin = objJSON.find(scszMin), itValidate = objJSON.find(scszValidate); QString strValidate; if ( itValidate != objJSON.end() ) { strValidate = itValidate.value().toString(); } bool blnSpinners((itMax != objJSON.end() && itMin != objJSON.end())); if ( blnSpinners == true ) { QString strMax(itMax.value().toString()), strMin(itMin.value().toString()); bool blnDblSpnr = (strMax.indexOf(".") >= 0) || (strMin.indexOf(".") >= 0); if ( blnDblSpnr == true ) { QDoubleSpinBox* pdblspbxInput(new QDoubleSpinBox()); pdblspbxInput->setMaximum(strMax.toDouble()); pdblspbxInput->setMinimum(strMin.toDouble()); pwdgtInput = static_cast<QWidget*>(pdblspbxInput); if ( strValidate.isEmpty() != true ) { mstkConnections.push(QObject::connect(pdblspbxInput, &QAbstractSpinBox::editingFinished, [this, pdblspbxInput, strValidate]() { QString strCurrent(QString::number(pdblspbxInput->value())); this->parseValidationExpression(strCurrent, strValidate, VT_DOUBLE); })); } } else { QSpinBox* pspbxInput(new QSpinBox()); pspbxInput->setMaximum(strMax.toInt()); pspbxInput->setMinimum(strMin.toInt()); pwdgtInput = static_cast<QWidget*>(pspbxInput); if ( strValidate.isEmpty() != true ) { mstkConnections.push(QObject::connect(pspbxInput, &QAbstractSpinBox::editingFinished, [this, pspbxInput, strValidate]() { QString strCurrent(QString::number(pspbxInput->value())); this->parseValidationExpression(strCurrent, strValidate, VT_INTERGER); })); } } } else { QLineEdit* plnEdit(new QLineEdit()); pwdgtInput = static_cast<QWidget*>(plnEdit); if ( strValidate.isEmpty() != true ) { mstkConnections.push(QObject::connect(plnEdit, &QLineEdit::editingFinished, [this, plnEdit, strValidate]() { QString strCurrent(plnEdit->text()); this->parseValidationExpression(strCurrent, strValidate, VT_STRING); })); } } } else if ( intInput == DataSets::IO_TEXT_ENTRY ) { QTextEdit* ptxtedInput(new QTextEdit()); pwdgtInput = static_cast<QWidget*>(ptxtedInput); } if ( pwdgtInput != nullptr && pgrdLayout != nullptr ) { //Add object to map so it can be recalled by name mmpWidgets.insert(strID, pwdgtInput); //Add label to layout QLabel* plblTag = new QLabel(strTag); pgrdLayout->addWidget(plblTag, ++intRow, 0, 1, 1, Qt::AlignRight); //Add the input control pgrdLayout->addWidget(pwdgtInput, intRow, 1, 1, 1, Qt::AlignLeft); } } //Add tabs to layout mpvbxLayout->addWidget(mpTabs); //Add list view to layout mpvbxLayout->addWidget(mplvRecs); //Add close button to layout mpvbxLayout->addWidget(mpbtnClose); //Set-up window title and geometry Qt::WindowFlags wndFlags; wndFlags |= Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::CustomizeWindowHint | Qt::WindowTitleHint; setWindowFlags(wndFlags); setWindowTitle("Datasets"); QRect rctGeom(pParent->geometry()); rctGeom.setHeight(DataSets::mscuint16Height); rctGeom.setWidth(DataSets::mscuint16Width); setGeometry(rctGeom); }The main layout is QVBoxLayout, I add the QTabWidget to this layout then a QListView which I want to display at least 4 rows in, but it doesn't. Then the QPushButton showing Close.
-
Hi,
@SPlatten said in Can anyone help me fix this layout?:
mpvbxLayout->addStretch(1);
That's the thing that is pushing the content downward since it's the first thing you add to your layout.
Next time, please make it minimal, having your parsing code in there just makes it hard to read.
If I may a recommendation: you should keep your layout creation close to where you actually add widgets/layouts/spacer to them. This will make your code easier to follow as well as maintain.
-
Hi,
@SPlatten said in Can anyone help me fix this layout?:
mpvbxLayout->addStretch(1);
That's the thing that is pushing the content downward since it's the first thing you add to your layout.
Next time, please make it minimal, having your parsing code in there just makes it hard to read.
If I may a recommendation: you should keep your layout creation close to where you actually add widgets/layouts/spacer to them. This will make your code easier to follow as well as maintain.