I need to display check box with label in table view cell, with current code It display the checkbox status instead of label. I have created custom delegate and model. Below is the completed code.
The result I got look like this
table_view_delegate.h
#include <QObject>
#include <QStyledItemDelegate>
#include "QSpinBox"
#include "QDoubleSpinBox"
#include "QCheckBox"
#include "QApplication"
#include "QStyle"
class table_view_delegate : public QStyledItemDelegate
{
Q_OBJECT
public:
table_view_delegate(QObject *parent = nullptr);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
signals:
};
table_view_delegate.cpp
#include "table_view_delegate.h"
#include "QMouseEvent"
#include "QAbstractItemModel"
#include "QCheckBox"
#include "QSpinBox"
table_view_delegate::table_view_delegate(QObject *parent) : QStyledItemDelegate{parent}
{
}
QWidget *table_view_delegate::createEditor(QWidget *parent, const QStyleOptionViewItem & option , const QModelIndex & index ) const
{
int c= index.column();
if(c == 0 ){
QCheckBox* check = new QCheckBox("Lable",parent);
return check;
}
else if(c == 1 ){
QSpinBox *editor = new QSpinBox(parent);
return editor;
}
else {
return NULL;
}
}
void table_view_delegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
int c= index.column();
if(c == 0 ){
int value = index.model()->data(index, Qt::CheckStateRole).toInt();
QCheckBox* check = static_cast<QCheckBox*>(editor);
if(value==0)
check->setChecked(false);
else
check->setChecked(true);
check->setText("test");
}
else if(c==1){
QString val_str = index.model()->data(index, Qt::EditRole).toString();
float value = val_str.toFloat();
QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
spinBox->setValue(value);
}
}
void table_view_delegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
int c= index.column();
if(c == 0 ){
QCheckBox* check = static_cast<QCheckBox*>(editor);
check->setText("test");
QString value =QString::number(check->isChecked());//check->isChecked();
model->setData(index, value, Qt::EditRole);
}
else if(c ==1){
QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
spinBox->interpretText();
QString value = QString::number(spinBox->value(), 'f', 2);
model->setData(index, value, Qt::EditRole);
}
}
void table_view_delegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
void table_view_delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter, option, index);
}
table_view_model.h
#include <QObject>
#include <QAbstractTableModel>
class table_view_model : public QAbstractTableModel
{
Q_OBJECT
public:
explicit table_view_model(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, 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;
void setDataList(QList <QStringList> alog_data_list);
QVariant headerData(int section, Qt::Orientation , int role) const override;
private:
QList <QStringList> model_data_list;
int col_count = 3;
QStringList header={"CheckBox","SpinBox","String"};
signals:
};
table_view_model.cpp
#include "table_view_model.h"
table_view_model::table_view_model(QObject *parent) : QAbstractTableModel{parent}
{
}
int table_view_model::rowCount(const QModelIndex & /*parent*/) const
{
return model_data_list.length();
}
int table_view_model::columnCount(const QModelIndex & /*parent*/) const
{
return col_count;
}
QVariant table_view_model::data(const QModelIndex &index, int role) const
{
int r = index.row();
int c = index.column();
if(model_data_list.length()==0)
return QVariant();
if (!index.isValid())
return QVariant();
if( c==0){
if (role == Qt::CheckStateRole&& c==0 ){
int checked = model_data_list[r][c].toInt();
if (checked)
return Qt::Checked;
else
return Qt::Unchecked;
}
else if(role == Qt::EditRole){
return model_data_list[r][c];
}
else if (role == Qt::DisplayRole)
return model_data_list[r][c];
}
else{
if (role == Qt::DisplayRole)
return model_data_list[r][c];
}
return QVariant();
}
bool table_view_model::setData(const QModelIndex &index, const QVariant &value, int role)
{
int c=index.column();
if( c==1){
if (role == Qt::EditRole) {
if (!checkIndex(index))
return false;
return true;
}
}
else if (role == Qt::CheckStateRole&&c==0)
{
model_data_list[index.row()][index.column()] = value.toString();
if ((Qt::CheckState)value.toInt() == Qt::Checked)
{
//user has checked item
return true;
}
else
{
//user has unchecked item
return true;
}
}
return false;
}
void table_view_model::setDataList(QList <QStringList> model_data_listTmp){
table_view_model::beginResetModel();
model_data_list.clear();
for(int i=0;i<model_data_listTmp.length();i ){
model_data_list.push_back(model_data_listTmp[i]);
}
table_view_model::endResetModel();
}
Qt::ItemFlags table_view_model::flags(const QModelIndex &index) const
{
int c=index.column();
//Editable column
if(c==1)
return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
else if(c==0)
return Qt::ItemIsUserCheckable | QAbstractTableModel::flags(index);
else
return QAbstractTableModel::flags(index);
}
QVariant table_view_model::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole){
return QVariant();
}
if (orientation == Qt::Horizontal)
return header.at(section);
else
return QString::number(section);
}
In MainWindow.cpp I am initializing the table view like.
table_view_model *model = new table_view_model();
//QSqlQueryModel *algo_model = db_conn->getAlgoTableData();
ui->tableView->setModel(model);
ui->tableView->horizontalHeader()->setStretchLastSection(true);
table_view_delegate *delgate = new table_view_delegate() ;
ui->tableView->setItemDelegate(delgate);
QList <QStringList> data_list;
QStringList r1;
r1.append("0");
r1.append("100");
r1.append("Str1");
data_list.append(r1);
QStringList r2;
r2.append("1");
r2.append("200");
r2.append("Str2");
data_list.append(r2);
model->setDataList(data_list);
CodePudding user response:
You are having a lot of confusion between roles.
Your model seems to need 3 roles. Quick description of them with my words. Make sure to check the documentation for the roles I have not described.
Qt::DisplayRole
: What shows on screen e.g. a formatted number (actual typeQString
) with its thousand separators.Qt::EditRole
: The underlying data displayed, e.g. the actual number of the model. It is the data you are expected to use in the delegate's editor. In many cases however, the data forQt::EditRole
is the same as the data forQt::DisplayRole
.
also, you may choose to implement a model that accepts being edited in itsQt::EditRole
andQt::DisplayRole
, the difference really is about formatting for screen display.Qt::CheckStateRole
: Checkstate (typeQt::CheckState
) at the index. It is a nullQVariant
by default meaning the view will not show a checkbox.
However, you can implement a delegate that will show a checkbox in all cases (or oppositely, have a delegate that shows no checkbox even if there should be one)
In your model, you store everything in QList <QStringList> model_data_list;
Sample from your data
method:
if( c==0){
if (role == Qt::CheckStateRole && c==0){
int checked = model_data_list[r][c].toInt(); //here, used for Qt::CheckStateRole
[...]
}
else if(role == Qt::EditRole){
return model_data_list[r][c]; //here, used for Qt::EditRole
}
else if (role == Qt::DisplayRole)
return model_data_list[r][c]; //here, used for Qt::DisplayRole
}
Then you should not be surprised the text shown is the same as the check state. You must create super clear boundaries between roles as to never run into that confusion.
One easy solution is to add a new member to your class to store check states, then use it in your data
and setData
methods. While you are at it, change the type of the other member, there is no need to constantly convert between QString
and QVariant
.
In your header:
QList <QVariantList> model_data_list; //QVariant is more suitable than QString to store your model's data.
QList<Qt::CheckState> model_checkstate_list; //1-dimensional list as only column 0 has checkstates.
With some serious refactoring, your source file becomes:
QVariant table_view_model::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return QVariant();
//Try to use auto to get the row and column of index.
//With any luck, Qt will some day change the return value to be qsizetype of similar.
//auto will make sure you benefit the change when it happens.
auto r = index.row(), c = index.column();
switch (role) {
case Qt::CheckStateRole: {
if (c == 0)
return model_checkstate_list[r];
else
return QVariant();
}
case Qt::DisplayRole: //We treat Display and Edit roles identically.
case Qt::EditRole: return value;
default: return QVariant();
}
}
bool table_view_model::setData(const QModelIndex &index, const QVariant &value, int role) {
if (!index.isValid())
return QVariant();
auto r = index.row(), c = index.column();
switch (role) {
case Qt::CheckStateRole: {
if (c == 0) {
model_checkstate_list[r] = value.value<Qt::CheckState>();
return true;
}
else
return false;
}
case Qt::DisplayRole: //We treat Display and Edit roles identically.
case Qt::EditRole: {
model_data_list[r][c] = value;
return true;
}
default: return false;
}
}
I am not going to address the delegate because this is not what causes your display issue and after I pointed it out in my comments, I am going to assume you corrected the setModelData
but I repeat you should seriously consider to have a different delegate for each column of your view. It would remove all the if (c == 0)
and allow for using the delegate in views/models where checkboxes are not in column 0
.