Help with how to do something...
-
I know what I want but I'm not sure how to achieve it with Qt. I have a dialog containing a QVBoxLayout, into this I have a QTabWidget, under the QTabWidget is a QListView and under that a QPushButton.
In each tab I want a scrollable area that will contain a number of rows, the number of rows is configurable depending on the database content.
Each row will have a label and to the right of the label a control which will be either a drop down list or a text entry.
So far:

The question is how do I create a scrollable area inside each tab area?
-
Hi,
Make a custom widget for the "row" handling and put it in a QScrollArea that you set on the tab.
-
Hi,
Make a custom widget for the "row" handling and put it in a QScrollArea that you set on the tab.
@SGaist , thank you, found this:
https://cpp.hotexamples.com/examples/-/QScrollArea/-/cpp-qscrollarea-class-examples.htmlWorking through Example #3.
This is the code, but nothing is appearing:
//Create close push button mpbtnClose = new QPushButton(this); mpbtnClose->setText("&Close"); 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); //Create layout QVBoxLayout* pvbxLayout = new QVBoxLayout(this); pvbxLayout->addStretch(1); //Get categories / tabs from database static const char scszErrorTitle[] = "DataSets::DataSets DB ERROR!", scpzTag[] = "tag"; //Perform categories query QSqlQuery queryCategories; queryCategories.prepare("SELECT" " vcDescription AS tab" " FROM" " categories" " ORDER BY vcDescription;"); queryCategories.exec(); //Any error? QSqlError err = queryCategories.lastError(); if ( err.type() != QSqlError::NoError ) { QString strError("Type["); strError += err.type(); strError += "] " + err.text(); QErrorMessage errmsg(this); errmsg.setWindowTitle(scszErrorTitle); errmsg.showMessage(strError); return; } while( queryCategories.next() ) { QSqlRecord record(queryCategories.record()); QJsonArray aryRecs; for( int f=0; f<record.count(); f++ ) { QSqlField field(record.field(f)); if ( field.name().compare("tab") != 0 ) { continue; } //Get text for tab QString strTab(field.value().toString()); if ( mpTabs == nullptr ) { //Create tabs widget mpTabs = new QTabWidget(this); } if ( mpTabs == nullptr ) { continue; } //Create scrollable area for tab content QScrollArea* psaTab = new QScrollArea(mpTabs); if ( psaTab == nullptr ) { continue; } QGridLayout* pgrdLayout = new QGridLayout(); if ( pgrdLayout == nullptr ) { delete psaTab; continue; } pgrdLayout->setHorizontalSpacing(5); pgrdLayout->setVerticalSpacing(5); pgrdLayout->setContentsMargins(5, 5, 5, 5); //Create a widget for holding the rows in the tab QWidget* pPage = new QWidget(); if ( pPage == nullptr ) { delete pgrdLayout; delete psaTab; continue; } //Set-up page widget pPage->setLayout(pgrdLayout); pPage->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); pPage->setMinimumSize(0, 0); //Set-up scrolling area psaTab->setFrameStyle(QFrame::NoFrame); psaTab->setWidget(pPage); psaTab->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); //Add scrollale area to tab map mmpTabs.insert(strTab, psaTab); //Add scrollable area to tabs area mpTabs->addTab(psaTab, strTab); //Now query any datatypes specific to this category QSqlQuery queryDataTypes; queryDataTypes.prepare("SELECT" " vcTag as tag" ",IF(tiType IS NULL,0,1) as input" ",jsonOption" " FROM" " datatypes" " WHERE" " biCategory=(SELECT" " biPK" " FROM" " categories" " WHERE" " vcDescription=?)" " ORDER BY" " tiOrder"); queryDataTypes.bindValue(0, strTab); queryDataTypes.exec(); //Any error? err = queryDataTypes.lastError(); if ( err.type() != QSqlError::NoError ) { continue; } int intRow = 0; while( queryDataTypes.next() ) { QSqlRecord record(queryDataTypes.record()); QJsonObject objJSON; for( int f=0; f<record.count(); f++ ) { QSqlField field(record.field(f)); objJSON.insert(field.name(), QJsonValue(field.value().toString())); } if ( objJSON.isEmpty() != true ) { QJsonObject::iterator itFound = objJSON.find(scpzTag); if ( itFound == objJSON.end() ) { continue; } //Add tag to layout QString strLabel(itFound.value().toString() + ":"); QLabel lblTag(strLabel); pgrdLayout->addWidget(&lblTag, intRow++, 0, 1, 1, Qt::AlignRight); aryRecs.append(objJSON); } } //Free up any resources used by query queryDataTypes.clear(); } } //Free up any resources used by query queryCategories.clear(); //Add tabs to layout pvbxLayout->addWidget(mpTabs); //Add list view to layout pvbxLayout->addWidget(mplvRecs); //Add close button to layout pvbxLayout->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);I've single stepped through the above and everyone the needs to be executed is executed.
Each label added to the grid layout has text, but nothing is showing the actual dialog looks identical to the original screenshot with no content.
-
I know what I want but I'm not sure how to achieve it with Qt. I have a dialog containing a QVBoxLayout, into this I have a QTabWidget, under the QTabWidget is a QListView and under that a QPushButton.
In each tab I want a scrollable area that will contain a number of rows, the number of rows is configurable depending on the database content.
Each row will have a label and to the right of the label a control which will be either a drop down list or a text entry.
So far:

