Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
881 views
in Technique[技术] by (71.8m points)

qt - Sort and filter a C++ model via QML functors?

I have a polymorphic (as in arbitrary roles) QObject model that is mostly instantiated declaratively from QML, as in this answer, and I would like to be able to have custom data "views" that sort and filter the model via arbitrary, and potentially - runtime generated from code strings JS functors, something like that:

  DataView {
    sourceModel: model
    filter: function(o) { return o.size > 3 }
    sort: function(a, b) { return a.size > b.size }
  }

The QSortFilterProxyModel interface doesn't seem to be particularly well suited to the task, instead being fixated on static roles and pre-compiled rules.

I tried using QJSValue properties on the C++ side, but it seems like it is not possible, the C++ code just doesn't compile with that property type. And if I set the property type to QVariant I get error messages from QML that functions can only be bound to var properties. Evidently, var to QVariant conversion doesn't kick in here as it does for return values.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As you mentionned, you could use QJSValue. But that's pretty static. What if you want to use a filter like filter: function(o) { return o.size > slider.value; } with a dynamic slider ? You'll have to manually call invalidateFilter().

As a more practical alternative, you could instead use QQmlScriptString as a property & QQmlExpression to execute it. Using QQmlExpression allows you to be notified of context changes with setNotifyOnValueChanged.

Your syntax would change to be like so : filter: o.size > slider.value.

If you are looking for an out of the box solution, I've implemented this in a library of mine : SortFilterProxyModel on GitHub

You can take a look at ExpressionFilter & ExpressionSorter, those do the same as what you initially wanted. You can check the complete source code in the repo.

How to use it :

import SortFilterProxyModel 0.2

// ...

SortFilterProxyModel {
    sourceModel: model
    filters: ExpressionFilter  { expression: model.size > 3 }
    sorters: ExpressionSorter { expression: modelLeft.size < modelRight.size }
}

But as @dtech mentionned, the overhead of going back and forth between qml and c++ for each row of the model is quite noticeable. That's why I created more specific filters and sorters. In your case, we would use RangeFilter and RoleSorter :

import SortFilterProxyModel 0.2

// ...

SortFilterProxyModel {
    sourceModel: model
    filters: RangeFilter  {
        roleName: "size"
        minimumValue > 3
        minimumInclusive: true
    }
    sorters: RoleSorter { roleName: "size" }
}

Doing like this, we have a nice declarative API and the parameters are only passed once from qml to c++. All the filtering and sorting is then entirely done on the c++ side.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...