C++數據模型應用在QML委托代理機制中
之前文章中介紹過在Qt-Widget和QML中如何使用委托代理機制(Model-View-Delegate),對應的文章鏈接分別如下所示:
在開發(fā)的過程中發(fā)現在QML中直接定義操作數據模型比較繁瑣費力,不如C++的數據模型好用。這里就介紹一下如何在QML中調用C++定義的數據模型,實現數據模型的混合使用。
定義數據模型
定義的C++數據模型和Qt-Widget中定義的數據模型相同。模型主要用來存儲本地圖片的ID的對應的圖片地址。
實現如下:
//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:
//自定義每個元素的數據類型
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:
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) {
//展示數據為圖片的名稱
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++數據模型的時候有幾點需要注意:
1.如果想在QML中訪問模型的某個方法的話需要在方法聲明的時候添加Q_INVOKABLE宏
Q_INVOKABLE void addPictureFromUrl(const QUrl& fileUrl);
2.在QML中通過每個元素類別的名稱來進行訪問,對應的類別名稱的定義如下:
QHash<int, QByteArray> PictureModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[Qt::DisplayRole] = "name";
roles[Roles::FilePathRole] = "filepath";
roles[Roles::UrlRole] = "url";
return roles;
}
定義圖片緩存器
由于數據模型中包含圖片數據,為了便于在QML中訪問圖片資源,添加圖片緩存器。緩存器繼承自QQuickImageProvider。對應的實現如下所示:
//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:
//數據模型
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();
}
//第幾個圖片數據
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("-");
//不包含圖片的時候創(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);
}
//包含的時候直接訪問緩存
else
{
cachePicture = mPicturesCache[key];
}
return cachePicture;
}初始化QML引擎
在QML引擎初始化的時候添加對應的數據模型和圖片緩存器,對應的實現如下:
#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();
//添加數據模型和圖片緩存器
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++數據模型
在QML中通過數據模型訪問數據,通過圖片緩存器訪問對應的圖片資源,對應的實現如下:
//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)
}
}
}
//網格視圖
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
//對應的每個元素的代理
delegate: Rectangle {
width: 290
height: 200
color: GridView.isCurrentItem?"#4d9cf8":"#ffffff" //選中顏色設置
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
}
//鼠標點擊設置當前索引
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()
}
}
}顯示效果如下圖所示:

到此這篇關于C++數據模型應用在QML委托代理機制中的文章就介紹到這了,更多相關C++數據模型內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C++?BoostAsyncSocket實現異步反彈通信的案例詳解
這篇文章主要為大家詳細介紹了C++?BoostAsyncSocket如何實現異步反彈通信,文中的示例代碼講解詳細,具有一定的學習價值,感興趣的可以了解一下2023-03-03

