Containers and the Model/View paradigm
discover the set of Conainters and get your feet wet with the MVC pattern.
Introduction
In this lecture, we will dive deeper into some advanced Qt widgets. We will check a set of containers to regroup a collection of widgets ( generally called items). We did already have some experience with an advanced container widget which is the QTableWidget which inherit from the predefined container QTableView.
We will check three basic containers:
- list : You already know what is a list.
- Tree: to manage a collection in the form of a tree.
- Table : to manage widgets in 2D lattice array.
For each type, we will check two variant:
-
Item Based: define a list in the simplest term and hold the set of widgets.
-
Model Based: A more advanced method using a full model to get its data and serve simply as a view.
The model based widgets use an advanced pattern MVC which makes it a little complicated. We will only stratch the surface with those models.
Items Based Widgets
First we will presnt the item based containers. Those widgets controls themselves their contents.
List Widgets
A QListWidget is a widget that provides an item-based list widget. As the name suggests, those widgets could be anything that inherits from the QListWidgetItem class.
As the documentation suggest, we will list the important methods and properties.
- Properties:
count
: holds the number of itemscurrentRow
: index of the current item.sortingEnabled
: holds whether sorting is enabled.
- Methods:
addItem(QString &)
: add a String itemaddItem(QWidget *)
: add a QListWidgetItemcurrentItem()
: get a pointer to current item.
I’ll leave to rest as an exercise to practice reading the documentation.
As an example, we will create the following widget to show the following items:
- We want to associate an icon to each item.
- Each time we click on the button, a Message dialog appear informing the user of the name of the clicked item.
- Modify your widget to modify the clicked item background to
red
.
Tree Widgets
A TreeWidget is container to present items as a Tree.
We will cover this structure in detail in next course. But for now, we will remember the following points:
- A Tree Node has a value and a set of children.
- The top down node of the tree is called root.
For our Qt class, each item is a member of the class QTreeWidgetItem.
Here are a set of intersting functions for our class:
-
addTopLevelItem(QTreeWidgetItem *item)
: to add a node a top level of the tree. -
columnCount
: property to return the number of columns.
For a Item you can add a single or multiple children using one of the following methods:
addChild(QTreeWidgetItem *item)
: to add a single itemaddChildren(QList<QTreeWidgetItem *>)
: to add multiple children at the same time.
Let’s practice this containter, to create the following view for:
-
Create a Qt project, and inherits from the QWidget class.
-
Add a QTreeWidget as a central component:
- In the constructor ten items for each number.
- In order to create an item, we use the following syntax:
auto one = new QTreeWidgetItem({"1"}); auto two = new QTreeWidgetItem({"2"}); ...
- In order to create an item, we use the following syntax:
- Set up the root node:
ui->treeWidget->addTopLevelItem(one);
-
Finally finish the structure of the tree by setting up the children:
// Add two as a child of one one->addChild(two); one->addChild(three); // adding four and five as children two->addChildren({five, six}); ...
Table Widgets
This will be, a short section, as you already used widget in your spreadsheet project.
The problem of these widgets, is that the should manage their own contents. Let Imagine for example, that we want to show the content of a folder. This is perfect scenario for a QTreeWidget but the creation of the hierarchy will be cumborsume.
In this case, using an MVC pattern will really help.
MVC Model
For a more flexible and advanced container widgets. Qt introduces the Model /View/ Control (MVC) paradigm. This is a design pattern originating from Smalltalk that is often used when building user interfaces.
Definition
MVC consists of three kinds of objects. The Model is the application object, the View is its scrren presenation, and the Controller define sthe way the user interface reacts to user input.
Before MVC, user interface designs tented to lump these ojects together. MVC decouples them to increase flexiblity and reuse.
The three main components of this model are:
-
The model: which holds the internal data for our application. Typically either in a data structure like an array or a Database.
-
The View: which offer a graphical reprsentation of the data like the ones we see such as Tree or List widgets.
-
The controller: which represents the brain for the application and takes responsibility for all the user interaction.
Qt does not offer a (controller) but rather a delegate. The discussion why this model is more flexible than the classical (MVC) is out of the scope of this course.
Those components communicates with each other using signals and slots.
-
Signals from the model inform the view about changes to the data held by the data source.
-
Signals from the view provide information about the users’ interaction with the items being displayed.
-
Signals from the delegate are used during editing to tell the model and view about the state of the editor.
Models
All the models are based on the QAbstractItemModel class. This class define an interface that is used by views and delegate to access data.
Also Qt offers a set of basic models:
- QStringListModel: provides a model to store QString.
- QFileSystremModel: provides a model to view the structure of a folder.
- QStandardItemModel: A model for a general data structure data.
- QSqlTableModel and QSqlRelationalTableModel: to manage a model with a given sql database.
If any of those models could solve your problem, you have to create your own class by deriving from the following base classes:
-
QAbstractItemModel: The more basic and abstract model. Need for complex adaptation.
-
QAbstractListModel: A model for list based item.
-
QAbstractTableModel: For a model that stores data in 2d array.
Views
Once we have a model, we have three kind of views ( as previously stated).
- QListView: a View for a list model.
-
QTableView: A view for a Table model.
- QTreeView: A view for a tree like model.
Once we declared a view, we can set up his model using polymorphism:
void QAbtractItemModel::setModel(QAbstractItemModel * model);
Example (Folder Model)
In order to simplify those concepts, we will create a widget to show the structure of a given project using different views.
-
Create a project inheriting from a simple widget and add the following items to your form:
- ListView
- TreeView
-
First in the constructor, we will create the QFileSystremModel:
// Creating the fileSystem model auto model = new QFileSystemModel(); // Setting up the model at the current file model->setRootPath(QDir::currentPath()); // Getting the index of the current folder auto index = model->index(QDir::currentPath());
-
Now that we have a model for our folder, we can create different view to see its contents:
// Seting up the list view ui->listView->setModel(model); ui->listView->setRootIndex(index); // Setting up the tree view ui->treeView->setModel(model); ui->treeView->setRootIndex(index);
- Try to add the third QTableWidget.
- Add a folder and a file in this folder to see the effect in the TreeView.
Personal Model (advanced)
In this optional part, we will tackle the principle and mechanism for creating you personal model by inheriting from the QAbstractTableModel
The basic steps for this concept is
- Create a class by inheriting from QAbstractTableModel.
- Define the abstract method to return the number of item (rows) of our
model
int QAbstractItemModel::rowCount(const QModelIndex &parent= QModelIndex())const;
- override the function to return the content of a item.
QVariant QAbstractItemModel::data(const QModelIndex &index, int role = Qt::displayRole) const;
Let’s show these concept, for a tabular model to print countries and their capital.
Here is the header file.
#ifndef CONTINENTMODEL_H
#define CONTINENTMODEL_H
#include <QAbstractTableModel>
class ContinentModel : public QAbstractTableModel
{
public:
//Constructor with a list of towns
ContinentModel(QStringList & items, QStringList &capitals, QObject *parent=nullptr);
// Abstract methods to implement
int rowCount(const QModelIndex &parent = QModelIndex())const override;
int columnCount(const QModelIndex &parent = QModelIndex())const override;
QVariant data(const QModelIndex &index, int role)const override;
private:
//define an enum for the columns
enum Colonnes{ country = 0, capital = 1};
QStringList items;
QStringList capitals;
};
#endif // CONTINENTMODEL_H
Now there is the implementation of the constructor:
// Simply store the list of countries and the capitals
ContinentModel::ContinentModel(QStringList &items, QStringList &capitals, QObject* parent):QAbstractTableModel(parent)
{
if(items.length() > 0)
this->items = items;
}
The implementation for the number of rows and columns is straightforward:
int ContinentModel::rowCount(const QModelIndex &parent) const
{
return items.length();
}
int ContinentModel::columnCount(const QModelIndex &parent) const
{
return 2;
}
And finally for the function to get the data:
QVariant ContinentModel::data(const QModelIndex &index, int role) const
{
//check if the inedex is valid
if(!index.isValid())
return QVariant();
//check if is withing the bounds
if (index.row()>items.count() || index.column()>=2)
return QVariant();
// set up the text.
if (role == Qt::DisplayRole || role == Qt::EditRole)
if(index.column() == country)
return items.at(index.row());
else if (index.column() == capital && index.row() < capitals.count())
return capitals.at(index.row());
return QVariant();
}
Now we are ready to use our model with all the predefined views:
//Creating the model
QStringList pays;
pays << "France" << "England" << "Morocco" << "Egypt" << "Brasil";
QStringList capitals;
capitals << "Paris" << "?" << " ?" << "?" << "?" ;
ContinentModel *model = new ContinentModel(pays, capitals);
//adding a view on this model
auto view = new QTableView;
view->setModel(model);
Wrap up
In this lecture, we cover a set of predefined Item based widgets containers such as List and Tree Widgets. We dive deeper in a more complex but flexible paradigm which is the (MVC) pattern. This principle even is it is complex to create, it offer more flexibly and reuse by separating the model from the view and the control of the data.