QtableView实现的表格中添加复选框checkBox控件

一、QtableView实现的表格中添加复选框checkBox控件常用方法。

方法一:设置QAbstractTableModel的flags()函数来实现。特点:直接显示,可定义样式,默认左对齐,很难实现居中、右对齐。
方法二:自定义委托QAbstractItemDelegate,通过paint()函数来实现。特点:这种方式比较复杂,但适合扩展,除了可以嵌入复选框,还可以绘制其它控件-按钮、图片等,定义样式无效。

二、QAbstractTableModel的flags()函数实现方式。

1、继承QAbstractTableModel类,实现MyTableModel类。

#ifndef MYTABLEMODEL_H
#define MYTABLEMODEL_H
 
#include <QAbstractTableModel>
 
/**
 * @brief 表格展示的一行的数据结构
 */
struct FileRecord
{
  bool bChecked;
  QString strFilePath;
};
 
/**
 * @brief 自定义TableModel,用于展示接口的使用
 * @details
 * 继承QAbstractTableModel需要实现至少三个接口:
 * rowCount、columnCount、data
 */
class MyTableModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    explicit MyTableModel(QObject *parent = nullptr);
 
    //更新数据
    void updateData(QList<FileRecord> recordList);
 
    //获取行数
    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;
 
private:
    //数据
    QList<FileRecord> m_recordList;
};
#endif // MYTABLEMODEL_H
#define CHECK_BOX_COLUMN 0			//复选框
#define FILE_PATH_COLUMN 1			//文件路径

MyTableModel::MyTableModel(QObject *parent)
    : QAbstractTableModel(parent)
{

}

MyTableModel::~MyTableModel()
{

}

// 更新表格数据
void MyTableModel::updateData(QList<FileRecord> recordList)
{
    beginResetModel();
    m_recordList = recordList;
    endResetModel();
}

// 行数
int MyTableModel::rowCount(const QModelIndex &parent) const
{
    return m_recordList.count();
}

// 列数
int MyTableModel::columnCount(const QModelIndex &parent) const
{
    return 2;
}

// 设置表格项数据
bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid())
        return false;

    int nColumn = index.column();
    FileRecord record = m_recordList.at(index.row());
    switch (role)
    {
    case Qt::DisplayRole:
    {
        if (nColumn == FILE_PATH_COLUMN)
        {
            record.strFilePath = value.toString();

            m_recordList.replace(index.row(), record);
            emit dataChanged(index, index);
            return true;
        }
    }
    case Qt::CheckStateRole:
    {
        if (nColumn == CHECK_BOX_COLUMN)
        {
            record.bChecked = (value.toInt() == Qt::Checked);

            m_recordList.replace(index.row(), record);
            emit dataChanged(index, index);

            //这里可以添加checkbox联动事件,emit到上层界面去处理。

            return true;
        }
    }
    default:
        return false;
    }
    return false;
}

// 表格项数据
QVariant MyTableModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    int nRow = index.row();
    int nColumn = index.column();
    FileRecord record = m_recordList.at(nRow);

    switch (role)
    {
    case Qt::TextColorRole:
        return QColor(Qt::white);
    case Qt::TextAlignmentRole:
        return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
    case Qt::DisplayRole:
    {
        if (nColumn == File_PATH_COLUMN)
            return record.strFilePath;
        return "";
    }
    case Qt::CheckStateRole:
    {
        if (nColumn == CHECK_BOX_COLUMN)
            return record.bChecked ? Qt::Checked : Qt::Unchecked;
    }
    default:
        return QVariant();
    }

    return QVariant();
}

// 表头数据
QVariant MyTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    switch (role)
    {
    case Qt::TextAlignmentRole:
        return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
    case Qt::DisplayRole:
    {
        if (orientation == Qt::Horizontal)
        {
            if (section == CHECK_BOX_COLUMN)
                return QStringLiteral("状态");

            if (section == FILE_PATH_COLUMN)
                return QStringLiteral("文件路径");
        }
    }
    default:
        return QVariant();
    }

    return QVariant();
}

// 表格可选中、可复选
Qt::MyTableModel TableModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return QAbstractItemModel::flags(index);

    Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    if (index.column() == CHECK_BOX_COLUMN)
        flags |= Qt::ItemIsUserCheckable;

    return flags;
}

2、设置样式。

