Попытка вызвать пользовательскую функцию из ListElement в ListModel QML

У меня есть, например, такая модель в моем QML:

ListModel{
    id: mainlist
    ListElement
    {
        name: "Name1"
        type: "subMenu"
        toogle: false
        iconSource: ""

        function actionClick()
        {
            console.log("actionclick is passed for 0 item!")
        }
    }
    ListElement
    {
        name: "Manage Favorites"
        type: "subMenu"
        toogle: false
        iconSource: "image://provider/common/endless_menu/list_icons/fav"

        function actionClick()
        {
            console.log("actionclick is passed for 1 item!")
        }
    }
    ListElement
    {
        name: "Name2"
        type: "subMenu"
        toogle: false
        iconSource: "image://provider/common/endless_menu/list_icons/active"

        function actionClick()
        {
            console.log("actionclick is passed for 2 item!")
        }
    }
    ListElement
    {
        name: "Name3"
        type: "subMenu"
        toogle: false
        iconSource: "image://provider/common/endless_menu/list_icons/scan"

        function actionClick()
        {
            console.log("actionclick is passed for 3 item!")
        }
    }
    ListElement
    {
        name: "Manual Frequency Input"
        type: "commonBtn"
        toogle: false
        iconSource: ""

        function actionClick()
        {
            console.log("actionclick is passed for 4 item!")
        }
    }

    function onStart(currIndex)
    {
        console.log("test is passed for " + currIndex + "item!")
    }
}

Итак, у меня есть элемент ListView с id: optionlist и ListDelegate для элементов списка управления. У меня есть несколько моделей - все эти элементы созданы для опций меню в проекте. Итак, код onModelChanged: optionlist.model.onStart() в файле списка работает отлично.

Проблема заключается в вызове функции actionClick() из сценария listDelegate OnClicked в элементе MouseArea. Можно ли сделать это? Что-то вроде этого: optionlist.model.ContentItem.children[currentIndex].actionClick() может быть или что-то другое?

ОБНОВЛЕНИЕ: Извините, Амит Томер, возможно, я неправильно объясняю задачу... Итак, мне нужен элемент в модели со следующими полями:

name: - text for element
type: - type of element (button, radio button, check button or submenu) - needed for corect action when user clicked on this item
toogle: - boolean value for radio/check buttons state, and for some internal operations.
iconSource: - path for icon, if it needed.

function actionClick() - function, which will be execute when user clicked on this item.

Все это необходимо сделать для наглядного и легко заполняемого дерева меню All option. Это дерево меню будет записано в отдельный файл.

В коде ниже я показываю работающую модель:

Item{
    id: menuoptions

    property ListModel prev: manageFavorites
    property bool root: true

//Main Menu
    property alias mainlist: mainlist
    ListModel{
        id: mainlist
        ListElement
        {
            name: "Band: "
            type: "subMenu"
            toggle: false
            iconSource: ""
        }
        ListElement
        {
            name: "Manage Favorites"
            type: "subMenu"
            toggle: false
            iconSource: "image://provider/common/endless_menu/list_icons/fav"
        }
        ListElement
        {
            name: "Show: "
            type: "subMenu"
            toggle: false
            iconSource: "image://provider/common/endless_menu/list_icons/active"
        }
        ListElement
        {
            name: "Scan"
            type: "subMenu"
            toggle: false
            iconSource: "image://provider/common/endless_menu/list_icons/scan"
        }
        ListElement
        {
            name: "Manual Frequency Input"
            type: "commonBtn"
            toggle: false
            iconSource: ""
        }

        function actionClick(currIndex)
        {
            switch(currIndex)
            {
                case 0:
                {
                    prev = mainlist
                    menuList.model = bandlist
                    break
                }
            case 1:
                {
                    prev = mainlist
                    menuList.model = manageFavorites
                    break
                }
            case 2:
                {
                    prev = mainlist
                    menuList.model = showlist
                    break
                }
            case 3:
                {
                    console.log("Scan started")
                    mainlist.setProperty(3, "name", getScan())
                    break
                }
            case 4:
                {
                    console.log("Speller for Manual Frequency Input open!")
                    break
                }
            }
        }

        function onStart()
        {
            console.log("root model loaded")
            root = true
            mainlist.setProperty(0, "name", "Band: " + getBand())
            mainlist.setProperty(2, "name", "Show: " + getShow())
            mainlist.setProperty(3, "name", getScan())
        }
    }

//First Lvl subMenu
    property alias bandlist: bandlist
    ListModel{
        id: bandlist
        ... Analog menulist
    }

    property alias manageFavorites: manageFavorites
    ListModel{
        id: manageFavorites
        ... Analog menulist
    }

    property alias showlist: showlist
    ListModel{
        id: showlist
        ... Analog menulist
    }
}

