1

オブジェクトのツリーを作成します。ツリーは非常に単純なロジックを使用して描画されます。

  • 各オブジェクトは、それ自体の表現とその子 (およびその子など...) の表現と同じ大きさです。
  • オブジェクトがその親の最初の子である場合、視覚的表現の下にインデントされて描画されます
  • 次のすべてのオブジェクトは、前の兄弟の下に直接描画されます

ただし、この単純なアルゴリズムは、新しいオブジェクトを挿入するときに失敗する場合があります。例えば:

ここに画像の説明を入力

この場合、ノード 1 と 2 はノード 0 に正常に挿入され、ノード 0 の全高 (各ノードの左側にある黒い線) は、それ自身の UI とその両方の子の合計になります。

しかし、ノード 3 がノード 1 に挿入されると、レイアウトが壊れます。ノード 2 は押し下げられません。その結果、新しく挿入されたノードと重なり、黒い線が見えなくなります。

追加の更新サイクルがトリガーされると、ツリーは通常に戻るため、グリッチは一時的な「または同期」プロパティが原因である必要があります。追加の 1 回の更新で常にツリーが整頓されるとは限らないことに注意してください。挿入が行われる場所によっては、バグが整列を妨げないように「カスケード アウト」するために数回の更新サイクルが必要になる場合があります。

この不規則な動作の原因および/またはそれを修正する方法はありますか? コードがときどき壊れる原因は何ですか? また、最終的には機能するのに、正常に機能するために 1 回から複数回の更新が必要になるのはなぜですか?

編集:私は、そのレベルの最後のノードで終わっていないノードに挿入するときに、位置合わせがいつ壊れるかを特定して特定することができました。すべてのバインディングは、新しいノードが最後に「開いている」ノードに挿入されている限り、適切に伝播されます。たとえば、そのレベルには次のノードはありませんが、前のノードに挿入するとバインディングが壊れます。そのため、影響を受けるすべてのレベルでバグを段階的に排除するには、追加の更新サイクルが必要です。しかし、何が原因で、どのように修正するかはまだわかりません。発生した場合のみです。

編集: もう少し調査すると、新しい子が親アイテムに挿入され、その子が順序に基づいて再配置された後、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 に答える