The question is how do I create a scrollable area inside each tab area?
-
@SPlatten said in Help with how to do something...:
Each row will have a label and to the right of the label a control which will be either a drop down list or a text entry.
Sounds like a
QTableViewwith a custom delegate to me -
You can use
QTableView+QStandardItemModelQStandardItemModel* model = new QStandardItemModel; model->insertColumns(0,2); model->insertRows(0, rowCount); for(int i=0;i<rowCount;++i){ QStandardItem* item = new QStandardItem; item->setData(tr("Label %1").arg(i+1),Qt::EditRole); item->setFlags(item->flags() & ~Qt::ItemIsEditable); model->setItem(i,0,item); } tableview->setModel(model);The text entry you get for free (it's the default editor) for the dropdown list you have 2 ways:
- subclass
QItemEditorFactoryand increateEditordecide to return the default lineedit or a combobox - subclass
QStyledItemDelegateand reimplementcreateEditor/setEditorData/setModelData
The latter is a tiny bit more involved but it gives you more control. It really depends on how you determine whether to show a lineedit or a combobox
- subclass
-
Hi
Adding to @VRonin
You can get a delegate from here
https://wiki.qt.io/Combo_Boxes_in_Item_Views -
@VRonin , thank you, I will look into this, can I drop in the QTableView to replace the QScrollArea?
Can you help me with where it would go in the structure I have?
-
@SPlatten said in Help with how to do something...:
can I drop in the QTableView to replace the QScrollArea?
QTableViewinheritsQScrollAreaso yes, it's a straight replacement@VRonin , before I dive in and rip out the code I currently have, what I want to achieve is have a scrollable list of rows where each row as on the left a label and on the right an input widget, one of the following either text entry, combo box or drop down.
I don't want a row highlight just a scrollbar if necessary. Will the QTableView be suitable for this?
-
@VRonin , before I dive in and rip out the code I currently have, what I want to achieve is have a scrollable list of rows where each row as on the left a label and on the right an input widget, one of the following either text entry, combo box or drop down.
I don't want a row highlight just a scrollbar if necessary. Will the QTableView be suitable for this?
-
@SPlatten said in Help with how to do something...:
Will the QTableView be suitable for this?
You will just need some styling of the tableview, nothing major
-
@SPlatten said in Help with how to do something...:
Will the QTableView be suitable for this?
You will just need some styling of the tableview, nothing major
@VRonin , to be honest, this is a bigger learning curve than I have time for, all I need is a scrollable area with a label and input widget on each row. I don't want something that looks like a spreadsheet, just a standard dialog look and feel with a scrollbar if required.
If I drag and drop the widgets using Qt Creator I can create what I want in the WYSIWYG editor, but I can't use this, I have to create the same in code.
-
Hi
I think the code your shown is missing something from the scroll area setup. like settings its content widget.
https://doc.qt.io/qt-5/qscrollarea.html#setWidgetJust to be clear. you are looking for something like this

auto scrollArea = new QScrollArea(centralWidget()); scrollArea ->move(10, 10); scrollArea ->resize(400, 400); // you dont need this as you should put it in a layout in your dialog scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setWidgetResizable(true); // set up its center widget as docs tells auto scrollAreaWidgetContents = new QWidget(); auto verticalLayout = new QVBoxLayout(scrollAreaWidgetContents); verticalLayout->setSpacing(4); verticalLayout->setContentsMargins(4, 4, 4, 4); // setup the "row" handler widget using a from layout auto rowHandlerwidget = new QWidget(scrollAreaWidgetContents); auto formLayout = new QFormLayout(rowHandlerwidget); formLayout->setSpacing(4); formLayout->setContentsMargins(1, 1, 1, 1); // add some "rows! for (int numRows = 0; numRows < 50; ++numRows) { auto label = new QLabel(rowHandlerwidget); label ->setText( "Im Setting:" + QString::number(numRows)); formLayout->setWidget(numRows, QFormLayout::LabelRole, label); if ( numRows % 3 ) { auto lineEdit = new QLineEdit(rowHandlerwidget); formLayout->setWidget(numRows, QFormLayout::FieldRole, lineEdit); } else { auto combo = new QComboBox(rowHandlerwidget); formLayout->setWidget(numRows, QFormLayout::FieldRole, combo); } } // assign row hander widget verticalLayout->addWidget(rowHandlerwidget); // assign scrollsbox widget scrollArea->setWidget(scrollAreaWidgetContents); // do you have this one ? -
A little progress, to see if I could figure out what was going wrong I set the background colour of the widget in the tab to red:
cid:image002.jpg@01D7433C.78A4BD40Why is the widget so small? It was added to the tab with:
psaTab->setWidget(pPage); //psaTab = pointer to QScrollArea mpTabs->addTab(psaTab, strTab); // mpTabs = member pointer to QTabsWidgetI thought the page would fill the area of the tab ?
-
Hi
I think the code your shown is missing something from the scroll area setup. like settings its content widget.
https://doc.qt.io/qt-5/qscrollarea.html#setWidgetJust to be clear. you are looking for something like this

auto scrollArea = new QScrollArea(centralWidget()); scrollArea ->move(10, 10); scrollArea ->resize(400, 400); // you dont need this as you should put it in a layout in your dialog scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setWidgetResizable(true); // set up its center widget as docs tells auto scrollAreaWidgetContents = new QWidget(); auto verticalLayout = new QVBoxLayout(scrollAreaWidgetContents); verticalLayout->setSpacing(4); verticalLayout->setContentsMargins(4, 4, 4, 4); // setup the "row" handler widget using a from layout auto rowHandlerwidget = new QWidget(scrollAreaWidgetContents); auto formLayout = new QFormLayout(rowHandlerwidget); formLayout->setSpacing(4); formLayout->setContentsMargins(1, 1, 1, 1); // add some "rows! for (int numRows = 0; numRows < 50; ++numRows) { auto label = new QLabel(rowHandlerwidget); label ->setText( "Im Setting:" + QString::number(numRows)); formLayout->setWidget(numRows, QFormLayout::LabelRole, label); if ( numRows % 3 ) { auto lineEdit = new QLineEdit(rowHandlerwidget); formLayout->setWidget(numRows, QFormLayout::FieldRole, lineEdit); } else { auto combo = new QComboBox(rowHandlerwidget); formLayout->setWidget(numRows, QFormLayout::FieldRole, combo); } } // assign row hander widget verticalLayout->addWidget(rowHandlerwidget); // assign scrollsbox widget scrollArea->setWidget(scrollAreaWidgetContents); // do you have this one ? -
@mrjj , thank you thats exactly the kind of thing I am looking for, all the examples I've seen online just add the QScrollArea to the tab, what do I need to do to get it to fill the tab area?
-
@SPlatten
Hi
you assign a layout to the tabpage to which you add the scroll area.
then layout will make it use all space.@mrjj , if you see the code I pasted earlier:
QScrollArea* psaTab = new QScrollArea(); QGridLayout* pgrdLayout = new QGridLayout(); QWidget* pPage = new QWidget(); pPage->setLayout(pgrdLayout); psaTab->setWidget(pPage); mpTabs->addTab(psaTab, strTab); -
@mrjj , if you see the code I pasted earlier:
QScrollArea* psaTab = new QScrollArea(); QGridLayout* pgrdLayout = new QGridLayout(); QWidget* pPage = new QWidget(); pPage->setLayout(pgrdLayout); psaTab->setWidget(pPage); mpTabs->addTab(psaTab, strTab); -
Hmm so you use the scroll area directly as a "page" for the tab control?
That should make it cover all area.