C++数据模型应用在QML委托代理机制中

目录
  • 定义数据模型
  • 定义图片缓存器
  • 初始化QML引擎
  • QML中访问C++数据模型

之前文章中介绍过在Qt-Widget和QML中如何使用委托代理机制(Model-View-Delegate),对应的文章链接分别如下所示:

QT中Model-View-Delegate委托代理机制

QML基础

在开发的过程中发现在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("-");
    //不包含图片的时候创建新的缓存
    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++数据模型内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • QML与C++交互的实现步骤

    目录 前言 第一个例子:QML中创建C++对象 第二个例子:C++中加载QML对象 参考 前言 文档如是说,QML旨在通过C ++代码轻松扩展.Qt QML模块中的类使QML对象能够从C ++加载和操作,QML引擎与Qt元对象系统集成的本质使得C ++功能可以直接从QML调用.这允许开发混合应用程序,这些应用程序是通过混合使用QML,JavaScript和C ++代码实现的. QML is designed to be easily extensible through C++ code. Th

  • 利用上下文属性将 C++ 对象嵌入 QML 里

    目录 一.设置简单的上下文属性 二.将对象设置为上下文属性 三.上下文属性与C++ 的数据模型示例 3.1.字符串列表模型 3.2.对象列表模型 3.3.QAbstractItemModel QQmlContext 类使将 C++ 数据注入 QML 对象的能力成为可能.此类向 QML 对象的上下文公开数据,以便可以直接从 QML 代码范围内引用数据. 一.设置简单的上下文属性 例如,这里有一个 QML 项,它引用了当前作用域中不存在的 currentDateTime 值: // MyItem.q

  • 将 C++ 类型属性暴露给 QML

    目录 一.数据类型处理和所有权 1.1.暴露属性 1.2.使用通知信号的注意事项 1.3.具有对象类型的属性 1.4.具有对象列表类型的属性 1.5.分组属性 二.暴露方法 三.暴露信号 可以使用 C++ 代码中定义的功能轻松扩展 QML.由于 QML 引擎与 Qt 元对象系统的紧密集成,QObject 派生类公开的任何功能都可以从 QML 代码访问.这使得 C++ 数据和函数可以直接从 QML 访问,通常几乎不需要修改. QML 引擎能够通过元对象系统反射 QObject 实例.这意味着任何

  • C/C++中的 Qt StandardItemModel 数据模型应用解析

    QStandardItemModel 是标准的以项数据为单位的基于M/V模型的一种标准数据管理方式,Model/View 是Qt中的一种数据编排结构,其中Model代表模型,View代表视图,视图是显示和编辑数据的界面组件,而模型则是视图与原始数据之间的接口,通常该类结构都是用在数据库中较多,例如模型结构负责读取或写入数据库,视图结构则负责展示数据,其条理清晰,编写代码便于维护. QStandardItemModel组件通常会配合TableView组件一起使用,当数据库或文本中的记录发生变化时会

  • C++数据模型应用在QML委托代理机制中

    目录 定义数据模型 定义图片缓存器 初始化QML引擎 QML中访问C++数据模型 之前文章中介绍过在Qt-Widget和QML中如何使用委托代理机制(Model-View-Delegate),对应的文章链接分别如下所示: QT中Model-View-Delegate委托代理机制 QML基础 在开发的过程中发现在QML中直接定义操作数据模型比较繁琐费力,不如C++的数据模型好用.这里就介绍一下如何在QML中调用C++定义的数据模型,实现数据模型的混合使用. 定义数据模型 定义的C++数据模型和Qt

  • JAVA反射机制中getClass和class对比分析

    java有两个获得类名的方法getClass()和class(),这两个方法看似一样,实则不然.这两个方法涉及到了java中的反射. 所谓反射,可以理解为在运行时期获取对象类型信息的操作.传统的编程方法要求程序员在编译阶段决定使用的类型,但是在反射的帮助下,编程人员可以动态获取这些信息,从而编写更加具有可移植性的代码.严格地说,反射并非编程语言的特性,因为在任何一种语言都可以实现反射机制,但是如果编程语言本身支持反射,那么反射的实现就会方便很多. 类型类 要知道类型信息在运行时是如何表示的,这是

  • Android Loop机制中Looper与handler详细分析

    目录 Looper是什么 简单使用 从源码了解loop原理 loop分析 myLooper() next() handler.dispatchMessage handler分析 消息入队 同步屏障 总结 Looper是什么 用于为线程运行消息循环的类.默认情况下,线程没有与之关联的消息循环.要创建一个,在要运行循环的线程中调用 prepare(),然后调用loop()让它处理消息,直到循环停止为止.与消息循环的大多数交互是通过 Handler类进行的. 意思大概就是让线程有处理消息的能力,并且这

  • Java服务器宕机的解决方法论

    1 宕机概要 1.1 定义 向服务器的请求都没有响应或者响应非常慢. 前端界面的崩溃并非宕机. 1.2 分类 进程闪退 内部崩溃 外部终止 线程锁死或者无限等待 内存溢出 下面分别进行详解 2 进程闪退 2.1 内部崩溃 JVM 发生内部崩溃,必然会生成"hs_err_pid"开头的文件. 下面讲一种常见情况: 无法申请内存,显示commit_memory错误 Current thread (0x00007f3e40013000): JavaThread "Unknown t

  • Lua中函数与面向对象编程的基础知识整理

    函数 1. 基础知识 调用函数都需要写圆括号,即使没有参数,但有一种特殊例外:函数若只有一个参数且参数是字面字符串或table构造式,则圆括号可有可无,如dofile 'a.lua',f{x=10, y=20}. Lua为面向对象式的调用提供冒号操作符的特殊语法,如o.foo(o, x)等价于o:foo(x).和Javascript类似,调用函数时提供的实参数量可以与形参数量不同,若实参多了则舍弃,不足则多余的形参初始化为nil. 1.1 多重返回值 Lua允许函数返回多个结果,函数返回如ret

  • 深入理解golang的异常处理机制

    前言 众所周知在java或php等很多面向对象的语言中, 异常处理是依靠throw.catch来进行的.在go语言中,panic和recover函数在作用层面分别对等throw和catch语句,当然也存在不同之处.下面话不多说,来一起看看详细的介绍吧. 从设计层面来看,panic和recover函数适用于那些真正的异常(例如整数除0),而throw catch finally机制常常被用来处理一些业务层面的自定义异常.因此在go语言中,panic和recover要慎用. 上述两种异常机制的使用中

  • Mysql数据库锁定机制详细介绍

    前言 为了保证数据的一致完整性,任何一个数据库都存在锁定机制.锁定机制的优劣直接应想到一个数据库系统的并发处理能力和性能,所以锁定机制的实现也就成为了各种数据库的核心技术之一.本章将对MySQL中两种使用最为频繁的存储引擎MyISAM和Innodb各自的锁定机制进行较为详细的分析. MySQL锁定机制简介 数据库锁定机制简单来说就是数据库为了保证数据的一致性而使各种共享资源在被并发访问访问变得有序所设计的一种规则.对于任何一种数据库来说都需要有相应的锁定机制,所以MySQL自然也不能例外.MyS

  • 详解Node.js中的事件机制

    前言 在前端编程中,事件的应用十分广泛,DOM上的各种事件.在Ajax大规模应用之后,异步请求更得到广泛的认同,而Ajax亦是基于事件机制的. 通常js给我们的第一印象就是运行在客户端浏览器上面的脚本,通过node.js我们可以在服务端运行javascript. node.js是基于单线程无阻塞异步式的I/O,异步式的I/O指的是当遇到I/O操作的时候,线程不阻塞而是进行下面的操作,那么I/O操作完成之后,线程时如何知道该操作完成的呢? 当操作完成耗时的I/O操作之后,会以事件的形式通知I/O操

  • JavaScript中闭包的详解

    闭包是什么 在 JavaScript 中,闭包是一个让人很难弄懂的概念.ECMAScript 中给闭包的定义是:闭包,指的是词法表示包括不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量. 是不是看完这个定义感觉更加懵逼了?别急,我们来分析一下. 闭包是一个函数 闭包可以使用在它外面定义的变量 闭包存在定义该变量的作用域中 好像有点清晰了,但是使用在它外面定义的变量是什么意思,我们先来看看变量作用域. 变量作用域 变量可分为全局变量和局部变量.全局变量的作用域就是全局性的,在 js

  • java与c#的区别、两者有什么不同?

    Java 的设计者是因为讨厌C++的复杂,于是Java 非常简洁,GC 也让内存管理非常方便,C# 是看中了Java 的GC,和虚拟机技术,希望把微软的几大语言集成到.NET 上来. 因此C#从语言上来讲并不简单甚至可以算的上复杂. 两种语言的设计思路也不一样,Java 是编译解释语言,C#是编译然后编译运行语言.Java 没有委托,C# 有委托.Java 倾向于用Interface 实现委托的功能,而 在C# 中,Abstract Class 比Interface 发挥了更大功能. Java

随机推荐