简体   繁体   English

使用 cpp 模型在拖放 QML listView 中插入/删除项目

[英]Inserting/Deleting Items in a drag&drop QML listView with cpp model

I tried to add to a ListView in QML of N Items a way to add and delete a new Item at a given index .我尝试在N ItemsQML中向ListView添加一种在给定索引添加删除new Item方法

I did the following example, but the problem is that when I move some Items , when I try to insert a new one, the position might be incorrect and I have no clue why.我做了下面的例子,但问题是当我移动一些Items ,当我尝试插入一个新的时,位置可能不正确,我不知道为什么。 When I check my DataList in my cpp model , positions are correct, however, new or deleted items won't be inserted / deleted at the right position .当我在我的cpp model检查我的DataList时,位置是正确的,但是,不会正确的位置inserted / deleted new or deleted items

It seems that the error occurs when I insert a new Item , then I move it , and then I try to delete this Item or insert an Item next to this New Item .当我插入一个new Item ,然后move它,然后我尝试deleteItem或在此新Item旁边insert一个Item,似乎会发生错误

Here is a simple example (you can run it if you need).这是一个简单的示例(如果需要,可以运行它)。 I called my Items Data : Blocks我称我的Items Data : Blocks

#include "mainwindow.h"
#include <QApplication>
#include <QtQml>
#include <QQuickView>
#include <QQuickWidget>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{

    QApplication a(argc, argv);
    MainWindow w;
    w.show();


    return a.exec();
}

main.cpp主程序

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "model.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    void addItem(int index);
    ~MainWindow();

private slots:


private:
    QList<QObject*> dataList;
    Ui::MainWindow *ui;
    BlockModel model;
    int cpt = 0;
};

#endif // MAINWINDOW_H

mainwindow.h主窗口.h

#include <QtQml>
#include <QQuickView>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QQuickWidget"
#include <QStringList>


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{

     int nbItems = 5;

     for(; cpt < nbItems; cpt ++) {
         Block a = Block(QString("Item ")+QString::number(cpt));
         model.addBlock(a);
     }

    ui->setupUi(this);

    QQuickWidget *view = new QQuickWidget;
    QQmlContext *ctxt = view->rootContext();

    ctxt->setContextProperty("myModel", &model);
    view->setSource(QUrl::fromLocalFile("main.qml"));
    view->setGeometry(0, 200, 600, 400);
    view->setResizeMode(QQuickWidget::SizeRootObjectToView);
    ui->dockWidget_3->setWidget(view);
}

MainWindow::~MainWindow()
{
    delete ui;
}

mainwindow.cpp主窗口.cpp

#include <QAbstractListModel>
#include <QStringList>
#include <qqmlcontext.h>
#include <QDebug>
#include <QStringList>

//![0]
class Block
{
public:
    Block(){
    }

    Block(const QString &name);

    QString nameBlock() const;

    void setName(QString n) {
        m_name = n;
    }

private:
    QString m_name;
};

class BlockModel : public QAbstractListModel
{
    Q_OBJECT
public:

    Block* getBlock(QString name);

    Q_INVOKABLE void moveBlock(int from,int to);
    Q_INVOKABLE void insertBlock(int index);
    Q_INVOKABLE void deleteBlock(int index);

    enum BlockRoles {
        nameRole = Qt::UserRole + 1,
    };

    BlockModel(QObject *parent = 0);

    void setContext(QQmlContext *ctx) {
        m_ctx = ctx;
    }

    void setName(const QString &name);

    void addBlock(const Block &Block);

    int rowCount(const QModelIndex & parent = QModelIndex()) const;

    QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
    QHash<int, QByteArray> roleNames() const;

private:
    QList<Block> m_blocks;
    QQmlContext*  m_ctx;
    int cpt = 0;
};

mode.h模式.h

#include "model.h"
#include "qDebug"
Block::Block(const QString &name)
    : m_name(name)
{
}



QString Block::nameBlock() const
{
    return m_name;
}


BlockModel::BlockModel(QObject *parent)
    : QAbstractListModel(parent)
{
}

void BlockModel::addBlock(const Block &Block)
{
    beginInsertRows(QModelIndex(), rowCount(), rowCount());
    m_blocks << Block;
    endInsertRows();
}



int BlockModel::rowCount(const QModelIndex & parent) const {
    Q_UNUSED(parent);
    return m_blocks.count();
}

void BlockModel::moveBlock(int from, int to) {
     m_blocks.move(from,to);
}

void BlockModel::insertBlock(int index) {
    Block b =(Block(QString("New Item ")+QString::number(cpt)));
    beginInsertRows(QModelIndex(),index+1,index+1);
    m_blocks.insert(index+1,b);
    endInsertRows();
    cpt++;
}

void BlockModel::deleteBlock(int index) {
    beginRemoveRows(QModelIndex(),index,index);
    m_blocks.removeAt(index);
    endRemoveRows();
}

QVariant BlockModel::data(const QModelIndex & index, int role) const {
    if (index.row() < 0 || index.row() >= m_blocks.count())
        return QVariant();

    const Block &Block = m_blocks[index.row()];
    if (role == nameRole)
        return Block.nameBlock();

    return QVariant();
}

//![0]
QHash<int, QByteArray> BlockModel::roleNames() const {
    QHash<int, QByteArray> roles;

    roles[nameRole] = "nameBlock";

    return roles;
}

