Adding text and numbers in an QAbstractItemModel was the easy part of the Qt Model/View design.

In this tutorial we are going a bit further by adding a QCheckBox directly on each item of a column.

But instead of using the createEditor() method we’re going to use only the paint() one.

Let’s painting.

First of all

For this example adding a visual QObject is generally made with the createEditor() method.

As the QCheckBox has already a role in the data() method of the QAbtractItemModel class, we’ll use it.

But this QCheckBox is by default on the left with a text area on the right.

So as we want this QCheckBox only in the center we’ll redraw it with the paint() method.

Let’s code a bit.

Code

BadprogData.cpp

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

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;
	void setNeedToCompute(bool state);
	bool getNeedToCompute() const;
	// ============================================================================
	// variables
	// ============================================================================
	private:
	QString _name;
	int _age;
	bool _needToCompute;
};
#endif // !BADPROG_DATA_H

BadprogDelegateCheckbox.cpp

#include "BadprogDelegateCheckbox.h"
#include <QCheckbox>
#include <QPushButton>
#include <QApplication>
#include <QMouseEvent>
#include <QPainter>
// Badprog.com
// ============================================================================
//
// ============================================================================
BadprogDelegateCheckbox::BadprogDelegateCheckbox(QObject *parent) {
}
// ============================================================================
//
// ============================================================================
BadprogDelegateCheckbox::~BadprogDelegateCheckbox() {
}
// ============================================================================
//
// ============================================================================
void BadprogDelegateCheckbox::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
	//
	if (2 == index.column()) {
		// inits
		Qt::CheckState state = (Qt::CheckState)index.data(Qt::CheckStateRole).toInt();
		QStyleOptionButton style;
		int x = -1;
		int y = -1;
		// sets if checkbox is enabled
		style.state = QStyle::State_Enabled;
		// sets the state
		switch (state) {
			case Qt::Unchecked:
			style.state |= QStyle::State_Off;
			break;
			case Qt::Checked:
			style.state |= QStyle::State_On;
			break;
		}
		// sets the checkbox style
		style.rect = QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &style, nullptr);
		x = option.rect.center().x() - style.rect.width() / 2;
		y = option.rect.center().y() - style.rect.height() / 2;
		style.rect.moveTo(x, y);
		// draws the checkbox
		QApplication::style()->drawControl(QStyle::CE_CheckBox, &style, painter);
	} else {
		//
		QStyledItemDelegate::paint(painter, option, index);
	}
}

BadprogDelegateCheckbox.h

