A QTreeView arranges a group of data elements based on a parent-child relationship. Top level items can be empty or have child items. Items which do have children can be expanded or collapsed and the child elements will be displayed or hidden. The QStandardItemModel will be used to store the data.
#include <QtCore>
#include <QtGui>
class MainWindow : public QWidget
{
public:
MainWindow();
private:
QTreeView *m_view;
struct TreeData {
int id;
QString name;
int parentId;
};
QList<TreeData> getData();
};
Our declaration for MainWindow has a new component which is a private nested structure called TreeData. By definition, the members of a struct in C++ are public unless otherwise specified. If TreeData had been a class then its members would have been private.
If the members of a nested struct or class are private they can only be used from within that struct or class. Since our example is going to read these members from the outside class, the TreeData members must be public.
MainWindow::MainWindow()
{
setMinimumSize(400, 500);
QLabel *label = new QLabel();
label->setText("Tree View");
label->setAlignment(Qt::AlignHCenter);
QFont font = label->font();
font.setPointSize(11);
label->setFont(font);
QList<MainWindow::TreeData> data = getData();
QStandardItemModel *model = new QStandardItemModel();
QStandardItem *rootItem = model->invisibleRootItem();
QMap<int, QStandardItem *> parentMap;
for (auto item : data) {
if (item.parentId == 0) {
QStandardItem *tmp = new QStandardItem;
tmp->setText(item.name);
tmp->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
rootItem->appendRow(tmp);
parentMap.insert(item.id, tmp);
}
}
for (auto item : data) {
if (item.parentId != 0) {
QStandardItem *tmp = new QStandardItem;
tmp->setText(item.name);
tmp->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
auto iter = parentMap.find(item.parentId);
if (iter != parentMap.end()) {
QStandardItem *parentItem = iter.value();
if (parentItem != nullptr) {
parentItem->appendRow(tmp);
}
}
}
}
model->setHeaderData(0, Qt::Horizontal,
QString("Recipe Categories"));
model->sort(0);
m_view = new QTreeView();
m_view->setModel(model);
m_view->expandAll();
QPushButton *pb_close = new QPushButton();
pb_close->setText("Close");
QHBoxLayout *layout = new QHBoxLayout();
layout->addStretch();
layout->addWidget(pb_close);
layout->addStretch();
QVBoxLayout *layout_main = new QVBoxLayout(this);
layout_main->setContentsMargins(20, 20, 20, 15);
layout_main->addWidget(label);
layout_main->addSpacing(10);
layout_main->addWidget(m_view);
layout_main->addSpacing(10);
layout_main->addLayout(layout);
connect(pb_close, &QPushButton::clicked,
this, &QWidget::close);
}
The call to the method getData() on line 13 returns a QList containing the data for our tree view. More information about this method is explained in the next section.
The data for a QTreeView must be stored in a model. since this class provides no internal storage containers. The purpose of using a model is simply to separate the data from the view. A pointer to a QStandardItemModel is declared and instantiated on line 15.
The data for this model is added using a parent-child relationship. All of the top level items are added to an imaginary root entry. This special element is retrieved by calling invisibleRootItem() on line 17. In the first for() loop we add every root entry by doing a query of the parentId member of the TreeData. If the value is 0 then this item is a root item. Since our model only stores the TreeData name, line 28 saves the id and the current QStandardItem to a map.
The second for() loop walks the TreeData and looks for child items. If the parentId is not equal to 0 then we look up parentId in our map to find the QStandardItem parent for the current child.
There are other designs we could have used such as pre-sorting the data before adding it to the model. The approach we choose works well for data sets which are not excessively large.
The call to sort() on line 54 is required to sort the parent items and each group of child items. On line 56 we create the tree view and the next line sets the model. This is followed by a call to expandAll() which will initially show the tree view with all child items visible.
Retrieving the Data
The following method returns the data for our tree view. The declaration of the return type is a QList<T> where the T is our nested struct MainWindow::TreeData. The outer class name of MainWindow must be used in the context of a return type.
Inside the body on line 3 the scope name MainWindow is not required. We have included it in our code for readability and consistency.
QList<MainWindow::TreeData> MainWindow::getData()
{
QList<MainWindow::TreeData> retval =
{ {101, "Salad", 0},
{102, "Bread", 0},
{103, "KitchenSink Cookies", 105},
{104, "Brownies", 105},
{105, "Dessert", 0},
{106, "Roasted Carrots", 114},
{107, "Thai Curry", 108},
{108, "Chicken", 0},
{109, "Blueberry Muffins", 102},
{110, "Chocolate Ice Cream", 105},
{111, "Corn Bread", 102},
{112, "Mixed Greens", 101},
{113, "Soup", 0},
{114, "Vegetables", 0},
};
return retval;
}
Main Function
Since the source code for main() has not changed there is no need to show it again. Refer to example 3 or download the full source for this example.
Running the Example
To build and run this example use the same CMake build file and commands as we showed for the first example. The only suggested modification is on line 2 of the CMakeLists.txt file, change “example_1” to “example_24”.