QT委托代理机制之Model View Delegate使用方法详解

目录
  • 本地数据加载(Data)
  • 添加数据模型(Model)
  • 添加代理模型(Proxy)
  • 添加元素的代理(Delegate)
  • 添加视图层(View)
  • 使用效果

之前的一篇文章中介绍过QT的委托代理机制,那时候由于理解的比较浅就简单的给了一个例子。最近又做了一部分相关的工作,发现之前的理解有点问题。这里就详细的介绍一下QT的委托代理机制的用法,希望对大家有帮助。

Model-View-Delegate机制可以简单的理解为将本地的一些数据以特定的UI形式呈现出来。常见的数据结构包括列表数据(list)、表格数据(table)、树状数据(tree),分别对应着QT中的QListView、QTableView、QTreeView控件。本地数据和视图代理之间的关系如下图所示:

数据模型中的数据来源可以是本地的XML文件、JSON文件、二进制数据,也可以数据库中的数据表。这些数据源中的数据按照一定的结构加载到对应的数据模型中,我们可以通过操作数据模型中的数据来间接的操作数据源中的数据。

有时候,我们需要对数据模型中的数据进行二次处理,包括数据筛选、数据排序、数据处理等等,这时候我们就得需要引入模型代理,负责对数据模型进行处理。当然模型代理不是必须的。QT中的模型代理有两种都是QAbstractProxyModel的子类。分别是QIdentityProxyModel和QSortFilterProxyModel。

QIdentityProxyModel代理不会修改原有的数据模型,只是重写了data()函数,对返回视图的数据进行了重新组合和修改。

QSortFilterProxyModel代理会对模型的数据进行筛选和排序。

有了这两个代理类,我们就可以对模型中的数据进行处理了。

数据模型加载完毕数据之后,View层就会对数据模型中的数据进行呈现了。由于数据模型中的数据都是以一个个数据单元存在的,我们可以为每个数据单元指定对应的UI。这就用到了委托代理Delegate,委托控件可以给数据模型中的每一个元素指定固定的UI。通过委托代理的机制,我们就可以以个性的图形界面呈现本地数据了。

下面以一个详细的例子,来说明一下委托代理机制的用法。例子主要功能是以缩略图的形式对本地的图片文件进行管理,类似于一个图片管理器。

本地数据加载(Data)

例子中的图片数据主要包含两个字段,一个字段是图片的ID,另一个字段是图片的URL。对应的数据结构如下所示:

//Picture
class Picture
{
public:
    Picture(const QString & filePath = "")
    {
         mPictureUrl = QUrl::fromLocalFile(filePath);
    }
    Picture(const QUrl& fileUrl)
    {
        mPictureUrl = fileUrl;
    }
    int pictureId() const
    {
        return mPictureId;
    }
    void setPictureId(int pictureId)
    {
        mPictureId = pictureId;
    }
    QUrl pictureUrl() const
    {
        return mPictureUrl;
    }
    void setPictureUrl(const QUrl &pictureUrl)
    {
        mPictureUrl = pictureUrl;
    }
private:
    int mPictureId;   // 图片ID
    QUrl mPictureUrl;  //图片的地址
};

由于本地的图片数据可能会很多,为了方便对大量的图片数据进行管理,这里我们采用SQLITE数据库对图片信息进行本地持久化。首先,我们新建数据库管理类,管理数据库连接。

