2
votes

I'm trying to communicate between qml components in a tree structure. I have a main.qml component with and id of root. It has two children, and each of those children has an arbitrary number of children dynamically created from a repeater and a model.

When one of the grandchildren is clicked I would like the others to know, and be able to take action. So if I could send signals between the grandchildren that would be fine.

The problem is none of them have their id property set because they are made dynamically, and some of them are in different scopes. To communicate between them I have done the following: Created a function in root, every grandchild can see that, and can call it with a message as parameter. The root function then emits a signal with the message as parameter. All the grandchildren can connect to the signal because they know the id of root.

What do people think of that? I'm getting the feeling that I've missed the point of signals in qml, feels like i've implemented a crude system and missed the whole point or something.

Also, I want to stay out of the C++ world, but do people think it would be best to use a C++ class so that I can use signals and slots.

What I'm aiming at is an MVC structure with very loose coupling, and a centralised Controller. What do people think about communicating between QML components in MVC.

The only similar questions I found here were about C++ or using hard-coded id's on components.

I don't think id's can be set dynamically, not even once at creation; am I wrong about that?

Also, the components are in different scopes, so id's can't be resolved; am I wrong about that?

I've written some code:

//qml.main
import QtQuick 2.4
import QtQuick.Controls 1.3

ApplicationWindow {
    id: root
    visible: true
    menuBar: MenuBar {
        Menu {
            title: qsTr("File")
            MenuItem {
                text: qsTr("&Open")
                onTriggered: console.log("Open action triggered");
            }
            MenuItem {
                text: qsTr("Exit")
                onTriggered: Qt.quit();
            }
        }
    }

    property string thisName: "root"

    signal rootSays(string broadcastMessage)

    function callRoot(message) {
        var response = message
        print("Root received: " + message)
        print("Root broadcasting: " + response)
        rootSays(response)
    }

    MajorComponent{//this is root's child A
        property string thisName: "A"
        thisModel: [{name:"First Grandchild of A", color:"red", y:0},
                    {name:"Second Grandchild of A", color:"green", y:80}]
    }

    MajorComponent{//this is root's child B
        property string thisName: "B"
        thisModel: [{name:"First Grandchild of B", color:"blue", y:210},
                    {name:"Second Grandchild of B", color:"yellow", y:290}]
    }


}


//qml.MinorComponent
import QtQuick 2.0

Rectangle {
    property string thisName: ""
    property string thisColor: ""

    color: thisColor
    height: 50; width: 200
    Text {
        anchors.centerIn: parent
        text: thisName
    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
            print(thisName + " clicked")
            print("Root called with: " + thisName)
            root.callRoot("Hello from " + thisName)
            print("---")
        }
    }
}


//qml.MajorComponent
import QtQuick 2.0

Rectangle {
    property var thisModel: []

    Repeater {
        model:thisModel
        MinorComponent {
            y: modelData.y
            thisName: modelData.name
            thisColor: modelData.color
            function handleResponse(response) {
                print(thisName + " received: " + response);
            }
            Connections {
                target: root
                onRootSays: handleResponse(broadcastMessage)
            }

        }
    }

}
1
It's an interesting question, but to answer it it'd be easier to have some code available that we can play around with and test. I mean, someone may give you the answer directly, but for something specific like that on QML and given Qt's track record with bugs or implementation details in QML, it'd be a lot easier to have some code.coyotte508
What's wrong with having your Controller as a C++-exposed object?peppe
@peppe how does that help ?GrecKo
You might find this read interesting : medium.com/@benlaud/… I use this pattern in a simplified way, I listen for AppActions directlyGrecKo
Because as soon as your application will grow, you'll need to hook up things from grand grand grand children into other grand grand grand children. That calls for a centralized design, which is easier in C++. (Or via pragma Singleton in QML)peppe

1 Answers

2
votes

I don't think id's can be set dynamically, not even once at creation; am I wrong about that?

ids are purely "compile" time construct. That being said, there is nothing preventing you from implementing and managing your own object registry system. A simple empty JS object would do, it can effectively be used as a QMap to lookup objects based on a key (the property name). If you set the map object as a property of the root object, it should be resolvable from every object in the tree because of dynamic scoping.

The approach with the signal is a sound one IMO. I've used something similar, combined with functors and capture by reference, allowing access to arbitrary and optionally existing objects in an arbitrary tree structure, filtering candidates by various criteria they must meet. You can do some very tricky stuff with this technique.

That being said, a practical example which illustrates what you actually want to achieve will be useful for providing a more specific answer.