简述
上节分享了使用自定义模型QAbstractTableModel来实现复选框。下面我们来介绍另外一种方式:
自定义委托-QAbstractItemDelegate。
效果
QAbstractTableModel
源码
table_model.cpp
#define CHECK_BOX_COLUMN 0
#define File_PATH_COLUMN 1
TableModel::TableModel(QObject *parent)
: QAbstractTableModel(parent)
{
}
TableModel::~TableModel()
{
}
// 更新表格数据
void TableModel::updateData(QList<FileRecord> recordList)
{
m_recordList = recordList;
beginResetModel();
endResetModel();
}
// 行数
int TableModel::rowCount(const QModelIndex &parent) const
{
return m_recordList.count();
}
// 列数
int TableModel::columnCount(const QModelIndex &parent) const
{
return 2;
}
// 设置表格项数据
bool TableModel::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.toBool();
m_recordList.replace(index.row(), record);
emit dataChanged(index, index);
return true;
}
}
default:
return false;
}
return false;
}
// 表格项数据
QVariant TableModel::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::UserRole:
{
if (nColumn == CHECK_BOX_COLUMN)
return record.bChecked;
}
default:
return QVariant();
}
return QVariant();
}
// 表头数据
QVariant TableModel::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::ItemFlags TableModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return QAbstractItemModel::flags(index);
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
return flags;
}
接口说明
updateData
主要用于更新数据,刷新界面。data
用来显示数据,根据角色(颜色、文本、对齐方式、选中状态等)判断需要显示的内容。setData
用来设置数据,根据角色(颜色、文本、对齐方式、选中状态等)判断需要设置的内容。headerData
用来显示水平/垂直表头的数据。flags
用来设置单元格的标志(可用、可选中、可复选等)。
QStyledItemDelegate
源码
CheckBoxDelegate::CheckBoxDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
}
CheckBoxDelegate::~CheckBoxDelegate()
{
}
// 绘制复选框
void CheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem viewOption(option);
initStyleOption(&viewOption, index);
if (option.state.testFlag(QStyle::State_HasFocus))
viewOption.state = viewOption.state ^ QStyle::State_HasFocus;
QStyledItemDelegate::paint(painter, viewOption, index);
if (index.column() == CHECK_BOX_COLUMN)
{
bool data = index.model()->data(index, Qt::UserRole).toBool();
QStyleOptionButton checkBoxStyle;
checkBoxStyle.state = data ? QStyle::State_On : QStyle::State_Off;
checkBoxStyle.state |= QStyle::State_Enabled;
checkBoxStyle.iconSize = QSize(20, 20);
checkBoxStyle.rect = option.rect;
QCheckBox checkBox;
checkBoxStyle.iconSize = QSize(20, 20);
checkBoxStyle.rect = option.rect;
QApplication::style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &checkBoxStyle, painter, &checkBox);
}
}
// 响应鼠标事件,更新数据
bool CheckBoxDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
QRect decorationRect = option.rect;
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
if (event->type() == QEvent::MouseButtonPress && decorationRect.contains(mouseEvent->pos()))
{
if (index.column() == CHECK_BOX_COLUMN)
{
bool data = model->data(index, Qt::UserRole).toBool();
model->setData(index, !data, Qt::UserRole);
}
}
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
接口说明
paint
用来设置表格样式,绘制复选框-状态、大小、显示区域等。editorEvent
用来设置数据,响应鼠标事件,判断当前选中状态,互相切换。
样式
复选框样式:
QCheckBox{
spacing: 5px;
color: white;
}
QCheckBox::indicator {
width: 17px;
height: 17px;
}
QCheckBox::indicator:enabled:unchecked {
image: url(:/Images/checkBox);
}
QCheckBox::indicator:enabled:unchecked:hover {
image: url(:/Images/checkBoxHover);
}
QCheckBox::indicator:enabled:unchecked:pressed {
image: url(:/Images/checkBoxPressed);
}
QCheckBox::indicator:enabled:checked {
image: url(:/Images/checkBoxChecked);
}
QCheckBox::indicator:enabled:checked:hover {
image: url(:/Images/checkBoxCheckedHover);
}
QCheckBox::indicator:enabled:checked:pressed {
image: url(:/Images/checkBoxCheckedPressed);
}
QCheckBox::indicator:enabled:indeterminate {
image: url(:/Images/checkBoxIndeterminate);
}
QCheckBox::indicator:enabled:indeterminate:hover {
image: url(:/Images/checkBoxIndeterminateHover);
}
QCheckBox::indicator:enabled:indeterminate:pressed {
image: url(:/Images/checkBoxIndeterminatePressed);
}
使用
QTableView *pTableView = new QTableView(this);
TableModel *pModel = new TableModel(this);
CheckBoxDelegate *pDelegate = new CheckBoxDelegate(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);
pTableView->setItemDelegate(pDelegate);
// 加载数据、更新界面
QList<FileRecord> recordList;
for (int i = 0; i < 5; ++i)
{
FileRecord record;
record.bChecked = false;
record.strFilePath = QString("E:/Qt/image_%1.png").arg(i + 1);
recordList.append(record);
}
pModel->updateData(recordList);