How to access dynamically/randomly loaded Repeater

2019-05-14 22:10发布

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

标签: qt qml
1条回答
Fickle 薄情
2楼-- · 2019-05-14 23:10

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

查看更多
登录 后发表回答