我认为我在重绘 Widget并切换到Qt::QueuedConnection
修复问题时遇到了类似于 Qt 崩溃中的问题。但是,在我的情况下,信号发射器和接收器总是在同一个线程(主线程)中。
我有一个带有条目行的 QAbstractItemModel 和一个用于过滤的 QSortFilterProxyModel。由于模型可能非常大,所以我想在过滤时制作一个进度条。更新过滤器基本上是在连接到QAction::toggled
信号的插槽中执行此操作:
m_ProgressBar = new QProgressBar(); // Put into status bar etc.
auto connection = connect(m_filteredModel, SIGNAL(filterProgressChanged(int)), m_ProgressBar, SLOT(setValue(int)), Qt::QueuedConnection);
m_filteredModel->UpdateFilter();
delete m_ProgressBar;
disconnect(connection);
UpdateFilter 基本上做了一些内务处理,然后调用 invalidate,使过滤器模型重新查询filterAcceptsRow
每一行。然后过滤器模型在其中发出filterProgressChanged(int)
信号filterAcceptsRow
(通过递增计数器并除以源模型的行数来工作,并且仅在实际 int 进度值更改时发出)。UpdateFilter 在过滤完成时返回。直到那时进度条才被删除(已验证),所以我认为它应该可以工作。不删除进度条会导致每次调用都会获得一个新的进度条,但崩溃仍然是一样的。
一切都在主线程中完成:创建进度条,调用 UpdateFilter,发出filterProgressChanged
信号。但是,当连接创建为Qt::AutoConnection
,即直接时,它会在重新绘制进度条时崩溃(仅在禁用过滤器时,出于某种原因)。当我直接在我自己的事件处理程序中调用 setValue 时也会发生同样的情况,这是我在切换到当前代码之前所做的。
现在我有一个可行的解决方案,但我不明白为什么原始代码不起作用。我认为只有当信号的发送者和接收者在不同的线程中时,DirectConnection 才会产生实际的差异,但事实并非如此。您可以在堆栈跟踪中轻松看到所有事情都发生在同一个线程中,排队连接也是如此。
那么,原始代码出了什么问题呢?有什么我错过的吗?有没有办法从实际崩溃中获取更多信息?
我只发现 in void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
,state()
返回 0,并且代码假定它永远不会返回 0,这是直接崩溃的原因,但可能不是原因。堆栈跟踪指向绘画作为问题区域,这就是我在调试时所看到的。
我在使用 Qt 5.4.2(也尝试过 5.7)和 MSVC 2013 的 Windows 上,如果有的话。
编辑:根据 code_fodder 的要求,我添加了 UpdateFilter 并发出代码(actualFilterFunction 执行实际的过滤,但与信号或 GUI 或任何东西无关)。
void MyModel::UpdateFilter() {
m_filterCounter = 0;
m_lastReportedProgress = -1;
invalidate();
}
bool MyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
m_filterCounter++;
int progress = (100 * m_filterCounter) / m_sourceModel->rowCount();
if (progress != m_lastReportedProgress) {
emit filterProgressChanged(m_lastReportedProgress = progress);
}
return actualFilterFunction();
}