The QAbstractItemModel is usually used with views like QTableView, QTreeView or QListView.

We’re going to see how to use this class in a very easy example.

To be honest nothing is easy but we’ll use the minimum elements in order to have something useful for every new project.

Let’s see that in this QAbstractItemModel tutorial.

First of all

The Model/View design is quite different from the classic Widgets.

Indeed, here the view and the model are separated in at least two different classes.

The main advantage with the Model/View is that you don’t have to refresh yourself data in all your view once the model is modified.

Everything is updating itself, or almost.

In this example we’ll use a QTableView and a QListView sharing the same model.

And, what a wondferful world, the two views will be modified when we’ll change a data in one view.

It’ll prove that the Model/View design works.

The QAbstractItemModel isn’t the simplest model to understand but with it you’ll be able to use all the views.

You can find indeed some examples with QAbstractTableModel for using with QTableView or QAbstractTreeModel to use with QTreeView.

Both of these models inherits from the QAbstractItemModel.

In this tutorial we’ll use instead directly the QAbstractItemModel.

Our Model will be taken from a file (name and age of people) and we’ll save this model further in the same file but potentially with different data.

That’s way we’ll be sure that our construction works well.

As QAbstractItemModel is an Abstract class, we’ll have to override some methods from this parent class.

All the details are in the code.

For the GUI you’ll have to add a TableView, a ListView and a PushButton on the MainWindow.

So let’s code a bit.

Code

BadprogData.cpp

#include "BadprogData.h"
// Badprog.com
// ============================================================================
//
// ============================================================================
BadprogData::BadprogData() {
}
// ============================================================================
//
// ============================================================================
BadprogData::~BadprogData() {
}
// ============================================================================
//
// ============================================================================
void BadprogData::setName(QString name) {
	_name = name;
}
// ============================================================================
//
// ============================================================================
void BadprogData::setAge(int age) {
	_age = age;
}
// ============================================================================
//
// ============================================================================
QString BadprogData::getName() const {
	return _name;
}
// ============================================================================
//
// ============================================================================
int BadprogData::getAge() const {
	return _age;
}

BadprogData.h

#ifndef BADPROG_DATA_H
#define BADPROG_DATA_H
#include <QString>
// Badprog.com
class BadprogData
{
	public:
	BadprogData();
	~BadprogData();
	// ============================================================================
	// methods
	// ============================================================================
	public:
	void setName(QString name);
	void setAge(int age);
	QString getName() const;
	int getAge() const;
	// ============================================================================
	// variables
	// ============================================================================
	private:
	QString _name;
	int _age;
};
#endif // !BADPROG_DATA_H

BadprogItemModel.cpp

