Why isn't my repaint() shown?
-
I've got some code in my QDialog sub-class that is triggered when a QFuture finishes:
connect(this, &QualityChart::showEccentricity, this, &QualityChart::plotEccentricity, Qt::QueuedConnection); connect(this, &QualityChart::showFWHM, this, &QualityChart::plotFWHM, Qt::QueuedConnection); void QualityChart::interpolationFinished() { interpolating = false; progressBar->reset(); message->setText(tr("Calculating contour plot. Please be patient.")); update(); QCoreApplication::processEvents(); if (!cancelled) { ZTRACE_RUNTIME("Interpolation complete"); // // Show the appropriate chart // if (radioEccentricity->isChecked()) { QTimer::singleShot(100, [this]() { emit showEccentricity(); }); } else { QTimer::singleShot(100, [this]() { emit showFWHM(); }); } } else { ZTRACE_RUNTIME("Interpolation cancelled"); reject(); } }I was expecting the update to the message and the progress bar to be handled before the moderately slow code connected to the emitted signals was run, but that's not what happens, the results of (e.g. plotEccentricity() are displayed and the message update and progress bar update only happen then.
I thought that by invoking e.g. plotEccentricity using a single shot would allow the update to be proceessed but clearly not.
What am I missing here?
Thanks David
-
I've got some code in my QDialog sub-class that is triggered when a QFuture finishes:
connect(this, &QualityChart::showEccentricity, this, &QualityChart::plotEccentricity, Qt::QueuedConnection); connect(this, &QualityChart::showFWHM, this, &QualityChart::plotFWHM, Qt::QueuedConnection); void QualityChart::interpolationFinished() { interpolating = false; progressBar->reset(); message->setText(tr("Calculating contour plot. Please be patient.")); update(); QCoreApplication::processEvents(); if (!cancelled) { ZTRACE_RUNTIME("Interpolation complete"); // // Show the appropriate chart // if (radioEccentricity->isChecked()) { QTimer::singleShot(100, [this]() { emit showEccentricity(); }); } else { QTimer::singleShot(100, [this]() { emit showFWHM(); }); } } else { ZTRACE_RUNTIME("Interpolation cancelled"); reject(); } }I was expecting the update to the message and the progress bar to be handled before the moderately slow code connected to the emitted signals was run, but that's not what happens, the results of (e.g. plotEccentricity() are displayed and the message update and progress bar update only happen then.
I thought that by invoking e.g. plotEccentricity using a single shot would allow the update to be proceessed but clearly not.
What am I missing here?
Thanks David
-
@Perdrix You should not block the Qt event loop. Using processEvents() is a dirty work around which may or may not work. Move long running calculations into a thread.
@jsulm https://forum.qt.io/topic/163776/
He does not care
-
I do very much care, but found that I can't move the invocation of QwtPlot::replot() into a thread. I tried.
@Perdrix
I don't know the details of your situation. However, I assumeQwtPlot::replot()causes a UI update. The Qt rule is: you must not attempt to update the UI from any secondary thread, only the main one.What you are supposed to do is move all calculations to a secondary thread. Then maybe have that emit a signal when done. At that point the main thread has a slot on that and does any updates or repaints. The intention is that time consuming calculations are removed from the main thread, hopefully making it "smoother", though the redraw has to be from main. Is that what you are doing?
-
Sure, I totally get that, and I've done precisely that for the interpolation code. But QwtPlot::replot() is not in my control and does block the event loop (it uses QFuture::waitForFinished() rather than using a QFutureWatcher and handling the
finishedsignal). -
Sure, I totally get that, and I've done precisely that for the interpolation code. But QwtPlot::replot() is not in my control and does block the event loop (it uses QFuture::waitForFinished() rather than using a QFutureWatcher and handling the
finishedsignal).@Perdrix
Good that you know about UI and threads. I don't get the stuff aboutQwtPlot::replot()and it usingQFuture::waitForFinished(), I don't see evidence for that. Nor do I see where you call it in your code. Unless you mean there is a call to it elsewhere in your own project, and that useswaitForFinished()? In which case you may need to alter that code? That's all I know. -
As I said Qwt isn't in my control. It's an external library. The code that was connected to showXXXX signals called QwtPlot::replot.
@Perdrix
I get that but you wroteBut QwtPlot::replot() is not in my control and does block the event loop (it uses QFuture::waitForFinished() rather than using a QFutureWatcher and handling the finished signal).
I don't get the
QFuturepart, you haven't explained. What uses "QFuture::waitForFinished()rather than ..."? The code ofQwtPlot::replot()from Qwt people? I don't see that. Code in an external library callsQwtPlot::replot()and also has its ownQFuture::waitForFinished(), is that what you are saying? I don't know, to me it's most unclear. -
QwtPlot::replot() is part of the Qwt library which I am using. For a spectrogram plot that eventually calls QwtPlotRasterItem::compose() which does:
QVector< QFuture< void > > futures; futures.reserve( numThreads - 1 ); for ( uint i = 0; i < numThreads; i++ ) { QRect tile( 0, i * numRows, image.width(), numRows ); if ( i == numThreads - 1 ) { tile.setHeight( image.height() - i * numRows ); qwtToRgba( &image, &alphaImage, tile, m_data->alpha ); } else { futures += QtConcurrent::run( &qwtToRgba, &image, &alphaImage, tile, m_data->alpha ); } } for ( int i = 0; i < futures.size(); i++ ) futures[i].waitForFinished();which completely blocks the Qt event loop until it finishes.
-
QwtPlot::replot() is part of the Qwt library which I am using. For a spectrogram plot that eventually calls QwtPlotRasterItem::compose() which does:
QVector< QFuture< void > > futures; futures.reserve( numThreads - 1 ); for ( uint i = 0; i < numThreads; i++ ) { QRect tile( 0, i * numRows, image.width(), numRows ); if ( i == numThreads - 1 ) { tile.setHeight( image.height() - i * numRows ); qwtToRgba( &image, &alphaImage, tile, m_data->alpha ); } else { futures += QtConcurrent::run( &qwtToRgba, &image, &alphaImage, tile, m_data->alpha ); } } for ( int i = 0; i < futures.size(); i++ ) futures[i].waitForFinished();which completely blocks the Qt event loop until it finishes.