简体   繁体   English

QML 形状:如何以干净的方式强制更新数据属性?

[英]QML Shape: How can I force data property to update in a clean way?

I am trying to make a window where I can draw triangles and delete any of them with a Shape{} .我正在尝试制作一个 window ,我可以在其中绘制三角形并使用Shape{}删除其中的任何一个。 In my example below, I can draw 2 types of triangle:在下面的示例中,我可以绘制两种类型的三角形:

  • Up triangle: green and filled向上三角形:绿色且已填充
  • Down triangle: yellow and not filled向下三角形:黄色且未填充

Basically, I choose the type of triangle (with a button on the right-bottom corner) then I click anywhere on the window to get a triangle.基本上,我选择三角形的类型(右下角有一个按钮),然后单击 window 上的任意位置以获得三角形。

Once I click a triangle is created dynamically and it is stored in the property triangleList .单击后,将动态创建一个三角形并将其存储在属性triangleList中。 Then I call the function shape.update() to update data property of shape.然后我调用 function shape.update()来更新形状的数据属性。 This part works well.这部分效果很好。

Here the function update I use in the Shape (since data is a list, I have to reassign to a new list.):这里是我在形状中使用的 function 更新(因为数据是一个列表,我必须重新分配给一个新列表。):

function update()
{
    data = [];
    var d = [];

    for (var i = 0; i < canvas.triangleList.length; i++)
    {
       d.push( canvas.triangleList[i] );
    }
    data = d;
}

My problem appears when I try to delete a triangle.当我尝试删除三角形时出现我的问题。 In my example, I can delete the first, the last or all triangles.在我的示例中,我可以删除第一个、最后一个或所有三角形。 When I delete a triangle, first I delete the value in triangleList then I call again shape.update() .当我删除三角形时,首先删除triangleList中的值,然后再次调用shape.update() It works when I delete all triangles or the last one.当我删除所有三角形或最后一个三角形时,它会起作用。

However, when I try to delete the first triangle, data doesn't update its objects even if I give it a new list .但是,当我尝试删除第一个三角形时,即使我给它一个新列表,数据也不会更新它的对象 In fact, it always deletes the last triangle.事实上,它总是删除最后一个三角形。 Below an example:下面是一个例子:

在此处输入图像描述

data property understands there is one less triangle but it doesn't update the other triangles. data 属性知道少了一个三角形,但它不会更新其他三角形。 The only solution I found is to change a property then come back to the original value.我找到的唯一解决方案是更改属性然后返回到原始值。 This way, it forces the data to update.这样,它强制更新数据。 But I have to do that for every property that can be different (colors and positions).但我必须为每个可能不同的属性(颜色和位置)这样做。 Hence, my update() function looks like that:因此,我的update() function 看起来像这样:

for (var i = 0; i < canvas.triangleList.length; i++)
                {
    d.push( canvas.triangleList[i] );

    ////// Change properties one by one to force the refresh

    // Force path redraw. Otherwise only the last path can be deleted
    d[i].startX++;d[i].startX--;

    // Force line color update
    d[i].strokeColor = "red"
    d[i].strokeColor = d[i].isUp ? "green" : "yellow";

    // Force fill color update
    d[i].fillColor = "red";
    d[i].fillColor = d[i].isUp ? "green" : "transparent";

    data = d;
}

I invite you to comment in/out these lines to see the difference.我邀请您对这些行进行评论/评论以查看差异。 I could use this trick to force the update but my real code is really bigger than this example and I use bindings.我可以使用这个技巧来强制更新,但我的实际代码确实比这个示例大,而且我使用了绑定。

So my question is: Is there a way to force the update without having to change each property?所以我的问题是:有没有办法强制更新而不必更改每个属性?

Here the full code if you want to test it:如果你想测试它,这里有完整的代码:

import QtQuick 2.9;
import QtQuick.Controls 2.2;
import QtQuick.Shapes 1.0;

