Home > Software design >  Qt TableView ChekcBox with Label
Qt TableView ChekcBox with Label

Time:01-09

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 type QString) 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 for Qt::EditRole is the same as the data for Qt::DisplayRole.
    also, you may choose to implement a model that accepts being edited in its Qt::EditRole and Qt::DisplayRole, the difference really is about formatting for screen display.
  • Qt::CheckStateRole: Checkstate (type Qt::CheckState) at the index. It is a null QVariant 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.

  • Related