//DatabaseManager.h
#ifndef DATABASEMANAGER_H
#define DATABASEMANAGER_H
#include <memory>
#include <QString>
#include "PictureDao.h"
class QSqlQuery;
class QSqlDatabase;
const QString DATABASE_FILENAME = "picture.db";
class DatabaseManager
{
public:
    static void debugQuery(const QSqlQuery& query);
    //数据库管理类是单例模式
    static DatabaseManager& instance();
    ~DatabaseManager();
protected:
    //用来构建固定名称的数据库
    DatabaseManager(const QString& path = DATABASE_FILENAME);
    DatabaseManager& operator=(const DatabaseManager& rhs);
private:
    std::unique_ptr<QSqlDatabase> mDatabase;
public:
    //图片数据操作类
    const PictureDao mpictureDao;
};
#endif // DATABASEMANAGER_H
//DatabaseManager.cpp
#include "DatabaseManager.h"
#include <QSqlDatabase>
#include <QDebug>
#include <QSqlError>
#include <QSqlQuery>
void DatabaseManager::debugQuery(const QSqlQuery& query)
{
    if (query.lastError().type() == QSqlError::ErrorType::NoError) {
        qDebug() << "Query OK:"  << query.lastQuery();
    } else {
       qWarning() << "Query KO:" << query.lastError().text();
       qWarning() << "Query text:" << query.lastQuery();
    }
}
DatabaseManager&DatabaseManager::instance()
{
    static DatabaseManager singleton;
    return singleton;
}
DatabaseManager::DatabaseManager(const QString& path) :
    mDatabase(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE"))),
    mpictureDao(*mDatabase)
{
    mDatabase->setDatabaseName(path);
    bool openStatus = mDatabase->open();
    qDebug() << "Database connection: " << (openStatus ? "OK" : "Error");
    mpictureDao.init();
}
DatabaseManager::~DatabaseManager()
{
    mDatabase->close();
}

完成数据库管理类的创建之后,我们需要添加图片数据表的数据库访问对象,访问对象负责完成对图片数据表的增删改查等基本操作,对应的实现如下所示:

//PictureDao.h
#ifndef PICTUREDAO_H
#define PICTUREDAO_H
#include <memory>
#include <vector>
class QSqlDatabase;
class Picture;
class PictureDao
{
public:
    explicit PictureDao(QSqlDatabase& database);
    void init() const;
    //添加图片
    void addPicture(Picture& picture) const;
    //删除图片
    void removePicture(int id) const;
    //加载图片
    std::unique_ptr<std::vector<std::unique_ptr<Picture>>> loadPictures() const;
    //删除所有的数据
    void removeAllPictures() const;
private:
    QSqlDatabase& mDatabase;
};
#endif // PICTUREDAO_H
//PictureDao.cpp
#include "PictureDao.h"
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QVariant>
#include "DatabaseManager.h"
#include "picturemodel.h"
using namespace std;
PictureDao::PictureDao(QSqlDatabase& database) :
    mDatabase(database)
{
}
void PictureDao::init() const
{
    if (!mDatabase.tables().contains("pictures")) {
        QSqlQuery query(mDatabase);
        query.exec(QString("CREATE TABLE pictures")
        + " (id INTEGER PRIMARY KEY AUTOINCREMENT, "
        + "url TEXT)");
        DatabaseManager::debugQuery(query);
    }
}
void PictureDao::addPicture(Picture& picture) const
{
    QSqlQuery query(mDatabase);
    query.prepare(QString("INSERT INTO pictures")
        + " (url)"
        + " VALUES ("
        + ":url"
        + ")");
    query.bindValue(":url", picture.pictureUrl());
    query.exec();
    DatabaseManager::debugQuery(query);
    picture.setPictureId(query.lastInsertId().toInt());
}
void PictureDao::removePicture(int id) const
{
    QSqlQuery query(mDatabase);
    query.prepare("DELETE FROM pictures WHERE id = (:id)");
    query.bindValue(":id", id);
    query.exec();
    DatabaseManager::debugQuery(query);
}
unique_ptr<vector<unique_ptr<Picture>>> PictureDao::loadPictures() const
{
    QSqlQuery query(mDatabase);
    query.prepare("SELECT * FROM pictures");
    query.exec();
    DatabaseManager::debugQuery(query);
    unique_ptr<vector<unique_ptr<Picture>>> list(new vector<unique_ptr<Picture>>());
    while(query.next()) {
        unique_ptr<Picture> picture(new Picture());
        picture->setPictureId(query.value("id").toInt());
        picture->setPictureUrl(query.value("url").toString());
        list->push_back(move(picture));
    }
    return list;
}
void PictureDao::removeAllPictures() const
{
    QSqlQuery query(mDatabase);
    query.prepare("DELETE FROM pictures WHERE 1=1");
    query.exec();
    DatabaseManager::debugQuery(query);
}

完成数据访问层的构建之后,我们的应用就具备了对底层原始数据操作的能力。这个是应用的基础能力。

添加数据模型(Model)

完成了数据操作类之后,接下来我们就需要构建对应的数据模型了。由于图片信息之间是没有关联关系的所以这里采用的是基于QAbstractListModel的列表数据模型,对应的实现如下所示:

//picturemodel.h
#ifndef PICTUREMODEL_H
#define PICTUREMODEL_H
#include <memory>
#include <vector>
#include <QAbstractListModel>
#include <QUrl>
#include "DatabaseManager.h"
class PictureModel : public QAbstractListModel
{
    Q_OBJECT
public:
    //自定义每个元素的数据类型
    enum Roles {
        UrlRole = Qt::UserRole + 1,
        FilePathRole
    };
    PictureModel(QObject* parent = 0);
    //向数据模型中添加单个数据
    QModelIndex addPicture(const Picture& picture);
    Q_INVOKABLE void addPictureFromUrl(const QUrl& fileUrl);
    //模型的行数
    int rowCount(const QModelIndex& parent = QModelIndex()) const override;
    //获取某个元素的数据
    QVariant data(const QModelIndex& index, int role) const override;
    //删除某几行数据
    Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
    //每个元素类别的名称
    QHash<int, QByteArray> roleNames() const override;
    //加载用户图片
    Q_INVOKABLE void loadPictures();
    //清空模型的中的数据,但不移除本地文件数据
    void clearPictures();
public slots:
    //清空模型,删除本地文件中的数据
    void deleteAllPictures();
private:
    void resetPictures();
    bool isIndexValid(const QModelIndex& index) const;
private:
    DatabaseManager& mDatabase;
    std::unique_ptr<std::vector<std::unique_ptr<Picture>>> mPictures;
};
#endif // PICTUREMODEL_H
//picturemodel.cpp
#include "picturemodel.h"
#include <QUrl>
using namespace std;
PictureModel::PictureModel(QObject* parent) :
    QAbstractListModel(parent),
    mPictures(new vector<unique_ptr<Picture>>()),
    mDatabase(DatabaseManager::instance())
{
}
QModelIndex PictureModel::addPicture(const Picture& picture)
{
    int rows = rowCount();
    beginInsertRows(QModelIndex(), rows, rows);
    unique_ptr<Picture>newPicture(new Picture(picture));
    mDatabase.mpictureDao.addPicture(*newPicture);
    mPictures->push_back(move(newPicture));
    endInsertRows();
    return index(rows, 0);
}
void PictureModel::addPictureFromUrl(const QUrl& fileUrl)
{
    addPicture(Picture(fileUrl));
}
int PictureModel::rowCount(const QModelIndex& /*parent*/) const
{
    return mPictures->size();
}
QVariant PictureModel::data(const QModelIndex& index, int role) const
{
    if (!isIndexValid(index))
    {
        return QVariant();
    }
    const Picture& picture = *mPictures->at(index.row());
    switch (role) {
        //展示数据为图片的名称
        case Qt::DisplayRole:
            return picture.pictureUrl().fileName();
            break;
        //图片的URL
        case Roles::UrlRole:
            return picture.pictureUrl();
            break;
        //图片地址
        case Roles::FilePathRole:
            return picture.pictureUrl().toLocalFile();
            break;

        default:
            return QVariant();
    }
}
bool PictureModel::removeRows(int row, int count, const QModelIndex& parent)
{
    if (row < 0
            || row >= rowCount()
            || count < 0
            || (row + count) > rowCount()) {
        return false;
    }
    beginRemoveRows(parent, row, row + count - 1);
    int countLeft = count;
    while(countLeft--) {
        const Picture& picture = *mPictures->at(row + countLeft);
        mDatabase.mpictureDao.removePicture(picture.pictureId());
    }
    mPictures->erase(mPictures->begin() + row,
                    mPictures->begin() + row + count);
    endRemoveRows();
    return true;
}
QHash<int, QByteArray> PictureModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[Qt::DisplayRole] = "name";
    roles[Roles::FilePathRole] = "filepath";
    roles[Roles::UrlRole] = "url";
    return roles;
}
void PictureModel::loadPictures()
{
    beginResetModel();
    mPictures = mDatabase.mpictureDao.loadPictures();
    endResetModel();
}
void PictureModel::clearPictures()
{
    resetPictures();
}
void PictureModel::resetPictures()
{
    beginResetModel();
    mPictures.reset(new vector<unique_ptr<Picture>>());
    endResetModel();
    return;
}
void PictureModel::deleteAllPictures()
{
    mDatabase.mpictureDao.removeAllPictures();
    resetPictures();
}
bool PictureModel::isIndexValid(const QModelIndex& index) const
{
    if (index.row() < 0
            || index.row() >= rowCount()
            || !index.isValid()) {
        return false;
    }
    return true;
}

