Issue with QApplication::processEvents()
-
The following code segfault with Qt 4.7.2. It looks to be a Qt bug. Any idea?
@
#include <QApplication>
#include <QMainWindow>
#include <QResizeEvent>
#include <QWidget>class View: public QWidget
{
public:
View(QWidget *parent): QWidget(parent) {};void resizeEvent(QResizeEvent*) { for (unsigned iv = 0; iv < 500; ++iv) qApp->processEvents(); }
};
int main(int argc, char** argv){
QApplication app(argc,argv);
QMainWindow mainWindow;
View view(&mainWindow);mainWindow.setCentralWidget(&view); mainWindow.show(); return (app.exec());
}
@ -
If you call processEvents() inside an event handler, you're at risk of infinite recursion -- processEvents() calls resizeEvent(), resizeEvent() calls processEvents(), processEvents() calls resizeEvent() again, and so on
-
Thanks for the reply. I printed out calls and found out that this is not the case: no recursive calls processEvents <-> resize event were detected. Also the code properly worked with the older versions of Qt. If it's prohibited now I think it should have been documented.
-
Where does the segfault occur?
Try allocating your View on the heap instead of on the stack. When QWidgets (and QObjects in general) are destroyed, they will destroy their children too. So, when the QMainWindow goes out of scope, it may try to delete your View -- causing it to be destroyed twice. I'm not 100% sure though.
Qt/C++ does not explicitly prohibit you from calling processEvents() within event handlers, but it is considered a dangerous practice.
-
let me explain. There is a very long drawing opration in the application. To make it more user responsive processEvents is called from the drawing loop to process other events like resizi event. So whenever application window is resized during the draw, resize event hadler is being called it starts drawing over again for the resized window. The sample I provided here is just a simplified model which reproduces the issue.
-
Does the segfault still occur if you allocate the View on the heap? (i.e. use new to create it)
-
[quote author="alex.araratyan" date="1356166469"]let me explain. There is a very long drawing opration in the application. To make it more user responsive processEvents is called from the drawing loop to process other events like resizi event. So whenever application window is resized during the draw, resize event hadler is being called it starts drawing over again for the resized window. The sample I provided here is just a simplified model which reproduces the issue. [/quote]
In the case you described processEvents would not be called from inside an event handler I think -
It will be called, because resize event handler calls the same drawing procedure which calls processEvents in its turn.
-
Segfault stil occurs when Views is allocated on the heap :-(
-
And here is the stack trace print out at the point of segfault, which happens upon window resizing:
(gdb) bt
#0 0x08751c9b in QVariantAnimationPrivate::valueAt(double) const ()
#1 0x08751d93 in QVariantAnimation::keyValueAt(double) const ()
#2 0x08751e1f in QVariantAnimation::startValue() const ()
#3 0x08754b64 in QPropertyAnimation::updateState(QAbstractAnimation::State, QAbstractAnimation::State) ()
#4 0x0874f92b in QAbstractAnimationPrivate::setState(QAbstractAnimation::State) ()
#5 0x08750187 in QAbstractAnimation::start(QAbstractAnimation::DeletionPolicy) ()
#6 0x0845e3f0 in QWidgetAnimator::animate(QWidget*, QRect const&, bool) ()
#7 0x086ce622 in QDockAreaLayout::apply(bool) ()
#8 0x08401772 in QMainWindowLayoutState::apply(bool) ()
#9 0x08405f35 in QMainWindowLayout::applyState(QMainWindowLayoutState&, bool) ()
#10 0x0840662b in QMainWindowLayout::setGeometry(QRect const&) ()
#11 0x08096e3f in QLayoutPrivate::doResize(QSize const&) ()
#12 0x0809831c in QLayout::widgetEvent(QEvent*) ()
#13 0x08078bfd in QApplicationPrivate::notify_helper(QObject*, QEvent*) ()
#14 0x0807dc94 in QApplication::notify(QObject*, QEvent*) ()
#15 0x088329cb in QCoreApplication::notifyInternal(QObject*, QEvent*) ()
#16 0x080d574d in QETWidget::translateConfigEvent(_XEvent const*) ()
#17 0x080e4fb8 in QApplication::x11ProcessEvent(_XEvent*) ()
#18 0x0810bedb in QEventDispatcherX11::processEvents(QFlagsQEventLoop::ProcessEventsFlag) ()
#19 0x08831c09 in QEventLoop::processEvents(QFlagsQEventLoop::ProcessEventsFlag) ()
#20 0x08831eaa in QEventLoop::exec(QFlagsQEventLoop::ProcessEventsFlag) ()
#21 0x088366cf in QCoreApplication::exec() ()
#22 0x08076c37 in QApplication::exec() ()
#23 0x080756ea in main (argc=2, argv=0xffff8224) at gdsview.cc:25 -
[quote author="alex.araratyan" date="1356166469"]let me explain. There is a very long drawing opration in the application. To make it more user responsive processEvents is called from the drawing loop to process other events like resizi event. So whenever application window is resized during the draw, resize event hadler is being called it starts drawing over again for the resized window. The sample I provided here is just a simplified model which rproduces the issue. [/quote]
The rule of thumb is: if the the drawing can't be done in time, it doesn't belong into <code>paintEvent()</code>. This includes the necessity of processEvents().
The usual solution is that drawing is not done directly to the widget surface, but rather to a QPixmap, which is then drawn to the widget surface in <code>paintEvent()</code> using for instance <code>QPainter::drawPixmap()</code>.
This has several advantages:
- the <code>paintEvent()</code> is quite fast,
- the widget content is only drawn when it has to be (the underlying data has changed); otherwise the cached QPixmap is used (for example during resize),
- the widget content can be drawn in a secondary thread and thus no longer blocks the main thread.
For an example see "C++ GUI Programming with Qt4 - Double Buffering":http://www.informit.com/articles/article.aspx?p=1405227&seqNum=4.
If you are out for advice invest your time in implementing a proper drawing strategy instead. The necessity of re-entering the event loop, especially in an event handler, is more often then not a clear indicator for a questionable design choice, and even if you solve the problem now, it might turn on you again later.
If you still think this is caused by undefined behaviour on the part of Qt please feel free to file a "bug report":https://bugreports.qt-project.org.
-
What currently is implemented is the following:
- there are two QPixmap off-screen surfaces
- one of them is used for actual drawing which sometimes takes long time
- by the end of drawing the content of the first QPixmap copied to the second one
- QPaintEvent used to render the content of the second one to the actual screen - QCanvas
- processEvents is used during drawing loop when drawing on the first QPixmap in order to quickly react to the user events like resize or zoom without waiting for the entire drawing. Then if during drawing resize event is processed its handler starts drawing again which leads to the situation when processEvent is being called from the event handler (resizeEvent).
So, if this is not good then the only way I can see is to perform drawing in the thread. But as I learned from Qt reference QPixmap cannot be used from the thread and QImage should be used instead. On the other hand Qt reference recommends QPixmap of off-screen drawing. So what is the solution of this contradiction?
-
After some more research now I realize that the application should be redesigned to implement drawing in non-gui thread and to use drawing on QImage instead of QPixmap. But when redesigning application to use non-gui thread for drawing it appears that we can’t use QBrush::setTexture(QPixmap) method any more, since QPixmap can’t be used from non-gui thread. Any idea how we can use pattern based brush for non-gui thread drawing?
-
Hi,
I didn't tried it but wouldn't be "setTextureImage":http://qt-project.org/doc/qt-4.8/qbrush.html#setTextureImage an alternative ?