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.

Representation of a list View on Qt.

As the documentation suggest, we will list the important methods and properties.

  • Properties:
    • count : holds the number of items
    • currentRow: index of the current item.
    • sortingEnabled: holds whether sorting is enabled.
  • Methods:
    • addItem(QString &): add a String item
    • addItem(QWidget *): add a QListWidgetItem
    • currentItem(): 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:

Listing the set of browsers using a ListWidget.
  1. We want to associate an icon to each item.
  2. Each time we click on the button, a Message dialog appear informing the user of the name of the clicked item.
  3. Modify your widget to modify the clicked item background to red.

Tree Widgets

A TreeWidget is container to present items as a Tree.

Representation of a simple 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 item
  • addChildren(QList<QTreeWidgetItem *>): to add multiple children at the same time.

Let’s practice this containter, to create the following view for:

Example of a tree that we should construct.
  1. Create a Qt project, and inherits from the QWidget class.

  2. Add a QTreeWidget as a central component:

    Illustration of the ui for our class.
  3. 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"});
       ...
      
  4. Set up the root node:
     ui->treeWidget->addTopLevelItem(one);
    
  5. 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.

Directory Hierarchy using a QTreeWidget

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.

Architecture of the MVC model. With a Model that holds the representation data. The view to represent the data to the user. And finally the controller which define the user interface to modify the model

The three main components of this model are:

  1. The model: which holds the internal data for our application. Typically either in a data structure like an array or a Database.

  2. The View: which offer a graphical reprsentation of the data like the ones we see such as Tree or List widgets.

  3. 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.

The model/view architecture with a delegate.

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:

If any of those models could solve your problem, you have to create your own class by deriving from the following base classes:

List of basic model with their specific indexation.

Views

Once we have a model, we have three kind of views ( as previously stated).

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.

  1. Create a project inheriting from a simple widget and add the following items to your form:

    • ListView
    • TreeView
    Ui form for our filesystem view.
  2. 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());
    
    
  3. 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);
    
The output for our file system widget.
  1. Try to add the third QTableWidget.
  2. 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

  1. Create a class by inheriting from QAbstractTableModel.
  2. Define the abstract method to return the number of item (rows) of our model
    int QAbstractItemModel::rowCount(const QModelIndex &parent=
    QModelIndex())const;
    
  3. 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);
Final result for our custom Model view.

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.