#include "BadprogItemModel.h"
#include <QColor>
#include <QFont>
// Badprog.com
// ============================================================================
//
// ============================================================================
BadprogItemModel::BadprogItemModel() {
}
// ============================================================================
//
// ============================================================================
BadprogItemModel::BadprogItemModel(int numberOfRows, int numberOfColumns) :
_numberOfRows(numberOfRows), _numberOfColumns(numberOfColumns) {
}
// ============================================================================
//
// ============================================================================
BadprogItemModel::~BadprogItemModel() {
}
// ============================================================================
//
// ============================================================================
QVariant BadprogItemModel::data(const QModelIndex &index, int role) const {
	//
	QVariant result = QVariant();
	int row = index.row();
	int column = index.column();
	//
	if (!index.isValid() || row >= rowCount() || column >= columnCount()) {
		return result;
	}
	switch (role) {
		//
		case Qt::DisplayRole:
		// checking row and column to get data
		switch (column) {
			case 0:
			result = _data.at(row).getName();
			break;
			case 1:
			result = _data.at(row).getAge();
			break;
			default:
			break;
		} // end switch column
		break;
		//
		case Qt::FontRole:
		if (2 == row) {
			QFont font;
			font.setBold(true);
			result = font;
		}
		break;
		//
		case Qt::ForegroundRole:
		if (1 == column) {
			result = QColor(Qt::red);
		}
		break;
		//
		case Qt::BackgroundRole:
		if (1 == column) {
			result = QColor(255, 255, 40);
		}
		break;
		//
		case Qt::TextAlignmentRole:
		result = Qt::AlignCenter;
		break;
		//
		default:
		break;
	}
	//
	return result;
}
// ============================================================================
//
// ============================================================================
int BadprogItemModel::rowCount(const QModelIndex &parent) const {
	(void)(parent);
	return _numberOfRows;
}
// ============================================================================
//
// ============================================================================
int BadprogItemModel::columnCount(const QModelIndex &parent) const {
	(void)(parent);
	return _numberOfColumns;
}
// ============================================================================
//
// ============================================================================
QVariant BadprogItemModel::headerData(int section, Qt::Orientation orientation, int role) const {
	//
	QVariant result = QVariant();
	//
	if (Qt::DisplayRole == role && Qt::Horizontal == orientation) { // H
		//
		switch (section) {
			//
			case 0:
			result = "Hello";
			break;
			//
			case 1:
			result = "World";
			break;
			//
			case 2:
			result = ":D";
			break;
			default:
			break;
		}
	} else if (Qt::DisplayRole == role && Qt::Vertical == orientation) { // V
		//
		switch (section) {
			//
			case 0:
			result = "Welcome";
			break;
			//
			case 1:
			result = "Aboard";
			break;
			//
			case 2:
			result = "Guys";
			break;
			default:
			break;
		}
	} else {
		// other stuff
	}
	//
	return result;
}
// ============================================================================
//
// ============================================================================
bool BadprogItemModel::setData(const QModelIndex &index, const QVariant &value, int role) {
	//
	bool result = false;
	if (index.isValid() && role == Qt::EditRole) {
		index.row(), index.column();
		int row = index.row();
		int column = index.column();
		switch (column) {
			case 0:
			_data.at(row).setName(value.toString());
			break;
			case 1:
			_data.at(row).setAge(value.toInt());
			break;
			default:
			break;
		} // end switch column
		emit dataChanged(index, index);
		result = true;
	}
	//
	return result;
}
// ============================================================================
//
// ============================================================================
Qt::ItemFlags BadprogItemModel::flags(const QModelIndex &index) const {
	//
	Qt::ItemFlags result = Qt::ItemIsEditable | QAbstractItemModel::flags(index);
	//
	return result;
}
// ============================================================================
//
// ============================================================================
QModelIndex BadprogItemModel::index(int row, int column, const QModelIndex &parent) const {
	// return QModelIndex();
	return createIndex(row, column, nullptr);
}
// ============================================================================
//
// ============================================================================
QModelIndex BadprogItemModel::parent(const QModelIndex &child) const {
	return QModelIndex();
}
// ============================================================================
//
// ============================================================================
void BadprogItemModel::addNewElementToData(BadprogData data) {
	_data.push_back(data);
}
// ============================================================================
//
// ============================================================================
std::vector<BadprogData> BadprogItemModel::getElementsFromData() const {
	return _data;
}

BadprogItemModel.h

#ifndef BADPROG_ITEM_MODEL_H
#define BADPROG_ITEM_MODEL_H
#include "BadprogData.h"
#include <QAbstractItemModel>
// Badprog.com
class BadprogItemModel : public QAbstractItemModel
{
	// ============================================================================
	// ctor
	// ============================================================================
	public:
	BadprogItemModel();
	BadprogItemModel(int numberOfRows, int numberOfColumns);
	~BadprogItemModel();
	// ============================================================================
	// methods
	// ============================================================================
	public:
	// override from virtual pure
	QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
	int rowCount(const QModelIndex &parent = QModelIndex()) const override;
	int columnCount(const QModelIndex &parent = QModelIndex()) const override;
	QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
	QModelIndex parent(const QModelIndex &child) const;
	// override from virtual
	QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
	bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
	Qt::ItemFlags flags(const QModelIndex &index) const override;
	// classic
	void addNewElementToData(BadprogData data);
	std::vector<BadprogData> getElementsFromData() const;
	// ============================================================================
	// variables
	// ============================================================================
	private:
	std::vector<BadprogData> _data;
	int _numberOfRows;
	int _numberOfColumns;
};
#endif // !BADPROG_ITEM_MODEL_H

BadprogMainWindow.cpp