QTableView
{     
    color: white;
    gridline-color: white;                           /*表格内框颜色*/     
    background-color: rgb(27, 27, 27);               /*表格内背景色*/     
    alternate-background-color: rgb(64, 64, 64);     
    selection-color: white;                          /*选中区域的文字颜色*/     
    selection-background-color: rgb(77, 77, 77);     /*选中区域的背景色*/     
    border: 1px white;                               /*边框像素和颜色*/   
    border-radius: 0px;                              /*边框圆角半径*/   
    padding: 1px 1px; 
    outline:0px;                                     /*去除选中时的虚线框*/
} 
QTableView::item {
    color: #5F5F5F;
    text-align: center;
    padding-left: 6px;
    padding-right: 6px;
}
 
 
/* 左上角的空白区域样式 */ 
QTableView QTableCornerButton::section
{   
   color: white;     
   background-color: skyblue;
   border: 1px solid white;     
   border-radius:0px;     
   border-color: rgb(64, 64, 64);  
}
 
 
/* 表头样式 */ 
QTableView QHeaderView {
    background: transparent;
}
QTableView QHeaderView::section {
    color: #8C8C8C;
    background-color: white;
    border-bottom: 1px solid #CED4DA;
    border-top: none;
    border-left: none;
    border-right: 1px solid #CED4DA;
    text-align: center;
    font-weight: bold;
    min-width: 30;
    min-height: 30;
}

QTableView::item{
border:0;}

/* 表格中复选框样式 */ 
QTableView::indicator {
    width: 20px;
    height: 20px;
}

QTableView::indicator:unchecked{image:url(:/SwitchOff.png);}
QTableView::indicator:checked{image:url(:/SwitchOn.png);}

3、使用

QTableView *pTableView = new QTableView(this);
MyTableModel *pModel = new MyTableModel(this);

pTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
pTableView->horizontalHeader()->setStretchLastSection(true);
pTableView->horizontalHeader()->setHighlightSections(false);
pTableView->verticalHeader()->setVisible(false);
pTableView->setShowGrid(false);
pTableView->setFrameShape(QFrame::NoFrame);
pTableView->setSelectionMode(QAbstractItemView::SingleSelection);
pTableView->setModel(pModel);

// 加载数据、更新界面
QList<FileRecord> recordList;
for (int i = 0; i < 5; ++i)
{
	FileRecord record;
	record.bChecked = false;
	record.strFilePath = QString("D:/image%1.png").arg(i + 1);
	recordList.append(record);
}
pModel->updateData(recordList);

三、自定义委托QAbstractItemDelegate,通过paint()函数实现方式。

QTableView如果实现指定项不可编辑状态方法:
1)重写Model的虚函数flags,指定index下的item的flags去除Qt::ItemIsEditable标志。
2)重写Delegate的createEditor,实现返回空的editor即可。

1、CheckBoxDelegate委托类。

ifndef CHECKBOXDELETAGE_H
#define CHECKBOXDELETAGE_H
 
#include <QStyledItemDelegate>
 
class QPixmap;
class CheckBoxDelegate : public QStyledItemDelegate
{
    Q_OBJECT
 
public:
    enum class CheckBoxPaintType : quint8
    {
        SysControl  = 0, // 系统控件
        OwnerDraw   = 1, // 自绘CheckBox
        DrawImage   = 2, // 绘制图片
    };
 
public:
    explicit CheckBoxDelegate(CheckBoxPaintType type = CheckBoxPaintType::SysControl, QWidget *parent = nullptr);
    ~CheckBoxDelegate();
 
    void initCheckBoxPixmaps(CheckBoxPaintType type);
    void initCheckBoxPixmapBySysControl(const QString& key, bool checked);
    void initCheckBoxPixmapByOwnerDraw(const QString& key, bool checked);
    void initCheckBoxPixmapByDrawImage(const QString& key, bool checked);
 
    // 虚函数
    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
    bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override;
 
signals:
 
private:
    QPixmap* mCheckedPix = nullptr;
    QPixmap* mUnCheckedPix = nullptr;
};
 
#endif // CHECKBOXDELETAGE_H
#include "checkboxdeletage.h"
#include <QCheckBox>
#include <QApplication>
#include <QMouseEvent>
#include <QPixmapCache>
#include <QPainter>
 
CheckBoxDelegate::CheckBoxDelegate(CheckBoxDelegate::CheckBoxPaintType type, QWidget *parent)
    : QStyledItemDelegate(parent)
{
    initCheckBoxPixmaps(type);
}
 
CheckBoxDelegate::~CheckBoxDelegate()
{
    delete mCheckedPix;
    delete mUnCheckedPix;
}
 
