C++數(shù)據(jù)模型應(yīng)用在QML委托代理機(jī)制中
之前文章中介紹過在Qt-Widget和QML中如何使用委托代理機(jī)制(Model-View-Delegate),對應(yīng)的文章鏈接分別如下所示:
QT中Model-View-Delegate委托代理機(jī)制
在開發(fā)的過程中發(fā)現(xiàn)在QML中直接定義操作數(shù)據(jù)模型比較繁瑣費(fèi)力,不如C++的數(shù)據(jù)模型好用。這里就介紹一下如何在QML中調(diào)用C++定義的數(shù)據(jù)模型,實(shí)現(xiàn)數(shù)據(jù)模型的混合使用。
定義數(shù)據(jù)模型
定義的C++數(shù)據(jù)模型和Qt-Widget中定義的數(shù)據(jù)模型相同。模型主要用來存儲本地圖片的ID的對應(yīng)的圖片地址。
實(shí)現(xiàn)如下:
//picturemodel.h #ifndef PICTUREMODEL_H #define PICTUREMODEL_H #include <memory> #include <vector> #include <QAbstractListModel> #include <QUrl> 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; //圖片的地址 }; 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: 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>>()) { } QModelIndex PictureModel::addPicture(const Picture& picture) { int rows = rowCount(); beginInsertRows(QModelIndex(), rows, rows); unique_ptr<Picture>newPicture(new Picture(picture)); 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); } 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(); endResetModel(); } void PictureModel::clearPictures() { resetPictures(); } void PictureModel::resetPictures() { beginResetModel(); mPictures.reset(new vector<unique_ptr<Picture>>()); endResetModel(); return; } void PictureModel::deleteAllPictures() { resetPictures(); } bool PictureModel::isIndexValid(const QModelIndex& index) const { if (index.row() < 0 || index.row() >= rowCount() || !index.isValid()) { return false; } return true; }
定義C++數(shù)據(jù)模型的時(shí)候有幾點(diǎn)需要注意:
1.如果想在QML中訪問模型的某個(gè)方法的話需要在方法聲明的時(shí)候添加Q_INVOKABLE宏
Q_INVOKABLE void addPictureFromUrl(const QUrl& fileUrl);
2.在QML中通過每個(gè)元素類別的名稱來進(jìn)行訪問,對應(yīng)的類別名稱的定義如下:
QHash<int, QByteArray> PictureModel::roleNames() const { QHash<int, QByteArray> roles; roles[Qt::DisplayRole] = "name"; roles[Roles::FilePathRole] = "filepath"; roles[Roles::UrlRole] = "url"; return roles; }
定義圖片緩存器
由于數(shù)據(jù)模型中包含圖片數(shù)據(jù),為了便于在QML中訪問圖片資源,添加圖片緩存器。緩存器繼承自QQuickImageProvider。對應(yīng)的實(shí)現(xiàn)如下所示:
//PictureImageProvider.h #ifndef PICTUREIMAGEPROVIDER_H #define PICTUREIMAGEPROVIDER_H #include <QQuickImageProvider> #include <QCache> class PictureModel; class PictureImageProvider : public QQuickImageProvider { public: static const QSize THUMBNAIL_SIZE; PictureImageProvider(PictureModel* pictureModel); //請求圖片 QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override; //獲取緩存 QPixmap* pictureFromCache(const QString& filepath, const QString& pictureSize); private: //數(shù)據(jù)模型 PictureModel* mPictureModel; //圖片緩存容器 QCache<QString, QPixmap> mPicturesCache; }; #endif // PICTUREIMAGEPROVIDER_H
//PictureImageProvider.cpp #include "PictureImageProvider.h" #include "PictureModel.h" //全屏顯示 const QString PICTURE_SIZE_FULL = "full"; //縮略顯示 const QString PICTURE_SIZE_THUMBNAIL = "thumbnail"; //縮略顯示的尺寸 const QSize PictureImageProvider::THUMBNAIL_SIZE = QSize(350, 350); PictureImageProvider::PictureImageProvider(PictureModel* pictureModel) : QQuickImageProvider(QQuickImageProvider::Pixmap), mPictureModel(pictureModel), mPicturesCache() { } QPixmap PictureImageProvider::requestPixmap(const QString& id, QSize* /*size*/, const QSize& /*requestedSize*/) { QStringList query = id.split('/'); if (!mPictureModel || query.size() < 2) { return QPixmap(); } //第幾個(gè)圖片數(shù)據(jù) int rowId = query[0].toInt(); //顯示模式是縮略顯示還是全屏顯示 QString pictureSize = query[1]; QUrl fileUrl = mPictureModel->data(mPictureModel->index(rowId, 0), PictureModel::Roles::UrlRole).toUrl(); return *pictureFromCache(fileUrl.toLocalFile(), pictureSize); } QPixmap* PictureImageProvider::pictureFromCache(const QString& filepath, const QString& pictureSize) { QString key = QStringList{ pictureSize, filepath } .join("-"); //不包含圖片的時(shí)候創(chuàng)建新的緩存 QPixmap* cachePicture = nullptr; if (!mPicturesCache.contains(key)) { QPixmap originalPicture(filepath); if (pictureSize == PICTURE_SIZE_THUMBNAIL) { cachePicture = new QPixmap(originalPicture .scaled(THUMBNAIL_SIZE, Qt::KeepAspectRatio, Qt::SmoothTransformation)); } else if (pictureSize == PICTURE_SIZE_FULL) { cachePicture = new QPixmap(originalPicture); } mPicturesCache.insert(key, cachePicture); } //包含的時(shí)候直接訪問緩存 else { cachePicture = mPicturesCache[key]; } return cachePicture; }
初始化QML引擎
在QML引擎初始化的時(shí)候添加對應(yīng)的數(shù)據(jù)模型和圖片緩存器,對應(yīng)的實(shí)現(xiàn)如下:
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "picturemodel.h" #include "PictureImageProvider.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); PictureModel pictureModel; QQmlApplicationEngine engine; QQmlContext* context = engine.rootContext(); //添加數(shù)據(jù)模型和圖片緩存器 context->setContextProperty("pictureModel", &pictureModel); //圖片Provider的ID是"pictures" engine.addImageProvider("pictures", new PictureImageProvider(&pictureModel)); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); if (engine.rootObjects().isEmpty()) return -1; return app.exec(); }
QML中訪問C++數(shù)據(jù)模型
在QML中通過數(shù)據(jù)模型訪問數(shù)據(jù),通過圖片緩存器訪問對應(yīng)的圖片資源,對應(yīng)的實(shí)現(xiàn)如下:
//main.qml import QtQuick 2.8 import QtQuick.Dialogs 1.2 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import QtQuick.Window 2.2 Window { visible: true width: 640 height: 480 title: qsTr("QML-MVC") RowLayout { id:tool_layout //添加圖片的按鈕 ToolButton { background: Image { source: "qrc:/image/photo-add.svg" } onClicked: { dialog.open() } } //刪除圖片的按鈕 ToolButton { background: Image { source: "qrc:/image/photo-delete.svg" } onClicked: { pictureModel.removeRows(pictureListView.currentIndex,1) } } } //網(wǎng)格視圖 GridView { id: pictureListView model: pictureModel anchors.top:tool_layout.bottom width: parent.width; height: parent.height - tool_layout.height anchors.leftMargin: 10 anchors.rightMargin: 10 cellWidth : 300 cellHeight: 230 //對應(yīng)的每個(gè)元素的代理 delegate: Rectangle { width: 290 height: 200 color: GridView.isCurrentItem?"#4d9cf8":"#ffffff" //選中顏色設(shè)置 Image { id: thumbnail anchors.fill: parent fillMode: Image.PreserveAspectFit cache: false //通過緩存器訪問圖片 //image://pictures/訪問器的ID //index + "/thumbnail" 圖片索引和顯示模式 source: "image://pictures/" + index + "/thumbnail" } //訪問圖片的名稱 Text { height: 30 anchors.top: thumbnail.bottom text: name font.pointSize: 16 anchors.horizontalCenter: parent.horizontalCenter } //鼠標(biāo)點(diǎn)擊設(shè)置當(dāng)前索引 MouseArea{ anchors.fill: parent onClicked: { pictureListView.currentIndex = index; } } } } //圖片選擇窗口 FileDialog { id: dialog title: "Select Pictures" folder: shortcuts.pictures onAccepted: { var pictureUrl = dialog.fileUrl pictureModel.addPictureFromUrl(pictureUrl) dialog.close() } } }
顯示效果如下圖所示:
到此這篇關(guān)于C++數(shù)據(jù)模型應(yīng)用在QML委托代理機(jī)制中的文章就介紹到這了,更多相關(guān)C++數(shù)據(jù)模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實(shí)現(xiàn)完整功能的通訊錄管理系統(tǒng)詳解
來了來了,通訊錄管理系統(tǒng)踏著七彩祥云飛來了,結(jié)合前面的結(jié)構(gòu)體知識和分文件編寫方法,我總結(jié)并碼了一個(gè)帶菜單的通訊錄管理系統(tǒng),在這篇文章中將會提到C的清空屏幕函數(shù),嵌套結(jié)構(gòu)體具體實(shí)現(xiàn),簡單且充實(shí),跟著我的思路,可以很清晰的解決這個(gè)項(xiàng)目2022-05-05C++?BoostAsyncSocket實(shí)現(xiàn)異步反彈通信的案例詳解
這篇文章主要為大家詳細(xì)介紹了C++?BoostAsyncSocket如何實(shí)現(xiàn)異步反彈通信,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的可以了解一下2023-03-03字符串中找出連續(xù)最長的數(shù)字字符串的實(shí)例代碼
這篇文章介紹了字符串中找出連續(xù)最長的數(shù)字字符串的實(shí)例代碼,有需要的朋友可以參考一下2013-09-09