目录
2.利用QAbstractItemView类的setIndexWidget函数实现
1.前言
工作中经常会遇到这样的需求:向QAbstractItemView子类如QTreeView、QTableView单元格插入窗体小部件,如:进度条、按钮、单行编辑框等。下面链接的系列博文就是讲解如何实现该功能的。
这些系列博文所说的技术点适用于同时满足下面条件的所有类:
-
模型类从 QAbstractItemModel派生。
-
代理类从QStyledItemDelegate或QItemDelegate派生。
-
视图类是QAbstractItemView的子类。
这些系列博文用到了Qt的model/view framework框架,如果对Qt的“模型/视图/代理”框架不懂,这些系列文章很难读懂。如果不懂这方面的知识,请在Qt Assistant 中输入Model/View Programming 学习了解。读者本机Qt安装目录下的Examples\Qt-XX.XX.XX\widgets\itemviews目录下有很多model/view framework的例子,可以进行自学了解,其中XX.XX.XX为Qt的版本号,如:5.14.1。
因为QColumnView、QHeaderView、QListView、QTableView、QTreeView、QListWidget 、QUndoView、QTableWidget、QTreeWidget都是从QAbstractItemView继承,故这些系列博文所说的技术点也适用于这些类。
本博文通过Qt的QAbstractItemView视图基类自带的函数来实现该功能。
2.利用QAbstractItemView类的setIndexWidget函数实现
QAbstractItemView类有个setIndexWidget函数,该函数声明如下:
void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget)
第1个参数是模型索引,你可以想象为就是一个单元格所在的行列索引号。如果该参数无效,例如是根节点索引时,则这个函数什么都不做。
第2个参数是单元格要插入的窗体部件。注意:窗体部件autoFillBackground 属性要设置为true,否则窗体背景色是透明的。如果在指定索引处的A窗体被后来的窗体B替换,则A窗体被删除,如下:
setIndexWidget(index, new QLineEdit);
...
setIndexWidget(index, new QTextEdit);
则QLineEdit会被删除。本函数仅仅能用来根据项的数据在可见区域显示一些静态内容,如果需要显示一些动态内容或者显示一个自定义的编辑框部件,需通过子类化QStyledItemDelegate类实现。
以QTableView为例子来说明。 为了给QTableView提供数据,必须实现一个模型即从QAbstractTableModel.类派生出自己的模型类来,模型类model.cpp代码如下:
#include "model.h"
CModel::CModel(QObject *parent)
: QAbstractTableModel(parent)
{}
CModel::~CModel()
{}
QVariant CModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const
{
return QVariant();
}
// 作为例子,假想QTableView有2列
int CModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const
{
return 2;
}
// 作为例子,假想QTableView有88行
int CModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const
{
return 88;
}
QVariant CModel::headerData(int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/) const
{
if (orientation != Qt::Horizontal) // 作为例子演示,我们只关心表头是水平的情况
return QVariant();
if (role != Qt::DisplayRole)// 作为例子演示,我们只关心 Qt::DisplayRole
return QVariant();
// 构造列,第1列列名为"button";第2列列名为"checkbox"
if(0 == section)
return "button";
else if(1 == section)
return "checkbox";
return QVariant();
}
QtWidgetsApplication1.cpp实现如下:
#include "QtWidgetsApplication1.h"
#include "model.h"
#include<QPushButton>
#include<QScrollBar>
#include<QCheckBox>
QtWidgetsApplication1::QtWidgetsApplication1(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
m_pModel = new CModel(this); // new出一个模型对象
ui.tableView->setModel(m_pModel);// 将表格视图的模型对象设置为上步构建出来的模型对象
installWidget();
}
void QtWidgetsApplication1::installWidget()
{
auto nRowCount = m_pModel->rowCount(); // 获取表视图有多少行
auto nColCount = m_pModel->columnCount();// 获取表视图有多少列
// 表视图对象中的每行第0列插入一个QPushButton按钮,在第1列插入一个QCheckBox对象
for (auto nRowIndx = 0; nRowIndx < nRowCount; ++nRowIndx)
{
auto modelIndx = m_pModel->index(nRowIndx, 0);
QString qsText(QString("button%1").arg(nRowIndx));
ui.tableView->setIndexWidget(modelIndx, new QPushButton(qsText, this));
modelIndx = m_pModel->index(nRowIndx, 1);
qsText = (QString("checkbox%1").arg(nRowIndx));
ui.tableView->setIndexWidget(modelIndx, new QCheckBox(qsText, this));
}
}
installWidget函数利用setIndexWidget函数在表视图对象中的每行第0列插入一个QPushButton按钮,在第1列插入一个QCheckBox对象。效果如下:
3.后记
如果是QTableWidget类,则也可以用如下函数插入窗体小部件:
void QTableWidget::setCellWidget(int row, int column, QWidget *widget)
如下代码:
setCellWidget(row, column, new QLineEdit);
setCellWidget(row, column + 1, new QTextEdit);
实现向第row行的column列和column+1列分别插入了一个QLineEdit和一个QTextEdit窗体小部件。