Resultados com erros de vinculações de propriedades, quebras de posição do objeto na inserção

StackOverflow https://stackoverflow.com//questions/21063748

Pergunta

Eu crio uma árvore de objetos, a árvore é desenhada usando uma lógica muito simples:

  • cada objeto é tão grande quanto sua própria representação mais a de seus filhos (e seus filhos e assim por diante...)
  • se o objeto for o primeiro filho de seu pai, ele será desenhado recuado sob sua representação visual
  • cada próximo objeto é desenhado diretamente sob seu irmão anterior

No entanto, este algoritmo simples falha ao inserir novos objetos em alguns casos.Por exemplo:

enter image description here

Nesse caso, os nós 1 e 2 são inseridos com sucesso no nó 0, com a altura total do nó 0 (a linha preta à esquerda de cada nó) sendo a soma de sua própria UI mais seus dois filhos.

Mas quando o nó 3 é inserido no nó 1, o layout é quebrado - o nó 2 não é empurrado para baixo e, como resultado, ele se sobrepõe ao nó recém-inserido, tornando sua linha preta invisível.

Quando um ciclo de atualização adicional é acionado, a árvore retorna ao normal, portanto a falha deve ser causada por algumas propriedades temporárias "ou de sincronização".Observe que uma atualização extra nem sempre coloca a árvore em ordem, dependendo de onde a inserção é feita, o bug às vezes pode levar alguns ciclos de atualização para "sair em cascata" e não perturbar o alinhamento.

Alguma idéia do que pode causar esse comportamento errático e/ou como corrigi-lo?O que faz com que o código seja quebrado ocasionalmente e por que, embora funcione, ele exige uma ou várias atualizações para ficar em ordem?

EDITAR:Consegui isolar e identificar quando o alinhamento é quebrado - ao inserir em um nó que não está terminando o último nó em seu nível.Todas as ligações se propagam corretamente, desde que cada novo nó seja inserido em um nó que seja o último e "aberto", por exemplo.não há próximo nó nesse nível, mas a inserção em qualquer nó anterior quebra as ligações.Portanto, são necessários ciclos de atualização adicionais para eliminar o bug em todos os níveis que ele afeta.Mas ainda não sei o que causa e como consertar, apenas quando ocorre.

EDITAR:Investigando um pouco mais, verifica-se que após o novo filho ser inserido no item pai e seus filhos serem realinhados com base em sua ordem, o childrenRect propriedade do pai não reflete as mudanças no tamanho imediatamente.Portanto, quando o próximo irmão do objeto inserido estiver sendo posicionado, ele ainda usará o antigo valor desatualizado para a altura do objeto, o que não afeta o objeto recém-inserido, pois não o utiliza.Então, acho que a próxima grande questão é como corrigi-lo para que as ligações ocorram como deveriam, para que o algoritmo possa funcionar conforme o esperado com os dados corretos.Tentei usar um cronômetro para atrasar a operação, mas não parece que seja uma questão de tempo, mas sim uma ordem lógica, por exemplo.a contagem de atualizações necessárias atualmente para eliminar o bug igual ao departamento em que ocorreu.

EDITAR:Eu pensei em como "resolver isso", mas...na verdade não, ainda não sei o que causa e como evitá-lo, mas uma solução um pouco mais "econômica" do que atualizar a árvore inteira até que seja consertada, basta descer os pais e atualizar apenas o ramo atual até atingir a raiz .Isso economiza muito, mas eu ainda preferiria ter os valores corretos imediatamente, já que as ligações devem funcionar, pelo menos na IMO.

EDITAR:Aqui está o código do pedido:

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

E um exemplo de como o QML é configurado:

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"
                }
            }
        }
    }
}
Foi útil?

Solução

Quando você pensa bem, esse comportamento faz todo o sentido, não há nada de errado com as amarras, apenas com as suas expectativas do que elas são capazes.

O motivo pelo qual sua "atualização em cascata" funciona é muito simples e mostra por que seu código não funciona e como corrigi-lo.

Basicamente, quando você insere um novo nó, ele empurra tudo para baixo "um nó", e é por isso que seu código não quebra desde que você insira um "último" nó.Mas se não estiver no final, vai empurrar tudo um pouco para baixo, mas essa mudança não vai refletir até que seu pai atualize as posições.Se você chamar update da raiz e a inserção estiver em qualquer lugar mais profundo, sua primeira passagem acionará apenas a primeira atualização da ligação para altura, para que no próximo ciclo de atualização seu próximo irmão possa ser posicionado corretamente.Se a inserção fosse mais profunda, haveria mais encadernações necessárias.

A razão pela qual o novo nó é inserido corretamente é que ele não depende da propriedade de que sua própria inserção não será alterada até a próxima atualização, que usará o valor ainda não atualizado e então avaliará a ligação para fazê-lo funcionar no próximo ciclo .

Acho que a maneira mais rápida de fazer isso será começar no nível da inserção e propagar a atualização "para fora" e "para baixo", por exemplo.atualize cada próximo objeto e, em seguida, os próximos irmãos do pai para os irmãos do próximo pai até atingir seu objeto raiz.Isso reposicionará apenas os objetos que estão "abaixo" e afetados pela inserção, o que deve reduzir muitos ciclos em comparação com sua melhor solução atual.

Outras dicas

Eu brinquei um pouco com QML e JS e descobri que não tenho problemas de interface gráfica.

Meu projeto de demonstração é capaz de adicionar objetos filhos, bem como irmãos, a uma árvore (clicando com o botão esquerdo e direito nas caixas vermelhas).A lógica pode não estar completa, mas o básico funciona muito bem.

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

A caixa vermelha e um item ao redor para colocar os irmãos 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)
                    }
                }
            }
        }
    }
}

E um pouco de 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());
    }
}

Espero que ajude.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top