首頁 > 軟體

QT委託代理機制之Model View Delegate使用方法詳解

2022-08-25 14:02:40

之前的一篇文章中介紹過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內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com