关于QT的MVD框架这里就不做赘述,通篇介绍的话得占不少版面,其实作为qt开发者,基本上只要有个能跑起来的demo ,相关的技术点不难理解,新手学习mvd的难点在于没有一个小型的,直观的demo能直接梳理出三者的关系。
关于MVD的理论,我们只需要知道以下几点:
1)一般来说自定义委托都继承自QStyledItemDelegate和QItemDelegate这个两个类。
QStyledItemDelegate是所有Qt项目视图的默认委托,并在它们创建时安装在它们上面。二者的区别在于绘制和向视图提供编辑器的方式。QStyledItemDelegate使用当前样式绘制,并且能够使用 Qt Style Sheet,因此我们推荐在自定义委托时,使用 QStyledItemDelegate作为基类。不过,除非自定义委托需要自己进行绘制,否则,二者的代码其实是一样的。
继承 QStyledItemDelegate需要实现以下几个函数:
· createEditor():返回一个组件。该组件会被作为用户编辑数据时所使用的编辑器,从模型中接受数据,返回用户修改的数据。(//创建你编辑时候的控件)
· setEditorData():提供上述组件在显示时所需要的默认值。(//编辑的时候设置数据到上面创建的editor中)
· updateEditorGeometry():确保上述组件作为编辑器时能够完整地显示出来。(//设置编辑控件的位置和大小。样式等)
·setModelData():返回给模型用户修改过的数据。(//编辑完成,保存数据到data中)
2)model的选择主要考虑是视图的类型
QStringListModel用于处理字符串列表的数据模型,可以作为QListView的数据模型,在界面上显示和编辑字符串列表。
QStandardItemModel以项数据(item data)为基础的标准数据结构模型类,通常与QTableView配合使用,实现通用的二维数据的管理。
3)视图类型
QListView:用于显示单列的列表数据,适用于一维数据的操作
QTreeView:用于显示树状结构数据,适用于树状结构数据的操作
QTableView:用于显示表格状数据,适用于二维表格型数据的操作
QColumnView:用多个QListView显示树状层次结构,树状结构的一层用一个QListView显示
QHeaderView:提供行表头或列表头的视图组件,如QTableView的行表头和列表头
牢记上面出现的类名以及其所在MVD框架的所属部分
关于MVD框架的学习,其实只有MD需要学习,view就是控件,默认的几种视图控件已经可以满足几乎全部的开发需求,不用在view上浪费时间
MD中,最重要的就是model,学会了model 整个框架就学会了80%,delegate的占比很小,但是挺难的,难在难以精通但跟我们学习MVD框架没太多影响
关于model的学习,直接给你讲实现接口根本不存在看懂的可能性,先根据下方视频链接,看完并实操一次,根据视频讲解理解自定义model 必须要实现的接口以及这些接口的功能
QListView使用自定义Model
看完上面的视频,基本对mvd有个了解,比如model是自己定义的,view是QlistView,那delegate呢,感觉没用过,其实用过了,视频里面展示的编辑功能就是delegate提供的,还记得上文中提到的QStyledItemDelegate是所有Qt项目视图的默认委托,编辑框的操作就是QStyledItemDelegate的默认实现。
上述视频需要反复观看理解自定义model必须实现的接口!!!
想要在qlistview视图里面展示进度条,需要自定义Delegate实现
关于实现进度条可参考下方链接
自定义代理(MVD)
注意,一定要理解设置进度数值如何反应到界面上。model是根源,view只做展示,但是如何展示是通过delegate来控制
对于该代码的理解需基本吃透上文知识点
上文中自定义的进度条只能显示数字,如何能显示文字加数字呢?原因在于model管理的数据是一维的,想要同时控制 文字 + 进度,则需要自定义数据类型来替换 model默认的QString类型
全部代码如下,可新建项目编译查看
/*
* 文件名: customlistmodel.h
* 文件说明 : QlistView 自定义model
* 该自定义model 使用的是自定义数据类型Beaninfo,必须搭配 customlistdelegate.h 使用
* 该Model 与 Delegate 存在bug_1,即addrow调用时设置的Progress > 100,则 Delegate 会原样显示 > 100的数值,该bug有利于理解MVD框架,因此未修改
* 使用范例:
* CustomListModel * CusModel = new CustomListModel();
* ui->listView->setModel(CusModel);
* CustomListDelegate * CusDelegate = new CustomListDelegate();
* ui->listView->setItemDelegate(CusDelegate);
*
* CusModel->addrow(BeanInfo{"download1",0});
* CusModel->Updaterow(0,BeanInfo{"download1",10});
*/
#ifndef CUSTOMLISTMODEL_H
#define CUSTOMLISTMODEL_H
#include
#include
#include
#include
//自定义 model 存储的数据类型
typedef struct BeanDownloadInfo
{
QString DownloadFilename; //当前下载的文件名字
int Progress; //当前的下载进度,取值为0~100;超过100会引发bug_1
}BeanInfo,*pBeanInfo;
class CustomListModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit CustomListModel(QObject *parent = nullptr);
/*
* UpdateProgress 用于向model中更新一行数据,使用的是自定义数据类型Beaninfo
*/
int Updaterow(int row,BeanInfo progressInfo);
/*
* addrow 用于向model尾部添加一行数据,使用的是自定义数据类型Beaninfo
* progressInfo.Progress > 100 会引发bug_1
*/
int addrow(BeanInfo progressInfo);
// Header:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
// Basic functionality:
QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
// Editable:
bool setData(const QModelIndex &index, const QVariant &value,int role = Qt::EditRole) override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
// Add data:
bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override;
// Remove data:
bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
bool removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) override;
private:
std::vector m_DownloadInfoList;
};
Q_DECLARE_METATYPE(BeanInfo)
#endif // CUSTOMLISTMODEL_H
#include "customlistmodel.h"
CustomListModel::CustomListModel(QObject *parent)
: QAbstractItemModel(parent)
{
}
int CustomListModel::addrow(BeanInfo progressInfo)
{
int i = rowCount();
m_DownloadInfoList.push_back(progressInfo);
emit dataChanged(index(i,1), index(i,1), {Qt::DisplayRole}); //list 永远只有1列
return rowCount();
}
QVariant CustomListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
return QStringLiteral("详细下载信息");
}
QModelIndex CustomListModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
return createIndex(row,column);
}
QModelIndex CustomListModel::parent(const QModelIndex &index) const
{
return QModelIndex();
}
int CustomListModel::rowCount(const QModelIndex &parent) const
{
/*
if (!parent.isValid())
return 0;
*/
return m_DownloadInfoList.size();
}
int CustomListModel::columnCount(const QModelIndex &parent) const
{
/*
if (!parent.isValid())
return 0;*/
return 1; //list 永远只有1列
}
QVariant CustomListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if(role == Qt::DisplayRole || role == Qt::EditRole)
{
return QVariant().fromValue(m_DownloadInfoList[index.row()]);
}
return QVariant();
}
bool CustomListModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (data(index, role) != value) {
if(role == Qt::EditRole)
{
m_DownloadInfoList[index.row()] = value.value();
}
emit dataChanged(index, index, {role});
return true;
}
return false;
}
Qt::ItemFlags CustomListModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}
bool CustomListModel::insertRows(int row, int count, const QModelIndex &parent)
{
beginInsertRows(parent, row, row + count - 1);
for(int i = row;i < row+ count;i++)
{
auto it = m_DownloadInfoList.begin() + i;
m_DownloadInfoList.insert(it,BeanInfo{});
}
endInsertRows();
return true;
}
//list列表仅有一列,无需实现该函数
bool CustomListModel::insertColumns(int column, int count, const QModelIndex &parent)
{
/*
beginInsertColumns(parent, column, column + count - 1);
endInsertColumns();
return true;*/
return false;
}
bool CustomListModel::removeRows(int row, int count, const QModelIndex &parent)
{
beginRemoveRows(parent, row, row + count - 1);
// FIXME: Implement me!
endRemoveRows();
return true;
}
bool CustomListModel::removeColumns(int column, int count, const QModelIndex &parent)
{
beginRemoveColumns(parent, column, column + count - 1);
// FIXME: Implement me!
endRemoveColumns();
return true;
}
int CustomListModel::Updaterow(int row,BeanInfo progressInfo)
{
if(progressInfo.Progress>=100)
progressInfo.Progress=100;
setData(index(row,0),QVariant().fromValue(progressInfo));
return progressInfo.Progress;
}
/*
* 文件名: customlistdelegate.h
* 文件说明 : QlistView 自定义 进度条Delegate
* 该自定义Delegate 使用的是自定义数据类型Beaninfo,必须搭配 customlistmodel.h 使用
* 该Model 与 Delegate 存在bug_1,即addrow调用时设置的Progress > 100,则 Delegate 会原样显示 > 100的数值,该bug有利于理解MVD框架,因此未修改
* 使用范例:
* CustomListModel * CusModel = new CustomListModel();
* ui->listView->setModel(CusModel);
* CustomListDelegate * CusDelegate = new CustomListDelegate();
* ui->listView->setItemDelegate(CusDelegate);
*
* CusModel->addrow(BeanInfo{"download1",0});
* CusModel->Updaterow(0,BeanInfo{"download1",10});
*/
#ifndef CUSTOMLISTDELEGATE_H
#define CUSTOMLISTDELEGATE_H
#include
#include
#include
#include
#include
#include
#include
class CustomListDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
CustomListDelegate();
protected:
void paint(QPainter* painter,const QStyleOptionViewItem &option, const QModelIndex &index) const;
private:
};
#endif // CUSTOMLISTDELEGATE_H
#include "customlistdelegate.h"
#include "customlistmodel.h"
CustomListDelegate::CustomListDelegate()
{
}
void CustomListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if(index.isValid() && index.column() == 0)
{
QStyleOptionProgressBar bar;
bar.rect = option.rect;
bar.progress = index.data().value().Progress;
bar.maximum = 100;
bar.minimum = 0;
if(bar.progress == 100)
bar.text = index.data().value().DownloadFilename + " Done";
else
bar.text = index.data().value().DownloadFilename + " " + QString::number(bar.progress)+"%";
bar.textVisible = true;
bar.textAlignment = Qt::AlignCenter;
QApplication::style()->drawControl(QStyle::CE_ProgressBar,&bar,painter);
}
else
{
QStyledItemDelegate::paint(painter,option,index);
}
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_CustomListModel = new CustomListModel();
ui->listView->setModel(m_CustomListModel);
m_CustomListDelegate = new CustomListDelegate();
ui->listView->setItemDelegate(m_CustomListDelegate);
}
MainWindow::~MainWindow()
{
delete ui;
}
static int g_progress = 0;
//增加 按钮点击
void MainWindow::on_pushButton_clicked()
{
g_progress +=4;
qDebug() << g_progress;
m_CustomListModel->addrow(BeanInfo{"download1",g_progress});
m_CustomListModel->Updaterow(0,BeanInfo{"download1",g_progress});
}
运行如图:
如果你能理解bug并修改bug,恭喜你,你已经完全学会了MVD框架入门,想要再深入学习就另请高明吧