ApplicationWindow {
    visible: true; width: 640; height: 480;

    Rectangle {
        id: canvas;
        anchors.fill: parent;
        color: "black";
        property var triangleList: [];
        property bool triangleUp: true;

        MouseArea {
            anchors.fill: parent;
            onClicked: {
                var triangle = componentTriangle.createObject(componentTriangle, {
                                                                "isUp" :  canvas.triangleUp,
                                                                "startX" :  mouse.x,
                                                                "startY" :  mouse.y,
                                                               }, canvas);

                canvas.triangleList.push(triangle);
                shape.update();
            }
        }   // MouseArea

        Shape {
            id: shape;

            anchors.fill: parent;

            function update()
            {
                data = [];
                var d = [];

                for (var i = 0; i < canvas.triangleList.length; i++)
                {
                    d.push( canvas.triangleList[i] );

                    ///////////// HOW TO AVOID THE PART BELOW? /////////////
                    ////// Change properties one by one to force the refresh

                    // Force path redraw. Otherwise only the last path can be deleted
                    d[i].startX++;d[i].startX--;

                    // Force line color update
                    d[i].strokeColor = "red"
                    d[i].strokeColor = d[i].isUp ? "green" : "yellow";

                    // Force fill color update
                    d[i].fillColor = "red";
                    d[i].fillColor = d[i].isUp ? "green" : "transparent";

                    //////////////////////////////////////////////////////
                }

                data = d;

                // I make sure data has at least one path to ensure the refresh
                if (data.length == 0)
                    data.push(Qt.createQmlObject('import QtQuick 2.9; import QtQuick.Shapes 1.0; ShapePath {startX:0;startY:0;}', canvas,
                              "force_refresh"));
            }
        }  // Shape
    }   // Rectangle

    //////////// Buttons to handle the triangles

    Column {
        anchors.bottom: parent.bottom;
        anchors.right: parent.right;

        Button {

            text: canvas.triangleUp? "Draw triangleUp" : "Draw triangleDown";
            onClicked: { canvas.triangleUp = !canvas.triangleUp; }
        }   // Button

        Button {
            text: "Clear first";
            onClicked: {
                canvas.triangleList[0].destroy();
                canvas.triangleList.splice(0,1);
                shape.update();
            }
        }   // Button

        Button {
            text: "Clear last";
            onClicked: {
                canvas.triangleList[canvas.triangleList.length -1].destroy();
                canvas.triangleList.splice(canvas.triangleList.length -1,1);
                shape.update();
            }
        }   // Button

        Button {
            text: "Clear all";
            onClicked: {
                for (var i = 0; i < canvas.triangleList.length; i++)
                    canvas.triangleList[i].destroy();
                canvas.triangleList = [];
                shape.update();
            }
        }   // Button
    }

    //////////// Component to draw the triangle
    Component {
        id: componentTriangle;

        ShapePath {
            property bool isUp;
            property real offsetX: isUp? -20 : 20;
            property real offsetY: isUp? -30 : 30;

            strokeColor: isUp ? "green" : "yellow";
            strokeWidth: 3;
            fillColor: isUp ? "green" : "transparent";

            PathLine { x: startX - offsetX; y: startY - offsetY }
            PathLine { x: startX + offsetX; y: startY - offsetY }
            PathLine { x: startX; y: startY }
        }   // ShapePath
    }
}

Thank you very much for your help and feel free to ask me if I was not clear.非常感谢您的帮助,如果我不清楚,请随时问我。

Have a nice day!祝你今天过得愉快!

If you are going to handle many items (Shape) it is advisable to use a Repeater with a model. The repeater is responsible for displaying the items based on the information of the model, and to remove the items you just have to remove items from the model.如果你要处理很多物品(Shape),建议使用带有model的Repeater。中继器负责根据model的信息显示物品,并删除你只需要从中删除物品的物品model。

main.qml主要.qml

import QtQuick 2.9;
import QtQuick.Controls 2.2;
import QtQuick.Shapes 1.0;

ApplicationWindow {
    visible: true; width: 640; height: 480;
    QtObject{
        id: internals
        property bool triangleUp: true;
    }
    ListModel{
        id: datamodel
    }
    Rectangle {
        id: canvas;
        anchors.fill: parent;
        color: "black";
        Repeater{
            model: datamodel
            Triangle{
                x: model.x
                y: model.y
                isUp: model.isUp
            }
        }
        MouseArea{
            anchors.fill: parent
            onClicked: datamodel.append({"x": mouse.x, "y": mouse.y, "isUp": internals.triangleUp})
        }
    }
    Column {
        anchors.bottom: parent.bottom;
        anchors.right: parent.right;
        Button {
            text: internals.triangleUp ? "Draw triangleUp" : "Draw triangleDown";
            onClicked: internals.triangleUp = !internals.triangleUp;
        }   // Button

        Button {
            text: "Clear first";
            onClicked: if(datamodel.count > 0) datamodel.remove(0)
        }   // Button

        Button {
            text: "Clear last";
            onClicked: if(datamodel.count > 0) datamodel.remove(datamodel.count - 1)
        }   // Button

        Button {
            text: "Clear all";
            onClicked: datamodel.clear()
        }   // Button
    }
}

Triangle.qml三角形.qml

import QtQuick 2.9;
import QtQuick.Shapes 1.0

Shape {
    id: shape
    property bool isUp: false
    QtObject{
        id: internals
        property real offsetX: isUp? -20 : 20;
        property real offsetY: isUp? -30 : 30;
    }
    ShapePath {
        strokeWidth: 3;
        strokeColor: isUp ? "green" : "yellow";
        fillColor: isUp ? "green" : "transparent";
        PathLine { x: -internals.offsetX ; y: -internals.offsetY }
        PathLine { x: internals.offsetX; y: -internals.offsetY }
        PathLine { x: 0; y: 0 }
    }
}

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

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