model.cpp模型.cpp

import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.2
import QtQml.Models 2.2
import QtQuick.Controls.Styles 1.4

Rectangle {
    id : rootRectangle
    visible: true
    ScrollView {
        anchors.fill:parent
        ListView{
            id: root
            width: parent.width; height: parent.height
            property int visualIndex: -1

            displaced: Transition {
                NumberAnimation { properties: "y"; easing.type: Easing.OutQuad }
            }

            model: DelegateModel {

                id: visualModel
                model: myModel
                delegate: Component {
                    MouseArea {

                        id: delegateRoot

                        property int visualIndex: DelegateModel.itemsIndex
                        cursorShape: Qt.PointingHandCursor
                        width: root.width; height: 100

                        drag.target:  icon
                        drag.axis: Drag.YAxis

                        Behavior on height {
                            PropertyAnimation { duration: 100 }
                        }

                        Rectangle {
                            anchors.top:  delegateRoot.top
                            anchors.left: delegateRoot.left
                            id: icon
                            objectName: nameBlock
                            width: root.width-5; height: 100
                            color:  "skyblue"

                            radius: 3
                            Text {
                                objectName: "rect"
                                id: title
                                anchors.fill: parent
                                anchors.margins: 10
                                horizontalAlignment: Text.AlignLeft
                                verticalAlignment: Text.AlignVCenter
                                text: nameBlock
                            }

                            Drag.active: delegateRoot.drag.active
                            Drag.source: delegateRoot
                            Drag.hotSpot.x: 36
                            Drag.hotSpot.y: 36

                                Button {
                                    id : buttonAdd
                                    text: "Add Block"

                                    anchors{
                                        right: parent.right
                                        top: parent.top
                                        bottom: parent.bottom
                                        margins: 30
                                    }


                                    onClicked: {
                                        myModel.insertBlock(visualIndex)
                                    }
                                }

                                Button {
                                    id : buttonDelete
                                    text: "Delete Block"
                                    anchors{
                                        right: buttonAdd.left
                                        top: parent.top
                                        bottom: parent.bottom
                                        margins: 30
                                    }
                                    onClicked: {
                                        myModel.deleteBlock(visualIndex)
                                    }
                                }


                            states: [
                                State {
                                    when: icon.Drag.active
                                    ParentChange {
                                        target: icon
                                        parent: root
                                    }
                                    AnchorChanges {
                                        target: icon;
                                        anchors.horizontalCenter: undefined;
                                        anchors.verticalCenter: undefined
                                    }
                                }
                            ]

                            transitions: Transition {
                                // Make the state changes smooth
                                ParallelAnimation {
                                    ColorAnimation { property: "color"; duration: 500 }
                                    NumberAnimation { duration: 300; properties: "detailsOpacity,x,contentY,height,width,font.pixelSize,font.bold,visible" }
                                }
                            }
                        }

                        DropArea {
                            anchors { fill: parent; margins: 15 }
                            onEntered: {
                                visualModel.items.move(drag.source.visualIndex, delegateRoot.visualIndex)
                                myModel.moveBlock(drag.source.visualIndex,delegateRoot.visualInde)
                            }
                        }
                    }
                }
            }
        }
    }
}

main.qml主文件

Do you have any idea of what I am doing wrong ?你知道我做错了什么吗? Thanks a lot and have a good day !非常感谢,祝你有美好的一天!

There are two bugs when moving items.移动物品时有两个错误。 In DropArea.onEntered , if you print out both drag.source.visualIndex and delegateRoot.visualIndex before and after visualModel.items.move , you'll see that values are modified after moving.DropArea.onEntered ,如果你打印出两个drag.source.visualIndexdelegateRoot.visualIndex前后visualModel.items.move ,你会看到,价值观正在修改后。 That means you are moving wrong rows when calling myModel.moveBlock .这意味着您在调用myModel.moveBlock时移动了错误的行。 To fix the problem, save the value before moving items:要解决此问题,请在移动项目之前保存该值:

DropArea {
    anchors { fill: parent; margins: 15 }
    onEntered: {
        var from = drag.source.visualIndex;
        var to = delegateRoot.visualIndex;
        myModel.moveBlock(from, to);
    }
}

When moving items in C++ model, QAbstractItemModel::beginMoveRows should be called just like insert/remove items.在 C++ 模型中移动项目时,应该像插入/删除项目一样调用QAbstractItemModel::beginMoveRows Otherwise the QML DelegateModel cannot correctly display your model.否则 QML DelegateModel无法正确显示您的模型。 Remember that when implementing BlockModel::moveBlock , the destination row for the model is different from the one for your source list m_blocks .请记住,在实现BlockModel::moveBlock ,模型的目标行与源列表m_blocks的目标行不同。 See the last example in QAbstractItemModel::beginMoveRows documentation for detail.有关详细信息,请参阅QAbstractItemModel::beginMoveRows文档中的最后一个示例。

void BlockModel::moveBlock(int from, int to) {
    if (from == to)
        return;
    auto modelFrom = from;
    auto modelTo = to + (from < to ? 1 : 0);

    beginMoveRows(QModelIndex(), modelFrom, modelFrom, QModelIndex(), modelTo);
    m_blocks.move(from,to);
    endMoveRows();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM