1

我创建了一个对象树,树是使用非常简单的逻辑绘制的:

  • 每个对象都与它自己的表示加上它的孩子(以及他们的孩子等等......)的表示一样大
  • 如果对象是其父对象的第一个子对象,则在其视觉表示下缩进绘制
  • 每个下一个对象都直接绘制在它的前一个兄弟之下

但是,在某些情况下插入新对象时,这种简单的算法会失败。例如:

在此处输入图像描述

在这种情况下,节点 1 和 2 成功插入到节点 0 中,节点 0 的全高(每个节点左侧的黑线)是它自己的 UI 加上它的两个子节点的总和。

但是当节点 3 插入节点 1 时,布局中断 - 节点 2 没有被下推,结果它与新插入的节点重叠,使其黑线不可见。

当触发额外的刷新周期时,树会恢复正常,因此故障一定是由一些临时的“或同步”属性引起的。请注意,一次额外的刷新并不总是使树按顺序排列,这取决于插入的位置,有时该错误可能需要几个刷新周期才能“级联”掉干扰对齐。

有什么想法可能导致这种不稳定的行为和/或如何解决它?是什么导致代码偶尔中断,为什么即使它最终可以工作,它也需要一到多次刷新才能使自己井井有条?

编辑:我已经能够隔离并查明对齐何时中断 - 当插入到未结束其级别上的最后一个节点的节点时。只要每个新节点都插入到最后一个并且“打开”的节点中,所有绑定都会正确传播,例如在该级别上没有下一个节点,但是插入任何先前的节点都会破坏绑定。因此,需要额外的刷新周期来逐步消除它影响的所有级别的错误。但我仍然不知道是什么原因导致它以及如何修复它,只有当它发生时。

编辑:再调查一下,似乎在将新子项插入父项并根据其顺序重新对齐其子项后,父项的childrenRect属性不会立即反映大小的变化。因此,当插入对象的下一个兄弟对象被定位时,它仍然使用旧的过时值作为对象高度,这不会影响新插入的对象,因为它不使用它。所以我想下一个大问题是如何修复它,以便绑定发生,以便算法可以按预期使用正确的数据?我尝试使用计时器来延迟操作,但它似乎不是时间问题,而是关于逻辑顺序的问题,例如,它当前需要的刷新计数以消除等于它发生的部门的错误。

编辑:我想出了如何“解决它”但是......不是真的,我仍然不知道是什么原因导致它以及如何避免它,但是比更新整个树直到它被修复更“经济”的解决方案去父母那里,只更新当前分支,直到到达根目录。这确实节省了很多,但我仍然希望立即拥有正确的值,因为绑定应该可以工作,至少 IMO 是这样。

编辑:这是订购代码:

for (int i = 0; i < _children.size(); ++i) {
        UI * ui = _children.at(i)->_ui;
        if (ui) {
            if (i) {
                UI * prev = _children.at(i-1)->_ui;
                ui->setX(prev->x());
                ui->setY(prev->height() + prev->y());
            } else {
                ui->setX(Object::_helper->nodeSize());
                ui->setY(_ui->childOffset());
            }
        }
    }

以及如何设置 QML 的示例:

UI {
    id: main
    width: childrenRect.width
    height: childrenRect.height    

    Item { 
        height: childrenRect.height

        Rectangle {
            width: Helper.nodeSize
            height: Helper.nodeSize
            color: "red"

            Text {
                anchors.centerIn: parent
                text: main.id
            }

            MouseArea {
                anchors.fill: parent   
                acceptedButtons: Qt.LeftButton | Qt.RightButton                 
                onClicked: {
                    if (mouse.button == Qt.RightButton)
                        Helper.gotoXY(main.absolutePosition())
                    else {
                        Helper.createObject(1, main)
                        Helper.updateRoot()
                    }
                }

                Rectangle {
                    height: main.height
                    width: 10
                    x: -10
                    color: "black"
                }
            }
        }
    }
}
4

2 回答 2

1

当您考虑它时,这种行为非常合理,绑定没有任何问题,只是您对它们的能力的期望。

您的“级联更新”起作用的原因非常简单,并指出了您的代码为什么不起作用以及如何修复它。

基本上,当您插入一个新节点时,它会将所有内容向下推到“一个节点”,这就是为什么只要您插入一个“最后一个”节点,您的代码就不会中断。但如果它不是在最后,它将把所有东西都往下推一点,但是这种变化在它的父级更新位置之前不会反映出来。如果您从根调用更新并且插入更深,您的第一次传递将仅触发高度绑定的第一次更新,因此下一个更新周期可以正确定位其下一个兄弟。如果插入更深,则需要进行更多绑定。

新节点被正确插入的原因是它不依赖于它自己的插入在下一次更新之前不会改变的属性,它将使用仍然未更新的值,然后评估绑定以使其在下一个周期中启动.

我认为最快的方法是从插入级别开始并“向外”和“向下”传播更新,例如更新每个下一个对象,然后将父级的下一个兄弟姐妹更新为下一个父级的兄弟姐妹,直到你点击你的根对象。这只会重新定位“下方”并受插入影响的对象,与您当前的最佳解决方案相比,这应该会减少很多周期。

于 2014-01-13T00:39:16.883 回答
0

我玩了一点 QML 和 JS,结果发现我没有图形 UI 问题。

我的演示项目能够将子对象以及兄弟对象添加到树中(通过左右单击红色框)。逻辑可能不完整,但基础运行非常顺利。

main.qml

import QtQuick 2.1
import QtQuick.Controls 1.0

ApplicationWindow {
    id: app
    width: 800
    height: 600

    property int lastNodeId: 0
    function getId() { return lastNodeId++; }

    Level { }
}

Level.qml

import QtQuick 2.0

Item {
    id: frame
    width: childrenRect.width
    height: childrenRect.height

    Rectangle {
        width: 10
        color: "#000000"
        height: parent.height
        x: 0
        y: 0
    }

    Column {
        x: 10
        id: content

        Position { }
    }
}

放置兄弟姐妹的红色盒子和周围的物品Position.qml

import QtQuick 2.0
import "component_creation.js" as CC

Item {
    id: position0
    width: childrenRect.width
    height: childrenRect.height

    Rectangle {
        color: "red"
        width: 80
        height: 30

        Text {
            anchors.fill: parent
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter

            Component.onCompleted: text = app.getId() // Set once, avoid bindung

            MouseArea {
                anchors.fill: parent
                acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
                onClicked: {
                    if (mouse.button == Qt.LeftButton)
                    {
                        CC.createChild(position0);
                    }
                    else if (mouse.button == Qt.RightButton)
                    {
                        CC.createSiblingAfter(position0)
                    }
                    else if (mouse.button == Qt.MiddleButton)
                    {
                        // no clue how to implement
                        // CC.createSiblingBefore(position0)
                    }
                }
            }
        }
    }
}

还有一些 JavaScript:component_creation.js

function createChild(parent_item) {
    var component = Qt.createComponent("Level.qml");
    if (component.status == Component.Ready)
    {
        var sprite = component.createObject(parent_item, {"x": 70, "y": 30});
        if (sprite == null) console.log("Error creating Level object");
    }
    else
    {
        console.log("Error loading component:", component.errorString());
    }
}

function createSiblingAfter(sibling_position)
{
    var component = Qt.createComponent("Position.qml");
    if (component.status == Component.Ready)
    {
        var sprite = component.createObject(sibling_position.parent);
        if (sprite == null) console.log("Error creating Position object");
    }
    else
    {
        console.log("Error loading component:", component.errorString());
    }
}

希望有帮助。

于 2014-01-14T21:43:28.220 回答