QT委托代理機(jī)制之Model?View?Delegate使用方法詳解
之前的一篇文章中介紹過QT的委托代理機(jī)制,那時(shí)候由于理解的比較淺就簡單的給了一個(gè)例子。最近又做了一部分相關(guān)的工作,發(fā)現(xiàn)之前的理解有點(diǎn)問題。這里就詳細(xì)的介紹一下QT的委托代理機(jī)制的用法,希望對(duì)大家有幫助。
Model-View-Delegate機(jī)制可以簡單的理解為將本地的一些數(shù)據(jù)以特定的UI形式呈現(xiàn)出來。常見的數(shù)據(jù)結(jié)構(gòu)包括列表數(shù)據(jù)(list)、表格數(shù)據(jù)(table)、樹狀數(shù)據(jù)(tree),分別對(duì)應(yīng)著QT中的QListView、QTableView、QTreeView控件。本地?cái)?shù)據(jù)和視圖代理之間的關(guān)系如下圖所示:
數(shù)據(jù)模型中的數(shù)據(jù)來源可以是本地的XML文件、JSON文件、二進(jìn)制數(shù)據(jù),也可以數(shù)據(jù)庫中的數(shù)據(jù)表。這些數(shù)據(jù)源中的數(shù)據(jù)按照一定的結(jié)構(gòu)加載到對(duì)應(yīng)的數(shù)據(jù)模型中,我們可以通過操作數(shù)據(jù)模型中的數(shù)據(jù)來間接的操作數(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的子類。分別是QIdentityProxyModel和QSortFilterProxyModel。
QIdentityProxyModel代理不會(huì)修改原有的數(shù)據(jù)模型,只是重寫了data()函數(shù),對(duì)返回視圖的數(shù)據(jù)進(jìn)行了重新組合和修改。
QSortFilterProxyModel代理會(huì)對(duì)模型的數(shù)據(jù)進(jìn)行篩選和排序。
有了這兩個(gè)代理類,我們就可以對(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。通過委托代理的機(jī)制,我們就可以以個(gè)性的圖形界面呈現(xiàn)本地?cái)?shù)據(jù)了。
下面以一個(gè)詳細(xì)的例子,來說明一下委托代理機(jī)制的用法。例子主要功能是以縮略圖的形式對(duì)本地的圖片文件進(jìn)行管理,類似于一個(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ù)庫對(duì)圖片信息進(jìn)行本地持久化。首先,我們新建數(shù)據(jù)庫管理類,管理數(shù)據(jù)庫連接。
//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ù)庫管理類是單例模式 static DatabaseManager& instance(); ~DatabaseManager(); protected: //用來構(gòu)建固定名稱的數(shù)據(jù)庫 DatabaseManager(const QString& path = DATABASE_FILENAME); DatabaseManager& operator=(const DatabaseManager& rhs); private: std::unique_ptr<QSqlDatabase> mDatabase; public: //圖片數(shù)據(jù)操作類 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ù)庫管理類的創(chuàng)建之后,我們需要添加圖片數(shù)據(jù)表的數(shù)據(jù)庫訪問對(duì)象,訪問對(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ù)訪問層的構(gòu)建之后,我們的應(yīng)用就具備了對(duì)底層原始數(shù)據(jù)操作的能力。這個(gè)是應(yīng)用的基礎(chǔ)能力。
添加數(shù)據(jù)模型(Model)
完成了數(shù)據(jù)操作類之后,接下來我們就需要構(gòu)建對(duì)應(yīng)的數(shù)據(jù)模型了。由于圖片信息之間是沒有關(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ù)類型 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è)元素類別的名稱 QHash<int, QByteArray> roleNames() const override; //加載用戶圖片 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ù)為圖片的名稱 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允許開發(fā)者針對(duì)數(shù)據(jù)模型中的每個(gè)數(shù)據(jù)單元ModelIndex定義不同的數(shù)據(jù)角色。簡單來說,就是每個(gè)數(shù)據(jù)單元可以提供各種類型的供外部使用的數(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); //通過重寫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,我們通過自定義的控件來呈現(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)題欄顯示圖片名稱 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); }
我們也可以通過實(shí)現(xiàn)QStyledItemDelegate::createEditor()接口,來對(duì)每一個(gè)元素代理中的數(shù)據(jù)進(jìn)行編輯,這里就不詳細(xì)介紹了,之前的文章中寫過。
添加視圖層(View)
完善了數(shù)據(jù)模型和元素代理之后,對(duì)應(yīng)的視圖層操作就比較簡單了。視圖層我們添加了和用戶交互的接口,用戶可以通過對(duì)應(yīng)的UI操作,對(duì)數(shù)據(jù)模型中的數(shù)據(jù)進(jìn)行增刪改查。同時(shí)視圖中我們?yōu)樵靥砑恿瞬藛?,我們可以通過右鍵菜單來刪除某個(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)); //開啟自定義的菜單 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解析,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05利用Qt實(shí)現(xiàn)仿QQ設(shè)置面板功能
這篇文章主要為大家詳細(xì)介紹了如何利用Qt實(shí)現(xiàn)仿QQ設(shè)置面板功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解一下2022-12-12C++小游戲教程之猜數(shù)游戲的實(shí)現(xiàn)
這篇文章主要和大家詳細(xì)介紹如何利用C++做一個(gè)簡易的猜數(shù)游戲,分為用戶猜數(shù)和系統(tǒng)猜數(shù)。文中的示例代碼講解詳細(xì) ,感興趣的小伙伴可以嘗試一下2022-11-11C++編寫的WebSocket服務(wù)端客戶端實(shí)現(xiàn)示例代碼
本文主要介紹了C++編寫的WebSocket服務(wù)端客戶端實(shí)現(xiàn)示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10關(guān)于C++函數(shù)模版的實(shí)現(xiàn)講解
今天小編就為大家分享一篇關(guān)于關(guān)于C++函數(shù)模版的實(shí)現(xiàn)講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12