簡述
使用QTableView,經常會遇到復選框,要實現一個好的復選框,除了常規的功能外,還應注意以下幾點:
- 三態:不選/半選/全選
- 自定義風格(樣式)
下面我們介紹一下常見的實現方式:
編輯委托。
方式:利用委托重載createEditor(),激活QCheckBox。
特點:必須雙擊/選中,才能顯示CheckBox控件。一般不滿足實際中的直接顯示的需要。使用QTableView的setIndexWidget(const QModelIndex &index, QWidget *widget)來實現。
此功能用來顯示可視區域內對應一個數據項的靜態內容。如果想顯示自定義的動態內容或執行自定義編輯器部件,子類化QItemDelegate代替。也就是說,這只適合做靜態數據的顯示,不適合做一些插入、更新、刪除操作的數據顯示。自定義模型QAbstractTableModel,通過flags()函數來實現。
方式:通過將flags()設置為Qt::ItemIsUserCheckable實現可選中,然后配合setData()與data()來實現。
特點:直接顯示,可定義樣式,默認左對齊,很難實現居中、右對齊。自定義委托QAbstractItemDelegate,通過paint()函數來實現。
方式:通過控制editorEvent()實現鼠標的點擊進行全選/半選/不選,然后由paint()實時繪制。
特點:這種方式比較復雜,但適合擴展,除了可以嵌入復選框,還可以繪制其它控件-按鈕、圖片等。
效果
下面我們來介紹如何利用QAbstractTableModel的flags()函數來實現復選功能。
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:
{
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 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::CheckStateRole:
{
if (nColumn == CHECK_BOX_COLUMN)
return record.bChecked ? Qt::Checked : Qt::Unchecked;
}
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;
if (index.column() == CHECK_BOX_COLUMN)
flags |= Qt::ItemIsUserCheckable;
return flags;
}
接口說明
updateData
主要用於更新數據,刷新界面。data
用來顯示數據,根據角色(顏色、文本、對齊方式、選中狀態等)判斷需要顯示的內容。setData
用來設置數據,根據角色(顏色、文本、對齊方式、選中狀態等)判斷需要設置的內容。headerData
用來顯示水平/垂直表頭的數據。flags
用來設置單元格的標志(可用、可選中、可復選等)。
樣式
QSS不僅可以設置表格、表格項樣式(背景、文本等),也可以通過indicator來設置復選框的樣式:
QTableView { border: 1px solid rgb(50, 50, 50); background: rgb(57, 58, 60); gridline-color: rgb(60, 60, 60); }
QTableView::item { border: none; border-bottom: 1px solid rgb(50, 50, 50); }
QTableView::item:selected { background: rgb(0, 160, 230); }
QTableView::indicator { width: 17px; height: 17px; }
QTableView::indicator:enabled:unchecked { image: url(:/Images/checkBox); }
QTableView::indicator:enabled:unchecked:hover { image: url(:/Images/checkBoxHover); }
QTableView::indicator:enabled:unchecked:pressed { image: url(:/Images/checkBoxPressed); }
QTableView::indicator:enabled:checked { image: url(:/Images/checkBoxChecked); }
QTableView::indicator:enabled:checked:hover { image: url(:/Images/checkBoxCheckedHover); }
QTableView::indicator:enabled:checked:pressed { image: url(:/Images/checkBoxCheckedPressed); }
QTableView::indicator:enabled:indeterminate { image: url(:/Images/checkBoxIndeterminate); }
QTableView::indicator:enabled:indeterminate:hover { image: url(:/Images/checkBoxIndeterminateHover); }
QTableView::indicator:enabled:indeterminate:pressed { image: url(:/Images/checkBoxIndeterminatePressed); }
使用
QTableView *pTableView = new QTableView(this);
TableModel *pModel = new TableModel(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("E:/Qt/image_%1.png").arg(i + 1);
recordList.append(record);
}
pModel->updateData(recordList);