Frage

Ich erstelle einen Baum von Objekten, der Baum wird mit einer sehr einfachen Logik gezeichnet:

  • Jedes Objekt ist so groß wie seine eigene Darstellung plus die seiner Kinder (und deren Kinder usw.)
  • Wenn das Objekt das erste untergeordnete Objekt seines übergeordneten Objekts ist, wird es unter seiner visuellen Darstellung eingerückt gezeichnet
  • Jedes nächste Objekt wird direkt unter seinem vorherigen Geschwister gezeichnet

Allerdings schlägt dieser einfache Algorithmus beim Einfügen neuer Objekte in manchen Fällen fehl.Zum Beispiel:

enter image description here

In diesem Fall werden Knoten 1 und 2 erfolgreich in Knoten 0 eingefügt, wobei die volle Höhe von Knoten 0 (die schwarze Linie links von jedem Knoten) die Summe seiner eigenen Benutzeroberfläche plus seiner beiden untergeordneten Knoten ist.

Wenn jedoch Knoten 3 in Knoten 1 eingefügt wird, wird das Layout unterbrochen – Knoten 2 wird nicht nach unten gedrückt, wodurch er sich mit dem neu eingefügten Knoten überlappt, wodurch seine schwarze Linie unsichtbar wird.

Wenn ein zusätzlicher Aktualisierungszyklus ausgelöst wird, kehrt die Struktur in den Normalzustand zurück, sodass der Fehler durch einige temporäre „Oder-von-Sync“-Eigenschaften verursacht werden muss.Beachten Sie, dass eine zusätzliche Aktualisierung den Baum nicht immer in Ordnung bringt. Je nachdem, wo die Einfügung vorgenommen wird, kann es manchmal einige Aktualisierungszyklen dauern, bis der Fehler aus der Störung der Ausrichtung heraus verschwindet.

Irgendwelche Ideen, was dieses unberechenbare Verhalten verursachen könnte und/oder wie man es beheben kann?Was führt dazu, dass der Code gelegentlich kaputt geht und warum, obwohl er letztendlich funktioniert, eine oder mehrere Aktualisierungen erforderlich sind, um ihn in Ordnung zu bringen?

BEARBEITEN:Ich konnte isolieren und lokalisieren, wann die Ausrichtung unterbrochen wird – beim Einfügen in einen Knoten, der nicht der letzte Knoten auf seiner Ebene ist.Alle Bindungen werden ordnungsgemäß weitergegeben, solange jeder neue Knoten in einen letzten und „offenen“ Knoten eingefügt wird, z. B.Auf dieser Ebene gibt es keinen nächsten Knoten, aber durch das Einfügen in einen vorherigen Knoten werden die Bindungen aufgehoben.Daher sind zusätzliche Aktualisierungszyklen erforderlich, um den Fehler auf allen betroffenen Ebenen zu beseitigen.Aber ich weiß immer noch nicht, was es verursacht und wie ich es beheben kann, sondern nur, wann es auftritt.

BEARBEITEN:Bei näherer Betrachtung stellt sich heraus, dass nach dem Einfügen des neuen untergeordneten Elements in das übergeordnete Element und der Neuausrichtung seiner untergeordneten Elemente basierend auf ihrer Reihenfolge die childrenRect Die Eigenschaft des übergeordneten Elements spiegelt die Größenänderungen nicht sofort wider.Wenn also das nächste gleichgeordnete Objekt des eingefügten Objekts positioniert wird, verwendet es immer noch den alten veralteten Wert für die Objekthöhe, was keine Auswirkungen auf das neu eingefügte Objekt hat, da es diesen nicht verwendet.Die nächste große Frage ist also wohl, wie man das Problem beheben kann, damit die Bindungen wie vorgesehen erfolgen, sodass der Algorithmus mit den richtigen Daten wie erwartet funktionieren kann.Ich habe versucht, einen Timer zu verwenden, um den Vorgang zu verzögern, aber es scheint nicht, dass es eine Frage der Zeit ist, sondern einer logischen Reihenfolge, z. B.Die Anzahl der Aktualisierungen, die derzeit zur Beseitigung des Fehlers erforderlich sind, entspricht der Abteilung, in der er aufgetreten ist.

