access items in different qml files

10,030

Solution 1

You cannot access objects in "different qml files" unless you have an instance of that qml type or a singleton.

Access through the id is only possibly if the object that tries to access it is created in the scope of the object it wants to access via the id.

You you have several screens, simply expose them as properties of the root qml object, then they will be accessible from the entire application because of dynamic scoping, unless they are shadowed by a different property with the same name.

Solution 2

As dtech said, you can't access objects in different files as they are no objects yet.

Let's say you have a file called BlueSqare.qml

import QtQuick 2.0
Rectangle {
    id: root
    width: 100
    height: 100
    color: 'purple'
}

Now you have your main.qml

import QtQuick 2.0
import QtQuick.Controls 2.0
ApplicationWindow {
    id: root
    width: 300
    height: 300
    visible: true
    Row {
        id: rowdi
        spacing: 3
        Repeater {
            model: 3
            delegate: BlueSquare {}
        }
    }
}

You'll see, that it creates three of my BlueSquares so it is undefined which one I want to change if I set root.color = 'blue' - or actually it is not: Because I have another object with the id: root in the main.qml, which is the ApplicationWindow, I will set it's color to blue.

You can see: ids need to be unique only within one file, and as such you can only reference objects within this one file by the id.

There is a exemption from this: You can move up the tree of files in which a qml file is used and reference their ids.

So if you'd try to reference rowdi in the BlueSqure in my example this would reference the Row which arranges them. This is possible as you can have a well defined rule, which object you try to reference by this id.

If the id is not found in the file (here: BlueSquare.qml) you try to reference it, then it will look in the file that instantiates the object (here: main.qml). If it finds it there, the search is over. If it does not find it, it will try to move one file up - which is impossible here, as with main.qml the story ends.

However this should be avoided if possible. Maybe you want to reuse BlueSquare somewhere else, and there you don't have a Row that also coincidently has the id: rowdi. Instead you should inject the references uppon instantiation.

import QtQuick 2.0
import QtQuick.Controls 2.0
ApplicationWindow {
    id: root
    width: 300
    height: 300
    visible: true
    Row {
        id: rowdi
        spacing: 3
        property color squareColors: 'blue'
        Repeater {
            model: 3
            delegate: BlueSquare {
               color: rowdi.squareColors
            }
        }
    }
}

Here we us the id rowdi in the file where we defined it, to inject the data to the BlueSquares.

You may also pass them an QtObject which holds all the values as a model. This is especially useful if you're going to change the values:

// MyModelCountingButton.qml

import QtQuick 2.0
import QtQuick.Controls 2.0
Button {
    property QtObject model // Better would be to be more specific the QtObject and have a object that guarantees to have your properties
    text: model ? model.count : '-'
    onClicked: {
        if (model) model.count++
    }
}

And then in your main.qml

import QtQuick 2.0
import QtQuick.Controls 2.0
ApplicationWindow {
    id: root
    width: 800
    height: 600
    visible: true

    QtObject {
        id: myCountingModel
        property int count: 0
    }

    MyModelCountingButton {
        id: button1
        model: myCountingModel
    }

    MyModelCountingButton {
        id: button2
        model: myCountingModel
    }
}

Now you can use the common model to change values that will affect the other object without referencing things outside the own file, which improves reusability.

Basically, uppon instantiation you tell them from where they shall read and store their data, and if this place is shared among your screens, you have won.

For your problem the easiest way would be to have a currentIndex-property in the model, a index assigned to each screen and then in the file

// Screen.qml

[...]
Item {
    id: root
    property int index
    property QtObject indexObject
    Binding {
        target: root
        property: 'state'
        value: 'inactive'
        when: index !== indexObject.currentIndex
    }
}

and in main.qml

[...]
ApplicationWindow {
    [...]
    QtObject {
        id: indexObject
        property int currentIndex: 0
    }

    Screen {
        index: 0
        indexObject: indexObject
    }
    Screen {
        index: 1
        indexObject: indexObejct
    }
    [...]
}

Or you have the Screen as:

[...]
Item {
    id: root
    property int index
    property int currentIndex
    signal turningActive // this must be emitted when you want to have this screen active.
    Binding {
        target: root
        property: 'state'
        value: 'inactive'
        when: index !== indexObject.currentIndex
    }
}

main.qml

[...]
ApplicationWindow {
    id: root
    [...]
    property int currentIndex: 0

    Screen {
        index: 0
        currentIndex: root.currentIndex
        onTuringActive: root.currentIndex = 0
    }
    Screen {
        index: 1
        currentIndex: root.currentIndex
        onTurningActive: root.currentIndex = 1
    }
    [...]
}

Usually, if it is only few properties, you create the bindings where you declare their instantiation. If there will be massive numbers, the QtObject as model is beneficial.
Only if you are really really really sure in which context a declared component will be used at all times, then you can think about referencing things that are from another file (e.g. using parent in the root node of a file or using ids that need to be resolved in another file)

Share:
10,030
numberCruncher
Author by

numberCruncher

Updated on June 14, 2022

Comments

  • numberCruncher
    numberCruncher about 2 years

    I have several qml files, each of them define a screen in my application. Sometimes, an action on one screen should change items on a different screen. Right now I do this through setting the property Item buttonId and then do

    for (var i = 0; i < buttonId.parent.children.length; i++) {
       buttonId.parent.children[i].state="inactive"
    }
    

    Is it possible to access Items/Objects in different files directly, e.g. through their id?