void CheckBoxDelegate::initCheckBoxPixmaps(CheckBoxDelegate::CheckBoxPaintType type)
{
    switch (type)
    {
    case CheckBoxPaintType::SysControl:
        initCheckBoxPixmapBySysControl("checked", true);
        initCheckBoxPixmapBySysControl("unchecked", false);
        break;
    case CheckBoxPaintType::OwnerDraw:
        initCheckBoxPixmapByOwnerDraw("checked", true);
        initCheckBoxPixmapByOwnerDraw("unchecked", false);
        break;
    case CheckBoxPaintType::DrawImage:
        initCheckBoxPixmapByDrawImage("checked", true);
        initCheckBoxPixmapByDrawImage("unchecked", false);
        break;
    default:
        break;
    }
}
 
void CheckBoxDelegate::initCheckBoxPixmapBySysControl(const QString &key, bool checked)
{
    QPixmap* pix = new QPixmap(13,13);
    QPainter painter(pix);
 
    QStyleOptionToolButton style;
    style.iconSize = QSize(13, 13);
    style.state = checked ? QStyle::State_On : QStyle::State_Off;
    style.state |= QStyle::State_Enabled;
    style.rect = QRect(0,0, 13,13);
    QCheckBox box;
    QApplication::style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &style, &painter, &box);
 
    if (checked)
        mCheckedPix = pix;
    else
        mUnCheckedPix = pix;
}
 
void CheckBoxDelegate::initCheckBoxPixmapByOwnerDraw(const QString &key, bool checked)
{
    QPixmap* pix = new QPixmap(13,13);
    QPainter painter(pix);
    painter.setPen(QColorConstants::Green);
 
    QRect rect(0,0,13,13);
    // 抗锯齿
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setBrush(QBrush(QColorConstants::White));
    painter.drawRect(rect);
 
    if (checked)
    {
        QPointF points[3] = {
            QPointF(rect.x() + rect.width() * 2 / 11, rect.y() + rect.height() * 6 / 11),
            QPointF(rect.x() + rect.width() * 4 / 11, rect.y() + rect.height() * 8 / 11),
            QPointF(rect.x() + rect.width() * 9 / 11, rect.y() + rect.height() * 3 / 11),
        };
 
        painter.setPen(QPen(QBrush(QColorConstants::Green),2));
        painter.drawPolyline(points, 3);
    }
 
    if (checked)
        mCheckedPix = pix;
    else
        mUnCheckedPix = pix;
}
 
void CheckBoxDelegate::initCheckBoxPixmapByDrawImage(const QString &key, bool checked)
{
    QPixmap* pix = new QPixmap(checked ? ":/image/checked.png" : ":/image/unchecked.png");
 
    if (checked)
        mCheckedPix = pix;
    else
        mUnCheckedPix = pix;
}
 
 
void CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    QStyleOptionViewItem viewOption(option);
    initStyleOption(&viewOption, index);
    if (viewOption.state.testFlag(QStyle::State_HasFocus))
        viewOption.state ^= QStyle::State_HasFocus;
 
    QStyledItemDelegate::paint(painter, option, index);
 
    bool checked = index.data(Qt::UserRole).toBool();
 
    QPixmap* pix = checked? mCheckedPix : mUnCheckedPix;
    if (pix)
    {
        // 居中显示
        QRect rect = pix->rect();
        int offsetX = option.rect.width() / 2 - rect.width() / 2;
        int offsetY = option.rect.height() / 2 - rect.height() / 2;
        rect.translate(option.rect.x() + offsetX, option.rect.y() + offsetY);
        painter->drawPixmap(rect, *pix);
    }
}
 
bool CheckBoxDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
    if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick)
    {
        // 鼠标单双击切换选中状态
        QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
        if (option.rect.contains(mouseEvent->pos()))
        {
            model->setData(index, !model->data(index, Qt::UserRole).toBool(), Qt::UserRole);
        }
    }
    return QStyledItemDelegate::editorEvent(event, model, option, index);
}

2、继承QAbstractTableModel类,实现MyTableModel类。

#ifndef MYTABLEMODEL_H
#define MYTABLEMODEL_H
 
#include <QAbstractTableModel>
 
/**
 * @brief 表格展示的一行的数据结构
 */
struct FileRecord
{
  bool bChecked;
  QString strFilePath;
};
 
/**
 * @brief 自定义TableModel,用于展示接口的使用
 * @details
 * 继承QAbstractTableModel需要实现至少三个接口:
 * rowCount、columnCount、data
 */
class MyTableModel : public QAbstractTableModel
{
    Q_OBJECT
public:
    explicit MyTableModel(QObject *parent = nullptr);
 
    //更新数据
    void updateData(QList<FileRecord> recordList);
 
