目录
开发过程中进程使用MVD模型,添加各种代理控件,本文在此基础上整理一些数据模型代理,包括:QPushbutton、QLineEdit、QPixmap、QChecledBox、QComboBox、QSpinBox、QProcessBar、只读列、自定义窗口等共13种代理方式,如图所示:
在此之前,先来说明一下对MVD做一简单说明:
1、Qt中MVD说明
在传统的MVC模型中,view用于向用户展示model数据。但是,Qt提供的并不是MVC架构,而是一个model/view的设计(也就是说,没有包含一个完整而独立的组件用于管理用户的交互)。一般来说,view仅仅是作用于model数据的展示和用户输入的处理,而不应该去做其他的工作。在这种结构中,为了获得对用户输入控制的灵活性,就将这种交互工作交给了delegate完成(也就是“委托”)。简单来说,就是view将用户输入委托给delegate处理,而不自己去处理。这些组件提供一种输入能力,并且能够在某些view中提供这种交互情形下的渲染,比如在table中双击某单元格进行编辑等。
1.1 View
QListView、QTreeView、QTableView
1.2 Delegate
Delegate可以用于渲染内容,这是通过paint() 和sizeHint()函数来完成的。但是对于一些简单的基于组件的delegate,可以通过继承QItemdelegate或者QStyledItemDelegate 来实现,而不需要重写虚函数。
Qt提供的标准组件中使用QItemDelegate提供编辑功能的支持。这种默认的实现被用在QListView,QTableView和QTreeView中。View实用的delegate可以通过itemDelegate()函数获得,其中,setItemDelegate()函数则可以为一个标准组件设置自定义的delegate.
在Qt4.4之后提供QItemDelegate/QStyledItemDelegate ,可以把控件设置成只读、表格的某一列制定自己需要的控件。默认的委托是QStyledItemDelegate。这两个类可以被相互替代,给view组件提供绘制和编辑功能。主要区别在于,QStyledItemDelegate使用当前的风格(style)去绘制组件,所以当需要设置 qt style sheet时建议使用QStyleItemDelegaet作为父类。使用这两个类的代码是一样的,除了需要使用style进行绘制的部分。
注意:绘制和向视图提供编辑器的方式,但使用起来感觉区别不是很大,之前使用过代理第一时间想到的是重写代理,实现虚函数,在虚函数中创建自己想要的控件并返回就OK了,但是运行程序时发现并没有默认显示出来,只有在编辑的时候显示(也就是双击的时候),这种方式用在输入限制的时候感觉比较合适(对此,官方文档是这样解释的:The QStyledItemDelegate class provides display and editing facilities for data items from a model.)。也就是说,如果想要在load的时候就显示出来控件,必须写在void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;里;如果控件需要有相应的动作,就需要写在editorEvent中
如果delegate没有支持为你的数据类型进行绘制,或者你希望自己绘制item,那么就可以继承QStyleItemDelegate类,并且重写paint()或者还需要重写sizeHit()函数。paint()函数会被每一个item独立调用,而sizeHint()函数则可以定义每一个item的大小。在重写paint()函数的时候,通常需要用if语句找到你需要进行渲染的诗句类型并进行绘制,其他的数据类型需要调用父类的实现进行绘制。
继承QStyledItemDelegate需要实现以下函数:
class MyDelegate: public QStyledItemDelegate
{
//创建你编辑时候的控件,返回修改数据的组件
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
//编辑的时候设置数据到上面创建的editor中。
void setEditorData(QWidget *editor, const QModelIndex &index) const;
//编辑完成,保存数据到model中
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
//设置编辑控件的位置和大小、样式等(保证editor显示在item view的合适位置以及大小)
void updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const;
}
例如,添加一个进度条控件:
void WidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.column() == 1) {
int progress = index.data().toInt();
QStyleOptionProgressBar progressBarOption;
progressBarOption.rect = option.rect;
progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
progressBarOption.progress = progress;
progressBarOption.text = QString::number(progress) + "%";
progressBarOption.textVisible = true;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
} else
QStyledItemDelegate::paint(painter, option, index);
}
1.3 Model/View的基本原理
1、数据(Data)是实际的数据,如数据库的一个数据表或 SQL查询结果,内存中的一个StringList,或磁盘文件结构等。
2、视图或视图组件(View)是屏幕上的界面组件,视图从数据模型获得每个数据项的模型索引(model index),通过模型索引获取数据,然后为界面组件提供显示数据,Qt提供一些现成的数据视图组件,如QListView、QTreeView 和QTableView等。
3、模型或数据模型(Model)与实际数据通信,并为视图组件提供数据接口。它从原始数据提取需要的内容,用于视图组件进行显示和编辑。Qt中有一些预定义的数据模型,如QStringListModel
可作为StringList的数据模型,QSqlTableModel 可以作为数据库中一个数据表的数据模型。
模型(Model)与数据源(Data)通信;视图(View)从模型(Model)中获取模型索引;在标准视图中,代理(delegate)渲染数据项。编辑项时,代理将直接使用模型索引(model index)与模型通信。模型、视图和代理之间使用信号和槽通信。当源数据发生变化时,数据模型发射信号通知视图组件:当用户在界面上操作数据时,视图组件发射信号表示这些操作信息;当编辑数据时,代理发射信号告知数据模型和视图组件编辑器的状态。
所有的基于项数据(item data)的数据模型(Model)都基于QAbstractltemModel类,此类定义了视图和代理访问数据的接口。数据本身不必存储在模型中;它可以保存在由单独的类、文件、数据库或其他应用程序组件提供的数据结构或存储库中。QabstracteModel提供了一个数据接口,该接口足够灵活,可以处理以table、list和tree的形式表示数据的视图。
Qt提供了一些现成的模型,可用于处理数据项:
- QStringListModel 用于处理QString列表的数据模型类
- QStandardItemModel 标准的基于项数据的数据模型类,管理更复杂的树结构,每个项数据内都可以包含任意类型的数据
- QFileSystemModel 提供有关本地文件系统中的文件和目录的信息,文件系统模型类
- QSqlQueryModel 用于数据库sql查询结果的数据模型类
- QSqlTableModel 用于数据库中一个数据表的数据模型类
- QSqlRelationalTableModel 用于关系型数据表的数据模型类
- QSortFilterProxyModel 与其他数据模型结合,提供排序和过滤功能的数据模型类
如果这些标准模型不符合您的要求,您可以将QAbstractItemModel、QAbstractListModel或QAbstractTableModel子类化,以创建自己的自定义模型。
一般需要实现的子类为:
//获得模型的行数
int rowCount(const QModelIndex &parent) const;
//获得模型的列数
int columnCount(const QModelIndex &parent) const;
//向模型中设置数据
bool setData(const QModelIndex &index, const QVariant &value, int role );
//获得模型的数据
QVariant data(const QModelIndex &index, int role) const;
//设置模型的标题
QVariant headerData(int section, Qt::Orientation orientation, int role ) const;
2、代码是现实示例
2.1 设置样式文件
QString qssData = nullptr; QFile fileqss(":/qss/QSSUITableView"); if(fileqss.open(QFile::ReadOnly)) { qssData = fileqss.readAll(); fileqss.close(); } ui->tableView->setStyleSheet(qssData);
2.2 set base attribute
ui->tableView->verticalHeader()->hide(); // 隐藏垂直头
ui->tableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // 隐藏水平头
//ui->tableView->horizontalHeader()->setStretchLastSection(true); // 设置随后一列拉伸
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); // 设置列平均分配
//ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); // 设置TreeWidget水平滚动和自适应宽度
//ui->tableView->setColumnWidth(headersList.count() - 1, TITLE_FIXED_HEIGHT); // 设置最后一列固定
//ui->tableView->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); // 行单选
ui->tableView->setSelectionMode(QAbstractItemView::SelectionMode::MultiSelection); // 行多选 (单选QAbstractItemView::SingleSelection 多选:QAbstractItemView::MultiSelection)
ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); // 不可编辑
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); // 设置选中模式为整行
ui->tableView->setShowGrid(false); // 显示/隐藏网格线
setFocusPolicy(Qt::FocusPolicy::NoFocus); // 设置选中之后无虚线焦点
//horizontalHeader()->setMinimumSectionSize(100); // 设置最小列宽
//horizontalHeader()->setMaximumSectionSize(100); // 设置最大列宽
ui->tableView->verticalHeader()->setDefaultSectionSize(25); // 可以设置tableview所有列的默认行高为15。
//horizontalHeader()->setDefaultSectionSize(15); // 可以设置tableview所有行的默认列宽为15。
ui->tableView->setWordWrap(false); // 设置不自动换行
setMouseTracking(true); // 设置鼠标追踪
// 设置第0列固定宽度
/*
ui->tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
ui->tableView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
ui->tableView->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
ui->tableView->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);
ui->tableView->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Fixed);
ui->tableView->horizontalHeader()->setSectionResizeMode(5, QHeaderView::Fixed);
ui->tableView->setColumnWidth(0, 50);
ui->tableView->setColumnWidth(2, 150);
ui->tableView->setColumnWidth(3, 150);
ui->tableView->setColumnWidth(4, 150);
ui->tableView->setColumnWidth(5, 150);
*/
2.3 设置model
model = new QStandardItemModel;
ui->tableView->setModel(model);
2.4 设置表头
QStringList headerList;
headerList<<"姓名"<<"QSpinBox"<<"QComboBox"<<"QPushButton"<<"CheckBox"<<"QPixmap"<<"QLineEdit"<<"ReadOnly"<<"Text"<<"ProcessBar"<<"DateEdit"<<"CustomWidget"<<"pDoubleProcessBar";
model->setHorizontalHeaderLabels(headerList);
2.5 设置数据
model->setItem(0,0,new QStandardItem("张三"));
model->setItem(1,0,new QStandardItem("李四"));
model->setItem(2,0,new QStandardItem("王二"));
model->setItem(3,0,new QStandardItem("小明同学"));
model->setItem(0,1,new QStandardItem("1"));
model->setItem(1,1,new QStandardItem("2"));
model->setItem(2,1,new QStandardItem("3"));
ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); // 选择一行
2.6 添加代理控件
2.6.1 添加 QSpinBox 代理
CUISpinBoxDelegate *pSpinBox = new CUISpinBoxDelegate(this);
ui->tableView->setItemDelegateForColumn(1, pSpinBox);
2.6.2 添加 QComboBox 代理
CUIComboBoxDelegate *pComboBox = new CUIComboBoxDelegate(this);
ui->tableView->setItemDelegateForColumn(2, pComboBox);
2.6.3 添加 QPushButton 代理
CUIMutipleButtonDeleagate *pMutipleBtn = new CUIMutipleButtonDeleagate(QStringList() << "修改" << "删除", this);
ui->tableView->setItemDelegateForColumn(3, pMutipleBtn);
connect(pMutipleBtn, &CUIMutipleButtonDeleagate::editData, [&](){
QMessageBox::information(this, "提示", "这是一个编辑按钮");
});
connect(pMutipleBtn, &CUIMutipleButtonDeleagate::deleteData, [&](){
QMessageBox::information(this, "提示", "这是一个删除按钮");
});
2.6.4 添加 CheckBox 代理
/*CUITableHeaderView **/pTableHeaderView = new CUITableHeaderView(Qt::Horizontal, ui->tableView);
connect(pTableHeaderView, &CUITableHeaderView::stateChanged, this, &MainWindow::headerStateChangedSlot);
connect(model, SIGNAL(itemChanged(QStandardItem*)), this, SLOT(itemChangedSlot(QStandardItem*)));
ui->tableView->setHorizontalHeader(pTableHeaderView);
CUICheckBoxDelegate *pCheckedBox = new CUICheckBoxDelegate(this);
ui->tableView->setItemDelegateForColumn(4, pCheckedBox);
2.6.5 添加 Pixmap 代理
CUIPixmapDelegate *pPixmap = new CUIPixmapDelegate(this);
ui->tableView->setItemDelegateForColumn(5, pPixmap);
2.6.6 添加 LineEdit 代理
CUILineEditDelegate *pLineEdit = new CUILineEditDelegate(this);
2.6.7 添加 ReadOnly 代理
CUIOnlyReadDelegate *pReadOnly = new CUIOnlyReadDelegate(this);
ui->tableView->setItemDelegateForColumn(7, pReadOnly);
2.6.8 添加 Text 代理
CUITextDelegate *pLabel = new CUITextDelegate(this);
ui->tableView->setItemDelegateForColumn(8, pLabel);
2.6.9 添加 QProcessBar 代理
CUIProcessBarDelegate *pProcessBar = new CUIProcessBarDelegate();
ui->tableView->setItemDelegateForColumn(9, pProcessBar);
QModelIndex index = model->index(0, 9, QModelIndex());
model->setData(index,29);
2.6.10 添加 DateEdit 代理
CUIDateEditDelegate *pDateEdit = new CUIDateEditDelegate();
ui->tableView->setItemDelegateForColumn(10, pDateEdit);
2.6.11 添加 添加自定义窗口 代理
CUICustomDelegate *pCustomWidget = new CUICustomDelegate(this);
ui->tableView->setItemDelegateForColumn(11, pCustomWidget);
2.6.12 添加 DoubleProcessBar 代理
CUIDoubleProcessBarDelegate *pDoubleProcessBar = new CUIDoubleProcessBarDelegate(this);
ui->tableView->setItemDelegateForColumn(12, pDoubleProcessBar);
2.7 获取某一单元格数据
//model->index(0, 1).data();
详细代码下载