QT委托代理機(jī)制之Model?View?Delegate使用方法詳解
之前的一篇文章中介紹過(guò)QT的委托代理機(jī)制,那時(shí)候由于理解的比較淺就簡(jiǎn)單的給了一個(gè)例子。最近又做了一部分相關(guān)的工作,發(fā)現(xiàn)之前的理解有點(diǎn)問(wèn)題。這里就詳細(xì)的介紹一下QT的委托代理機(jī)制的用法,希望對(duì)大家有幫助。
Model-View-Delegate機(jī)制可以簡(jiǎn)單的理解為將本地的一些數(shù)據(jù)以特定的UI形式呈現(xiàn)出來(lái)。常見(jiàn)的數(shù)據(jù)結(jié)構(gòu)包括列表數(shù)據(jù)(list)、表格數(shù)據(jù)(table)、樹(shù)狀數(shù)據(jù)(tree),分別對(duì)應(yīng)著QT中的QListView、QTableView、QTreeView控件。本地?cái)?shù)據(jù)和視圖代理之間的關(guān)系如下圖所示:

數(shù)據(jù)模型中的數(shù)據(jù)來(lái)源可以是本地的XML文件、JSON文件、二進(jìn)制數(shù)據(jù),也可以數(shù)據(jù)庫(kù)中的數(shù)據(jù)表。這些數(shù)據(jù)源中的數(shù)據(jù)按照一定的結(jié)構(gòu)加載到對(duì)應(yīng)的數(shù)據(jù)模型中,我們可以通過(guò)操作數(shù)據(jù)模型中的數(shù)據(jù)來(lái)間接的操作數(shù)據(jù)源中的數(shù)據(jù)。
有時(shí)候,我們需要對(duì)數(shù)據(jù)模型中的數(shù)據(jù)進(jìn)行二次處理,包括數(shù)據(jù)篩選、數(shù)據(jù)排序、數(shù)據(jù)處理等等,這時(shí)候我們就得需要引入模型代理,負(fù)責(zé)對(duì)數(shù)據(jù)模型進(jìn)行處理。當(dāng)然模型代理不是必須的。QT中的模型代理有兩種都是QAbstractProxyModel的子類(lèi)。分別是QIdentityProxyModel和QSortFilterProxyModel。
QIdentityProxyModel代理不會(huì)修改原有的數(shù)據(jù)模型,只是重寫(xiě)了data()函數(shù),對(duì)返回視圖的數(shù)據(jù)進(jìn)行了重新組合和修改。
QSortFilterProxyModel代理會(huì)對(duì)模型的數(shù)據(jù)進(jìn)行篩選和排序。
有了這兩個(gè)代理類(lèi),我們就可以對(duì)模型中的數(shù)據(jù)進(jìn)行處理了。
數(shù)據(jù)模型加載完畢數(shù)據(jù)之后,View層就會(huì)對(duì)數(shù)據(jù)模型中的數(shù)據(jù)進(jìn)行呈現(xiàn)了。由于數(shù)據(jù)模型中的數(shù)據(jù)都是以一個(gè)個(gè)數(shù)據(jù)單元存在的,我們可以為每個(gè)數(shù)據(jù)單元指定對(duì)應(yīng)的UI。這就用到了委托代理Delegate,委托控件可以給數(shù)據(jù)模型中的每一個(gè)元素指定固定的UI。通過(guò)委托代理的機(jī)制,我們就可以以個(gè)性的圖形界面呈現(xiàn)本地?cái)?shù)據(jù)了。
下面以一個(gè)詳細(xì)的例子,來(lái)說(shuō)明一下委托代理機(jī)制的用法。例子主要功能是以縮略圖的形式對(duì)本地的圖片文件進(jìn)行管理,類(lèi)似于一個(gè)圖片管理器。
本地?cái)?shù)據(jù)加載(Data)
例子中的圖片數(shù)據(jù)主要包含兩個(gè)字段,一個(gè)字段是圖片的ID,另一個(gè)字段是圖片的URL。對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)如下所示:
//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; //圖片的地址
};由于本地的圖片數(shù)據(jù)可能會(huì)很多,為了方便對(duì)大量的圖片數(shù)據(jù)進(jìn)行管理,這里我們采用SQLITE數(shù)據(jù)庫(kù)對(duì)圖片信息進(jìn)行本地持久化。首先,我們新建數(shù)據(jù)庫(kù)管理類(lèi),管理數(shù)據(jù)庫(kù)連接。
//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);
//數(shù)據(jù)庫(kù)管理類(lèi)是單例模式
static DatabaseManager& instance();
~DatabaseManager();
protected:
//用來(lái)構(gòu)建固定名稱(chēng)的數(shù)據(jù)庫(kù)
DatabaseManager(const QString& path = DATABASE_FILENAME);
DatabaseManager& operator=(const DatabaseManager& rhs);
private:
std::unique_ptr<QSqlDatabase> mDatabase;
public:
//圖片數(shù)據(jù)操作類(lèi)
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();
}完成數(shù)據(jù)庫(kù)管理類(lèi)的創(chuàng)建之后,我們需要添加圖片數(shù)據(jù)表的數(shù)據(jù)庫(kù)訪問(wèn)對(duì)象,訪問(wèn)對(duì)象負(fù)責(zé)完成對(duì)圖片數(shù)據(jù)表的增刪改查等基本操作,對(duì)應(yīng)的實(shí)現(xiàn)如下所示:
//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;
//刪除所有的數(shù)據(jù)
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);
}完成數(shù)據(jù)訪問(wèn)層的構(gòu)建之后,我們的應(yīng)用就具備了對(duì)底層原始數(shù)據(jù)操作的能力。這個(gè)是應(yīng)用的基礎(chǔ)能力。
添加數(shù)據(jù)模型(Model)
完成了數(shù)據(jù)操作類(lèi)之后,接下來(lái)我們就需要構(gòu)建對(duì)應(yīng)的數(shù)據(jù)模型了。由于圖片信息之間是沒(méi)有關(guān)聯(lián)關(guān)系的所以這里采用的是基于QAbstractListModel的列表數(shù)據(jù)模型,對(duì)應(yīng)的實(shí)現(xiàn)如下所示:
//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:
//自定義每個(gè)元素的數(shù)據(jù)類(lèi)型
enum Roles {
UrlRole = Qt::UserRole + 1,
FilePathRole
};
PictureModel(QObject* parent = 0);
//向數(shù)據(jù)模型中添加單個(gè)數(shù)據(jù)
QModelIndex addPicture(const Picture& picture);
Q_INVOKABLE void addPictureFromUrl(const QUrl& fileUrl);
//模型的行數(shù)
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
//獲取某個(gè)元素的數(shù)據(jù)
QVariant data(const QModelIndex& index, int role) const override;
//刪除某幾行數(shù)據(jù)
Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
//每個(gè)元素類(lèi)別的名稱(chēng)
QHash<int, QByteArray> roleNames() const override;
//加載用戶(hù)圖片
Q_INVOKABLE void loadPictures();
//清空模型的中的數(shù)據(jù),但不移除本地文件數(shù)據(jù)
void clearPictures();
public slots:
//清空模型,刪除本地文件中的數(shù)據(jù)
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) {
//展示數(shù)據(jù)為圖片的名稱(chēng)
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允許開(kāi)發(fā)者針對(duì)數(shù)據(jù)模型中的每個(gè)數(shù)據(jù)單元ModelIndex定義不同的數(shù)據(jù)角色。簡(jiǎn)單來(lái)說(shuō),就是每個(gè)數(shù)據(jù)單元可以提供各種類(lèi)型的供外部使用的數(shù)據(jù)。這里我們定義了UrlRole和FilePathRole分別代表著圖片的URL和圖片的地址。
添加代理模型(Proxy)
模型代理就是對(duì)原始模型中的數(shù)據(jù)進(jìn)行二次處理,包括排序篩選等等操作。模型代理不能直接修改模型中的數(shù)據(jù),只是負(fù)責(zé)對(duì)數(shù)據(jù)模型中的數(shù)據(jù)進(jìn)行二次處理操作。同時(shí)模型代理也不是必須的,我們也可以直接用原始的數(shù)據(jù)模型和視圖進(jìn)行交互。模型代理對(duì)應(yīng)的實(shí)現(xiàn)如下所示:
//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);
//通過(guò)重寫(xiě)data接口對(duì)數(shù)據(jù)進(jìn)行二次處理
QVariant data(const QModelIndex& index, int role) const override;
//設(shè)置獲取源數(shù)據(jù)模型
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
{
//對(duì)原始數(shù)據(jù)模型中的數(shù)據(jù)進(jìn)行二次加工處理
//供前端調(diào)用
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)
元素代理就是數(shù)據(jù)表中每個(gè)元素對(duì)應(yīng)的UI,我們通過(guò)自定義的控件來(lái)呈現(xiàn)對(duì)應(yīng)的數(shù)據(jù)。這里我們采用的是QStyledItemDelegate而不是QItemDelegate,是因?yàn)镼StyledItemDelegate支持樣式表的操作,而QItemDelegate不支持,對(duì)應(yīng)的實(shí)現(xiàn)如下所示:
//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>
//標(biāo)題欄的尺寸樣式
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();
//繪制對(duì)應(yīng)的圖片
QPixmap pixmap = index.model()->data(index, Qt::DecorationRole).value<QPixmap>();
painter->drawPixmap(option.rect.x(), option.rect.y(),PIXMAP_WIDTH,PIXMAP_HEIGHT,pixmap);
//繪制圖片的標(biāo)題欄顯示圖片名稱(chēng)
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);
//繪制標(biāo)題文字
QString filename = index.model()->data(index, Qt::DisplayRole).toString();
painter->setPen(LABEL_TEXT_COLOR);
painter->drawText(bannerRect, Qt::AlignCenter, filename);
//設(shè)置元素被選中之后的顏色
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);
}我們也可以通過(guò)實(shí)現(xiàn)QStyledItemDelegate::createEditor()接口,來(lái)對(duì)每一個(gè)元素代理中的數(shù)據(jù)進(jìn)行編輯,這里就不詳細(xì)介紹了,之前的文章中寫(xiě)過(guò)。
添加視圖層(View)
完善了數(shù)據(jù)模型和元素代理之后,對(duì)應(yīng)的視圖層操作就比較簡(jiǎn)單了。視圖層我們添加了和用戶(hù)交互的接口,用戶(hù)可以通過(guò)對(duì)應(yīng)的UI操作,對(duì)數(shù)據(jù)模型中的數(shù)據(jù)進(jìn)行增刪改查。同時(shí)視圖中我們?yōu)樵靥砑恿瞬藛?,我們可以通過(guò)右鍵菜單來(lái)刪除某個(gè)特定的元素。
//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();
//設(shè)置數(shù)據(jù)模型
void setPictureModel(PictureProxyModel *pictureModel);
//設(shè)置選中的數(shù)據(jù)模型
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;
//圖片數(shù)據(jù)模型
PictureProxyModel* mPictureModel;
//選中元素的數(shù)據(jù)模型
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);
//設(shè)置元素之間的間隔
ui->pic_list_view->setSpacing(5);
//設(shè)置尺寸變化策略
ui->pic_list_view->setResizeMode(QListView::Adjust);
//設(shè)置元素增減的時(shí)候的變化模式
ui->pic_list_view->setFlow(QListView::LeftToRight);
//設(shè)置伸縮的時(shí)候是否自動(dòng)換行
ui->pic_list_view->setWrapping(true);
//設(shè)置每個(gè)元素的代理
ui->pic_list_view->setItemDelegate(new PictureDelegate(this));
//開(kāi)啟自定義的菜單
ui->pic_list_view->setContextMenuPolicy(Qt::CustomContextMenu);
//初始化功能菜單
m_func_menu = new QMenu(this);
m_del_current_pic = new QAction("刪除當(dāng)前圖片",this);
m_func_menu->addAction(m_del_current_pic);
connect(m_del_current_pic,&QAction::triggered,this,&MyListView::delCurrentPicture);
//對(duì)圖片數(shù)據(jù)進(jìn)行增刪改查
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);
//選中前一個(gè)圖片
QModelIndex previousModelIndex = mPictureModel->sourceModel()->index(row - 1, 0);
if(previousModelIndex.isValid()) {
previousModelIndex = mPictureModel->index(previousModelIndex.row(),previousModelIndex.column());
mPictureSelectionModel->setCurrentIndex(previousModelIndex, QItemSelectionModel::SelectCurrent);
return;
}
//選中后一個(gè)圖片
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層的最后一步操作了,對(duì)應(yīng)的實(shí)現(xiàn)如下:
//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()
{
}使用效果

