Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to create n Objects dynamically to populate StackView



  • During runtime i'm generating n Datasets in QML.

    I have a qml item which I construct with one of the Datasets.

    Now I want to do the following:

    Create n qml items out of the datasets.

    Populate a StackView with these n objects.

    So how can I dynamically create n Objects and populate them in the StackView?

    I understand there is loader to load a single page from file Dynamically but that does not help here...


  • Qt Champions 2018

    I would use an Instantiator and push items on the StackView in onObjectAdded.

    Also are you sure StackView is the correct component to use? Have you considered SwipeView with a Repeater?



  • @GrecKo said in How to create n Objects dynamically to populate StackView:

    I would use an Instantiator and push items on the StackView in onObjectAdded.

    Also are you sure StackView is the correct component to use? Have you considered SwipeView with a Repeater?

    Im not sure if I can use Instantiator to create all the Objects. Here is one Object created from the first data:

        QuizPage{
            question: randomQuestions[0]
        }
    

    How can i tell Instantiator to make n QuizPages?.

    With Stack or SwipeView im not completly sure. the Behaviour will be like this. I show the first page with a button at the end you can show the next page etc... I maybe want a small animation on Page change so I thought StackView would be good.



  • I switched to the SwipeView because it is really what I want when it is inactive. However I could not get the Instantiator to work so I tried it like this:

    import QtQuick 2.15
    import QtQuick.Controls 2.15
    
    Item {
        id: quiz
    
        required property var randomQuestions
    
        width: parent.width
        height: parent.height
    
        SwipeView{
            id: quizSwipeView
            interactive: false
            anchors.fill: parent
        }
    
        Component.onCompleted: {
            var quizPage;
            for(var i=0; i<randomQuestions.length; ++i) {
                quizPage = createQuizPage(quizSwipeView, randomQuestions[i]);
                quizSwipeView.addItem(quizPage);
            }
            quizSwipeView.setCurrentIndex(0)
            console.log(quizSwipeView.count)
            console.log(quizSwipeView.currentItem.width)
            console.log(quizSwipeView.currentItem.height)
        }
    
        function createQuizPage(parent, randomQuestion) {
            var component = Qt.createComponent("QuizPage.qml");
            var object = component.createObject(parent, {question: randomQuestion});
    
            if (object === null) {
                console.log("Error creating quizPage");
            }
            return object
        }
    }
    

    Unfortunately when I load the File the programm freezes and I don't see the QuizPage.

    However in

        console.log(quizSwipeView.currentItem.width)
        console.log(quizSwipeView.currentItem.height)
    

    I can see the correct size of the Current Quiz Page. So why is it not loading?

    If you need more information you can check the full source code here: https://preview.tinyurl.com/yy84ofd7



  • @sandro4912 said in How to create n Objects dynamically to populate StackView:

    SwipeView{
    id: quizSwipeView
    interactive: false
    anchors.fill: parent
    }

      SwipeView{
            id: quizSwipeView
            interactive: false
            anchors.fill: parent
    
            Repeater {
               model: randomQuestions
               delegate: QuizPage {
                     question: modelData
               }
            }
        }
    

    This was from memory, so you may have to lookup the syntax for things.



  • That givies similar errors like my complicated solution:

    qrc:/qml/quiz/QuizPage.qml:30: TypeError: Cannot read property 'answer1' of undefined
    qrc:/qml/quiz/QuizPage.qml:30: TypeError: Cannot read property 'answer1' of undefined
    qrc:/qml/quiz/QuizPage.qml:30: TypeError: Cannot read property 'answer1' of undefined
    qrc:/qml/quiz/QuizPage.qml:30: TypeError: Cannot read property 'answer1' of undefined
    qrc:/qml/quiz/QuizPage.qml:30: TypeError: Cannot read property 'answer1' of undefined
    qrc:/qml/quiz/QuizPage.qml:30: TypeError: Cannot read property 'answer1' of undefined
    qrc:/qml/quiz/QuizPage.qml:30: TypeError: Cannot read property 'answer1' of undefined
    qrc:/qml/quiz/QuizPage.qml:30: TypeError: Cannot read property 'answer1' of undefined
    qrc:/qml/quiz/QuizPage.qml:30: TypeError: Cannot read property 'answer1' of undefined
    qrc:/qml/quiz/QuizPage.qml:30: TypeError: Cannot read property 'answer1' of undefined
    qrc:/qml/quiz/Quiz.qml:21:23: QML QuizPage: SwipeView has detected conflicting anchors. Unable to layout the item.
    qrc:/qml/quiz/Quiz.qml:21:23: QML QuizPage: SwipeView has detected conflicting anchors. Unable to layout the item.
    qrc:/qml/quiz/Quiz.qml:21:23: QML QuizPage: SwipeView has detected conflicting anchors. Unable to layout the item.
    qrc:/qml/quiz/Quiz.qml:21:23: QML QuizPage: SwipeView has detected conflicting anchors. Unable to layout the item.
    qrc:/qml/quiz/Quiz.qml:21:23: QML QuizPage: SwipeView has detected conflicting anchors. Unable to layout the item.
    qrc:/qml/quiz/Quiz.qml:21:23: QML QuizPage: SwipeView has detected conflicting anchors. Unable to layout the item.
    qrc:/qml/quiz/Quiz.qml:21:23: QML QuizPage: SwipeView has detected conflicting anchors. Unable to layout the item.
    qrc:/qml/quiz/Quiz.qml:21:23: QML QuizPage: SwipeView has detected conflicting anchors. Unable to layout the item.
    qrc:/qml/quiz/Quiz.qml:21:23: QML QuizPage: SwipeView has detected conflicting anchors. Unable to layout the item.
    qrc:/qml/quiz/Quiz.qml:21:23: QML QuizPage: SwipeView has detected conflicting anchors. Unable to layout the item.
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: modelData is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: modelData is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: modelData is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: modelData is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: modelData is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: modelData is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: modelData is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:22: ReferenceError: modelData is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:22: ReferenceError: modelData is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:22: ReferenceError: modelData is not defined
    

    Strangley I can read randomQuestions in onCompleted:

    import QtQuick 2.15
    import QtQuick.Controls 2.15
    
    Item {
        id: quiz
    
        required property var randomQuestions
    
        Component.onCompleted: {
            // this works
            console.log(randomQuestions[0].id)
            console.log(randomQuestions[0].answer1)
            console.log(randomQuestions[0].answer2)
            console.log(randomQuestions[0].answer3)
            console.log(randomQuestions[0].answer4)
            console.log(randomQuestions[0].askedQuestion)
        }
    
        width: parent.width
        height: parent.height
    
        SwipeView{
            id: quizSwipeView
            interactive: false
            anchors.fill: parent
    
            Repeater{
                model: randomQuestions
                delegate: QuizPage{
                    question: modelData
                }
            }
        }
    }
    

    randomQuestions is an QQmlListProperty <Question> exposed in C++:



  • @sandro4912 said in How to create n Objects dynamically to populate StackView:

    QQmlListProperty

    It looks like it is repeating, but not creating modelData. Not sure if QQmlListProperty is 100% supported, but it should be creating an "index" variable you can use.

           Repeater{
                model: randomQuestions
                delegate: QuizPage{
                    question: randomQuestions[index]
                }
            }
    


  • I tried that but it also insist that index is not existing:

    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: index is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: index is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: index is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: index is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: index is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: index is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: index is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:30: ReferenceError: index is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:30: ReferenceError: index is not defined
    qrc:/qml/quiz/QuizPage.qml:51: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:56: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:63: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:30: ReferenceError: index is not defined
    

    Some more background information of random Questions.

    A Question is defined like this:

    class Question : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(int id READ getId CONSTANT)
        Q_PROPERTY(QString askedQuestion READ getAskedQuestion CONSTANT)
        Q_PROPERTY(QString answer1 READ getAnswer1 CONSTANT)
        Q_PROPERTY(QString answer2 READ getAnswer2 CONSTANT)
        Q_PROPERTY(QString answer3 READ getAnswer3 CONSTANT)
        Q_PROPERTY(QString answer4 READ getAnswer4 CONSTANT)
        Q_PROPERTY(int correctAnswer READ getCorrectAnswer CONSTANT)
        Q_PROPERTY(QString picture READ getPicture CONSTANT)
    public:
        enum class Correct{
            Answer1 = 1,
            Answer2 = 2,
            Answer3 = 3,
            Answer4 = 4
        };
    
        Question() = default;
    
        Question(int id,
                 QString askedQuestion,
                 QString answer1,
                 QString answer2,
                 QString answer3,
                 QString answer4,
                 Correct correctAnswer,
                 QString picture = QString{});
    
        int getId() const;
        QString getAskedQuestion() const;
        QString getAnswer1() const;
        QString getAnswer2() const;
        QString getAnswer3() const;
        QString getAnswer4() const;
        int getCorrectAnswer() const;
        QString getPicture() const;
    
    private:
        int mId;
        QString mAskedQuestion;
        QString mAnswer1;
        QString mAnswer2;
        QString mAnswer3;
        QString mAnswer4;
        Correct mCorrectAnswer;
        QString mPicture;
    };
    

    In annother class which has sql connection I read the randomQuestions:

    class QuestionSqlTableModel : public QSqlTableModel
    {
        Q_OBJECT
        Q_PROPERTY(QQmlListProperty<Question> randomQuestions
                   READ getRandomQuestions
                   NOTIFY randomQuestionsChanged)
    public:
        explicit QuestionSqlTableModel(QObject *parent = nullptr,
                                       const QSqlDatabase &db = QSqlDatabase());
    
        Q_INVOKABLE QVariant data(const QModelIndex &index, int role) const override;
    
    
        Q_INVOKABLE bool addNewEntry(const QString& askedQuestion,
            const QString& answer1,
            const QString& answer2,
            const QString& answer3,
            const QString& answer4,
            int correctAnswer,
            const QString& picturePath);
    
        Q_INVOKABLE void generateNewRandomQuestions(int count);
    
        QQmlListProperty <Question> getRandomQuestions();
    
    signals:
        void randomQuestionsChanged();
    
    private:
        void appendRandomQuestion(Question *question);
        int randomQuestionsCount() const;
        Question* randomQuestionAt(int index) const;
        void randomQuestionsClear();
    
        static void appendRandomQuestion(
                QQmlListProperty<Question>* list, Question *question);
        static int randomQuestionsCount(QQmlListProperty<Question>* list);
        static Question* randomQuestionAt(
                QQmlListProperty<Question>* list, int index);
        static void randomQuestionsClear(QQmlListProperty<Question>* list);
    
        QVector<Question*> mRandomQuestions;
    };
    

    They get registered like this:

        qmlRegisterAnonymousType<Question>("sandro.custom.types",1);
    
        auto context = engine.rootContext();
        context->setContextProperty("questionSqlTableModel", questionSqlTableModel);
    

    I pass the random questions like this to the Quiz:

            ToolButton {
                text: qsTr("New Quiz")
                icon.name: "address-book-new"
                onClicked: {
                    questionSqlTableModel.generateNewRandomQuestions(10)
                    loader.setSource("quiz/Quiz.qml",
                                     {"randomQuestions":
                                         questionSqlTableModel.randomQuestions})
                }
            }
    

    What I could already do is show a single page likes this:

    import QtQuick 2.15
    import QtQuick.Controls 2.15
    
    Item {
        id: quiz
    
        required property var randomQuestions
        width: parent.width
        height: parent.height
    
        QuizPage{
            id: quizPage
            anchors.fill: parent
            question: randomQuestions[0]
        }
    }
    

    This works without problem so I really wonder why the delegate solution does not work.

    For completion here is QuizPage:

    import QtQuick 2.15
    import QtQuick.Layouts 1.15
    import QtQuick.Controls 2.15
    
    Item{
        id: root
    
        required property var question
    
        property var __shuffledAnswers
        property bool __correctAnswer: false
    
        signal answeredCorrectly()
        signal answeredWrong()
    
        width: parent.width
        height: parent.height
        anchors.fill: parent
    
    
        Rectangle {
            id: dialog
    
            width: parent.width
            height: parent.height
    
            Component.onCompleted: {
                checkButton.enabled = false;
                nextQuestionButton.enabled = false;
    
                root.__shuffledAnswers = makeAnswerArray(question.answer1,
                                                    question.answer2,
                                                    question.answer3,
                                                    question.answer4);
                shuffleArray(root.__shuffledAnswers);
    
                answer1TextField.text = root.__shuffledAnswers[0];
                answer2TextField.text = root.__shuffledAnswers[1];
                answer3TextField.text = root.__shuffledAnswers[2];
                answer4TextField.text = root.__shuffledAnswers[3];
            }
    
            ButtonGroup {
                id: radioGroup
            }
    
            ColumnLayout{
                anchors.fill: parent
    
                RowLayout{
                    Text{
                        text: qsTr("Question: " + question.id)
                    }
                }
                RowLayout{
                    Text{
                        text: qsTr(question.askedQuestion)
                    }
                }
                RowLayout{
                    Image{
                        id: image
    
                        source: question.picture.length > 0 ?
                                    "data:image/png;base64," + question.picture: ""
                    }
                }
                RowLayout{
                    RadioButton{
                        id: radioButtonAnswer1
                        ButtonGroup.group: radioGroup
                        onCheckedChanged: {
                            if(checked) {
                                checkButton.enabled = true
                                if(root.question.correctAnswer === 1) {
                                    __correctAnswer = true
                                }
                                else {
                                    __correctAnswer = false
                                }
                            }
                        }
                    }
                    AnswerTextField{
                        Layout.fillWidth: true
                        id: answer1TextField
                    }
                }
                RowLayout{
                    RadioButton{
                        id: radioButtonAnswer2
                        ButtonGroup.group: radioGroup
                        onCheckedChanged: {
                            if(checked) {
                                checkButton.enabled = true
                                if(root.question.correctAnswer === 2) {
                                    __correctAnswer = true
                                }
                                else {
                                    __correctAnswer = false
                                }
                            }
                        }
                    }
                    AnswerTextField{
                        Layout.fillWidth: true
                        id: answer2TextField
                    }
                }
                RowLayout{
                    RadioButton{
                        id: radioButtonAnswer3
                        ButtonGroup.group: radioGroup
                        onCheckedChanged: {
                            if(checked) {
                                checkButton.enabled = true
                                if(root.question.correctAnswer === 3) {
                                    __correctAnswer = true
                                }
                                else {
                                    __correctAnswer = false
                                }
                            }
                        }
                    }
                    AnswerTextField{
                        Layout.fillWidth: true
                        id: answer3TextField
                    }
                }
                RowLayout{
                    RadioButton{
                        id: radioButtonAnswer4
                        ButtonGroup.group: radioGroup
                        onCheckedChanged: {
                            if(checked) {
                                checkButton.enabled = true
                                if(root.question.correctAnswer === 4) {
                                    __correctAnswer = true
                                }
                                else {
                                    __correctAnswer = false
                                }
                            }
                        }
                    }
                    AnswerTextField{
                        Layout.fillWidth: true
                        id: answer4TextField
                    }
                }
                RowLayout{
                    Layout.alignment: Qt.AlignRight
                    Button{
                        id: checkButton
                        text: qsTr("Check Answer")
                        onPressed: {
                            markAnswers(root.question.correctAnswer);
                            disableRadioButtons();
                            enabled = false
                            nextQuestionButton.enabled = true
                        }
                    }
                    Button{
                        id: nextQuestionButton
                        text: qsTr("Next Question")
                        onPressed: {
                            if(root.__correctAnswer) {
                                root.answeredCorrectly()
                            }
                            else {
                                root.answeredWrong()
                            }
                        }
                    }
                }
            }
        }
    
        function makeAnswerArray(answer1, answer2, answer3, answer4)
        {
            return [answer1, answer2, answer3, answer4];
        }
    
        function shuffleArray(array)
        {
            for (var i = array.length - 1; i > 0; i--) {
                var j = Math.floor(Math.random() * (i + 1));
                var temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }
        }
    
        readonly property string correctAnswerColor: "#99FFCC"
        readonly property string wrongAnswerColor: "#FF9999"
    
        function markAnswers(correctAnswer)
        {
            switch(correctAnswer)
            {
                case 1:
                    markAnswersIfAnswer1IsCorrect();
                    break;
                case 2:
                    markAnswersIfAnswer1IsCorrect();
                    break;
                case 3:
                    markAnswersIfAnswer1IsCorrect();
                    break;
                case 4:
                    markAnswersIfAnswer1IsCorrect();
                    break;
                default:
                    console.assert(false, "Invalid Value for correctAnswer: "
                                    + correctAnswer)
    
            }
        }
    
        function markAnswersIfAnswer1IsCorrect()
        {
            answer1TextField.backgroundColor = correctAnswerColor
            answer2TextField.backgroundColor = wrongAnswerColor
            answer3TextField.backgroundColor = wrongAnswerColor
            answer4TextField.backgroundColor = wrongAnswerColor
        }
    
        function markAnswersIfAnswer2IsCorrect()
        {
            answer1TextField.backgroundColor = wrongAnswerColor
            answer2TextField.backgroundColor = correctAnswerColor
            answer3TextField.backgroundColor = wrongAnswerColor
            answer4TextField.backgroundColor = wrongAnswerColor
        }
    
        function markAnswersIfAnswer3IsCorrect()
        {
            answer1TextField.backgroundColor = wrongAnswerColor
            answer2TextField.backgroundColor = wrongAnswerColor
            answer3TextField.backgroundColor = correctAnswerColor
            answer4TextField.backgroundColor = wrongAnswerColor
        }
    
        function markAnswersIfAnswer4IsCorrect()
        {
            answer1TextField.backgroundColor = wrongAnswerColor
            answer2TextField.backgroundColor = wrongAnswerColor
            answer3TextField.backgroundColor = wrongAnswerColor
            answer4TextField.backgroundColor = correctAnswerColor
        }
    
        function disableRadioButtons()
        {
            radioButtonAnswer1.enabled = false
            radioButtonAnswer2.enabled = false
            radioButtonAnswer3.enabled = false
            radioButtonAnswer4.enabled = false
        }
    }
    
    


  • I remembered I had this index issue before and asked it on stack overflow:
    https://stackoverflow.com/questions/62484078/required-property-not-working-with-repeater

    So solution is in QuizPage add:

    required property int index
    

    Than index can be accessed. Weired but thats the way.



  • What does your repeater code look like?
    It cannot inject a property into an object. It has to be set inside the repeater statement.



  • Repeater code is like you suggested:

    SwipeView{
           id: quizSwipeView
           interactive: false
           anchors.fill: parent
    
           Repeater{
               model: randomQuestions
               delegate: QuizPage{
                   question: randomQuestions[index]
               }
           }
       }
    
    Item{
        id: root
    
        required property var question
        required property int index
    //....
    }
    

    If I drop the required in question i can skip defining the index it is very weired behaviour.

    See also here: https://stackoverflow.com/questions/62484078/required-property-not-working-with-repeater



  • @sandro4912 said in How to create n Objects dynamically to populate StackView:

    Item{
    id: root

    required property var question
    required property int index
    

    //....
    }

    What is the reason for this property index inside your object? This doesn't make sense if you are using "question" as your property.



  • @sandro4912 said in How to create n Objects dynamically to populate StackView:

    Repeater{
    model: randomQuestions
    delegate: QuizPage{
    question: randomQuestions[index]
    }
    }

    What does this do?

    Repeater{
               model: randomQuestions
               delegate: Item{
                  Component.onCompleted: {
                    console.log(index)
                  }
               }
           }
    

    This "should" print out the index for each entry in randomQuestions. If it doesn't then you need a data model Repeater can use.



  • this

            Repeater{
               model: randomQuestions
               delegate: Item{
                  Component.onCompleted: {
                    console.log(index)
                  }
               }
           }
    

    gives:

    qml: 0
    qml: 1
    qml: 2
    qml: 3
    qml: 4
    qml: 5
    qml: 6
    qml: 7
    qml: 8
    qml: 9
    

    Even without the added index property.

    But if I do:

    Repeater{
             model: randomQuestions
             delegate: QuizPage{
                 question: randomQuestions[index]
             }
         }
    

    It only works with adding into QuizPage:

    required property int index
    

    Otherwise:

    qrc:/qml/quiz/QuizPage.qml:52: TypeError: Cannot read property 'id' of undefined
    qrc:/qml/quiz/QuizPage.qml:57: TypeError: Cannot read property 'askedQuestion' of undefined
    qrc:/qml/quiz/QuizPage.qml:64: TypeError: Cannot read property 'picture' of undefined
    qrc:/qml/quiz/Quiz.qml:0: ReferenceError: index is not defined
    


  • @sandro4912 said in How to create n Objects dynamically to populate StackView:

    required property int index

    Why are you doing this? This makes zero sense to create a require property when all your info is in your question property.



  • @sandro4912 said in How to create n Objects dynamically to populate StackView:

    question.answer1

    Also, for every usage of question inside of QuizPage do this:
    Change this:
    question.answer1
    To this:
    root.question.answer1

    I think you may be having scoping issues with the question property.



  • @fcarney

    check out this:

    https://stackoverflow.com/questions/62484078/required-property-not-working-with-repeater

    The issue is if you have a required property in an item you put into an repeater it only gets access to the index in the weired way that you add an index as required property. I also don't understand why it is necessary.

    You can try it out by copy the project on git hub and remove the line index:
    https://github.com/SandroWissmann/Quiz



  • The problem they had is that you have to use the delegate property to get access to the properties the Repeater adds to the delegate.

    If the "required" part makes it not work, then get rid of it.



  • @sandro4912 I ran your program. I removed the property index from QuizPage and got rid of "required" from question. It then showed the quiz correctly. When I clicked next question it crashed. Not sure what was wrong there.



  • Of course I could change

    required property var question
    

    to

    property var question
    

    but I thought it would be good to make it required to indicate the page only works with a question supplied. To me that all sounds like a weired bug that index is only available when you don't make a property required.

    so

    required property var index
    

    looks like a workarround.



  • @fcarney
    you are right it does not work anymore. I will check that issue tomorrow and then come back to here.



  • @sandro4912 said in How to create n Objects dynamically to populate StackView:

    To me that all sounds like a weired bug that index is only available when you don't make a property required.

    That issue on SO was because the OP was not using "delegate" inside of Repeater. The relevance to "required" was due to the repeater not being able to set the property.


  • Qt Champions 2018

    @fcarney said in How to create n Objects dynamically to populate StackView:

    That issue on SO was because the OP was not using "delegate" inside of Repeater. The relevance to "required" was due to the repeater not being able to set the property.

    No, it was not because of not using delegate, as a comment said delegate is a default property so you can omit it.
    Repeater { Item {} } is the same as Repeater { delegate : Item {} }.

    The problem was indeed because of the required property. Since Qt 5.15, if a delegate has a required property, the view won't assign the delegate context properties based on the roles of the model. It will only assign roles corresponding to the required properties present in the delegate.

    https://doc.qt.io/qt-5/qtquick-modelviewsdata-modelview.html#models

    To get finer control over which roles are accessible, and to make delegates more self-contained and usable outside of views, required properties can be used. If a delegate contains required properties, the named roles are not provided. Instead, the QML engine will check if the name of a required property matches that of a model role. If so, that property will be bound to the corresponding value from the model.
    [...]
    Note: model, index, and modelData roles are not accessible if the delegate contains required properties, unless it has also required properties with matching names.

    That's why you can't access index or modelData if you don't declare them as required property in QuizPage.

    And instead of index, you should use modelData :

    delegate: QuizPage{
        required property var modelData
        question: modelData
    }
    

    Note that QQmlListProperty is not meant to expose C++ data to QML, it is meant to allow the QML to populate a C++ list from QML.
    You should use QList<QObject*>, QVariantList or even better QAbstractListModel if you need a dynamic model. QQmlListProperty does work, but it's not its job.



  • @GrecKo
    No wonder I was going crazy trying to understand this problem. Thanks for shedding light on this.



  • @GrecKo

    Thanks for clarify. I was very confused how to expose my randomQuestions correctly.

    Now comes the big Question.

    Currently it is like this:

    All Questions are in a class Derived from QSqlTableModel.

    Now for RandomQuestions currently I create with an additional function in that class the QQmlListProperty<Questions>.

    Wouldn't it the best to implement QSortFilterProxModel which picks um the required random questions to expose that to QML?


Log in to reply