Using C++ Models in QML



  • 参考视频:Using C++ Models in QML {tutorial}

    这个视频比较长,40多分钟,内容也比较丰富。

    主要功能

    • 一个简单的待办事项
    • 可以增加新的待办事项,也可以删除已经完成的事情

    源码

    ToDoList.qml

    import QtQuick 2.11
    import QtQuick.Controls 2.4
    import QtQuick.Layouts 1.3
    import ToDo 1.0
    ColumnLayout{
        Frame {
            Layout.fillWidth: true
            ListView{
                id:listView
                implicitWidth: 300
                implicitHeight: 300
                anchors.fill: parent
                clip: true
    
                model: ToDoModel{
                    list:toDoList
                }
    
                delegate: RowLayout{
                    width: listView.width
                    CheckBox{
                        checked: model.done
                        onCheckedChanged: model.done = checked
                    }
                    TextField{
                        Layout.fillWidth: true
                        text: model.description
                        onEditingFinished: model.description = text
                    }
                }
            }
        }
        RowLayout{
            Button{
                text:qsTr("Add new item")
                onClicked: toDoList.appendItem()
                Layout.fillWidth: true
            }
            Button{
                text:qsTr("Remove complete items")
                onClicked: toDoList.removeCompletedItem()
                Layout.fillWidth: true
            }
        }
    }
    
    

    main.qml

    import QtQuick 2.11
    import QtQuick.Controls 2.4
    
    ApplicationWindow {
        visible: true
        width: 640
        height: 480
        title: qsTr("To Do List")
    
        ToDoList{
            anchors.centerIn: parent
        }
    }
    
    

    ToDoList.h

    #ifndef TODOLIST_H
    #define TODOLIST_H
    #include <QObject>
    #include <QVector>
    struct ToDoItem{
        bool done;
        QString description;
        bool equals(const ToDoItem& item) const {
            return this->done == item.done && this->description == item.description;
        }
    };
    class ToDoList : public QObject
    {
        Q_OBJECT
    public:
        explicit ToDoList(QObject *parent = nullptr);
        QVector<ToDoItem> items() const;
        bool setItemAt(int index, const ToDoItem &item);
    signals:
        void preItemAppended();
        void postItemAppended();
    
        void preItemRemoved(int index);
        void postItemRemoved();
    public slots:
        void appendItem();
        void removeCompletedItem();
    private:
        QVector<ToDoItem> m_items;
    };
    
    #endif // TODOLIST_H
    
    

    ToDoList.cpp

    #include "ToDoList.h"
    
    ToDoList::ToDoList(QObject *parent) : QObject(parent)
    {
        m_items.append({true,"Wash the car"});
        m_items.append({false,"Fix the sink"});
    }
    
    QVector<ToDoItem> ToDoList::items() const
    {
        return m_items;
    }
    
    bool ToDoList::setItemAt(int index, const ToDoItem &item)
    {
        if(index < 0 || index >= m_items.size()){
            return false;
        }
        const ToDoItem& oldItem = m_items[index];
        if(oldItem.equals(item)){
            return false;
        }
        m_items[index] = item;
        return true;
    }
    
    void ToDoList::appendItem()
    {
        emit preItemAppended();
        ToDoItem item;
        item.done = false;
        m_items.append(item);
        emit postItemAppended();
    }
    
    void ToDoList::removeCompletedItem()
    {
        for(int i=0;i<m_items.size();){
            if(m_items[i].done){
                emit preItemRemoved(i);
                m_items.remove(i);
                emit postItemRemoved();
            }else{
                i++;
            }
        }
    }
    
    

    ToDoModel.h

    #ifndef TODOMODEL_H
    #define TODOMODEL_H
    
    #include <QAbstractListModel>
    class ToDoList;
    
    class ToDoModel : public QAbstractListModel
    {
        Q_OBJECT
        Q_PROPERTY(ToDoList *list READ list WRITE setList)
    public:
        explicit ToDoModel(QObject *parent = nullptr);
        enum{
            DoneRole = Qt::UserRole,
            DescriptionRole
        };
        // Basic functionality:
        int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    
        QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    
        // Editable:
        bool setData(const QModelIndex &index, const QVariant &value,
                     int role = Qt::EditRole) override;
    
        Qt::ItemFlags flags(const QModelIndex& index) const override;
    
    private:
        ToDoList* m_list;
        // QAbstractItemModel interface
    public:
        virtual QHash<int, QByteArray> roleNames() const override;
        ToDoList *list() const;
        void setList(ToDoList *list);
    };
    
    #endif // TODOMODEL_H
    
    

    ToDoModel.cpp

    #include "ToDoModel.h"
    #include "ToDoList.h"
    ToDoModel::ToDoModel(QObject *parent)
        : QAbstractListModel(parent),
          m_list(nullptr)
    {
    }
    
    int ToDoModel::rowCount(const QModelIndex &parent) const
    {
        // For list models only the root node (an invalid parent) should return the list's size. For all
        // other (valid) parents, rowCount() should return 0 so that it does not become a tree model.
        if (parent.isValid() || m_list == nullptr)
            return 0;
    
        return m_list->items().size();
    }
    
    QVariant ToDoModel::data(const QModelIndex &index, int role) const
    {
        if (!index.isValid() || m_list==nullptr)
            return QVariant();
        auto item = m_list->items().at(index.row());
        switch (role) {
        case DoneRole:{
            return QVariant(item.done);
        }
        case DescriptionRole:{
            return QVariant(item.description);
        }
        }
        return QVariant();
    }
    
    bool ToDoModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
        if(m_list == nullptr){
            return false;
        }
        auto item = m_list->items().at(index.row());
    
        switch (role) {
        case DoneRole:{
            item.done = value.toBool();
            break;
        }
        case DescriptionRole:{
            item.description = value.toString();
            break;
        }
        }
        if (m_list->setItemAt(index.row(),item)) {
            emit dataChanged(index, index, QVector<int>() << role);
            return true;
        }
        return false;
    }
    
    Qt::ItemFlags ToDoModel::flags(const QModelIndex &index) const
    {
        if (!index.isValid())
            return Qt::NoItemFlags;
    
        return Qt::ItemIsEditable;
    }
    
    ToDoList *ToDoModel::list() const
    {
        return m_list;
    }
    
    void ToDoModel::setList(ToDoList *list)
    {
        beginResetModel();
        if(m_list != nullptr){
            m_list->disconnect(this);
        }
        m_list = list;
        if(m_list!=nullptr){
            connect(m_list,&ToDoList::preItemAppended,[=](){
                const int index = m_list->items().size();
                beginInsertRows(QModelIndex(),index,index);
            });
            connect(m_list,&ToDoList::postItemAppended,[=](){
                endInsertRows();
            });
            connect(m_list,&ToDoList::preItemRemoved,[=](int index){
                beginRemoveRows(QModelIndex(),index,index);
            });
            connect(m_list,&ToDoList::postItemRemoved,[=](){
                endRemoveRows();
            });
        }
        endResetModel();
    }
    
    QHash<int, QByteArray> ToDoModel::roleNames() const
    {
        QHash<int, QByteArray> names;
        names[DoneRole] = "done";
        names[DescriptionRole] = "description";
        return names;
    }
    
    

    main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include "ToDoModel.h"
    #include "ToDoList.h"
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
        qmlRegisterType<ToDoModel>("ToDo",1,0,"ToDoModel");
        qmlRegisterUncreatableType<ToDoList>("ToDo",1,0,"ToDoList",
            QStringLiteral("ToDoList should not be created in QMl"));
        ToDoList toDoList;
        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty("toDoList",&toDoList);
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
    
        return app.exec();
    }
    
    

    截图

    最后的运行效果

    image

    自己做的时候留下的部分截图

    image
    image
    image

    思考与总结

    虽然是一个简单的ToDo-List,却是目前做过的最难的一个qml Demo,牵涉到的Qt相关的知识非常多。

    • 在Layout中使用fillWidth: true使控件填充满

    不同于css中设置display的方式,qml的layout采用这种方式使得控件填充满layout,同时还可以实现对其,如这个例子中的两个Button的宽度和ListView宽度一样

    • Model-View-Delegate的使用

    在我的使用经历中,Qt的这个模式是一个很难的点,我看了很多视频教程,也尝试着自己去写基于这个模式的Demo,往往是失败告终。在这次的ToDo-List中,视频非常详细的介绍了从简单的model,一步一步到复杂的model,如何编写,如何使用。我认为是这个视频对我最大的帮助之一。

    • 使用信号槽实现的双向绑定

    我开始只能把model的数据展示在listview上,如果我去勾选框或者去编辑,model是感应不到的,是不会改变的。后来是onCheckedChanged和onEditingFinished两个槽函数,把改变的数据赋给模型,才完成这个双向绑定。

    • 在QML是用C++编写的models

    本来这是这个视频的重点,结果发现,在最后使用的时候,非常简单,两个注册函数就搞定了。反倒是把大量的精力花在了如何编写model上。在编辑器中,qml对于C++槽函数的智能提示也做得非常的好,很方便。
    GitHub: https://github.com/PikachuHy/qml-demo/tree/master/using-cpp-models-in-qml


 

