このページでは

QML 動的ビュー順序設定チュートリアル 4 - 項目の並べ替え

ビュー内の項目の順序を変更するには、ドラッグ&ドロップ以外にも方法があります。DelegateModel を使用すれば、モデルデータに基づいて項目を並べ替えることも可能です。そのためには、DelegateModel インスタンスを次のように拡張します:

    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
    }
手順

DelegateModel 内のアイテムは、DelegateModelGroup 型で表されるグループにフィルタリングされます。通常、モデル内のすべてのアイテムはデフォルトのitems グループに属しますが、このデフォルト設定はincludeByDefaultプロパティで変更可能です。 ソートを実装するには、まずアイテムを「未ソート」グループに追加し、そこから「items」グループ内のソート済み位置へ移動させる必要があります。そのためには、「items」グループの`includeByDefault`をクリアし、新しいグループ名「unsorted」に対して設定します。

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

            includeByDefault: true
        }

アイテムのソートは、まず「items」グループ内で最初の未ソートアイテムを挿入する位置を見つけ、そのアイテムを「items」グループに移してからあらかじめ決められたインデックスへ移動させ、未ソートグループが空になるまでこの処理を繰り返します。

項目の挿入位置を特定するには、get() 関数を使用して、未ソートグループからその項目のハンドルを取得します。このハンドルの model プロパティを通じて、その項目のデリゲートインスタンスで利用可能なのと同じモデルデータにアクセスし、他の項目と比較することで相対的な位置を決定できます。

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

sort関数のlessThan引数は、リストの順序を決定する比較関数です。この例では、以下のいずれかになります:

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

新しいアイテムが未ソートグループ(DelegateModel )に追加されるたびにソートがトリガーされ、その通知は `DelegateModelGroup `onChanged ハンドラによって行われます。現在選択されているソート関数がない場合は、未ソートグループからアイテムグループへすべてのアイテムを単純に移動させます。それ以外の場合は、選択されたソート関数を使用して `sort` を呼び出します。

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

最後に、選択された並べ替え順序が変更された場合、`items`グループ内のすべてのアイテムを`unsorted`グループに移動させることで、リストの完全な再並べ替えをトリガーできます。これにより、`DelegateModelGroup `onChanged ハンドラーが呼び出され、アイテムは正しい順序で`items`グループに戻されます。 なお、DelegateModelGroup onChanged ハンドラーは再帰的に呼び出されないため、ソート中に呼び出されても問題はありません。

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

サンプルプロジェクト @ 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.