Search code examples
qtqmlqtquickcontrols2

How to link from ComboBox to another ComboBox


I try to make a converter app depending on two ComboBoxes using Controls 2. For example:

// First combobox is input unit:
ComboBox {
            id: res_combo1
            width: res_comborect1.width
            currentIndex: 0
            model: ListModel {
                id: model1
                ListItem { text: qsTr("meter") }
                ListItem { text: qsTr("mile") }
                ListItem { text: qsTr("km") }
            }
        }
    }
// Second combobox is output unit:
ComboBox {
            id: res_combo2
            width: res_comborect1.width
            currentIndex: 0
            model: ListModel {
                id: model2
                ListItem { text: qsTr("meter") }
                ListItem { text: qsTr("mile") }
                ListItem { text: qsTr("km") }
            }
        }
    }

When current index is selected from first combobox, second combobox should remove the current index. When another index is selected from first, second should restore previous index and it should remove current index dynamically. For example if meter is selected, second combobox should be {"mile", "km"} I only know a very long way that can be performed with combination of a few index, but my combobox data includes 20 item so I cannot apply this way. Long way:

   function visible1(){
        if(res_combo1.currentIndex==0){
            return true
        }
        else{
            return false
        }
    }
    function visible2(){
        if(res_combo1.currentIndex==1){
            return true
        }
        else{
            return false
        }
    }
    function visible3(){
        if(res_combo1.currentIndex==2){
            return true
        }
        else{
            return false
        }
    }
// if meter is selected from first combobox, second combobox:
RowLayout{
       visible: parent.visible1()
    ComboBox {   
        id: outcombo1
        currentIndex: 0
        model: ListModel {
            id: model_o1
            ListElement { text: qsTr("mile") }
            ListElement { text: qsTr("km") }
        }
    }
   }
// if mile is selected from first combobox:
RowLayout{
       visible: parent.visible2()
    ComboBox {   
        id: outcombo1
        currentIndex: 0
        model: ListModel {
            id: model_o2
            ListElement { text: qsTr("meter") }
            ListElement { text: qsTr("km") }
        }
    }
   }
// if km is selected from first combobox:
RowLayout{
       visible: parent.visible3()
    ComboBox {   
        id: outcombo1
        currentIndex: 0
        model: ListModel {
            id: model_o3
            ListElement { text: qsTr("meter") }
            ListElement { text: qsTr("mile") }
        }
    }
   }

I think we cannot change ListItem dynamically, so JavaScript with JSON list model is required by finding the current index via:

function find(model, criteria) {
  for(var i = 0; i < model.count; ++i) if (criteria(model.get(i))) return i
  return null
}

But I couldn't do this in QML. Is there any solution? Thanks


Solution

  • You cannot change a ListItem, but you CAN change the ListModel itself.

    ListModel {
        id: fromModel
        ListElement {text: "m" }
        ListElement {text: "mile" }
        ListElement {text: "km" }
    }
    
    ListModel {
        id: toModel
    
        function updateModel()
        {
            // Update model Here
        }
    }
    
    RowLayout
    {
        anchors.centerIn: parent
        ComboBox
        {
            id: fromCombobox
            model: fromModel
            onCurrentIndexChanged: toModel.updateModel()
        }
        ComboBox
        {
            id: toComboBox
            model: toModel
        }
    }
    

    This way, every time the first combo changes, the second combo's model will be updated.

    Before you update the model 2, check if the previously selected item in combo 2 will still be available after the update, in order to restore it once the model2 is rebuilt.

        function updateModel()
        {
            // Save old selection
            var selectedFrom = fromModel.get(fromCombobox.currentIndex).text
            if(toComboBox.currentText != selectedFrom)
                var valueToRestore = toComboBox.currentText
            else
                valueToRestore = ""
    
            // Update model
            clear()
            for(var i=0; i<fromModel.count; i++)
            {
                if(i == fromCombobox.currentIndex)
                    continue
                append(fromModel.get(i))
            }
    
            //Restore selection
            for(var i=0; i<toModel.count; i++)
            {
                // If no value to restore, select first available
                if(valueToRestore == "")
                {
                    if(toModel.get(i).text != selectedFrom)
                    {
                        toComboBox.currentIndex = i
                        break
                    }
                }
    
                // Else, restore previously selected item
                else
                {
                    if(toModel.get(i).text == valueToRestore)
                    {
                        toComboBox.currentIndex = i
                        break
                    }
                }
    
            }
        }