Résultats bogués des liaisons de propriétés, la position de l'objet est interrompue lors de l'insertion

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

Question

Je crée un arbre d'objets, l'arbre est dessiné selon une logique très simple :

  • chaque objet est aussi grand que sa propre représentation plus celle de ses enfants (et de leurs enfants et ainsi de suite...)
  • si l'objet est le premier enfant de son parent, il est dessiné en retrait sous sa représentation visuelle
  • chaque objet suivant est dessiné directement sous son frère précédent

Cependant, cet algorithme simple échoue dans certains cas lors de l’insertion de nouveaux objets.Par exemple:

enter image description here

Dans ce cas, les nœuds 1 et 2 sont insérés avec succès dans le nœud 0, la hauteur totale du nœud 0 (la ligne noire à gauche de chaque nœud) étant la somme de sa propre interface utilisateur et de ses deux enfants.

Mais lorsque le nœud 3 est inséré dans le nœud 1, la disposition se rompt - le nœud 2 n'est pas poussé vers le bas, par conséquent il chevauche le nœud nouvellement inséré, rendant sa ligne noire invisible.

Lorsqu'un cycle de rafraîchissement supplémentaire est déclenché, l'arborescence revient à la normale, le problème doit donc être causé par certaines propriétés temporaires "ou de synchronisation".Notez qu'un rafraîchissement supplémentaire ne met pas toujours l'arborescence en ordre, selon l'endroit où l'insertion est effectuée, le bogue peut parfois prendre quelques cycles de rafraîchissement pour "sortir en cascade" après avoir perturbé l'alignement.

Avez-vous une idée de ce qui pourrait causer ce comportement erratique et/ou comment y remédier ?Qu'est-ce qui provoque des pannes occasionnelles du code et pourquoi, même s'il fonctionne finalement, il nécessite une ou plusieurs actualisations pour se remettre en ordre ?

MODIFIER:J'ai pu isoler et identifier quand l'alignement se rompt - lors de l'insertion dans un nœud qui ne termine pas le dernier nœud de son niveau.Toutes les liaisons se propagent correctement tant que chaque nouveau nœud est inséré dans un nœud qui est le dernier et "ouvert", par exemple.il n'y a pas de nœud suivant à ce niveau, mais l'insertion dans n'importe quel nœud précédent rompt les liaisons.Des cycles d’actualisation supplémentaires sont donc nécessaires pour éliminer progressivement le bug à tous les niveaux qu’il affecte.Mais je ne sais toujours pas quelle en est la cause ni comment y remédier, seulement quand cela se produit.

MODIFIER:En enquêtant un peu plus, il apparaît qu'une fois le nouvel enfant inséré dans l'élément parent et ses enfants réalignés en fonction de leur ordre, l'élément childrenRect la propriété du parent ne reflète pas immédiatement les changements de taille.Ainsi, lorsque le prochain frère de l'objet inséré est positionné, il utilise toujours l'ancienne valeur obsolète pour la hauteur de l'objet, ce qui n'affecte pas l'objet nouvellement inséré puisqu'il ne l'utilise pas.Je suppose donc que la prochaine grande question est de savoir comment résoudre ce problème pour que les liaisons aient lieu comme elles le devraient afin que l'algorithme puisse fonctionner comme prévu avec les bonnes données ?J'ai essayé d'utiliser une minuterie pour retarder l'opération mais il ne semble pas que ce soit une question de temps mais d'ordre logique, par ex.le nombre de rafraîchissements dont il a actuellement besoin pour éliminer le bug est égal au service dans lequel il s'est produit.

MODIFIER:J'ai pensé comment "le résoudre" mais...pas vraiment, je ne sais toujours pas quelle est la cause et comment l'éviter, mais solution un peu plus "économique" que de mettre à jour l'arborescence entière jusqu'à ce qu'elle soit corrigée, il suffit de descendre les parents et de mettre à jour uniquement la branche actuelle jusqu'à ce que la racine soit atteinte .Cela permet d'économiser beaucoup, mais je préférerais quand même avoir les bonnes valeurs tout de suite, car les liaisons sont censées fonctionner, du moins à l'OMI.

MODIFIER:Voici le code de commande :

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

Et un exemple de configuration de 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"
                }
            }
        }
    }
}
Était-ce utile?

La solution

Quand on y pense, ce comportement est parfaitement logique, il n'y a rien de mal avec les fixations, juste avec vos attentes quant à ce dont elles sont capables.

La raison pour laquelle votre « mise à jour en cascade » fonctionne est très simple et indique pourquoi votre code ne fonctionne pas et comment y remédier.

Fondamentalement, lorsque vous insérez un nouveau nœud, il pousse tout vers le bas "un nœud", c'est pourquoi votre code ne s'interrompt pas tant que vous insérez un "dernier" nœud.Mais s'il n'est pas à la fin, il fera tout baisser un peu, mais ce changement ne se reflétera que lorsque son parent mettra à jour les positions.Si vous appelez update depuis la racine et que l'insertion est plus profonde, votre premier passage déclenchera uniquement la première mise à jour de la liaison pour la hauteur, de sorte que lors du prochain cycle de mise à jour, son prochain frère puisse être correctement positionné.Si l'insert était plus profond, il y aurait davantage de liaisons à effectuer.

La raison pour laquelle le nouveau nœud est inséré correctement est qu'il ne repose pas sur la propriété selon laquelle sa propre insertion ne changera pas jusqu'à la prochaine mise à jour qui utilisera la valeur encore non mise à jour, puis évaluera la liaison pour la faire démarrer au cycle suivant. .

Je pense que le moyen le plus rapide de procéder sera de commencer au niveau de l'insertion et de propager la mise à jour "vers l'extérieur" et "vers le bas", par exemple.mettez à jour chaque objet suivant, puis les frères et sœurs suivants du parent vers les frères et sœurs du parent suivant jusqu'à ce que vous atteigniez votre objet racine.Cela ne repositionnera que les objets qui sont "sous" et affectés par l'insert, ce qui devrait réduire de nombreux cycles par rapport à votre meilleure solution actuelle.

Autres conseils

J'ai joué un peu avec QML et JS et il s'est avéré que je n'avais aucun problème d'interface utilisateur graphique.

Mon projet de démonstration est capable d'ajouter des objets enfants ainsi que des frères et sœurs à un arbre (par des clics gauche et droit sur les cases rouges).La logique n’est peut-être pas complète, mais les bases fonctionnent très bien.

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

La boîte rouge et un élément environnant pour placer les frères et sœurs 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)
                    }
                }
            }
        }
    }
}

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

J'espère que cela pourra aider.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top