BEARBEITEN:Ich habe mir überlegt, wie ich es „lösen“ kann, aber ...Nicht wirklich, ich weiß immer noch nicht, was es verursacht und wie ich es vermeiden kann, aber eine etwas „wirtschaftlichere“ Lösung, als den gesamten Baum zu aktualisieren, bis das Problem behoben ist. Gehen Sie einfach zu den übergeordneten Zweigen und aktualisieren Sie nur den aktuellen Zweig, bis der Stamm erreicht ist .Das spart zwar viel, aber ich würde es trotzdem vorziehen, sofort die richtigen Werte zu haben, da Bindungen funktionieren sollen, zumindest meiner Meinung nach.

BEARBEITEN:Hier ist der Bestellcode:

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

Und ein Beispiel, wie das QML aufgebaut ist:

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"
                }
            }
        }
    }
}
War es hilfreich?

Lösung

Wenn Sie darüber nachdenken, dass dieses Verhalten einen vollkommenen Sinn macht, ist nichts falsch mit den Bindungen, nur mit Ihren Erwartungen an das, was sie in der Lage sind.

Der Grund, warum sich Ihr "Cascading Update" -Alte sehr einfach ist und weist darauf hin, warum Ihr Code nicht funktioniert und wie Sie es beheben können.

Grundsätzlich, wenn Sie einen neuen Knoten einfügen, drückt er alles auf "einen Knoten", weshalb Ihr Code nicht bricht, solange Sie einen "letzten" Knoten einfügen. Wenn es jedoch nicht am Ende ist, wird alles ein bisschen nach unten drücken, aber diese Änderung reflektiert sich nicht, wenn sein Übergeordneter die Positionen aktualisiert. Wenn Sie das Update von der Wurzel anrufen und der Einsatz tiefer ist, löst Ihr erster Pass nur die erste Aktualisierung der Bindung für die Höhe aus, sodass der nächste Update-Zyklus sein nächster Geschwister ordnungsgemäß positioniert werden kann. Wenn der Einsatz tiefer war, gäbe mehr Bindungen, die stattfinden müssen.

Der Grund, warum der neue Knoten richtig eingelegt ist, ist er nicht auf der Unterkunft angewiesen, dass sich ein eigenes Einfügen erst ändert, wenn das nächste Update nicht ändert, wodurch der noch nicht aktualisierte Wert verwendet wird und die Bindung anschließend auswertet, um ihn treten der nächste Zyklus.

Ich denke, der schnellste Weg, dies zu tun, wird darin bestehen, auf der Ebene des Einsatzes zu beginnen und das Update "nach außen" ausbreiten und "nach unten" z. Aktualisieren Sie jedes nächste Objekt, dann die nächsten Geschwister des übergeordneten Elements der Geschwister der nächsten Eltern, bis Sie Ihr Root-Objekt treffen. Dadurch werden nur Objekte neu positioniert, die "unter" sind und von der Einlage betroffen sind, was viele Zyklen im Vergleich zu Ihrer aktuellen besten Lösung abrassieren sollte.

Andere Tipps

Ich habe ein wenig mit QML und JS herumgespielt und festgestellt, dass ich keine Probleme mit der grafischen Benutzeroberfläche habe.

Mein Demoprojekt ist in der Lage, sowohl untergeordnete Objekte als auch Geschwister zu einem Baum hinzuzufügen (durch Links- und Rechtsklicks auf die roten Kästchen).Die Logik ist vielleicht nicht vollständig, aber die Grundlagen funktionieren sehr reibungslos.

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

Die rote Box und ein umgebender Gegenstand zum Platzieren von Geschwistern 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)
                    }
                }
            }
        }
    }
}

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

Hoffentlich hilft das.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top