I think I'm having a problem similar to the one in Qt crash when redrawing Widget, and switching to Qt::QueuedConnection
fixed the problem. However, in my case, both the signal emitter and received are always in the same thread (the main thread).
I have a QAbstractItemModel with entry rows, and a QSortFilterProxyModel for filtering. Since the model can be very large, I wanted to make a progress bar when filtering. Updating the filter basically does this in a slot that is connected to a QAction::toggled
signal:
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 basically does some housekeeping and then calls invalidate, making the filter model requery filterAcceptsRow
for every row.
The filter model then emits the filterProgressChanged(int)
signal within filterAcceptsRow
(works by incrementing a counter and dividing by the source model's row count, and is only emitted when the actual int progress value changes).
UpdateFilter returns when the filtering is complete. The progress bar is not deleted until then (verified), so it should work in my opinion. Not deleting the progress bar leads to getting a new one every call, but the crash is still the same.
Everything is done in the main thread: Creating the progress bar, calling UpdateFilter, emitting the filterProgressChanged
signal. However, when the connection is created as Qt::AutoConnection
, i.e. direct, it crashes (only when disabling the filter, for some reason) within repainting the progress bar. The same happens when I call setValue directly in my own event handler, which is what I did prior to switching to the current code.
Now I have a solution that works, but I don't understand why the original code does not work. I thought that a DirectConnection would only make an actual difference when sender and receiver of the signal are in different threads, but they're not. You can easily see in the stack trace that everything happens within the same thread, and that is even true with the queued connection.
So, what's going wrong in the original code? Is there something I just missed? Is there any way to get more information out of the actual crash?
I only found out that in void QRasterPaintEngine::clip(const QRect &rect, Qt::ClipOperation op)
, state()
returns 0, and the code assumes it never returns 0, which is the immediate crash reason, but likely not the reason. And the stack trace points to painting as the problem area, that's all I saw when debugging this.
I'm on Windows with Qt 5.4.2 (also tried 5.7), and with MSVC 2013, if any of that matters.
Edit: As requested by code_fodder, I added the UpdateFilter and emit code (actualFilterFunction performs the actual filtering, but has nothing to do with signals or GUI or anything).
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();
}