Sur cette page

Tutoriel QML sur le tri dynamique des éléments d'une vue 4 - Trier les éléments

Le glisser-déposer n’est pas le seul moyen de réorganiser les éléments d’une vue ; à l’aide d’une interface « DelegateModel », il est également possible de trier les éléments en fonction des données du modèle. Pour ce faire, nous étendons notre instance DelegateModel comme suit :

    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
    }
Démonstration

Les éléments d’une vue « DelegateModel » sont filtrés en groupes représentés par le type « DelegateModelGroup ». Normalement, tous les éléments du modèle appartiennent à un groupe par défaut « items », mais cette valeur par défaut peut être modifiée à l’aide de la propriété « includeByDefault ». Pour mettre en œuvre notre tri, nous souhaitons que les éléments soient d'abord ajoutés à un groupe non trié, d'où nous pourrons les transférer vers une position triée au sein du groupe « items ». Pour ce faire, nous désactivons la propriété « includeByDefault » sur le groupe « items » et la définissons sur un nouveau nom de groupe : « unsorted ».

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

            includeByDefault: true
        }

Nous trions les éléments en déterminant d’abord la position dans le groupe « items » où insérer le premier élément non trié, puis en transférant cet élément vers le groupe « items » avant de le déplacer vers l’index prédéterminé ; nous répétons cette opération jusqu’à ce que le groupe « unsorted » soit vide.

Pour déterminer la position d’insertion d’un élément, nous demandons un descripteur pour cet élément du groupe « unsorted » à l’aide de la fonction ` get()`. Grâce à la propriété `model` de ce descripteur, nous pouvons accéder aux mêmes données de modèle que celles disponibles dans une instance de délégué de cet élément et les comparer à d’autres éléments afin de déterminer la position relative.

        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)
            }
        }

L’argument « lessThan » de la fonction de tri est une fonction de comparaison qui déterminera l’ordre de la liste. Dans cet exemple, il peut s’agir de l’une des fonctions suivantes :

        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
            }
        ]

Un tri est déclenché chaque fois que de nouveaux éléments sont ajoutés au groupe « unsorted » ( DelegateModel ), ce dont nous sommes informés par le gestionnaire « DelegateModelGroup » ( onChanged ). Si aucune fonction de tri n’est actuellement sélectionnée, nous transférons simplement tous les éléments du groupe « unsorted » vers le groupe « items » ; dans le cas contraire, nous appelons la fonction de tri avec la fonction sélectionnée.

        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])
            }
        }

Enfin, lorsque l’ordre de tri sélectionné change, nous pouvons déclencher un retriage complet de la liste en déplaçant tous les éléments du groupe « items » vers le groupe « unsorted », ce qui déclenchera le gestionnaire DelegateModelGroup onChanged et transférera les éléments vers le groupe « items » dans le bon ordre. Notez que le gestionnaire « DelegateModelGroup » onChanged ne sera pas appelé de manière récursive ; il n’y a donc aucun problème à ce qu’il soit appelé pendant un tri.

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

Exemple de projet sur 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.