到此這篇關(guān)于QT委托代理機(jī)制之Model View Delegate使用方法詳解的文章就介紹到這了,更多相關(guān)QT Model View Delegate內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Cmake中強(qiáng)大的輸出函數(shù)message示例解析
這篇文章主要介紹了Cmake中強(qiáng)大的輸出函數(shù)message解析,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05
C語(yǔ)言銀行系統(tǒng)課程設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言銀行系統(tǒng)課程設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
利用Qt實(shí)現(xiàn)仿QQ設(shè)置面板功能
這篇文章主要為大家詳細(xì)介紹了如何利用Qt實(shí)現(xiàn)仿QQ設(shè)置面板功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解一下2022-12-12
C++小游戲教程之猜數(shù)游戲的實(shí)現(xiàn)
這篇文章主要和大家詳細(xì)介紹如何利用C++做一個(gè)簡(jiǎn)易的猜數(shù)游戲,分為用戶(hù)猜數(shù)和系統(tǒng)猜數(shù)。文中的示例代碼講解詳細(xì) ,感興趣的小伙伴可以嘗試一下2022-11-11
C++編寫(xiě)的WebSocket服務(wù)端客戶(hù)端實(shí)現(xiàn)示例代碼
本文主要介紹了C++編寫(xiě)的WebSocket服務(wù)端客戶(hù)端實(shí)現(xiàn)示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10
關(guān)于C++函數(shù)模版的實(shí)現(xiàn)講解
今天小編就為大家分享一篇關(guān)于關(guān)于C++函數(shù)模版的實(shí)現(xiàn)講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12