QT允许开发者针对数据模型中的每个数据单元ModelIndex定义不同的数据角色。简单来说,就是每个数据单元可以提供各种类型的供外部使用的数据。这里我们定义了UrlRole和FilePathRole分别代表着图片的URL和图片的地址。

添加代理模型(Proxy)

模型代理就是对原始模型中的数据进行二次处理,包括排序筛选等等操作。模型代理不能直接修改模型中的数据,只是负责对数据模型中的数据进行二次处理操作。同时模型代理也不是必须的,我们也可以直接用原始的数据模型和视图进行交互。模型代理对应的实现如下所示:

//picproxymodel.h
#ifndef PICTURE_PROXY_MODEL_H
#define PICTURE_PROXY_MODEL_H
#include <QIdentityProxyModel>
#include <QHash>
#include <QPixmap>
class PictureModel;
class PictureProxyModel : public QIdentityProxyModel
{
public:
    PictureProxyModel(QObject* parent = 0);
    //通过重写data接口对数据进行二次处理
    QVariant data(const QModelIndex& index, int role) const override;
    //设置获取源数据模型
    void setSourceModel(QAbstractItemModel* sourceModel) override;
    PictureModel* pictureModel() const;
private:
    //重新加载缩略图
    void reloadPictures();
    //生成缩略图
    void generatePictures(const QModelIndex& startIndex, int count);
private:
   QHash<QString, QPixmap*>mPictureHashMaps;
};
#endif
//picproxymodel.cpp
#include "picproxymodel.h"
#include "PictureModel.h"
const unsigned int PICTURE_SIZE = 350;
PictureProxyModel::PictureProxyModel(QObject* parent) :
    QIdentityProxyModel(parent),
    mPictureHashMaps()
{
}
QVariant PictureProxyModel::data(const QModelIndex& index, int role) const
{
    //对原始数据模型中的数据进行二次加工处理
    //供前端调用
    if (role != Qt::DecorationRole) {
        return QIdentityProxyModel::data(index, role);
    }
    QString filepath = sourceModel()->data(index, PictureModel::Roles::FilePathRole).toString();
    return *mPictureHashMaps[filepath];
}
void PictureProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
{
    QIdentityProxyModel::setSourceModel(sourceModel);
    if (!sourceModel) {
        return;
    }
    connect(sourceModel, &QAbstractItemModel::modelReset, [this] {reloadPictures();});
    connect(sourceModel, &QAbstractItemModel::rowsInserted, [this](const QModelIndex& /*parent*/, int first, int last) {
        generatePictures(index(first, 0), last - first + 1);
    });
}
PictureModel* PictureProxyModel::pictureModel() const
{
    return static_cast<PictureModel*>(sourceModel());
}
void PictureProxyModel::reloadPictures()
{
    qDeleteAll(mPictureHashMaps);
    mPictureHashMaps.clear();
    generatePictures(index(0, 0), rowCount());
}
void PictureProxyModel::generatePictures(const QModelIndex& startIndex, int count)
{
    if (!startIndex.isValid()) {
        return;
    }
    const QAbstractItemModel* model = startIndex.model();
    int lastIndex = startIndex.row() + count;
    for(int row = startIndex.row(); row < lastIndex; row++) {
        QString filepath = model->data(model->index(row, 0), PictureModel::Roles::FilePathRole).toString();
        QPixmap pixmap(filepath);
        auto thumbnail = new QPixmap(pixmap.scaled(PICTURE_SIZE, PICTURE_SIZE,Qt::KeepAspectRatio,
                                             Qt::SmoothTransformation));
        mPictureHashMaps.insert(filepath, thumbnail);
    }
}

