Qtブログ(日本語)

QMLモデル活用:リアルタイム並び替え・フィルタリング

作成者: Qt Group 日本オフィス|Sep 21, 2025 8:00:00 PM
本稿は「QML Model: Sort and filter the data on the fly」の抄訳です。

データ駆動型アプリケーションの多くにおいて、ソートやフィルタリングは非常に一般的であり、しばしば必要不可欠です。Qtは、元のデータを変更することなくソートやフィルタリングを処理できる組み込みのプロキシモデルを提供することで、これを容易にします。このモデルはビューにアタッチでき、MVCフレームワーク内でデータ表示に使用できます。開発者はQSortFilterProxyModelを継承し、そのフィルタリングおよびソートAPIをオーバーライドすることで機能を拡張でき、完全にカスタマイズされた動作を実現できます。これにより、開発者はこれらの機能をゼロから構築する必要がなくなり、大幅な時間短縮が図れます。同時に、必要に応じて柔軟にカスタマイズする余地も残されています。

Qt Quickに関しては、現代的なUI開発パラダイムが宣言的な手法を提供し、開発者が動的でアニメーション化された高性能なユーザーインターフェースを迅速に構築することを可能にします。QMLプロパティとJavaScript式を用いた簡潔なコードは、可読性と理解しやすさも向上させます。なお、6.10以前のバージョンでは、ネイティブのQMLプロキシモデルが存在しなかったため、開発者はC++で手動でソートやフィルタリングのロジックを記述する必要がありました。これにより、異なるソーターやフィルタモデルを使用する場合、冗長なコードが生じる可能性がありました。宣言的なモデル手法により、開発者はソースデータの操作部分をUIに近接させつつ、ソースモデルに対するデータの実際のビジネスロジック処理をC++で管理することが可能となります。

Qt 6.10 では、QML の SortFilterProxyModel が提供され、宣言的な方法でモデルデータのソートとフィルタリングが可能となりました。SortFilterProxyModel は内部で QAbstractProxyModel を継承しており、QSortFilterProxyModel と同様に、ソースモデル内のデータの実際のインデックスは変更されません。ソースモデルはSortFilterProxyModelmodel属性で設定でき、プロキシモデルはビューのmodel属性で設定できます。以下の例では、SortFilterProxyModelを使用して、ユーザーが指定した割合以上CPUを使用しているプロセスをprocessModelからフィルタリングし、それらをユーザーIDでソートする方法を説明します。


	SortFilterProxyModel {
	    id: sfpmProcessModel
	    model: processModel // Source model can be specified here
	    sorters: [
		RoleSorter {
		    roleName: "uid"
		}
	    ]
	    filters: [
		FunctionFilter {
		    component RoleData: QtObject { property qreal cpuUsage }
		    function filter(data: RoleData) : bool {
		        return (data.cpuUsage >  searchField.cpuUage)
		    }
		}
	    ]
	}

        TableView {
            id: processView
            anchors.fill: parent
            model: sfpmProcessModel
            clip: true
            delegate: Rectangle {
                implicitWidth: 200
                implicitHeight: 40
                border.color: "lightgray"
                border.width: 1
                Text {
                    anchors.centerIn: parent
                    text: display
                }
            }
        }

【注】 本稿の考え方や内容は、既存プロジェクトhttps://github.com/oKcerG/SortFilterProxyModelに触発され、それを基に作成されたものです。