    //获取行数
    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;
 
private:
    //数据
    QList<FileRecord> m_recordList;
};
#endif // MYTABLEMODEL_H
#define CHECK_BOX_COLUMN 0			//复选框
#define FILE_PATH_COLUMN 1			//文件路径

MyTableModel::MyTableModel(QObject *parent)
    : QAbstractTableModel(parent)
{

}

MyTableModel::~MyTableModel()
{

}

// 更新表格数据
void MyTableModel::updateData(QList<FileRecord> recordList)
{
    beginResetModel();
    m_recordList = recordList;
    endResetModel();
}

// 行数
int MyTableModel::rowCount(const QModelIndex &parent) const
{
    return m_recordList.count();
}

// 列数
int MyTableModel::columnCount(const QModelIndex &parent) const
{
    return 2;
}

// 设置表格项数据
bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid())
        return false;

    int nColumn = index.column();
    FileRecord record = m_recordList.at(index.row());
    switch (role)
    {
    case Qt::DisplayRole:
    {
        if (nColumn == FILE_PATH_COLUMN)
        {
            record.strFilePath = value.toString();

            m_recordList.replace(index.row(), record);
            emit dataChanged(index, index);
            return true;
        }
    }
    case Qt::CheckStateRole:
    case Qt::UserRole:
    {
        if (nColumn == CHECK_BOX_COLUMN)
        {
            record.bChecked = (value.toInt() == Qt::Checked);

            m_recordList.replace(index.row(), record);
            emit dataChanged(index, index);
            return true;
        }
    }
    default:
        return false;
    }
    return false;
}

// 表格项数据
QVariant MyTableModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    int nRow = index.row();
    int nColumn = index.column();
    FileRecord record = m_recordList.at(nRow);

    switch (role)
    {
    case Qt::TextColorRole:
        return QColor(Qt::white);
    case Qt::TextAlignmentRole:
        return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
    case Qt::DisplayRole:
    {
        if (nColumn == File_PATH_COLUMN)
            return record.strFilePath;
        return "";
    }
    case Qt::CheckStateRole:
    case Qt::UserRole:
    {
        if (nColumn == CHECK_BOX_COLUMN)
            return record.bChecked ? Qt::Checked : Qt::Unchecked;
    }
    default:
        return QVariant();
    }

    return QVariant();
}

// 表头数据
QVariant MyTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    switch (role)
    {
    case Qt::TextAlignmentRole:
        return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
    case Qt::DisplayRole:
    {
        if (orientation == Qt::Horizontal)
        {
            if (section == CHECK_BOX_COLUMN)
                return QStringLiteral("状态");

            if (section == FILE_PATH_COLUMN)
                return QStringLiteral("文件路径");
        }
    }
    default:
        return QVariant();
    }

    return QVariant();
}

// 表格可选中、可复选
Qt::MyTableModel TableModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return QAbstractItemModel::flags(index);

    Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    if (index.column() == CHECK_BOX_COLUMN)
        flags |= Qt::ItemIsUserCheckable;

    return flags;
}

3、使用

QTableView *pTableView = new QTableView(this);
MyTableModel *pModel = new MyTableModel(this);

pTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
pTableView->horizontalHeader()->setStretchLastSection(true);
pTableView->horizontalHeader()->setHighlightSections(false);
pTableView->verticalHeader()->setVisible(false);
pTableView->setShowGrid(false);
pTableView->setFrameShape(QFrame::NoFrame);
pTableView->setItemDelegateForColumn(0, new CheckBoxDelegate(CheckBoxDelegate::CheckBoxPaintType::SysControl, this));
//pTableView->setItemDelegateForColumn(0, new CheckBoxDelegate(CheckBoxDelegate::CheckBoxPaintType::OwnerDraw, this));
//pTableView->setItemDelegateForColumn(0, new CheckBoxDelegate(CheckBoxDelegate::CheckBoxPaintType::DrawImage, this));
// 不可编辑
pTableView->setEditTriggers(QAbstractItemView::EditTrigger::NoEditTriggers);
// 整行选中
pTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
// 多行可选
pTableView->setSelectionMode(QAbstractItemView::SelectionMode::ExtendedSelection);
pTableView->setModel(pModel);

// 加载数据、更新界面
QList<FileRecord> recordList;
for (int i = 0; i < 5; ++i)
{
	FileRecord record;
	record.bChecked = false;
	record.strFilePath = QString("D:/image%1.png").arg(i + 1);
	recordList.append(record);
}
pModel->updateData(recordList);

猜你喜欢

转载自blog.csdn.net/byxdaz/article/details/131842114
今日推荐