添加元素的代理(Delegate)

元素代理就是数据表中每个元素对应的UI,我们通过自定义的控件来呈现对应的数据。这里我们采用的是QStyledItemDelegate而不是QItemDelegate,是因为QStyledItemDelegate支持样式表的操作,而QItemDelegate不支持,对应的实现如下所示:

//picturedelegate.h
#ifndef PICTUREDELEGATE_H
#define PICTUREDELEGATE_H
#include <QStyledItemDelegate>
#include <QMouseEvent>
class PictureDelegate : public QStyledItemDelegate
{
    Q_OBJECT
public:
    PictureDelegate(QObject* parent = 0);
    //代理的绘制事件
    void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
    //代理的尺寸
    QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
protected:
};
#endif // PICTUREDELEGATE_H
//picturedelegate.cpp
#include "picturedelegate.h"
#include <QPainter>
//标题栏的尺寸样式
const unsigned int LABEL_HEIGHT = 20;
const unsigned int LABEL_COLOR = 0x303030;
const unsigned int LABEL_ALPHA = 200;
const unsigned int LABEL_TEXT_COLOR = 0xffffff;
const unsigned int HIGHLIGHT_ALPHA = 100;
//图片的尺寸样式
const unsigned int PIXMAP_WIDTH = 200;
const unsigned int PIXMAP_HEIGHT = 200;
PictureDelegate::PictureDelegate(QObject* parent) :
    QStyledItemDelegate(parent)
{
}
void PictureDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    painter->save();
    //绘制对应的图片
    QPixmap pixmap = index.model()->data(index, Qt::DecorationRole).value<QPixmap>();
    painter->drawPixmap(option.rect.x(), option.rect.y(),PIXMAP_WIDTH,PIXMAP_HEIGHT,pixmap);
    //绘制图片的标题栏显示图片名称
    QRect bannerRect = QRect(option.rect.x(), option.rect.y(), PIXMAP_WIDTH, LABEL_HEIGHT);
    QColor bannerColor = QColor(LABEL_COLOR);
    bannerColor.setAlpha(LABEL_ALPHA);
    painter->fillRect(bannerRect, bannerColor);
    //绘制标题文字
    QString filename = index.model()->data(index, Qt::DisplayRole).toString();
    painter->setPen(LABEL_TEXT_COLOR);
    painter->drawText(bannerRect, Qt::AlignCenter, filename);
    //设置元素被选中之后的颜色
    if (option.state.testFlag(QStyle::State_Selected)) {
        QColor selectedColor = option.palette.highlight().color();
        selectedColor.setAlpha(HIGHLIGHT_ALPHA);
        painter->fillRect(option.rect, selectedColor);
    }
    painter->restore();
}
QSize PictureDelegate::sizeHint(const QStyleOptionViewItem& /*option*/, const QModelIndex& index) const
{
    const QPixmap& pixmap = index.model()->data(index, Qt::DecorationRole).value<QPixmap>();
    return QSize(PIXMAP_WIDTH,PIXMAP_HEIGHT);
}

