qt MVD 框架入门教学归纳实例:QListView + QAbstractItemModel + QStyledItemDelegate 实现自定义进度条(同时显示文件名 + 实时跟新进度)

前置理论基础

关于QT的MVD框架这里就不做赘述,通篇介绍的话得占不少版面,其实作为qt开发者,基本上只要有个能跑起来的demo ,相关的技术点不难理解,新手学习mvd的难点在于没有一个小型的,直观的demo能直接梳理出三者的关系。

关于MVD的理论,我们只需要知道以下几点:

1)一般来说自定义委托都继承自​​QStyledItemDelegate​​​和​​QItemDelegate​​这个两个类。

QStyledItemDelegate是所有Qt项目视图的默认委托,并在它们创建时安装在它们上面。二者的区别在于绘制和向视图提供编辑器的方式。QStyledItemDelegate使用当前样式绘制,并且能够使用 Qt Style Sheet,因此我们推荐在自定义委托时,使用 QStyledItemDelegate作为基类。不过,除非自定义委托需要自己进行绘制,否则,二者的代码其实是一样的。

继承 QStyledItemDelegate需要实现以下几个函数:

    · createEditor():返回一个组件。该组件会被作为用户编辑数据时所使用的编辑器,从模型中接受数据,返回用户修改的数据。(//创建你编辑时候的控件)

    · setEditorData():提供上述组件在显示时所需要的默认值。(//编辑的时候设置数据到上面创建的editor中)

    · updateEditorGeometry():确保上述组件作为编辑器时能够完整地显示出来。(//设置编辑控件的位置和大小。样式等)

    ·setModelData():返回给模型用户修改过的数据。(//编辑完成,保存数据到data中)

qt MVD 框架入门教学归纳实例:QListView + QAbstractItemModel + QStyledItemDelegate 实现自定义进度条(同时显示文件名 + 实时跟新进度)_第1张图片

2)model的选择主要考虑是视图的类型

QStringListModel用于处理字符串列表的数据模型,可以作为QListView的数据模型,在界面上显示和编辑字符串列表。

QStandardItemModel以项数据(item data)为基础的标准数据结构模型类,通常与QTableView配合使用,实现通用的二维数据的管理。

qt MVD 框架入门教学归纳实例:QListView + QAbstractItemModel + QStyledItemDelegate 实现自定义进度条(同时显示文件名 + 实时跟新进度)_第2张图片

3)视图类型

QListView:用于显示单列的列表数据,适用于一维数据的操作
QTreeView:用于显示树状结构数据,适用于树状结构数据的操作
QTableView:用于显示表格状数据,适用于二维表格型数据的操作
QColumnView:用多个QListView显示树状层次结构,树状结构的一层用一个QListView显示
QHeaderView:提供行表头或列表头的视图组件,如QTableView的行表头和列表头

qt MVD 框架入门教学归纳实例:QListView + QAbstractItemModel + QStyledItemDelegate 实现自定义进度条(同时显示文件名 + 实时跟新进度)_第3张图片

牢记上面出现的类名以及其所在MVD框架的所属部分

关于MVD框架的学习,其实只有MD需要学习,view就是控件,默认的几种视图控件已经可以满足几乎全部的开发需求,不用在view上浪费时间

MD中,最重要的就是model,学会了model 整个框架就学会了80%,delegate的占比很小,但是挺难的,难在难以精通但跟我们学习MVD框架没太多影响

自定义Model

关于model的学习,直接给你讲实现接口根本不存在看懂的可能性,先根据下方视频链接,看完并实操一次,根据视频讲解理解自定义model 必须要实现的接口以及这些接口的功能

QListView使用自定义Model

看完上面的视频,基本对mvd有个了解,比如model是自己定义的,view是QlistView,那delegate呢,感觉没用过,其实用过了,视频里面展示的编辑功能就是delegate提供的,还记得上文中提到的QStyledItemDelegate是所有Qt项目视图的默认委托,编辑框的操作就是QStyledItemDelegate的默认实现。

上述视频需要反复观看理解自定义model必须实现的接口!!!

自定义Delegate

想要在qlistview视图里面展示进度条,需要自定义Delegate实现

关于实现进度条可参考下方链接

自定义代理(MVD)

注意,一定要理解设置进度数值如何反应到界面上。model是根源,view只做展示,但是如何展示是通过delegate来控制

升级版MVD进度条

对于该代码的理解需基本吃透上文知识点

上文中自定义的进度条只能显示数字,如何能显示文字加数字呢?原因在于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框架入门,想要再深入学习就另请高明吧

你可能感兴趣的:(qt,开发语言)