Как видите, я написал примерно так, как Вы говорите. Ничего страшного, если я не найду лучшего решения. Лучшее решение - удалить общую функцию actionClick() и добавить ее часть (код в блоках case) в ListElement соответственно. И вызовите его, когда элемент щелкнет.

Я не знаю, как вызвать эту функцию из ListElement. В WPF я бы просто создал пользовательский компонент, который заменил бы ListElement и все! Но я не знаю, как это сделать в QML.

Мне что-то непонятно - спрашивайте.

ОБНОВЛЕНИЕ: Проблема решена. Мой вариант в ответах. Спасибо всем.


person Corwin    schedule 16.01.2013    source источник
comment
Я думаю, что теперь это стало более запутанным :-(   -  person Amit Tomar    schedule 17.01.2013
comment
Мне что-то непонятно - спрашивайте(   -  person Corwin    schedule 17.01.2013


Ответы (2)


Как насчет этого:

import QtQuick 1.0

Rectangle
{
    color: "grey"
    width:  800
    height: 800

    function actionClick(index)
    {
        if( 0 == listModel.get(index).internalIndex )
           functionForElement1()
        else if( 1 == listModel.get(index).internalIndex )
           functionForElement2()
        else if( 2 == listModel.get(index).internalIndex )
           functionForElement3()
        else if( 3 == listModel.get(index).internalIndex )
           functionForElement4()
        else if( 4 == listModel.get(index).internalIndex )
           functionForElement5()
    }

    functionForElement1() {  console.log("Inside Function 1")     }
    functionForElement2() {  console.log("Inside Function 2")     }
    functionForElement3() {  console.log("Inside Function 3")     }
    functionForElement4() {  console.log("Inside Function 4")     }
    functionForElement5() {  console.log("Inside Function 5")     }

    ListView
    {
        anchors.centerIn: parent
        width: parent.width
        height: parent.height
        model: listModel
        spacing: 10
        delegate: Component
        {
            Rectangle
            {
               x: 350
               y: 350
               width: 100
               height: 50
               Text { text: name ; anchors.centerIn: parent }

               MouseArea
               {
                  anchors.fill: parent
                  onClicked: actionClick( index )
               }
            }
        }

        ListModel
        {
            id: listModel

                 ListElement { name: "Apple",          internalIndex: 1}
                 ListElement { name: "badApple" ,      internalIndex: 2}
                 ListElement { name: "goodApple" ,     internalIndex: 3}
                 ListElement { name: "worseApple" ,    internalIndex: 4}
                 ListElement { name: "spoilledApple" , internalIndex: 5}
        }
    }
}
person Amit Tomar    schedule 16.01.2013
comment
Это не вариант. Потому что некоторые элементы являются подменю, а функция перезагрузки модели. Поэтому я не могу написать такую ​​функцию - каждый элемент в каждой модели вызывает уникальный метод из бэкенда и выполняет действие, например кнопку, или входит в подменю. Я оставил только console.log в функции только для примера. Каждая функция уникальна. - person Corwin; 16.01.2013
comment
Я отредактировал решение для вашего требования. Пожалуйста, проверьте. - person Amit Tomar; 16.01.2013
comment
Теперь actionClick(index) я помещаю в ListModel. Как onStart(currIndex). И actionClick написал с делом, как вы советуете. Но это нормально, только если нет возможности вызвать функцию из элемента. Потому что если я меняю положение элементов в модели - мне нужно изменить actionClick. Это нехорошо, как мне сказали. Мне просто нужно знать - есть ли возможность вызвать функцию из элемента, как в моем примере. Для меня не очень хорошо менять свои модели. - person Corwin; 16.01.2013
comment
Кажется, на этот раз я понял. Я добавил специальное свойство internalIndex в каждый элемент. Даже если ваши элементы в списке будут переупорядочены, это свойство все равно будет частью этого элемента. Таким образом, вы сможете правильно вызывать соответствующие функции. Я отредактировал его снова, пожалуйста, проверьте сейчас. И Дарн! это должно работать сейчас .. :-) - person Amit Tomar; 16.01.2013

Проблема решена. Но не совсем так, как планировалось сначала.

import QtQuick 2.0

/*
Elements of model:
name - displayed text
type - type of element, for clicked event
toggle - state for radio and check btn
iconSource - path to icon

*/
/*
TYPE:
radioBtn
checkBtn
commonBtn
subMenu
*/
Item{
    id: menuoptions

    //property ListModel prev: listmodel

    property Item curr: mainList

    //TEST FUNCTIONS
    property string band: "AM"
    property string show: "All"
    //END TEST FUNCTION

//Main Menu Radio
    Item{
        id: mainList
        property int count: 7
        property Item prev

        children:
        [
            Item
            {
                property string name: "Band: "
                property string type: "subMenu"
                property bool toggle: false
                property string iconSource: ""

                property bool need: true

                function onClick()
                {
                    curr = bandList
                    loadModel()
                }
            },
            Item
            {
                property string name: "Manage Favorites"
                property string type: "subMenu"
                property bool toggle: false
                property string iconSource: "image://provider/common/endless_menu/list_icons/fav"

                property bool need: true

                function onClick()
                {
                    curr = manageFavList
                    loadModel()
                }
            },
            Item
            {
                property string name: "Show: "
                property string type: "subMenu"
                property bool toggle: false
                property string iconSource: "image://provider/common/endless_menu/list_icons/active"

                property bool need: true

                function onClick()
                {
                    curr = showList
                    loadModel()
                }
            },
            Item
            {
                property string name: "Scan"
                property string type: "subMenu"
                property bool toggle: false
                property string iconSource: "image://provider/common/endless_menu/list_icons/scan"

                property bool need: true

                function onClick()
                {
                    if (!listmodel.get(menuList.currentIndex).toggle)
                    {
                        listmodel.setProperty(menuList.currentIndex, "toggle", true)
                        listmodel.setProperty(menuList.currentIndex, "name", "Scan")
                    }
                    else
                    {
                        listmodel.setProperty(menuList.currentIndex, "toggle", false)
                        listmodel.setProperty(menuList.currentIndex, "name", "Stop")
                    }

                }
            },
//optional
            Item
            {
                property string name: "Alternative Frequency"
                property string type: "checkBtn"
                property bool toggle: false
                property string iconSource: ""

                property bool need: false

                function onClick()
                {
                    if (listmodel.get(menuList.currentIndex).toggle)
                    {
                        listmodel.setProperty(menuList.currentIndex, "toggle", false)
                        listmodel.setProperty(menuList.currentIndex + 1, "toggle", false)
                    }
                    else
                    {
                        listmodel.setProperty(menuList.currentIndex, "toggle", true)
                    }
                }
            },
            Item
            {
                property string name: "Regionalization"
                property string type: "checkBtn"
                property bool toggle: false
                property string iconSource: "image://provider/common/endless_menu/list_icons/scan"

                property bool need: false

                function onClick()
                {
                    if (listmodel.get(menuList.currentIndex - 1).toggle)
                    {
                        if (listmodel.get(menuList.currentIndex).toggle)
                        {
                            listmodel.setProperty(menuList.currentIndex, "toggle", false)
                        }
                        else
                        {
                            listmodel.setProperty(menuList.currentIndex, "toggle", true)
                        }
                    }

                }
            },
//end optional
            Item
            {
                property string name: "Manual Frequency Input"
                property string type: "commonBtn"
                property bool toggle: false
                property string iconSource: ""

                property bool need: true

                function onClick()
                {
                    console.log("Speller for Manual Frequency Input open!")
                }
            }
        ]
        function preLoad()
        {
            console.log("preload Func")
            children[0].name = "Band: " + band
            children[2].name = "Show: " + show
            if (band == "SAT" || band == "AM")
            {
                children[4].need = false
                children[5].need = false
            }
            if (band == "FM")
            {
                children[4].need = true
                children[5].need = true
            }
        }
    }

//First Lvl subMenu Radio
    Item{
        id: bandList
        property int count: 3
        property Item prev: mainList

    //Analog as mainList
    }

    Item{
        id: manageFavList
        property int count: 3
        property Item prev: mainList

    //Analog as mainList
        function preLoad()
        {
            console.log("preload Func for Manage Favorite List")
            //Insert into model fav stations from 0 item
            //favcount = gateway.getFavCount()
        }
        function favClick()
        {
            console.log("Favorite item clicked")
        }
    }

    Item{
        id: showList
        property int count: 2
        property Item prev: mainList

    //Analog as mainList
    }



//Service Items
    property alias listmodel: listmodel
    ListModel{
        id: listmodel
}
    function loadModel()
    {
        console.log("loader menu")
        favcount = 0;
        listmodel.clear()
        curr.preLoad()
        for (var i=0;i<curr.count;i++)
        {
            if (curr.children[i].need){
                listmodel.append({"name": curr.children[i].name,
                                  "type":curr.children[i].type,
                                  "toggle":curr.children[i].toggle,
                                  "iconSource":curr.children[i].iconSource})
            }
        }
    }

    function actionClick(currIndex)
    {
        if (favcount == 0)
            curr.children[currIndex].onClick()
        else
        {
            if (currIndex <= favcount - 1)
                curr.favClick()
            else
               curr.children[currIndex-favcount].onClick()
        }
    }

    function toggled(index)
    {
        for (var i=0; i<listmodel.count; i++)
            listmodel.setProperty(i, "toggle", false)
        listmodel.setProperty(index, "toggle", true)
    }
}

Как видите, изменение optionMenu решается очисткой модели и заполнением элементами из Item.

Каждый Item - это один экран.

Предметы обслуживания - общий компонент и функции.

Теперь я думаю, что было бы лучше отделить menuTree (компоненты элементов) в другом файле, потому что будет 3 разных OptionMenu с разными деревьями.

person Corwin    schedule 18.01.2013