我们也可以通过实现QStyledItemDelegate::createEditor()接口,来对每一个元素代理中的数据进行编辑,这里就不详细介绍了,之前的文章中写过。

添加视图层(View)

完善了数据模型和元素代理之后,对应的视图层操作就比较简单了。视图层我们添加了和用户交互的接口,用户可以通过对应的UI操作,对数据模型中的数据进行增删改查。同时视图中我们为元素添加了菜单,我们可以通过右键菜单来删除某个特定的元素。

//mylistview.h
#ifndef MYLISTVIEW_H
#define MYLISTVIEW_H
#include <QWidget>
#include <QItemSelectionModel>
#include <QMouseEvent>
#include <QMenu>
namespace Ui {
class MyListView;
}
class PictureProxyModel;
class MyListView : public QWidget
{
    Q_OBJECT
public:
    explicit MyListView(QWidget *parent = 0);
    ~MyListView();
    //设置数据模型
    void setPictureModel(PictureProxyModel *pictureModel);
    //设置选中的数据模型
    void setPictureSelectionModel(QItemSelectionModel *selectionModel);
private slots:
    void addPictures();
    void delPictures();
    void clearPictures();
    void delAllPicture();
    void delCurrentPicture();
    void showCustomMenu(const QPoint& pos);
private:
    Ui::MyListView *ui;
    //图片数据模型
    PictureProxyModel* mPictureModel;
    //选中元素的数据模型
    QItemSelectionModel* mPictureSelectionModel;
    QModelIndex mCurrentIndex;
    QMenu* m_func_menu = nullptr;
    QAction* m_del_current_pic = nullptr;
};
#endif // MYLISTVIEW_H
//mylistview.cpp
#pragma execution_character_set("utf-8")
#include "mylistview.h"
#include "picturedelegate.h"
#include "picproxymodel.h"
#include "ui_mylistview.h"
#include "picturemodel.h"
#include <QFileDialog>
#include <QInputDialog>
#include <QStandardPaths>
MyListView::MyListView(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MyListView)
{
    ui->setupUi(this);
    //设置元素之间的间隔
    ui->pic_list_view->setSpacing(5);
    //设置尺寸变化策略
    ui->pic_list_view->setResizeMode(QListView::Adjust);
    //设置元素增减的时候的变化模式
    ui->pic_list_view->setFlow(QListView::LeftToRight);
    //设置伸缩的时候是否自动换行
    ui->pic_list_view->setWrapping(true);
    //设置每个元素的代理
    ui->pic_list_view->setItemDelegate(new PictureDelegate(this));
    //开启自定义的菜单
    ui->pic_list_view->setContextMenuPolicy(Qt::CustomContextMenu);
    //初始化功能菜单
    m_func_menu = new QMenu(this);
    m_del_current_pic = new QAction("删除当前图片",this);
    m_func_menu->addAction(m_del_current_pic);
    connect(m_del_current_pic,&QAction::triggered,this,&MyListView::delCurrentPicture);
    //对图片数据进行增删改查
    connect(ui->add_pic_btn, &QPushButton::clicked, this, &MyListView::addPictures);
    connect(ui->clear_btn, &QPushButton::clicked,this, &MyListView::clearPictures);
    connect(ui->del_pic_btn, &QPushButton::clicked, this, &MyListView::delPictures);
    connect(ui->del_all_pic_btn,&QPushButton::clicked,this,&MyListView::delAllPicture);
    connect(ui->pic_list_view,&QListView::customContextMenuRequested,this,&MyListView::showCustomMenu);
}
MyListView::~MyListView()
{
    delete ui;
}
void MyListView::setPictureModel(PictureProxyModel* pictureModel)
{
    mPictureModel = pictureModel;
    ui->pic_list_view->setModel(pictureModel);
}
void MyListView::setPictureSelectionModel(QItemSelectionModel* selectionModel)
{
    mPictureSelectionModel = selectionModel;
    ui->pic_list_view->setSelectionModel(selectionModel);
}
void MyListView::addPictures()
{
    QStringList filenames = QFileDialog::getOpenFileNames(this,
                                                          "添加图片",
                                                          QStandardPaths::writableLocation(QStandardPaths::DesktopLocation),
                                                          "Picture files (*.jpg *.png)");
    if (!filenames.isEmpty()) {
        QModelIndex lastModelIndex;
        for (auto filename : filenames) {
            Picture picture(filename);
            lastModelIndex = mPictureModel->pictureModel()->addPicture(picture);
            lastModelIndex = mPictureModel->index(lastModelIndex.row(),lastModelIndex.column());
        }
        if(lastModelIndex.isValid())
        {
            ui->pic_list_view->setCurrentIndex(lastModelIndex);
        }
    }
}
void MyListView::delPictures()
{
    if (mPictureSelectionModel->selectedIndexes().isEmpty()) {
        return;
    }
    int row = mPictureSelectionModel->currentIndex().row();
    mPictureModel->sourceModel()->removeRow(row);
    //选中前一个图片
    QModelIndex previousModelIndex = mPictureModel->sourceModel()->index(row - 1, 0);
    if(previousModelIndex.isValid()) {
        previousModelIndex = mPictureModel->index(previousModelIndex.row(),previousModelIndex.column());
        mPictureSelectionModel->setCurrentIndex(previousModelIndex, QItemSelectionModel::SelectCurrent);
        return;
    }
    //选中后一个图片
    QModelIndex nextModelIndex = mPictureModel->sourceModel()->index(row, 0);
    if(nextModelIndex.isValid()) {
        nextModelIndex = mPictureModel->index(nextModelIndex.row(),nextModelIndex.column());
        mPictureSelectionModel->setCurrentIndex(nextModelIndex, QItemSelectionModel::SelectCurrent);
        return;
    }
}
void MyListView::clearPictures()
{
    PictureModel* pic_model = (PictureModel*)mPictureModel->sourceModel();
    pic_model->clearPictures();
}
void MyListView::delAllPicture()
{
    PictureModel* pic_model = (PictureModel*)mPictureModel->sourceModel();
    pic_model->deleteAllPictures();
}
void MyListView::delCurrentPicture()
{
    if(mCurrentIndex.isValid())
    {
        PictureModel* pic_model = (PictureModel*)mPictureModel->sourceModel();
        pic_model->removeRow(mCurrentIndex.row());
    }
}
void MyListView::showCustomMenu(const QPoint &pos)
{
    QPoint point = pos;
    mCurrentIndex = ui->pic_list_view->indexAt(pos);
    if(mCurrentIndex.isValid() && mCurrentIndex.row() >= 0)
    {
        m_func_menu->exec(ui->pic_list_view->mapToGlobal(point));
    }
}

