通过 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.
相关文章