#ifndef BADPROG_DELEGATE_CHECKBOX_H
#define BADPROG_DELEGATE_CHECKBOX_H
#include <QStyledItemDelegate>
// Badprog.com
class BadprogDelegateCheckbox : public QStyledItemDelegate {
	// ============================================================================
	// ctor
	// ============================================================================
	public:
	BadprogDelegateCheckbox(QObject *parent = 0);
	~BadprogDelegateCheckbox();
	// ============================================================================
	// methods
	// ============================================================================
	public:
	// override
	//
	void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
#endif // !BADPROG_DELEGATE_CHECKBOX_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;
			case 2:
			result = _data.at(row).getNeedToCompute();
			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;
		//
		case Qt::CheckStateRole:
		//
		if (column == 2) {
			result = _data.at(row).getNeedToCompute();
			//
			if (true == result) {
				result = Qt::CheckState::Checked;
			} else {
				result = Qt::CheckState::Unchecked;
			}
		}
		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;
	int row = index.row();
	int column = index.column();
	if (index.isValid() && role == Qt::EditRole) {
		//
		switch (column) {
			case 0:
			_data.at(row).setName(value.toString());
			break;
			case 1:
			_data.at(row).setAge(value.toInt());
			break;
			case 2:
			_data.at(row).setNeedToCompute(value.toBool());
			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 = QAbstractItemModel::flags(index);
	//
	if (index.isValid() && (2 != index.column())) {
		result |= Qt::ItemIsEditable;
	}
	//
	return result;
}
// ============================================================================
//
// ============================================================================
QModelIndex BadprogItemModel::index(int row, int column, const QModelIndex &parent) const {
	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 "BadprogDelegateCheckbox.h"
#include <QTextStream>
#include <QMessageBox>
#include <QFile>
// Badprog.com
// ============================================================================
//
// ============================================================================
BadprogMainWindow::BadprogMainWindow(QWidget *parent) : QMainWindow(parent) {
	// inits
	ui.setupUi(this);
	_model = nullptr;
	unsigned int numberOfRows = 0;
	unsigned int numberOfColumns = 3;
	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);
	// number of lines
	managerNumberOfLines(stream, numberOfRows);
	_model = new BadprogItemModel(numberOfRows, numberOfColumns);
	// reading the file
	managerReadFile(stream);
	// setting the model
	ui.tableView->setModel(_model);
	ui.listView->setModel(_model);
	// settings the delegate
	_delegate = new BadprogDelegateCheckbox(this);
	ui.tableView->setItemDelegate(_delegate);
	//
	QObject::connect(
	ui.tableView, &QTableView::clicked,
	this, &BadprogMainWindow::slotGetClickItem
	);
	// if file exists we allow saving mode
	if (result) {
		QObject::connect(
		ui.pushButton, &QPushButton::clicked,
		this, &BadprogMainWindow::slotSaveData);
	}
}
// ============================================================================
//
// ============================================================================
void BadprogMainWindow::managerNumberOfLines(QTextStream &stream, unsigned int &numberOfRows) {
	// gets number of lines
	while (!stream.atEnd()) {
		stream.readLine();
		++numberOfRows;
	}
	stream.seek(0); // sets the cursor to begin
}
// ============================================================================
//
// ============================================================================
void BadprogMainWindow::managerReadFile(QTextStream &stream) {
	//
	while (!stream.atEnd()) {
		QString line = stream.readLine();
		QStringList fields = line.split("::");
		BadprogData data;
		data.setName(fields.at(0));
		data.setAge(fields.at(1).toInt());
		bool state = false;
		if (fields.at(2) == "true") {
			state = true;
		}
		data.setNeedToCompute(state);
		_model->addNewElementToData(data);
	}
}
// ============================================================================
//
// ============================================================================
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;
	QString delimiter = "::";
	//
	file.setFileName(_fileName);
	managerFile(file, QIODevice::WriteOnly);
	// writing to the file
	out.setDevice(&file);
	for (auto &element : _model->getElementsFromData()) {
		QString state = "false";
		if (element.getNeedToCompute()) {
			state = "true";
		}
		out << element.getName() << delimiter << element.getAge() << delimiter << state;
		out << "\n";
	}
}
// ============================================================================
//
// ============================================================================
void BadprogMainWindow::slotGetClickItem(const QModelIndex &index) {
	//
	if (index.isValid() && (2 == index.column())) {
		//
		bool state = index.data().toBool();
		//
		_model->setData(index, !state, Qt::EditRole);
	}
}

BadprogMainWindow.h

#ifndef BADPROG_MAIN_WINDOW_H
#define BADPROG_MAIN_WINDOW_H
#include "ui_BadprogMainWindow.h"
#include "BadprogItemModel.h"
#include <QPointer>
// Badprog.com
class QFile;
class BadprogDelegateCheckbox;
class BadprogMainWindow : public QMainWindow
{
	Q_OBJECT
	// ============================================================================
	// ctor
	// ============================================================================
	public:
	BadprogMainWindow(QWidget *parent = Q_NULLPTR);
	// ============================================================================
	// methods
	// ============================================================================
	public:
	bool managerFile(QFile &file, QIODevice::OpenModeFlag flag);
	void managerNumberOfLines(QTextStream &stream, unsigned int &numberOfRows);
	void managerReadFile(QTextStream &stream);
	// ============================================================================
	// slots
	// ============================================================================
	public slots:
	void slotSaveData();
	void slotGetClickItem(const QModelIndex &index);
	// ============================================================================
	//
	// ============================================================================
	private:
	Ui::BadprogMainWindowClass ui;
	QPointer<BadprogItemModel> _model;
	QString _fileName;
	QPointer<BadprogDelegateCheckbox> _delegate;
};
#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>1345</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>1345</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

I'm here::9854::true
John::12::false
Jack::1525::true

Result

Each time you click on a QCheckBox the state changes.

Conclusion

This paint() method is great.

Indeed, with it you can do whatever you want by modifying the GUI.

If you got it, good job, once a gain you did it.