【问题标题】:QML Shape: How can I force data property to update in a clean way?QML Shape:如何强制数据属性以干净的方式更新?
【发布时间】:2020-02-22 19:57:21
【问题描述】:

我正在尝试创建一个窗口,我可以在其中绘制三角形并使用 Shape{} 删除其中的任何一个。在下面的示例中,我可以绘制 2 种类型的三角形:

  • 上三角:绿色和填充
  • 向下三角形:黄色且未填充

基本上,我选择三角形的类型(右下角有一个按钮),然后单击窗口上的任意位置以获取三角形。

一旦我点击了一个三角形,就会动态创建一个三角形,并将其存储在属性 triangleList 中。然后我调用函数 shape.update() 来更新 shape 的 data 属性。这部分效果很好。

这里是我在Shape中使用的函数更新(由于数据是一个列表,我必须重新分配给一个新列表。):

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

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

当我尝试删除三角形时出现了我的问题。在我的示例中,我可以删除第一个、最后一个或所有三角形。当我删除一个三角形时,首先我删除 triangleList 中的值,然后再次调用 shape.update()。当我删除所有三角形或最后一个三角形时,它会起作用。

但是,当我尝试删除第一个三角形时,即使我给它一个新列表,数据也不会更新它的对象。事实上,它总是删除最后一个三角形。下面是一个例子:

data 属性知道少了一个三角形,但它不会更新其他三角形。我找到的唯一解决方案是更改属性然后返回原始值。这样,它会强制数据更新。 但我必须为每一个可能不同的属性(颜色和位置)这样做。因此,我的 update() 函数如下所示:

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;
}

我邀请您评论/评论这些行以查看差异。 我可以使用这个技巧来强制更新,但我的真实代码确实比这个示例大,而且我使用绑定。

所以我的问题是:有没有办法强制更新而不必更改每个属性?

如果你想测试它,这里是完整的代码:

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
    }
}

非常感谢您的帮助,如果我不清楚,请随时问我。

祝你有美好的一天!

【问题讨论】:

    标签: qt qml shapes qqmlcomponent


    【解决方案1】:

    如果您要处理许多项目(形状),建议使用带有模型的中继器。中继器负责根据模型的信息显示项目,并删除您只需从模型中删除项目的项目。

    ma​​in.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

    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 }
        }
    }
    

    【讨论】:

    • 嗨 eyllanesc。感谢您的回答。在我的示例中,您的方法确实很好。不幸的是,如果我只使用三角形(或一种独特的类型),它会很好地工作。在我的工作中,我可以绘制很多形状(线条、圆形、矩形、三角形)或者我可以只绘制(没有预定义的形状)。我不确定中继器在这种情况下是否会很好。 PS:我不使用 ListModel 因为我必须绑定一些属性。我不是 100% 确定,但我认为 ListModel 不支持 Qt.bindings
    • 你是对的。我会更具体。你的回答对我的例子很完美,所以我会把它标记为正确的。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-08
    • 2016-06-06
    • 1970-01-01
    • 1970-01-01
    • 2012-05-24
    • 2012-09-23
    • 1970-01-01
    相关资源
    最近更新 更多