En esta página

Tutorial QML de ordenación dinámica de vistas 4 - Ordenar elementos

Arrastrar y soltar no es la única forma de reordenar los elementos de una vista, utilizando DelegateModel también es posible ordenar los elementos basándose en los datos del modelo. Para ello extendemos nuestra instancia DelegateModel de la siguiente manera:

    DelegateModel {
        id: visualModel
        property var lessThan: [
            function(left, right) { return left.name < right.name },
            function(left, right) { return left.type < right.type },
            function(left, right) { return left.age < right.age },
            function(left, right) {
                if (left.size == "Small")
                    return true
                else if (right.size == "Small")
                    return false
                else if (left.size == "Medium")
                    return true
                else
                    return false
            }
        ]

        property int sortOrder: orderSelector.selectedIndex
        onSortOrderChanged: items.setGroups(0, items.count, "unsorted")

        function insertPosition(lessThan, item) {
            let lower = 0
            let upper = items.count
            while (lower < upper) {
                const middle = Math.floor(lower + (upper - lower) / 2)
                const result = lessThan(item.model, items.get(middle).model)
                if (result) {
                    upper = middle
                } else {
                    lower = middle + 1
                }
            }
            return lower
        }

        function sort(lessThan) {
            while (unsortedItems.count > 0) {
                const item = unsortedItems.get(0)
                const index = insertPosition(lessThan, item)

                item.groups = "items"
                items.move(item.itemsIndex, index)
            }
        }

        items.includeByDefault: false
        groups: DelegateModelGroup {
            id: unsortedItems
            name: "unsorted"

            includeByDefault: true
            onChanged: {
                if (visualModel.sortOrder == visualModel.lessThan.length)
                    setGroups(0, count, "items")
                else
                    visualModel.sort(visualModel.lessThan[visualModel.sortOrder])
            }
        }
        model: PetsModel {}
        delegate: dragDelegate
    }
Recorrido

Los elementos de un DelegateModel se filtran en grupos representados por el tipo DelegateModelGroup, normalmente todos los elementos del modelo pertenecen a un grupo por defecto items pero este valor por defecto se puede cambiar con la propiedad includeByDefault. Para implementar nuestra ordenación queremos que los elementos se añadan primero a un grupo sin ordenar desde el que podemos transferirlos a una posición ordenada en el grupo de elementos. Para ello borramos includeByDefault en el grupo de elementos y lo establecemos en un nuevo grupo llamado 'unsorted'.

        items.includeByDefault: false
        groups: DelegateModelGroup {
            id: unsortedItems
            name: "unsorted"

            includeByDefault: true
        }

Ordenamos los ítems encontrando primero la posición en el grupo de ítems para insertar el primer ítem sin ordenar y luego transferimos el ítem al grupo de ítems antes de moverlo al índice predeterminado y repetimos hasta que el grupo sin ordenar esté vacío.

Para encontrar la posición de inserción de un ítem solicitamos un handle para el ítem del grupo sin clasificar con la función get(). A través de la propiedad model de este manejador podemos acceder a los mismos datos del modelo que están disponibles en una instancia delegada de ese elemento y compararlos con otros elementos para determinar la posición relativa.

        function insertPosition(lessThan, item) {
            let lower = 0
            let upper = items.count
            while (lower < upper) {
                const middle = Math.floor(lower + (upper - lower) / 2)
                const result = lessThan(item.model, items.get(middle).model)
                if (result) {
                    upper = middle
                } else {
                    lower = middle + 1
                }
            }
            return lower
        }

        function sort(lessThan) {
            while (unsortedItems.count > 0) {
                const item = unsortedItems.get(0)
                const index = insertPosition(lessThan, item)

                item.groups = "items"
                items.move(item.itemsIndex, index)
            }
        }

El argumento lessThan de la función sort es una función de comparación que determinará el orden de la lista. En este ejemplo puede ser uno de los siguientes:

        property var lessThan: [
            function(left, right) { return left.name < right.name },
            function(left, right) { return left.type < right.type },
            function(left, right) { return left.age < right.age },
            function(left, right) {
                if (left.size == "Small")
                    return true
                else if (right.size == "Small")
                    return false
                else if (left.size == "Medium")
                    return true
                else
                    return false
            }
        ]

La ordenación se activa cada vez que se añaden nuevos elementos a la lista sin ordenar DelegateModel, lo que nos es notificado por el manejador DelegateModelGroup onChanged . Si no hay ninguna función de ordenación seleccionada, simplemente transferimos todos los elementos del grupo sin ordenar al grupo de elementos, de lo contrario llamamos a ordenar con la función de ordenación seleccionada.

        groups: DelegateModelGroup {
            id: unsortedItems
            name: "unsorted"

            includeByDefault: true
            onChanged: {
                if (visualModel.sortOrder == visualModel.lessThan.length)
                    setGroups(0, count, "items")
                else
                    visualModel.sort(visualModel.lessThan[visualModel.sortOrder])
            }
        }

Finalmente, cuando el orden seleccionado cambia, podemos reordenar la lista moviendo todos los ítems del grupo de ítems al grupo sin ordenar, lo que activará el manejador DelegateModelGroup onChanged y transferirá los ítems de vuelta al grupo de ítems en el orden correcto. Ten en cuenta que el manejador DelegateModelGroup onChanged no será invocado recursivamente, por lo que no hay ningún problema con que sea invocado durante una ordenación.

        property int sortOrder: orderSelector.selectedIndex
        onSortOrderChanged: items.setGroups(0, items.count, "unsorted")

Proyecto de ejemplo @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.