完善了列表视图之后,我们就可以在主界面中,添加视图控件了,这也是UI层的最后一步操作了,对应的实现如下:

//mainwwindow.h
#ifndef MAINWWINDOW_H
#define MAINWWINDOW_H
#include <QWidget>
#include "mylistview.h"
namespace Ui {
class MainwWindow;
}
class MainwWindow : public QWidget
{
    Q_OBJECT
public:
    explicit MainwWindow(QWidget *parent = 0);
    ~MainwWindow();
private:
    MyListView* mListView=nullptr;
};
#endif // MAINWWINDOW_H
//mainwwindow.cpp
#include "mainwwindow.h"
#include "ui_mainwwindow.h"
#include "picturemodel.h"
#include "picproxymodel.h"
#include <QHBoxLayout>
MainwWindow::MainwWindow(QWidget *parent) :
    QWidget(parent)
{
    mListView = new MyListView(this);
    PictureModel* pic_model = new PictureModel(this);
    PictureProxyModel* pic_proxy_model = new PictureProxyModel(this);
    pic_proxy_model->setSourceModel(pic_model);
    QItemSelectionModel* pictureSelectionModel = new QItemSelectionModel(pic_proxy_model, this);
    mListView->setPictureModel(pic_proxy_model);
    mListView->setPictureSelectionModel(pictureSelectionModel);
    pic_model->loadPictures();
    QHBoxLayout* main_layout = new QHBoxLayout(this);
    main_layout->addWidget(mListView);
    this->setLayout(main_layout);
    this->setFixedSize(910,600);
}
MainwWindow::~MainwWindow()
{
}

使用效果