#include "BadprogMainWindow.h"
#include "BadprogItemModel.h"
#include <QDebug>
#include <QMessageBox>
#include <QFile>
// Badprog.com
// ============================================================================
//
// ============================================================================
BadprogMainWindow::BadprogMainWindow(QWidget *parent) : QMainWindow(parent) {
	// inits
	ui.setupUi(this);
	_model = nullptr;
	unsigned int numberOfRows = 0;
	QTextStream stream;
	QFile file = nullptr;
	bool result = false;
	// file
	_fileName = "people.txt";
	file.setFileName(_fileName);
	// deals with the file
	result = managerFile(file, QIODevice::ReadOnly);
	stream.setDevice(&file);
	// gets number of lines
	while (!stream.atEnd()) {
		stream.readLine();
		++numberOfRows;
	}
	stream.seek(0); // sets the cursor to begin
	_model = new BadprogItemModel(numberOfRows, 2);
	// reading the file
	while (!stream.atEnd()) {
		QString line = stream.readLine();
		QStringList fields = line.split(" ");
		BadprogData data;
		data.setName(fields.at(0));
		data.setAge(fields.at(1).toInt());
		_model->addNewElementToData(data);
	}
	// sets the model
	ui.tableView->setModel(_model);
	ui.listView->setModel(_model);
	// if file exists we allow saving mode
	if (result) {
		// slots
		QObject::connect( ui.pushButton, &QPushButton::clicked,
		this, &BadprogMainWindow::slotSaveData);
	}
}
// ============================================================================
//
// ============================================================================
bool BadprogMainWindow::managerFile(QFile &file, QIODevice::OpenModeFlag flag) {
	//
	bool result = true;
	//
	if (!file.open(flag)) {
		QMessageBox::information( // parent, title, message
		this,
		"Problem with the file",
		"File: \"" + file.fileName() + "\".\n" + file.errorString());
		result = false;
	}
	//
	return result;
}
// ============================================================================
//
// ============================================================================
void BadprogMainWindow::slotSaveData() {
	// inits
	QFile file;
	QTextStream out;
	//
	file.setFileName(_fileName);
	managerFile(file, QIODevice::WriteOnly);
	// writing to the file
	out.setDevice(&file);
	for (auto &element : _model->getElementsFromData()) {
		out << element.getName() << " " << element.getAge();
		out << "\n";
	}
}

BadprogMainWindow.h

#ifndef BADPROG_MAIN_WINDOW_H
#define BADPROG_MAIN_WINDOW_H
#include <QtWidgets/QMainWindow>
#include "ui_BadprogMainWindow.h"
#include "BadprogItemModel.h"
#include <QPointer>
// Badprog.com
class QFile;
class BadprogMainWindow : public QMainWindow
{
	Q_OBJECT
	// ============================================================================
	// ctor
	// ============================================================================
	public:
	BadprogMainWindow(QWidget *parent = Q_NULLPTR);
	// ============================================================================
	// methods
	// ============================================================================
	public:
	bool managerFile(QFile &file, QIODevice::OpenModeFlag flag);
	// ============================================================================
	// slots
	// ============================================================================
	public slots:
	void slotSaveData();
	// ============================================================================
	//
	// ============================================================================
	private:
	Ui::BadprogMainWindowClass ui;
	QPointer<BadprogItemModel> _model;
	QString _fileName;
};
#endif // !BADPROG_MAIN_WINDOW_H

BadprogMainWindow.ui

<?xml version="1.0" encoding="UTF-8"?>

<ui version="4.0">

<class>BadprogMainWindowClass</class>

<widget class="QMainWindow" name="BadprogMainWindowClass">

<property name="geometry">

<rect>

<x>0</x>

<y>0</y>

<width>1106</width>

<height>400</height>

</rect>

</property>

<property name="windowTitle">

<string>BadprogMainWindow</string>

</property>

<widget class="QWidget" name="centralWidget">

<layout class="QGridLayout" name="gridLayout">

<item row="0" column="0">

<widget class="QTableView" name="tableView"/>

</item>

<item row="0" column="1">

<widget class="QListView" name="listView"/>

</item>

<item row="1" column="1">

<widget class="QPushButton" name="pushButton">

<property name="text">

<string>Save data</string>

</property>

</widget>

</item>

</layout>

</widget>

<widget class="QMenuBar" name="menuBar">

<property name="geometry">

<rect>

<x>0</x>

<y>0</y>

<width>1106</width>

<height>21</height>

</rect>

</property>

</widget>

<widget class="QToolBar" name="mainToolBar">

<attribute name="toolBarArea">

<enum>TopToolBarArea</enum>

</attribute>

<attribute name="toolBarBreak">

<bool>false</bool>

</attribute>

</widget>

<widget class="QStatusBar" name="statusBar"/>

</widget>

<layoutdefault spacing="6" margin="11"/>

<resources>

<include location="BadprogMainWindow.qrc"/>

</resources>

<connections/>

</ui>

main.cpp

#include "BadprogMainWindow.h"
#include <QtWidgets/QApplication>
// Badprog.com
int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	BadprogMainWindow w;
	w.show();
	return a.exec();
}

people.txt

Katy 10

John 47

Elon 1337

Result

As a result you have a window with 2 views: A TableView and a ListView with a PushButton.

On the TableView there are the name of each person taken from the file as well as their age.

The second column of the TableView is on yellow and the last row is in bold, just to show how it works.

On the right you have the ListView with the name from the file.

So as we set the both views with the same model each time we change a name from a view, instantly the other view updates its content with the new model.

At the end you can save the new model on the same file by clicking on the PushButton.

Conclusion

This QAbstractItemModel example seemed quite complex at first but by extracting the basics we have seen that it became something extremely powerful.

Once again, good job if you got it.