目录
所谓MV结构,是"model-view"(模型-视图)的简称。也就是说,表格的数据保存在model中,而视图由view实现。在我前面的很多博客,如设置QTableView的内容自动换行(2)_qstandarditem 文本换行显示-CSDN博客
如何截获QTableView的鼠标事件?_qtableview里面捕获鼠标移动事件-CSDN博客
Qt如何正确的显示、修改表格(QTableView)的内容_qt tableview修改表格内容-CSDN博客
我已经花了很多笔墨描述Qt表格的软件设计方法。在本篇文章中,我将介绍如何编辑表格--表格代理。
预备知识
模型
模型用来存储表格数据。这个类通常派生自QAbstractTableModel。这个派生类的通常由如下几个部分组成:
成员变量:
m_data m_data通常是一个list,vector或者map等常见的数据结构。
用来存储数据。
以下为虚函数:
columnCount 列数
rowCount 行数
data 表格显示的内容
flags flags决定了各个单元格可否被编辑。假如不需要编辑表格,
也可以不重写此虚函数
headerData headerData返回值决定了各行/列标题名称
setData setData决定了编辑单元格以后,编辑结果如何影响m_data。
假如不需要编辑表格,也可以不重写此虚函数
关联
通过QTableView::setModel()建立与模型的关联
刷新
当m_data发生变化时,或者界面需要刷新时,刷新动作由dataChanged信号触发。很多人奇怪,怎么没看到对应dataChanged的槽函数?Qt内部已经将dataChanged与对应的槽函数做了关联,开发者不必操心,只要发出信号即可。
示例
代理
专门建立一个代理类
cpp
#include "EdtDelegate.h"
EdtDelegate::EdtDelegate(int iMin, int iMax, QObject *parent)
: QStyledItemDelegate(parent)
{
m_pIntVld = new QIntValidator(iMin, iMax, this);
}
EdtDelegate::~EdtDelegate()
{
}
QWidget *EdtDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &/* index */) const
{
QLineEdit *editor = new QLineEdit(parent);
editor->setValidator(m_pIntVld);
return editor;
}
void EdtDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QString qstrTxt = index.model()->data(index, Qt::DisplayRole).toString();
QLineEdit *Edt = static_cast<QLineEdit*>(editor);
Edt->setText(qstrTxt);
}
void EdtDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QLineEdit *edt = static_cast<QLineEdit*>(editor);
auto qstrTxt = edt->text();
model->setData(index, qstrTxt, Qt::EditRole);
}
void EdtDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
cpp
#include "ComboDelegate.h"
#include <QComboBox>
ComboDelegate::ComboDelegate(QStringList qstrlst, QObject *parent)
: QStyledItemDelegate(parent), m_qstrlst(qstrlst)
{
}
QWidget *ComboDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/* option */,
const QModelIndex &/* index */) const
{
QComboBox *editor = new QComboBox(parent);
editor->addItems(m_qstrlst);
return editor;
}
void ComboDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QString qstrTxt = index.model()->data(index, Qt::DisplayRole).toString();
QComboBox *cmbBox = static_cast<QComboBox*>(editor);
cmbBox->setCurrentText(qstrTxt);
}
void ComboDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
QComboBox *cmbBox = static_cast<QComboBox*>(editor);
auto qstrTxt = cmbBox->currentText();
model->setData(index, qstrTxt, Qt::EditRole);
}
void ComboDelegate::updateEditorGeometry(QWidget *editor,
const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
{
editor->setGeometry(option.rect);
}
这里有人会问,createEditor里面有new的动作,为什么没看到释放?createEditor返回一个指针。Qt内部有一套机制,在编辑结束后,自动释放该指针。
模型
cpp
#include "Model.h"
//Model
Model::Model(int iTotalCol, QStringList qstrlstHeader, QList<int> lstNonEditableCols,
QObject *parent) : QAbstractTableModel(parent),
m_lstNonEditableCols(lstNonEditableCols),
m_iTotalCol(iTotalCol), m_qstrlstHeader(qstrlstHeader)
{
}
int Model::rowCount(const QModelIndex &) const
{
return m_data.size();
}
int Model::columnCount(const QModelIndex &) const
{
return m_iTotalCol;
}
QVariant Model::data(const QModelIndex &index, int role) const
{
int i = index.row(), j = index.column();
if ((i >= 0) && (i < m_data.size()))
{
if(m_data.at(i).size() > j)
{
if(role == Qt::DisplayRole)
{
//m_data的类型是QList<QStringList>,每一个QStringList对应表格里一行数据
//m_data.at(i).at(j)对应的就是第i行,第j列的数据
return m_data.at(i).at(j);
}
else if(role == Qt::TextAlignmentRole)
return Qt::AlignCenter;
else
{
}
}
else
{
return QString("");
}
}
else
{
}
return QVariant();
}
QVariant Model::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role == Qt::DisplayRole)
{
if(orientation == Qt::Horizontal)//横向排列的标题栏,也就是各个列的标题
{
if(m_qstrlstHeader.size() > section)
return m_qstrlstHeader.at(section);
else
{
return QString("");
}
}
else
{
//纵向排列的标题栏,也就是各个行的标题
return QString("");
}
}
else if(role == Qt::TextAlignmentRole)
{
//文字对齐方式设置为居中对齐
return Qt::AlignCenter;
}
else
{
}
return QAbstractTableModel::headerData(section, orientation, role);
}
void Model::vSetData(QList<QStringList> & lstData)
{
m_data.clear();
m_data.append(lstData);
//发出dataChanged信号,界面上才更新表格内容
emit dataChanged(createIndex(0,0), createIndex(0, columnCount()-1));
}
Qt::ItemFlags Model::flags(const QModelIndex &index) const
{
if(m_lstNonEditableCols.contains(index.column()))
//假如index对应的列是不可编辑的列
return QAbstractTableModel::flags(index);
else
//假如index对应的列是可编辑的列,就给它添加ItemIsEditable属性
return Qt::ItemIsEditable | QAbstractTableModel::flags(index);
}
bool Model::setData(const QModelIndex &index, const QVariant &value, int role)
{
if(role == Qt::EditRole)
{
//编辑单元格之后,用编辑结果更新对应的m_data
int row = index.row(), col = index.column();
QStringList qstrlst = m_data.at(row);
qstrlst.replace(col, value.toString());
m_data.replace(row, qstrlst);
}
return true;
}
界面
cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_pModel = new Model(4, {"1", "2", "3", "4"}, {0,1});
ui->tableView->setModel(m_pModel);
//setEditTriggers(QTableView::DoubleClicked);注明触发编辑单元格的方式是鼠标双击
ui->tableView->setEditTriggers(QTableView::DoubleClicked);
QList<QStringList> lstqstrlst;
lstqstrlst<<QStringList{"a", "b", "c", "d"};
//向表格中注入数据
m_pModel->vSetData(lstqstrlst);
m_scpCmbDelg.reset(new ComboDelegate({"a","b","c"}, this));
//表格第2列采用combobox代理(其实是第3列,因为从0开始)
ui->tableView->setItemDelegateForColumn(2, m_scpCmbDelg.data());
m_scpEdtDelg.reset(new EdtDelegate(0,10, this));
//表格第3列采用lineedit代理
ui->tableView->setItemDelegateForColumn(3, m_scpEdtDelg.data());
}
MainWindow::~MainWindow()
{
delete ui;
}
结果
双击第一列、第二列,无法编辑单元格。但是双击第三列,可以编辑combobox;双击第四列,可以编辑lineedit:
qt代理,访问csdn金色熊族,获取完整信息