Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access dynamically/randomly loaded Repeater items in QML?

Tags:

qt

qml

The answer provided by @TheBootroo here: link

provides a way to load and change between QML files/screens/views. But when doing it like this how can one use signal and slots?

One can access the items created by the repeater by using the Repeater::itemAt(index) method, but since I don't know in what order the items are loaded I don't know what index screen2, screen3, screen4 etc. is at.

Is there any way to solve this or do one have to instantiate all the screens in memory at start up?

My code below:

main.qml:

//List of screens
    property variant screenList: [
        "main",
        "screen2",
        "screen3",
        ...
    ]

    //Set this to change screen
    property string currentScreen: "main"


    Repeater {
        id: screens
        model: screenList
        delegate: Loader {
            active: false;
            asynchronous: true
            anchors.fill:  parent
            source: "%1.qml".arg(modelData)
            visible: (currentScreen === modelData)
            onVisibleChanged: {
                loadIfNotLoaded()
            }
            Component.onCompleted: {
                loadIfNotLoaded()
            }

            function loadIfNotLoaded () {
                //To load start screen
                if(visible && !active) {
                    active = true;
                }
            }
        }
    }

    Connections {
        target: screens.itemAt(indexHere)
        //screen is here the string passed with the signal from screen2.
        onChangeScreen: currentScreen = screen
    }

    Button {
        id: button1
        text: "Go To Screen 2"
        onClicked: {
            currentScreen = "screen2"
        }
    }

And in screen2.qml:

signal changeScreen(string screen)

Button {
    text: "Go To Main"
    onClicked: {
        changeScreen("main")
    }
}
like image 374
uniquenamehere Avatar asked Oct 02 '22 09:10

uniquenamehere


1 Answers

One can access the items created by the repeater by using the Repeater::itemAt(index) method, but since I don't know in what order the items are loaded I don't know what index screen2, screen3, screen4 etc. is at.

The order of the items the Repeater instantiates is actually defined - the items will be instantiated in the order of the model, so in your case "main" will be the first, then "screen2" and so on. Now, each item inside of the Repeater is a Loader - the Repeater will create 3 Loaders in a defined order. The Loaders themselves load their source item on demand.

I think the only thing missing in your code is that the Connections refers to the Loader, but you need it to refer to the item the Loader creates. So change the Connections to

Connections {
    target: screens.itemAt(indexHere).item
    onChangeScreen: currentScreen = screen
}

Notice the additional .item.

After fixing this, there is one additional problem though: The Repeater hasn't yet instantiated its items when the Connections element is created, so screens.itemAt(indexHere) will return null. One way to fix this would be to use a currentIndex property instead of a currentScreen property and use that in the binding for target, and set currentIndex after the Repeater has instantiated its items:

property int currentIndex: -1
...
Connections {
    target: screens.itemAt(currentIndex).item
    ...
}
Component.onCompleted: currentIndex = 0

Even easier would probably be to put the Connections element inside the Repeater's delegate, i.e. have 3 Connections instead of one (assuming you have 3 screens).

like image 77
Thomas McGuire Avatar answered Oct 22 '22 23:10

Thomas McGuire