C++ - Qt Framework - Using QStyledItemDelegate for displaying a QCheckBox in a QTableView

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

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.