在qml委派视觉化模型变更时更新c++模型

0x6upsns  于 2022-12-01  发布在  其他
关注(0)|答案(2)|浏览(182)

在c++中定义了一个简单的QStandardItemModel,我通过自定义的Delegate和一个DelegateModel将其显示在QMLListView中。

// The DropArea is part of the delegate `comp_container`
DropArea{
    anchors{fill: parent}
    keys: ["pageitem"]
    onEntered: {
        let from = drag.source.DelegateModel.itemsIndex
        let to = dragAreaPage.DelegateModel.itemsIndex
        if ( pageItemDragOperationStartIndex === -1 ){
            pageItemDragOperationStartIndex = from
        }
        pageItemDragOperationFinalIndex = to
        console.log(from + "->" + to)
        visualModel.items.move(from,to)
    }
}

这里是委托模型,pageproxymodel是c++模型。

DelegateModel {
    id: visualModel
    model: pageproxymodel
    delegate: comp_container
}

如何更新C++模型?委托的顶层项是MouseArea,我在release handler中处理重新排序:

onReleased: {
    if ( pageItemDragOperationStartIndex !== -1 && pageItemDragOperationFinalIndex !== -1 ){
        console.log("Page item final drag operation: " + pageItemDragOperationStartIndex + "->" + pageItemDragOperationFinalIndex)
        pageproxymodel.move(pageItemDragOperationStartIndex, pageItemDragOperationFinalIndex)
        pageItemDragOperationStartIndex = -1
        pageItemDragOperationFinalIndex = -1
    }
}

C++模型的move函数将调用转发给以下处理程序:

bool PageModel::moveRow(const QModelIndex &sourceParent,
                        int sourceRow,
                        const QModelIndex &destinationParent,
                        int destinationChild)
{
    if ( sourceRow < 0 || sourceRow > rowCount()-1 ||
         destinationChild < 0 || destinationChild > rowCount() )
    {
        return false;
    }

    beginMoveRows(sourceParent, sourceRow, sourceRow, destinationParent, destinationChild);
 
    QList<QStandardItem*> rowItems = takeRow(sourceRow);
    insertRow(destinationChild, rowItems);

    endMoveRows();

    return true;
}

对于上面的C++模型代码,它在QML中的释放处理程序处崩溃:

我尝试了其他方法来观察效果,没有崩溃,但也没有预期的行为。

  • 删除单个行(这将删除QML ListView中的2(!)行)
  • 删除没有开始/结束调用的单行(删除QML ListView中的1行,但不能是正确的)
  • 无需开始/结束调用即可删除和插入单行(QML ListView在一段时间内看起来很好,但移动几次后就会失去同步)

基本上,我想做的就是通过c++模型保存ListView状态,毕竟这是一个标准的用例,我这边肯定有什么简单的错误,但我看不到它。

f2uvfpb9

f2uvfpb91#

我喜欢对DelegateModel做的一件事就是利用DelegateModelGroup。通过声明一个名为“all”的组,它引入了一个附加属性allIndex,这对于在项目被重新排序后跟踪它很有用。下面的示例实现了一个同时具有MouseAreaDropAreaDelegateModel。在拖动模式下,我禁用了所有的MouseArea,这样DropArea就有机会做出响应。

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Page {
    property int activeMouseArea: -1
    ListView {
        id: listView
        width: 420
        height: parent.height
        model: SampleDelegateModel { }
        ScrollBar.vertical: ScrollBar {
            width: 20
            policy: ScrollBar.AlwaysOn
        }
    }
    footer: Text { id: dbg }
}

// SampleData.qml
import QtQuick
import QtQuick.Controls

ListModel {
    ListElement { name: "Steve Jobs" }
    ListElement { name: "Jeff Bezos" }
    ListElement { name: "Bill Gates" }
    ListElement { name: "Elon Musk" }
}

// SampleDelegateModel.qml
import QtQuick
import QtQuick.Controls
import QtQml.Models

DelegateModel {
    id: delegateModel
    model: SampleData { }
    delegate: SampleDelegate { }
    groups: [
        DelegateModelGroup {
            id: allItems
            name: "all"
            includeByDefault: true
        }
    ]
    filterOnGroup: "all"
    function moveItem(from, to) {
        dbg.text = `Debugging: moveItem(${from},${to})`;
        allItems.move(from, to);
    }
}

// SampleDelegate.qml
import QtQuick
import QtQuick.Controls
import QtQml.Models

Rectangle {
    property int allIndex: DelegateModel.allIndex
    width: 400
    height: labelText.height + 20
    border.color: "grey"
    z: mouseArea.drag.active || mouseArea.pressed ? 2 : 1
    property int dragTo: -1
    Drag.active: mouseArea.drag.active

    Text {
        id: labelText
        anchors.centerIn: parent
        text: allIndex + ": [" + index + "] " + name
    }

    DropArea {
        anchors.fill: parent
        onEntered: drag.source.dragTo = allIndex
    }

    MouseArea {
        id: mouseArea
        anchors.fill: parent
        drag.target: parent
        property point startPoint
        enabled: activeMouseArea === -1
        onPressed: {
            activeMouseArea = allIndex;
            dragTo = -1;
            startPoint = Qt.point(parent.x, parent.y);
        }
        onReleased: {
            activeMouseArea = -1;
            [parent.x,parent.y] = [startPoint.x, startPoint.y];
            Qt.callLater(delegateModel.moveItem, allIndex, dragTo);
        }
    }
}

您可以Try it Online!

l3zydbqr

l3zydbqr2#

发现了错误:pageItemDragOperationStartIndexpageItemDragOperationFinalIndex变量是每个委托的一部分,但不是页面的一部分。另外,正如在注解中指出的,使用QStandardItemModel不必调用begin/end函数。现在它的工作方式就像一个护身符。

相关问题