我认为我遇到了与Qt crash when redrawing Widget类似的问题,并切换到Qt::QueuedConnection
解决了问题。但是,就我而言,信号发射器和接收器总是在同一个线程(主线程)中。
我有一个带有入口行的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只会在信号的发送方和接收方处于不同的线程中时产生实际的差异,但它们并非如此。您可以在堆栈跟踪中轻松地看到所有内容都发生在同一个线程中,对于排队连接,情况也是如此。
那么,原始代码出了什么问题?有什么我错过的吗?有没有办法从实际崩溃中获取更多信息?
我只发现在void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
中,state()
返回0,代码假定它永远不会返回0,这是直接崩溃的原因,但可能不是原因。堆栈跟踪指向绘画作为问题区域,这是我在调试时所看到的。
我在Windows上使用Qt 5.4.2(也尝试过5.7),以及使用MSVC 2013,如果有任何重要的话。
编辑:根据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();
}