到此这篇关于QT委托代理机制之Model View Delegate使用方法详解的文章就介绍到这了,更多相关QT Model View Delegate内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C/C++ Qt TableDelegate 自定义代理组件使用详解

    TableDelegate 自定义代理组件的主要作用是对原有表格进行调整,例如默认情况下Table中的缺省代理就是一个编辑框,我们只能够在编辑框内输入数据,而有时我们想选择数据而不是输入,此时就需要重写编辑框实现选择的效果,代理组件常用于个性化定制Table表格中的字段类型. 在自定义代理中QAbstractItemDelegate是所有代理类的抽象基类,我们继承任何组件时都必须要包括如下4个函数: CreateEditor() 用于创建编辑模型数据的组件,例如(QSpinBox组件) SetE

  • C/C++ QT实现解析JSON文件的示例代码

    JSON是一种轻量级的数据交换格式,它是基于ECMAScript的一个子集,使用完全独立于编程语言的文本格式来存储和表示数据,简洁清晰的的层次结构使得JSON成为理想的数据交换语言,Qt库为JSON的相关操作提供了完整的类支持. 创建一个解析文件,命名为config.json我们将通过代码依次解析这个JSON文件中的每一个参数,具体解析代码如下: { "blog": "https://www.cnblogs.com/lyshark", "enable&qu

  • C/C++ Qt 数据库与Chart历史数据展示

    在前面的博文中具体介绍了QChart组件是如何绘制各种通用的二维图形的,本章内容将继续延申一个新的知识点,通过数据库存储某一段时间节点数据的走向,当用户通过编辑框提交查询记录时,程序自动过滤出该时间节点下所有的数据,并将该数据动态绘制到图形组件内,实现动态查询图形的功能. 首先通过如下代码,创建Times表,表内记录有某个主机某个时间节点下的数值: #include <QCoreApplication> #include <QSqlDatabase> #include <QS

  • C++ Qt QColorDialog使用方法

    目录 QColorDialog使用方法 getColor的调用 QPalette setColor Qt提供了颜色选择框,如下图: QColorDialog使用方法 例如下面的代码,点击按钮弹出颜色选择框,选择颜色,改变QLabel的背景色 #include "widget.h" #include "ui_widget.h" #include <QColorDialog> #include <QPalette> Widget::Widget(

  • C/C++ Qt 运用JSON解析库的实例代码

    JSON是一种简单的轻量级数据交换格式,Qt库为JSON的相关操作提供了完整的类支持,使用JSON解析文件之前需要先通过TextStream流将文件读入到字符串变量内,然后再通过QJsonDocument等库对该JSON格式进行解析,以提取出我们所需字段. 首先创建一个解析文件,命名为config.json我们将通过代码依次解析这个JSON文件中的每一个参数,具体解析代码如下: { "blog": "https://www.cnblogs.com/lyshark",

  • C++ Qt绘制时钟界面

    首先来看一下效果: 大概就是这样子,需要注意的是,这里你需要创建一个基类为Widget的界面来绘制时钟,以至于后期你想把这个时钟放到任意一个界面,你只需要在那个界面的ui里面添加一个widget,然后将它提升为这个时钟界面,并设定它的大小. widget.h文件 #ifndef WIDGET_H #define WIDGET_H #include<math.h> #include<QResizeEvent> #include<QGroupBox> #include<

  • C++中的Qt QTableView详解

    目录 一.常用接口 1.设置model,添加model数据 2.节点可以带一些数据 3.一些别的常用设置 4.自动调整行高和列宽 5.设置表格不可编辑 6.设置行列头不显示 二.设置item属性 三.右键弹出菜单 四.源码 一.常用接口 1.设置model,添加model数据 model = new QStandardItemModel(this); model->setHorizontalHeaderLabels(QStringList()<<"姓名"<<

  • QT委托代理机制之Model View Delegate使用方法详解

    目录 本地数据加载(Data) 添加数据模型(Model) 添加代理模型(Proxy) 添加元素的代理(Delegate) 添加视图层(View) 使用效果 之前的一篇文章中介绍过QT的委托代理机制,那时候由于理解的比较浅就简单的给了一个例子.最近又做了一部分相关的工作,发现之前的理解有点问题.这里就详细的介绍一下QT的委托代理机制的用法,希望对大家有帮助. Model-View-Delegate机制可以简单的理解为将本地的一些数据以特定的UI形式呈现出来.常见的数据结构包括列表数据(list)

  • Android 使用View Binding的方法详解

    前言 Android Studio稳定版发布了3.6版本,带来了一些新变化:首先外观,启动页变了,logo改了,更显现代化:增加Multi Preview功能,能同时预览多个尺寸屏幕的显示效果:模拟器支持多屏:也终于支持全新的视图绑定组件View Binding:等. 之前我们与视图交互的方式有findViewById.kotlin中引入Android Kotlin Extensions后直接通过id进行访问.前者模板化严重,重复代码多:后者最为方便.现在有了新的选择–View Binding,

  • Qt控件点击消息获取的方法详解

    目录 1.QPushButton响应鼠标点击消息 1.1自身响应消息clicked 1.2事件过滤器响应消息 1.3mousePressEvent事件获取 2.QLabel响应鼠标点击消息 2.1事件过滤器响应消息 2.2mousePressEvent事件获取 今日为大家分享一个小功能实现:如何获取控件点击响应. 在这里,我以两个最简单并且具有代表性的控件来进行功能讲解. 举例控件:QLabel.QPushButton 对于这两个控件大家应该都不会陌生了,做界面开发经常会用到的. 开发环境:VS

  • PHP基于反射机制实现自动依赖注入的方法详解

    本文实例讲述了PHP基于反射机制实现自动依赖注入的方法.分享给大家供大家参考,具体如下: 依赖注入又叫控制反转,使用过框架的人应该都不陌生.很多人一看名字就觉得是非常高大上的东西,就对它望而却步,今天抽空研究了下,解开他它的神秘面纱.废话不多说,直接上代码: /** * * 工具类,使用该类来实现自动依赖注入. * */ class Ioc { // 获得类的对象实例 public static function getInstance($className) { $paramArr = sel

  • iOS自动进行View标记的方法详解

    缘起 一切都源于我的上一篇博客,我写的是一篇 UITableViewCell使用自动布局的"最佳实践" ,我需要给我的图片里面的UIView元素添加上边距的标记,这让我感到很为难,我觉得我得发点时间写一个程序让这个步骤自动化,我只要一键就能让我的程序自动标记边距,这个比我要手动去标记来的酷很多不是吗! 结果 所以,我发了点时间实现了我的想法,下面是实现的结果截图: 以及代码开源托管地址:代码链接 (本地下载) 预览图 过去几小时内的想法 静下心来整理我的想法和寻找方案,大概的整理下了一

  • Android屏幕及view的截图实例详解

    Android屏幕及view的截图实例详解 屏幕可见区域的截图 整个屏幕截图的话可以用View view = getWindow().getDecorView(); public static Bitmap getNormalViewScreenshot(View view) { view.setDrawingCacheEnabled(true); view.buildDrawingCache(); return view.getDrawingCache(); } scrollview的整体截屏

  • Android View.onMeasure方法详解及实例

    Android View.onMeasure方法详解及实例 View在屏幕上显示出来要先经过measure(计算)和layout(布局). 1.什么时候调用onMeasure方法? 当控件的父元素正要放置该控件时调用.父元素会问子控件一个问题,"你想要用多大地方啊?",然后传入两个参数--widthMeasureSpec和heightMeasureSpec. 这两个参数指明控件可获得的空间以及关于这个空间描述的元数据. 更好的方法是你传递View的高度和宽度到setMeasuredDi

  • Pytorch之view及view_as使用详解

    view()函数是在torch.Tensor.view()下的一个函数,可以有tensor调用,也可以有variable调用. 其作用在于返回和原tensor数据个数相同,但size不同的tensor [Numpy中的size是元素个数,但是在Pytorch中size等价为Numpy中的shape] view函数的-1参数的作用在于基于另一参数,自动计算该维度的大小 很重要的一点 view函数只能由于contiguous的张量上,具体而言,就是在内存中连续存储的张量. 具体而言,可以参看 htt

  • pytorch:model.train和model.eval用法及区别详解

    使用PyTorch进行训练和测试时一定注意要把实例化的model指定train/eval,eval()时,框架会自动把BN和DropOut固定住,不会取平均,而是用训练好的值,不然的话,一旦test的batch_size过小,很容易就会被BN层导致生成图片颜色失真极大!!!!!! Class Inpaint_Network() ...... Model = Inpaint_Nerwoek() #train: Model.train(mode=True) ..... #test: Model.ev

  • python为QT程序添加图标的方法详解

    Qt是一种基于C++的跨平台图形用户界面应用程序开发框架.如何跨平台?上到服务器上位机,下到嵌入式GUI,上天入地无所不能.Qt最早是由1991年由Qt Company开发,但是到2008年,Qt Company科技被诺基亚公司收购,是的,就是拥有着我们很多情怀的诺基亚.但在2012年,Qt又被Digia收购.等到了2014年,跨平台集成开发环境Qt Creator 3.1.0正式发布出来,至此,全面支持iOS.Android.WP,QT的时代开始逐步展开. 本文重点给大家介绍python为QT

随机推荐