تنتج الأخطاء من روابط الخاصية، وفواصل موضع الكائن عند الإدراج

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

سؤال

أقوم بإنشاء شجرة من الكائنات، ويتم رسم الشجرة باستخدام منطق بسيط جدًا:

  • كل كائن كبير مثل تمثيله الخاص بالإضافة إلى تمثيل أبنائه (وأطفالهم وما إلى ذلك ...)
  • إذا كان الكائن هو الطفل الأول لوالده، فسيتم رسمه بمسافة بادئة تحت تمثيله المرئي
  • يتم رسم كل كائن تالي مباشرة تحت أخيه السابق

ومع ذلك، تفشل هذه الخوارزمية البسيطة عند إدراج كائنات جديدة في بعض الحالات.على سبيل المثال:

enter image description here

في هذه الحالة، تم إدراج العقدتين 1 و2 بنجاح في العقدة 0، مع الارتفاع الكامل للعقدة 0 (الخط الأسود على يسار كل عقدة) وهو مجموع واجهة المستخدم الخاصة بها بالإضافة إلى كلا العقدتين الفرعيتين.

ولكن عند إدراج العقدة 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"
                }
            }
        }
    }
}
هل كانت مفيدة؟

المحلول

عندما تفكر في الأمر، تجد أن هذا السلوك منطقي تمامًا، فلا حرج في الارتباطات، فقط في توقعاتك لما هي قادرة على فعله.

سبب عمل "التحديث المتتالي" بسيط جدًا ويشير إلى سبب عدم عمل الكود الخاص بك وكيفية إصلاحه.

في الأساس، عندما تقوم بإدراج عقدة جديدة، فإنها تدفع كل شيء إلى أسفل "عقدة"، ولهذا السبب لا ينقطع الكود الخاص بك طالما قمت بإدراج عقدة "أخيرة".ولكن إذا لم يكن في النهاية، فسوف يدفع كل شيء إلى الأسفل قليلاً، لكن هذا التغيير لن ينعكس حتى يقوم الأصل بتحديث المواقف.إذا قمت باستدعاء التحديث من الجذر وكان الإدراج أعمق في أي مكان، فإن تمريرك الأول سيؤدي فقط إلى التحديث الأول للربط للارتفاع، بحيث يمكن وضع شقيقه التالي بشكل صحيح في دورة التحديث التالية.إذا كان الإدخال أعمق، فسيكون هناك المزيد من الارتباطات التي يجب إجراؤها.

سبب إدراج العقدة الجديدة بشكل صحيح هو أنها لا تعتمد على خاصية أن الإدراج الخاص بها لن يتغير حتى التحديث التالي الذي سيستخدم القيمة التي لم يتم تحديثها بعد ثم تقييم الارتباط لجعله يبدأ في الدورة التالية .

أعتقد أن أسرع طريقة للقيام بذلك هي البدء بمستوى الإدخال ونشر التحديث "للخارج" و"للأسفل" على سبيل المثال.قم بتحديث كل كائن تالٍ، ثم الأشقاء التاليين للوالد إلى أشقاء الوالد التالي حتى تصل إلى الكائن الجذر.لن يؤدي هذا إلا إلى تغيير موضع الكائنات الموجودة "تحت" والمتأثرة بالإدراج، الأمر الذي من شأنه أن يقلل الكثير من الدورات مقارنة بالحل الأفضل الحالي لديك.

نصائح أخرى

لقد لعبت مع QML وJS قليلًا وتبين لي أنه ليس لدي أي مشاكل في واجهة المستخدم الرسومية.

لي مشروع تجريبي قادر على إضافة كائنات فرعية بالإضافة إلى أشقاء إلى شجرة (من خلال النقر باليسار واليمين على المربعات الحمراء).قد لا يكون المنطق كاملاً ولكن الأساسيات تعمل بسلاسة شديدة.

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

وبعض جافا سكريبت: 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());
    }
}

امل ان يساعد.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top