走马观花

最近的回复

  • MStatus是描述Maya状态的一个类。其中MStatus.perror()表示在Output Window中显示错误信息。
    MPxLocatorNode 是NPxNode的一个子类,它表示的是类似坐标轴等辅助的节点,它不参加渲染,只是起辅助作用。可以使用原生的OpenGL调用。
    MPxDeformerNode 是描述的是形变的节点,定义的是形变的内容。

    获取物体的类别可以写这样的Python代码:
    import maya.cmds
    print cmds.getClassification( "lambert" )
    可得:[u'drawdb/shader/surface/lambert:shader/surface']
    参考:https://help.autodesk.com/view/MAYAUL/2019/CHS/?guid=__CommandsPython_getClassification_html

    MPx开头的类表明是Proxy的类
    MFn开头的类表明是Function Set类

    Maya的例子FootPrintNode和RawFootPrintNode的区别是RawFootPrintNode它有实现draw()函数,它实现了跨渲染器(DX和OpenGL)的渲染。😛

    阅读更多
  • 由于我使用的是Maya 2018,而Maya 2018是在Visual Studio 2017上进行编译的,所以我们需要下载Visual Studio 2017。
    1、首先打开Visual Studio 2017,创建一个简单的VS项目。Ctrl + Shift + N,使用Windows桌面向导进行创建。

    注意不要创建stdafx.h 头,不使用stdafx头进行构建。
    然后创建一个文件FirstMayaPlugin.cpp 进行处理。

    2、将Debug改为ReleaseDebug,并且将win32改为x64。

    3、设置下面的属性
    3.1 附加包含目录中,要有D:\Develop\devkitBase\include
    3.2 优化改为已禁用,为了调试符号的正确性
    3.3 预处理宏中添加这些:

    NDEBUG OSWin_ WIN32 _WINDOWS _USRDLL NT_PLUGIN _HAS_ITERATOR_DEBUGGING=0 _SECURE_SCL=0 _SECURE_SCL_THROWS=0 _SECURE_SCL_DEPRECATE=0 _CRT_SECURE_NO_DEPRECATE TBB_USE_DEBUG=0 __TBB_LIB_NAME=tbb.lib Bits64_

    3.4 附加库目录设为D:\Develop\devkitBase\lib
    3.5 附加依赖项设为
    Foundation.lib
    OpenMaya.lib
    3.6 改目标文件扩展名为.mll

    4、在FirstMayaPlugin.cpp 中添加以下的代码:

    #include <maya/MStatus.h> #include <maya/MObject.h> #include <maya/MFnPlugin.h> #include <maya/MStatus.h> #include <maya/MObject.h> #include <maya/MFnPlugin.h> MStatus initializePlugin( MObject obj ) { MStatus status; MFnPlugin plugin( obj, "Next Digital", "1.0", "Any" ); return status; } MStatus uninitializePlugin( MObject obj ) { MStatus status; return status; }

    就可以顺利地编译了。

    这个插件就可以在Maya中顺利地进行载入了。

    阅读更多
  • 一些数据结构以及释义
    MVertexBuffer:对显卡的Vertex Buffer进行封装;
    MIndexBuffer:对显卡的Index Buffer进行封装;
    MGeometryRequirement
    MRenderItem:渲染的项目,包含了一个index buffer,多个vertex buffer,选定的着色器
    renderpasses:渲染的次数
    MVertexBufferDescriptor
    MVertexBufferDescriptionList
    MRenderItemList
    MPxDrawOverride:重新组织渲染语言的API,包括OpenGL的调用
    MPxGeometryOverride:整合外部着色器和DAG对象
    MPxShaderOverride:低层着色组织
    NPxSbadingNodeOverride:如何插件与其它软件的交互
    对比:NPxShaderOverride:全部渲染网络需要,NPxShadingNodeOverride:仅仅与其它插件的交互

    RenderOperation:类似一个renderpass
    渲染的Operation
    1、背景的Operation
    2、3D场景的Operation
    3、2D的HUD的Operation
    4、用户自定义Operation
    5、四方blt的Operation
    6、对象presentation的Operation

    MShaderManager

    开发者例子:viewRenderOverride

    Maya的渲染节点构成的是DAG(有向无环图),包含了transforms和shapes。
    MFnAssembly、MPxAssembly。

    阅读更多
  • 网上被黑客服都会说1,你的账户存在风险需要你提供身份信息2,银行维护,3, 系统维护,,出款系统维护4 你的账户存在套利遇到这些情况其实就是他们想黑你,正规的也是存在维护的,银行也确实会维护,但只要超过一天以上,那你基本就是被黑了,所以还是劝朋友们远离赌博,因为网络虚拟无保障,所以很多玩家被假网黑钱的事时有发生。国内法律不允许。
    虚拟的网络世界真真假假,虚虚实实。网上娱乐的黑网也是铺天盖地,不计其数。网赌被黑出款扣193+5829*223、以及出黑中的各种骗局,防不胜防。网赌害人!远离网赌

    阅读更多

关注我们

微博
QQ群











召唤伊斯特瓦尔