Finding GUI Event-Handling Crashes
-
This morning I added a post about a crash I'm trying to find (see "Read access violation in QAbstractItemView::event -> QAbstractScrollArea::event -> QPainter::QPainter -> QPainter::begin"). Since there have been no replies I figure either nobody has any ideas on how I should go about finding the cause of the crash, or my post was too wordy/long for anyone to read.
In case it is the second of the two, I'll resubmit in shorter form.
I'm getting an intermittent crash in Qt event processing for a QEvent::Paint. The crash is happening in the begin method of a QPainter object that's being instantiated by a QAbstractScrollArea object.
The crash happens during QPainter::begin when the QPaintDevice* parameter is assigned an invalid pointer by a QPaintDevice->redirected call.
It looks like the redirected method returns a QPaintDevice* that has been deleted in garbage collection.
Question: Is there a good place to set a breakpoint in the Qt code to try to find where/who/why the redirected QPaintDevice pointer is becoming invalid?
Background Summary:
-
I didn't write the program - I've just been tasked with debugging it
-
Platform: Windows XP (originally), now Windows 7, 8 & 10
-
Qt version: 5.2.1
-
VS studio used: VS 2008, C++ Solution, uses MFC in a shared DLL, not using ATL, uses Multi-Byte character set, links in lots of libraries but for Qt: Qt5Core.lib, Qt5Gui.lib, Qt5Sql.lib, Qt5Network.lib, Qt5Widgets.lib
-
How you installed Qt. ( self compiled, from Qt online installer ): I don't know. It's included as a distribution project (submodule) via git/Gerrit. You check out this program and it pulls in Qt header files and DLL's.
See my earlier post for a more detailed explanation: https://forum.qt.io/topic/74952/read-access-violation-in-qabstractitemview-event-qabstractscrollarea-event-qpainter-qpainter-qpainter-begin
Stack Trace Follows...thanks in advance for any suggestions you can supply!
Stack Trace:
Qt5Guid.dll!QPainter::begin(QPaintDevice * pd=0x0d1d050c) Line 1726 + 0xd bytes
Qt5Guid.dll!QPainter::QPainter(QPaintDevice * pd=0x101de450) Line 1469
Qt5Widgetsd.dll!QAbstractScrollArea::event(QEvent * e=0x0018c5bc) Line 1017
Qt5Widgetsd.dll!QAbstractItemView::event(QEvent * event=0x0018c5bc) Line 1623
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver=0x101de448, QEvent * e=0x0018c5bc) Line 3482 + 0x11 bytes
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver=0x101de448, QEvent * e=0x0018c5bc) Line 3447 + 0x10 bytes
Qt5Cored.dll!QCoreApplication::notifyInternal(QObject * receiver=0x101de448, QEvent * event=0x0018c5bc) Line 881 + 0x15 bytes
Qt5Cored.dll!QCoreApplication::sendSpontaneousEvent(QObject * receiver=0x101de448, QEvent * event=0x0018c5bc) Line 235 + 0x38 bytes
Qt5Widgetsd.dll!QWidgetPrivate::drawWidget(QPaintDevice * pdev=0x0d1d050c, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5138 + 0xe bytes
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x0000001a, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5333
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x0000001c, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5320
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x0000001d, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5320
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x0000001e, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5320
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x0000001f, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5320
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x00000020, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5320
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x00000021, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5320
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x00000022, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5320
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x00000023, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5320
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x00000024, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5320
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x00000025, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5320
Qt5Widgetsd.dll!QWidgetPrivate::paintSiblingsRecursive(QPaintDevice * pdev=0x0d1d050c, const QList<QObject *> & siblings={...}, int index=0x00000028, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000004, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5320
Qt5Widgetsd.dll!QWidgetPrivate::drawWidget(QPaintDevice * pdev=0x0d1d050c, const QRegion & rgn={...}, const QPoint & offset={...}, int flags=0x00000005, QPainter * sharedPainter=0x00000000, QWidgetBackingStore * backingStore=0x102a8ee0) Line 5191
Qt5Widgetsd.dll!QWidgetBackingStore::sync() Line 1084
Qt5Widgetsd.dll!QWidgetPrivate::syncBackingStore() Line 1688
Qt5Widgetsd.dll!QWidget::event(QEvent * event=0x0d4e6d60) Line 8237
Qt5Widgetsd.dll!QApplicationPrivate::notify_helper(QObject * receiver=0x0d2573e8, QEvent * e=0x0d4e6d60) Line 3482 + 0x11 bytes
Qt5Widgetsd.dll!QApplication::notify(QObject * receiver=0x0d2573e8, QEvent * e=0x0d4e6d60) Line 3447 + 0x10 bytes
Qt5Cored.dll!QCoreApplication::notifyInternal(QObject * receiver=0x0d2573e8, QEvent * event=0x0d4e6d60) Line 881 + 0x15 bytes
Qt5Cored.dll!QCoreApplication::sendEvent(QObject * receiver=0x0d2573e8, QEvent * event=0x0d4e6d60) Line 232 + 0x39 bytes
Qt5Cored.dll!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver=0x00000000, int event_type=0x00000000, QThreadData * data=0x059b2bc8) Line 1485 + 0xd bytes
Qt5Cored.dll!QCoreApplication::sendPostedEvents(QObject * receiver=0x00000000, int event_type=0x00000000) Line 1343 + 0x11 bytes
qwindowsd.dll!0f0ee6a1()
[Frames below may be incorrect and/or missing, no symbols loaded for qwindowsd.dll]
Qt5Cored.dll!qt_internal_proc(HWND__ * hwnd=0x002106ee, unsigned int message=0x00000401, unsigned int wp=0x00000000, long lp=0x00000000) Line 423
user32.dll!760962fa()
user32.dll!76096d3a()
user32.dll!76096ce9()
user32.dll!760a0d3f()
user32.dll!760a853a()
mfc90d.dll!AfxActivationWndProc(HWND_ * hWnd=0x002106ee, unsigned int nMsg=0x00000401, unsigned int wParam=0x00000000, long lParam=0x00000000) Line 463 + 0x1a bytes
user32.dll!760962fa()
user32.dll!76096d3a()
user32.dll!76096ce9() -
-
We faced similar intermittent crash. Issue was in compatibility wit library. We were using C++ library written by us. We kept on adding new functions to library. But did not change header file while compiling. Since we were not using new functions it did not give issue. After changing appropriate header file it worked fine.
-
@JohnF-n-Kansas you also have to take into account that this is a community forum so having answers several hours to one day later is to be expected since people coming here lives in several different timezones.
-
@SGaist -- oh, fair enough, and thank you for pointing that out. I definitely understand that scenario. We have offices on the other side of the world where emails sent/received betwixt us will be a full 24-hour response cycle at a minmum.
In this case I was simply making sure I hadn't written so verbosely or ambiguously that I scared a potential lifeline away. :)
-
@dheerendra - was your crash similar in that it was a bad or invalid (like deallocated) redirected QPaintDevice pointer in the handling of a Paint event? Each time this particular crash happens it appears to be from the same cause in the same location of QPainter::begin.
Also with respect to the library, was your library essentially a Qt wrapper? In this case the application does have Qt-derived dialogs, containers, and other classes, but they are recompiled each time you build the application. The Qt runtime resides in libraries (.lib files) that are statically-linked at link-edit. I do know what you mean regarding changes to a C++ object's runtime memory footprint and how that can cause a lot of weird things.
I know I've also seen weird crashes if you mix libraries or DLL's built with different versions of Visual Studio (or C++ runtimes), but I don't believe that's the case here.
-
@JohnF-n-Kansas Better verbose than
doesn't work
;) -
Maybe I could ask this another way that would be more effective.
The crash appears to be the setting of the QPaintDevice to a an invalid pointer to a redirected QPaintDevice.
Here's a couple of questions that might be enough for me to track this down:
-
Why does Qt use a redirected QPaintDevice and in what cases would Qt redirect a QPaintDevice?
-
Is it common for Qt to destroy (delete) and re-instantiate a QPaintDevice?
-
If so, what events would tend to cause the recycling of redirected QPaintDevice's?
I could create mutex logic all over the place, but I'm thinking maybe if I know what I'm trying to prevent I could be a bit more savvy in how to find it and prevent it from crashing the application.
Thanks!
-
-
- From the doc:
Sometimes it is desirable to make someone else paint on an unusual QPaintDevice. QPainter supports a static function to do this, setRedirected(). Warning: When the paintdevice is a widget, QPainter can only be used inside a paintEvent() function or in a function called by paintEvent().
- From a quick look at the Qt sources, I'd say no.
- Misbehaving code ;) More seriously, 0 doesn't mean it was delete, you may not have a redirected object and that would be normal e.g. an QOpenGLWindow which doesn't have the current context..
-
-
I had seen the setRedirected() documentation and searched the source code (non-Qt sources, i.e. the code written here) to see if the app was intentionally doing this (and in a non-safe way), but couldn't find it, and didn't see any attempted chicanery on the part of the app to try to tweak the output device. That said I'll set a breakpoint in the setRedirected() entrypoint to see who's calling it.
-
Thank you for checking that!
-
Right, however, the 0xfdfd and 0xabababab in the member variables which looked like possibly memory clearing constants...e.g., I believe the C++ Windows runtime memory management code sets unused variables to strings of 0xcd, but if those values didn't jump out at anyone then my presumption about deallocated-memory-filling was wrong. Interestingly yesterday afternoon I did notice in what appeared to be adjacent memory those same values, so maybe there is some kind of unintended memory corruption or maybe even a pointer++ that I haven't seen. My entire premise was that the original author decided it would be a good idea to use Qt, or maybe had an intellectual curiosity about Qt, and thus cobbled together a chunk of code that "seems to work" but violates some of the Qt "no no's" in regards to stack variables, heap variables, or something along those lines.
I really appreciate your responses. Thank you for taking time to do so!
-
-
One thing I would do, since you can't rewrite everything is to first cleanup the model related code. If the model is properly implemented, it should trigger the update to the view without additional help even if it's modified from somewhere else it the code.
That might help clear the issue you are having.
-
Thank you. I definitely believe that would help even if all it did was reduce the fog factor. By the same token, because of the size of the app and the way it's been implemented, correcting flow and components to be MVC/Delegate-pattern compliant is a bigger job than the return on the investment to do that. Users like the Find dialog and the functionality it provides. They just want it to stop crashing. If I could figure out a way to quickly/inexpensively rewrite or re-architect I certainly would, but I just can't get there painlessly.
So I'm hoping to find (or stumble over) badly-written code using pointers or stack space incorrectly, or maybe code that simply needs to be wrapped in a mutex. Since this beast works most of the time, I'm inclined to think the crashes occur because of an race condition or bad memory or thread misuse; I'm hoping I can find that place and make a small tweak that circumvents the issue.
A couple of general usage questions that may help.
-
Is there anything wrong in declaring a CWnd-derived class member variable as a QPointer variable declared as:
QPointer<DlgFindObjectU> m_pFindDialog;
The View class, which is where the main app displays objects, is not a Qt class but rather a CView class that's derived off an MFC CWnd class. There's a method that does the following which is called from code scattered all over and around the application:
if ( ! m_pFindDialog.isNull() ) { m_pFindDialog->Restart(); ...
Note that this code could be getting called from threads other than the main thread.
-
When Find is clicked from the main window, a new instance is created as follows -- from purely a Qt standpoint, does the following look okay:
// Use a new "win" to prevent "Debug Assertion Failed!" in the end.
QWinWidget *win = new QWinWidget(theApp.GetMainWnd());
win->showCentered();
m_pFindDialog = new DlgFindObjectU( this, win );
m_pFindDialog->setAttribute(Qt::WA_DeleteOnClose);
m_pFindDialog->show();
m_pFindDialog->Restart();
Note that the creation of the instance of the Qt Find dialog is happening on the Main (GUI) thread, but it happens each time you click the "Find" menu option. The variable named theApp is a staticly-declared instance of the main application which is derived from CWinApp and utlimately becomes the "parent" object in the QDialog constructor. The DlgFindObjectU is derived from QDialog.
-
is it acceptable to allocate the Find dialog as a member variable in a CWnd object, then fire off calls like the following that could at times possibly originate from background threads in the CWnd-derived class:
m_pFindDialog->Restart();
which then calls:
void DlgFindByObjectU::Restart() { ScheduleDialogRestart(); }
which then calls the following code that gets generated in the moc_DlgFindByObjectU.cpp file:
// SIGNAL 1 void DlgFindByObjectNameU::ScheduleDialogRestart() { QMetaObject::activate(this, &staticMetaObject, 1, 0); }
Is that kind of usage okay? Or should it move the request over to the GUI thread, akin to the way Windows Forms apps do an Invoke() on Windows?
I searched to determine if there's anything problematic with what's being done, but I couldn't find anything obvious. If I did then I missed it when reading.
Thanks for any comments you have time for and are willing to provide!
-
-
Warning: I haven't use the QtWinMigrate.
That said, the way it is used looks a bit weird.
- AFAIK, there's nothing wrong in having a QPointer member of a class however, the dialog has the
WA_DeleteOnClose
attribute set so I fail to see the usefulness of that member. - From a quick look at the example from the QtWinMigrate module, it looks a bit fishy.
- That restart call doesn't look clean at all. By the way, is it really activate ? I can't find it in Qt 4 nor Qt 5 QMetaObject doc.and I don't have a Qt 3 handy to check.
Everything involving a GUI related activity should be called on the GUI thread however model related stuff can be multithreaded as long as you put in place the proper protections.
I can understand the motivation of creating that dialog only once but the implementation doesn't look to be doing that.
I'd rather start by cleaning up the dialog handling itself using a dummy QDialog just to ensure that showing, dismissing and showing again works correctly. Then I'd gradually add back the searching functionality.
- AFAIK, there's nothing wrong in having a QPointer member of a class however, the dialog has the
-
I know this is old, but I had to park this and just came back to it recently.
I got the okay to do some remodeling and I took your advice to make some major changes. I've reduced the emission of UI-updates, and I'm now using a QThread to get all the items and sort them and then signal back to the UI thread when complete.
Everything is working really well except for one quirky issue that I'm guessing is due to view/model violations. I'm not deeply familiar with Qt's rules of engagement on this, but here's where it seems to be brittle.
The underlying container structure is a std::vector. QDialog containing a QTableView in which these items are shown flies as a non-model child window with the main app. The main app shows a graphical map with features shown as rows in the QDialog. The user can select items on the map and "Unload" them.
The Unload operation invokes code in the QDialog implementation which clears the underlying vector in the model like this:
m_model->beginResetModel();
m_vector.clear();
m_model->endResetModel();The m_model member variable is defined like this:
QPointer<MapFeatureTableModel> m_model;
MapFeatureTableModel is declared as:
class MapFeatureTableModel : public QAbstractTableModel
The member variables of the MapFeatureTableModel are:
std::vector<FeatureObj*>* m_pVector;
QTableView* m_pTable;The m_pVector is a pointer to the m_vector item that gets cleared in the scenario I stated.
I'm not sure this relationship is setup correctly, but it's what was there when I started.
In any event, I believe the beginResetModel(); endResetModel(); guardians are correct, but is it acceptable to clear the vector that the model has a pointer to? Should the MapFeatureTableModel class house the actual vector and not a pointer, and then use some kind of "clear()" invocation betwixt the beginResetModel() / endResetModel() ?
My suspicion is that even though the updates occur inside the begin/end pair, modifying the vector in this way is possibly leaving the model in an undefined state.
I tried calling
m_model->clear();
inside the begin/end, but this generates a compiler error that the clear() method is not defined for a QAbstractTableModel, and after trying several incantations including dynamic_cast's and still getting the compiler errors I aborted the mission.
If it helps to illuminate, the version of Qt (moc.exe) I see in the moc_* files is:
Created by: The Qt Meta Object Compiler version 67 (Qt 5.2.1)
Thanks for any insights or suggestions anyone might throw out there. In the interim I'm going to continue to see if I can find exactly why this call causes issues.
Oh, one other question, should this be using a QVector instead of std::vector?
-
Why is
m_pVector
a pointer ?You should first delete all objects from the vector before calling clear.
-
@SGaist I know, it's odd. The vector instance resides in the backing code for the QDialog. From what I can gather about the history of this code it was originally written as pure MFC then "crossbred" with Qt in 2010 at which point it was a single class, then refactored into two classes in 2013 and created the MapFeatureTableModel class. The engineer who did this was not the original author, so I suppose he wanted to impose the View/Model pattern and maybe didn't fully understand how to do that...not sure.
Do you think I should move the std::vector into the MapFeatureTableModel class and access it through the data() interface? Would that add stability to the Qt framework?
The architecture of this thing is akin to "Frankenstein's Monster" IMHO, and while I've made some (what I believe to be) good changes in the architecture for acquiring the data, I haven't done anything, sans adding a QPointer<> here or there, to reconfigure the class hierarchy, ownership, chain of responsibility, etc. At the same time, I'm learning Qt as I go, so I'm reluctant to impose my dangerously-ignorant level of Qt "best practices" into the mix.
If the model is where the container should live, but that's predominately a "the correct way to do it" and is unlikely to improve stability, I'm not keen on changing it. If it would make it less brittle, I'm all for it.
Last side notes:
-
The objects in the vector are not owned by this code -- they're pointers to objects owned by the main app which manages the map. That's why the "clear"-like logic simply empties the vector of pointers rather than looping through and issuing deletes on them.
-
To be clear (slight pun intended), I can't call "clear()" because it seems to not be part of the inheritance chain of a QAbstractTableModel. I found this stack-overflow article that describes the wall I've hit:
https://stackoverflow.com/questions/22917170/qt-clearing-qtableviews-contents
One answer says that QAbstractItemModel does not contain the clear() method, and QAbstractTableModel derives from that. The poster goes on to say "You should call clear method of your model". Does this suggest I should create a "clear()" method in which the "m_vector.clear()" would be moved and call that instead of clearing the vector inline? If that were the case, why wouldn't the begin/endResetModel() calls reside there? Guessing it's not the recommended pattern to follow.
- My apologies for what are surely ridiculously pedestrian questions. I'm well versed in many systems, but not Qt, and once I'm done with this fix I don't anticipate developing in Qt going forward (we are not a Qt shop -- Qt was used as a means to support unicode because MFC, being the T-Rex that it is, does not). As a result, I'm trying to learn just enough Qt to fix this without causing new problems for the next Qt-naiveté to attempt to fix, if that makes sense.
Thanks for the suggestions, guidance, or commentary you've made and any you choose to make now. Gratefully received on this end to be sure!
-
-
Not a question of Qt stability, but moving the all the data handling inside the model will allow you to control it's existence in only one place with a clean interface (single responsibility) thus you can then share the model with all the classes that needs to access these data.
Note that you don't need a view to interact with a model.
-
@SGaist That would limit scope and definitely makes sense. Thank you. I am clueless as to why this structured as it is, but you'll have to take my word for it that this is not the only thing going on with this code that makes my brain hurt. At least there are no goto's or setjmp/longjmp's...
I'm also presuming "clear()" is not a method of the Model framework but simply a pseudo-code placeholder for whatever is apropos for the container anchored in the Model, true? When I'd read this "http://doc.qt.io/qt-5/qabstractitemmodel-obsolete.html#reset" about reset() being deprecated in Qt 5.x and how one should wrap the "myData.clear()" with the begin/end-ResetModel() calls, my initial thought was that "clear()" was part of Qt's Model, but on further review (and a search of the header files) it appears my initial understand was wrong. So if I'm still wrong, don't hesitate to point that out.
My plan now will be to ll encapsulate the vector inside the Model. Access will be done through calls to the Model. That makes sense, and I'll see if that in conjunction with tighter controls helps to stabilize behavior.
One last question: if one wishes to relinquish control to the Qt event-dispatch loop:
A) Is it acceptable to do such a thing?
B) If the answer to A is "yes" does it matter whether the app calls "qApp->processEvents();" vs "QApplication::processEvents();"? I tried to find out and can't find any caveats -- do you know if there are implications of using one over the other?In case you're wondering, my reason for doing this is to set a data-acquisition-is-active flag just before starting the QTread, and then to use that flag to disable UI controls. The controls' event handlers are what launch the background thread. Once the thread begins pulling in data, I want to prevent the users from changing the request while the previous one is in flight.
I set the flag and the "UI refresh" code interprets the flag to enable/disable the controls accordingly, but I found if I don't relinquish control after setting the flag and emitting a UI-refresh signal, a window -- albeit short -- exists where the thread is running and the controls are enabled. When I call "processEvents()" the UI refresh bubbles off the queue and gets processed.
As an alternative, I could check the flag within every "OnClicked", "OnTextChanged", yadda, event handler, but that's noisy and a lot of extra code. If there's a real-time way to immediately disable a control this is a place where that would be optimal, but it seems that would be contrary to the event-handling architecture around with Qt is built. <shrug> True/False?
Thanks for any thoughts, suggests, criticisms or observations put forth. All will be considered thoughtfully and are appreciated and valued.
-
I want to add to my previous post that the UI-refresh signal I'm emitting uses Qt::AutoConnection on the connect() call...I'm ONLY using this right after I set the thread-active flag...all other UI-refresh requests emit a similar (ends up in the same place) but different signal that is connect()'d via Qt::QueuedConnection.
I thought it was possibly an important element that I'd failed to mention. Even with Qt::AutoConnection it seems the request is being deferred for later processing.
-
Indeed, clear is up to you to implement if it makes sense for your model.
A) There should be no need to force the event processing. It looks like something is blocking the event loop if you need to call that.
B) qApp is the instance of the QApplication but processEvent is static so it's rather a question of taste in this case. AFAIK most Qt examples uses the application object (i.e. QSplashScreen documentation).