デフォルトで提供されている特定の Filter および Sorter が用意されており、これらを適切に設定することで、ご希望の結果を得ることが可能です。リスト内で設定された個々のフィルターとソーターには優先順位を付けることもでき、これによりデータに適用される操作の順序が決まります。例えば、まずCPU使用率に基づいてデータをフィルタリングし、その後プロセスIDでさらにソートするように設定することが可能です。


	SortFilterProxyModel {
	    id: sfpm
	    model: processModel
	    sorters: [
                // First sort with process id and then further sort it with name
		RoleSorter {
		    roleName: "cpu"
		    priority: 0 // Priority can be specified here
		},
		RoleSorter {
		    roleName: "pid"
		    priority: 1 // Priority can be specified here
		},
                RoleSorter {
		    roleName: "name"
		    priority: 2 // Priority can be specified here
		},
               RoleSorter {
		    roleName: "uid"
		    priority: 3 // Priority can be specified here
		},
                RoleSorter {
		    roleName: "state"
		    priority: 4 // Priority can be specified here
		}
	    ]
	    filters: [
		FunctionFilter {
		    component RoleData: QtObject { property qreal cpuUsage }
		    function filter(data: RoleData) : bool {
		        return (data.cpuUsage > 0)
		    }
		}
	    ]
	}

モデル内のデータは、指定された列に基づいてフィルタリングすることが可能です。これにより、すべての列に対してではなく、選択的にフィルタを適用することが可能となります。この動作はfilterKeyColumnプロパティと同様であり、個々のフィルタごとに設定可能となるよう設計されています。なお、ここで参照される列インデックスは、視覚的なインデックスではなく論理的なインデックスに対応している点にご留意ください。


	SortFilterProxyModel {
	    id: sfpm
	    model: processModel
	    sorters: [
		RoleSorter {
		    roleName: "name"
                    column: 1 // Column number can be specified here
		}
	    ]
	    filters: [
		FunctionFilter {
		    component RoleData: QtObject { property qreal cpuUsage }
		    function filter(data: RoleData) : bool {
		        return (data.cpuUsage > 1)
		    }
		}
	    ]
	}

正規表現を用いたデータフィルタリング(setFilterRegularExpressionと同様の機能)は、FunctionFilter(および同様のQMLコンストラクトであるFunctionSorterによるソート)を通じて実現できます。この際、対象となるコンポーネント内で指定された対応するrole名を使用します。



    SortFilterProxyModel {
        id: sfpmProcessModel
        model: processModel
        sorters: [
            RoleSorter {
                roleName: "name"
                column: 1
            }
        ]
        filters: [
            FunctionFilter {
                id: filter
                component RoleData: QtObject {
                    property var pid
                    property var name
                    property var user
                    property var state
                    property var cpu
                }
                // JS regular expression comparision can be made as mentioned below
                property var regExp: new RegExp(searchField.text, "i")
                onRegExpChanged: invalidate()
                function filter(data: RoleData) : bool {
                    if (searchField.text === "")
                        return true
                    switch (filterCb.currentIndex) {
                    case 0: return (data.pid == parseInt(searchField.text))
                    case 1: return regExp.test(data.name)
                    case 2: return regExp.test(data.user)
                    case 3: return regExp.test(data.state)
                    case 4: return (data.cpu >= parseInt(searchField.text))
                    default: true
                    }
                }
            }
        ]
    }

SortFilterProxyModel のドキュメントには、対応するフィルターおよびソーターのプロパティに関する詳細な説明と、それに関連する情報が記載されております。SortFilterProxyModel は、データ操作においてQSortFilterProxyModel とほぼ同様のアルゴリズムを内部的に使用し、採用しております。この機能はバージョン 6.10 時点では技術プレビュー段階となりますため、フィードバックに基づき API の変更が生じる可能性がございます。あらかじめご了承ください。

今後の展望

今後のバージョンでは、ネストされたフィルターとソーターのサポートを検討しております。

現在のmodel属性はQAbstractItemModelのみをサポートしております。しかしながら、SortFilterProxyModel内のモデルの実際の型はQVariantとして選択されており、将来的にはJavaScript配列のサポートも拡張可能となります。

また、カスタムフィルターやソーターの定義は、現時点ではベースが内部仕様であるため不可能です。この要件に対応するため、将来的には公開される可能性があります。

本ブログをお読みいただきありがとうございます。本機能の改善に向けたご意見・ご感想をいただければ、大変参考になります。