как сделать обновляемую модель в QML - PullRequest
0 голосов
/ 21 февраля 2020

Я пытаюсь создать интерактивный виджет QML, который будет обновлять свою модель.

Прямое изменение модели не работает, поэтому я создаю сигнал updateModel(int index, real x, real y) в InteractiveGraph. Однако изменение, сделанное в onUpdateModel, не влияет на пользовательский интерфейс.

Я также пытался использовать обработчик onUpdateModel, например, такой: { var newModel = model.slice(); newModel[index].x = x; newModel[index].y = y; model = newModel }, но результат еще хуже, поскольку он нарушает самотаскивание элемента вершины, и модель все равно не обновляется в пользовательском интерфейсе.

Разве класс Array не является хорошей моделью для этого варианта использования? Есть какая-нибудь готовая модель, которая более подходит?

Application screenshot

InteractiveGraph.qml :

import QtQuick 2.0

/* An interactive undirected graph, whose vertices are draggable */

Item {
    id: root
    property alias model: repeater.model
    property real size: 20
    signal updateModel(int index, real x, real y)

    Canvas { // render lines connecting the vertices
        anchors.fill: parent
        onPaint: {
            var ctx = getContext("2d")
            ctx.beginPath()
            for(var i = 0; i < model.length; i++) {
                if(i === 0) ctx.moveTo(model[i].x, model[i].y);
                else ctx.lineTo(model[i].x, model[i].y);
            }
            ctx.closePath()
            ctx.stroke()
        }
    }

    Repeater { // instantiate items corresponding to vertices
        id: repeater
        delegate: Rectangle {
            id: self
            color: "red"
            border.color: "#000"
            width: size
            height: size
            x: modelData.x - size/2
            y: modelData.y - size/2
            Text { anchors.centerIn: parent; text: index }
            MouseArea { // make items self-draggable
                anchors.fill: parent
                drag.target: self
            }
            // invert the x and y relation, and send back the change to model
            onXChanged: root.updateModel(index, x + size/2, y + size/2)
            onYChanged: root.updateModel(index, x + size/2, y + size/2)
        }
    }
}

main.qml :

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    id: root

    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    InteractiveGraph {
        id: interactiveGraph
        anchors.fill: parent
        model: [
            {x: 15, y: 20},
            {x: 100, y: 220},
            {x: 145, y: 230},
            {x: 225, y: 130},
            {x: 140, y: 88},
            {x: 290, y: 60},
        ]
        onUpdateModel: { model[index].x = x; model[index].y = y; model = model; }
    }
}

1 Ответ

0 голосов
/ 21 февраля 2020

Я должен был прочитать Модели и виды в Qt Quick , чтобы узнать, какая модель подходит для использования: ListModel.

Единственное Предостережение заключается в том, что мы не можем использовать x / y имен свойств внутри ListElement дочернего элемента ListModel, так как при использовании ListElement переменная modelData больше не используется, а непосредственно имя свойства выставляется, и x / y может столкнуться со свойствами делегата с тем же именем.
(В случае имени cla sh мы можем использовать model.fieldName вместо fieldName.)

InteractiveGraph.qml :

Item {
    id: root
    property alias model: repeater.model
    property real size: 20
    Canvas {
        id: canvas
        anchors.fill: parent
        onPaint: {
            var ctx = getContext("2d")
            ctx.fillStyle = "#eee"; ctx.fillRect(0, 0, width, height)
            ctx.beginPath()
            for(var i = 0; i < model.count; i++) {
                var item = model.get(i)
                if(i === 0) ctx.moveTo(item.xPos, item.yPos)
                else ctx.lineTo(item.xPos, item.yPos)
            }
            ctx.stroke()
        }
    }
    Repeater {
        id: repeater
        delegate: Rectangle {
            id: self
            color: "red"; border.color: "#000"
            x: xPos - size/2; y: yPos - size/2; width: size; height: size
            onXChanged: canvas.requestPaint(); onYChanged: canvas.requestPaint()
            Text { anchors.centerIn: parent; text: index }
            MouseArea {
                anchors.fill: parent
                drag.target: self
                onPositionChanged: model.set(index, {xPos: self.x + size/2, yPos: self.y + size/2})
            }
        }
    }
}

main.qml :

import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    ListModel {
        id: graphModel
        ListElement {xPos:  15; yPos:  20}
        ListElement {xPos: 100; yPos: 220}
        ListElement {xPos: 145; yPos: 230}
        ListElement {xPos: 225; yPos: 130}
        ListElement {xPos: 140; yPos:  88}
        ListElement {xPos: 290; yPos:  60}
    }

    InteractiveGraph {
        id: interactiveGraph
        anchors.fill: parent
        model: graphModel
    }
}
...