不能仅“解冻”输入小部件。输入被冻结是因为您很长一段时间没有将控件返回到事件循环,因此整个GUI被冻结。
排队的连接没有帮助,因为您在一个批处理中运行所有代码。
最简单的方法是使用过滤代理模型。
class myViewArea : .... {
QSortFilterProxyModel viewModel;
...
};
myViewArea::myViewArea(...) {
...
viewModel.setSourceModel(&model);
viewModel.setFilterKeyColumn(...); // the column holding the name
viewModel.setFilterCaseSensitivity(false);
setModel(&viewModel);
}
void myViewArea::applyItemsFilter_viewModel(const QString &needle) {
// allows all when needle is empty
NoUpdates noUpdates(this);
viewModel.setFilterFixedString(needle);
}
class NoUpdates {
Q_DISABLE_COPY(NoUpdates)
QWidget *const w;
bool const prev = w->updatesEnabled();
public:
NoUpdates(QWidget *w) : w(w) { w->setUpdatesEnabled(false); }
~NoUpdates() { w->setUpdatesEnabled(prev); }
};
或者,您需要使代码不会阻塞事件循环很长时间。一种方法是与主线程协同运行搜索。索引最好在最小化重绘成本的方向上迭代,即总是从模型的部分开始,由处理过的行定义,该部分包含最多的项。
void myViewArea::applyItemsFilter_gui(const QString &needle)
{
bool inFirstHalf = rowAt(0) <= model.rowCount()/2;
// iterate backwards in the first half of the rows
int dir = inFirstHalf ? -1 : +1;
int i = dir > 0 ? model.rowCount()-1 : 0;
auto isValidRow = [this, dir](int i){
return (dir > 0 && i < model.rowCount()) || i >= 0;
};
runCooperatively(this, [this, needle, isValidRow, i, dir,
n = NoUpdates(this)]() mutable
{
if isValidRow (i) do {
setRowVisible(i, needle.isEmpty() || model.isMatched(i, needle), this);
i += dir;
} while isRowInViewport(i); // update all visible rows in one go
return isValidRow(i);
});
}
bool myViewArea::isRowInViewport(int row) const {
auto first = indexAt(viewport()->rect().topLeft());
auto last = indexAt(viewport()->rect().bottomRight());
return row >= 0 && row < model.rowCount()
&& row >= first.row() && (!last.isValid() || row <= last.row());
}
void myViewArea::applyItemsFilter_concurrent(const QString &needle)
{
auto visible = QtConcurrent::mapped(getNames(),
[needle](const QString &name){ return isMatched(needle, name); });
runCooperativelyAfter(this, future, [this, visible, i = 0,
n = NoUpdates(this)]() mutable
{
if (i >= rowCount() || i >= visible.resultCount()) return false;
setRowVisible(i, visible.resultAt[i], this);
return ++i;
});
}
前面两种方法在代码外观上相当相似,并且是以连续传递的方式编写的。当然,如果有联谊会就好得多了——这是一件必须做的事。
以最通用的方式协同运行代码可以执行以下操作:
/// Runs a functor cooperatively with the event loop in the context object's
/// thread, as long as the functor returns true. The functor will run at least
/// once, unless the context object gets destroyed before control returns to
/// event loop.
template <class Fun>
static void runCooperatively(QObject *context, Fun &&fun) {
QMetaObject::invokeMethod(context, [context, f = std::forward<Fun>(fun)]{
auto *hook = new QTimer(context);
QObject::connect(hook, &QTimer::timeout, [hook, fun = std::forward<Fun>(f)]{
if (!fun()) hook->deleteLater();
});
hook->start();
});
}
/// Runs a functor cooperatively after a future is completed
template <class Fun, typename Res>
static void runCooperativelyAfter(QObject *ctx, const QFuture<Res> &future, Fun &&fun) {
auto *watcher = new QFutureWatcher<Res>(ctx);
watcher->setFuture(future);
QObject::connect(watcher, &QFutureWatcher::finished,
[future, ctx, f = std::forward<Fun>(fun) {
future->deleteLater();
runCooperatively(ctx, [fun = std::forward<Fun>(f)]{ fun(); });
}
);
}
其他共享功能如下:
// Note: an empty needle would match per QString search semantics,
// but it's unexpected - better to make it explicit so that
// a maintainer doesn't have to dig in the documentation.
// ***Static Method***
bool myModel::isMatched(const QString &needle, const QString &name)
{
assert(!needle.isEmpty());
return name.contains(needle, Qt::CaseInsensitive);
}
bool myModel::isMatched(const int row, const QString &needle) const
{
return row >= 0 && row < items.size() &&
isMatched(needle, items.at(row).name);
}
QStringList myModel::getNames() const
{
// The below should be fast enough, but let's time it to make sure
return time([this]{
QStringList names;
names.reserve(items.size());
for (auto &item : items) names.push_back(item.name);
return names;
});
}
template <class C> static void setRowVisible(int row, bool vis, C *obj) {
obj->setRowHidden(row, !vis);
}
template <class Fun>
static typename std::result_of<Fun()>::type time(const Fun &code) {
struct Timing {
QElapsedTimer timer;
Timing () { timer.start(); }
~Timing () { qDebug() << timer.elapsed(); }
} timing;
return code();
}
你的原始代码有很多双重颠倒的逻辑,这使得你很难理解发生了什么。是的,三元算子的发现会使人头晕,但我明白:但是这种复杂是无偿的,因为C++有短路评估,而三元运算体操是不必要的。