通过 QML 函子对 C++ 模型进行排序和过滤?
我有一个多态(如任意角色)QObject
模型,它主要从 QML 中以声明方式实例化,就像在这个答案中一样,我希望能够拥有自定义数据视图",通过任意的方式对模型进行排序和过滤,并且可能 - 从代码字符串 JS 函子生成的运行时,例如:
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 }
}
QSortFilterProxyModel
接口似乎不是特别适合该任务,而是专注于静态角色和预编译规则.
The QSortFilterProxyModel
interface doesn't seem to be particularly well suited to the task, instead being fixated on static roles and pre-compiled rules.
我尝试在 C++ 端使用 QJSValue
属性,但似乎不可能,C++ 代码无法使用该属性类型进行编译.如果我将属性类型设置为 QVariant
,我会从 QML 收到错误消息,指出函数只能绑定到 var
属性.显然,var
到 QVariant
的转换不会像返回值那样在这里进行.
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.
推荐答案
更新:
重新审视这个问题,我终于找到了一个最终的解决方案,所以我决定放弃一些更新.一、相关代码:
Revisiting the issue, I finally came with a finalized solution, so I decided to drop in some updates. First, the relevant code:
void set_filter(QJSValue f) {
if (f != m_filter) {
m_filter = f;
filterChanged();
invalidate();
}
}
void set_sorter(QJSValue f) {
if (f != m_sort) {
m_sort = f;
sorterChanged();
sort(0, Qt::DescendingOrder);
}
}
bool filterAcceptsRow(int sourceRow, const QModelIndex & sourceParent) const {
if (!m_filter.isCallable()) return true;
QJSValueList l;
l.append(_engine->newQObject(sourceModel()->index(sourceRow, 0, sourceParent).data().value<QObject*>()));
return m_filter.call(l).toBool();
}
bool lessThan(const QModelIndex & left, const QModelIndex & right) const {
if (!m_sort.isCallable()) return false;
QJSValueList l;
l.append(_engine->newQObject(sourceModel()->data(left).value<QObject*>()));
l.append(_engine->newQObject(sourceModel()->data(right).value<QObject*>()));
return m_sort.call(l).toBool();
}
我发现此解决方案比 QQmlScriptString & 更简单、更安全且性能更好.QQmlExpression duo 确实提供通知的自动更新,但正如 GrecKo 回答下面的评论中已经详细说明的那样,它有点不稳定,不值得.
I found this solution to be simpler, safer and better performing than the QQmlScriptString & QQmlExpression duo, which does offer automatic updates on notifications, but as already elaborated in the comments below GrecKo's answer, was kinda flaky and not really worth it.
获取外部上下文属性更改自动更新的技巧是在返回实际函子之前简单地引用它们:
The hack to get auto-updates for external context property changes is to simply reference them before returning the actual functor:
filter: { expanded; SS.showHidden; o => expanded && (SS.showHidden ? true : !o.hidden) }
这是一个使用新的速记函数语法的简单表达式,它引用了expanded;SS.showHidden;
为了在这些变化时触发重新评估,然后隐式返回函子
Here is a simple expression using the new shorthand function syntax, it references expanded; SS.showHidden;
in order to trigger reevaluations if those change, then implicitly returns the functor
o =>扩展&&(SS.showHidden ? true : !o.hidden)
类似于:
return function(o) { return 展开 &&(SS.showHidden ? true : !o.hidden) }
根据父节点是否展开、子节点是否隐藏以及隐藏对象是否仍然显示来过滤对象.
which filters out objects based on whether the parent node is expanded, whether the child node is hidden and whether hidden objects are still displayed.
此解决方案无法自动响应对 o.hidden
的更改,因为 o
在评估时被插入到函子中,并且无法在绑定中引用表达式,但这可以在需要动态响应此类更改的视图委托中轻松实现:
This solution has no way to automatically respond to changes to o.hidden
, as o
is inserted into the functor upon evaluation and can't be referenced in the binding expression, but this can easily be implemented in the delegates of views that need to dynamically respond to such changes:
Connections {
target: obj
onHiddenChanged: triggerExplicitEvaluation()
}
请记住,用例涉及无模式/单个 QObject*
角色 model 促进了变形数据模型,其中模型项数据通过 QML 属性实现,因此这里没有任何角色或正则表达式股票过滤机制适用,但同时,这提供了使用单一机制来实现基于任何标准和任意项目数据的排序和过滤的通用性,尽管我最初担心,但性能非常好.它没有实现排序顺序,只需简单地翻转比较表达式结果即可轻松实现.
Remember that the use case involves a schema-less / single QObject*
role model that facilitates a metamorphic data model where model item data is implemented via QML properties, so none of the role or regex stock filtering mechanisms are applicable here, but at the same time, this gives the genericity to use a single mechanism to implement sorting and filtering based on any criteria and arbitrary item data, and performance is very good, despite my initial concerns. It doesn't implement a sorting order, that is easily achievable by simply flipping the comparison expression result.
相关文章