Splitting qml file into several files

Following https://stackoverflow.com/questions/45652525/qml-filling-menu-with-model-items I made a menu that behaves similarly to standard Makie menu. To make my project more manageable I am trying to split my files into smaller files. Following Breaking up QML into different files. [solved] | Qt Forum I put my menu into a seperate qml file and imported the directory. While I am able to import the separate qml file into my main file and use the component, the imported menu does not behave as the one written out in the main.qml file - more precisely the new menu is empty, in the sense that it has no menu items. The text on the button does appear correctly though.

Any thoughts?

File structure:

├── main.jl
├── main.qml
└── qml_components
    └── SensorMenu.qml

Main julia file, main.jl:

#main.jl
using QML, Observables
mutable struct menu_item
    text::String
end

items = ["x", "y", "z"]
items = [menu_item(item) for item in items]
item_model = JuliaItemModel(items)

current_item = Observable("x")

loadqml(
    "main.qml",
    observables=JuliaPropertyMap(
        "current_item" =>  current_item
    ),
    item_model = item_model
)

exec()

main qml file, main.qml:

//main.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import jlqml
import "qml_components" as Comp

ApplicationWindow {
    id: root
    title: "menus"
    visible: true
    width: 640
    height: 488
    onClosing: Qt.quit()

    RowLayout {
        // menu which works as expected, when called in the main qml file
        Button {
            id: fileButton
            text: observables.current_item
            onClicked: menu.open()

            Menu {
                id: menu
                y: fileButton.height
                visible: true
                Instantiator {
                    id: item_instatiator
                    model: item_model
                    delegate: MenuItem {
                        text: model.text
                        onTriggered: {
                            observables.current_item = model.text;
                        }
                    }

                    onObjectAdded: menu.insertItem(index, object)
                    onObjectRemoved: menu.removeItem(object)
                }
            }
        }

        // same menu but imported from another qml-file
        // expected to work as the first one, though is empty
        Comp.SensorMenu {
            text: observables.current_item
            lm: item_model
        }
    }
}

extra qml for holding the custom menu, SensorMenu.qml:

//SensorMenu.qml
import QtQuick
import QtQuick.Controls
import jlqml

Button {
    id: root
    property ListModel lm

    onClicked: menu.open()

    Menu {
        id: menu
        y: parent.height
        visible: true
        Instantiator {
            id: menu_instantiator
            model: root.lm
            delegate: MenuItem {
                text: model.text
                onTriggered: {
                    root.text = model.text;
                }
            }

            onObjectAdded: menu.insertItem(index, object)
            onObjectRemoved: menu.removeItem(object)
        }
    }
}

1 Like

So the example ends up looking as shown in the screencapture below:

After inspecting the Qt docs QML Object Attributes | Qt Qml | Qt 6.10.2 i managed to solve part of the issue by using a property alias in the SensorMenu definition as seen below.

//SensorMenu
import QtQuick
import QtQuick.Controls

Button {
    id: root
    property alias model: menu_instantiator.model

    onClicked: menu.open()

    Menu {
        id: menu
        y: parent.height
        visible: true
        Instantiator {
            id: menu_instantiator
            //model: root.lm
            delegate: MenuItem {
                text: model.text
                onTriggered: {
                    root.text = model.text;
                }
            }

            onObjectAdded: menu.insertItem(index, object)
            onObjectRemoved: menu.removeItem(object)
        }
    }
}

Now since i have renamed the property to model instead of lm this has to be changed in the main.qml file as well.

Now, while the menu does populate as expected, the text update seems to disconnect the text from the observable. See the video below, where the button texts are synced while changing only the left button, but using the right button seems to disconnect the two.

Digging a bit more around the Qt docs, Signal and Handler Event System | Qt Qml | Qt 6.10.2, I did get the text update to update the observable as well using signals.

//SensorMenu
import QtQuick
import QtQuick.Controls

Button {
    id: root
    property string current_text
    property alias model: menu_instantiator.model

    signal changed(text: string)
    text: current_text
    onClicked: menu.open()

    Menu {
        id: menu
        y: parent.height
        visible: true
        Instantiator {
            id: menu_instantiator
            //model: root.lm
            delegate: MenuItem {
                text: model.text
                onTriggered: {
                    current_text = model.text;
                    root.changed(current_text);
                }
            }

            onObjectAdded: menu.insertItem(index, object)
            onObjectRemoved: menu.removeItem(object)
        }
    }
}

//main.qml
...
        Comp.SensorMenu {
            current_text: observables.current_item
            model: item_model

            onChanged: current_text => {
                observables.current_item = current_text;
            }
        }
 ...

If anyone is interested I’ve made a quick repository to host the final state of the code in this post.