Focus stealing progress dialog
-
Nothing obvious jumps to my eyes.
Would it be possible to have the same dialog but buildable so I can test it on my machine ?
-
Hmmm! Odder and odder the focus stealing doesn't happen on my Windows system but does on many ?? It also happens consistently on both Linux and Mac. It's not related to the QWidget::raise() calls as they only happen when the text of the dlg is updated. The focus is stolen by the progress bar updates.
-
@SGaist The Creator Project is here:
https://www.dropbox.com/scl/fi/h929bym63f2sf1y3904fw/DSS_ProgressDlg.zip?rlkey=vhhntwxapk347ipc6ng2hkf59&dl=0It's setup to use MSVC2022_x64 on Windows.
Focus theft only seems to occur on some Windows systems?? Go figure.
Moving the dialog - it always snaps back to where it started - commenting the calls to QWidget::show() in applyStart1Text() and applyStart2Text() prevents that problem.
It is still a focus thief on Linux and MacOS. To build on MacOS you'll need to install:
https://mac.r-project.org/openmp/openmp-19.1.0-darwin20-Release.tar.gz
so the files end up in /usr/local/include and /usr/local/lib. For example:cd ~/Downloads curl -O https://mac.r-project.org/openmp/openmp-19.1.0-darwin20-Release.tar.gz sudo tar -zxvf openmp-19.1.0-darwin20-Release.tar.gz -C /
For more details see:
https://mac.r-project.org/openmp/You will likely want to change the .pro file so it looks like (not necessarily 100% correct - I'm not totally familiar with qmake):
unix:!macx: QMAKE_CXXFLAGS+=-fopenmp macx: QMAKE_CXXFLAGS+=-Xclang -fopenmp win32: QMAKE_CXXFLAGS+=/openmp:experimental # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target unix:!macx: LIBS += -L/usr/lib/x86_64-linux-gnu/ -lomp5 macx: LIBS += -L/usr/local/lib/ -lomp INCLUDEPATH += /usr/local/include DEPENDPATH += /usr/local/include
I build for Linux on Lubuntu 22.04 FWIW.
David
-
@Axel-Spoerl This may interest you ...
-
Thanks @Perdrix for pinging.
Generally, aQProgressBar
doesn't claim focus. My suspicion is that it happens on Window manager level. So the progress bar is the beneficiary of focus theft, but somebody else does the stealing ;-)
I also wonder who has told you the lie, that you need to callprocessEvents()
for anything to happen. Unless an application implements blocking code, that's not necessary.I wonder if you could isolate the antisocial behaviour into a small reproducer...
-
This is a small a demo as I can manage. We do need the calls to processEvents()- try commenting the calls out and see what happens - the code we are tracking the progress of is running primarily on the GUI thread with ancillary threads created by openmp.
David
-
This is a small a demo as I can manage. We do need the calls to processEvents()- try commenting the calls out and see what happens - the code we are tracking the progress of is running primarily on the GUI thread with ancillary threads created by openmp.
David
@Perdrix said in Focus stealing progress dialog:
This is a small a demo as I can manage.
So you tell us that you need openmp to reproduce the issue?
provide a minimal, compileable example of the problem so we can take a look on it. -
No - you don't need openmp - just comment out the openmp pragma in dialog.cpp and remove references to openmp in the .pro file It will then run single threaded - and display the same problem. I wanted to include a complete example of our dialog in full use just in case...
-
namespace DSS { void ProgressBase::UpdateProcessorsUsed() { int currentThreadCount = omp_get_num_threads(); // Returns 1 if outside parallel region, else will return threads/cores used! applyProcessorsUsed(currentThreadCount); } }
This doesn't compile without openmp.
Please provide a minimal compilable example.
It has to be small enough to post it here. I normally don't download stuff from dropbox. -
Here's the proof, that focus isn't stolen (tested on Windows, macOS and Linux)
#include <QApplication> #include <QThread> #include <QProgressBar> #include <QLineEdit> #include <QTimer> #include <QVBoxLayout> class Updater : public QObject { Q_OBJECT public: Updater(QProgressBar *bar, QObject *parent = nullptr) : QObject(parent), bar(bar) {} public slots: void update() { emit valueChanged(++value); } void onValueChanged(int v) { value = v; if (!QThread::currentThread()->isInterruptionRequested()) QTimer::singleShot(400, this, [&]{ update(); }); } signals: void valueChanged(int value); private: QProgressBar *bar; int value = 0; }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget w; auto *lay = new QVBoxLayout; auto *edit = new QLineEdit; auto *bar = new QProgressBar; lay->addWidget(edit); lay->addWidget(bar); w.setLayout(lay); auto *u = new Updater(bar, &a); QObject::connect(edit, &QLineEdit::textChanged, u, &Updater::update); QObject::connect(bar, &QProgressBar::valueChanged, u, &Updater::onValueChanged); QObject::connect(u, &Updater::valueChanged, bar, &QProgressBar::setValue); QThread t(&a); u->moveToThread(&t); t.start(); w.show(); bar->setValue(1); const int ret = a.exec(); t.requestInterruption(); t.quit(); t.wait(); return ret; } #include "main.moc"
-
Try running the code I pointed you to on Dropbox. The focus IS definitely stolen - that's why I am here - I tried trivial test cases like what you just posted and they caused no problems. I can't see (and I can tell you I have looked until I am blue in the face) what is wrong with the code for DSS::ProgressDlg but something is clearly causing focus to be stolen whenever the progress bars are updated.
It's truly dis-heartening when you go somewhere for help and get told that there's no problem, when clearly there is - it's just that neither you nor I yet know what code is causing it.
I've now burnt about 7 weeks on this and it's driving me nuts.
I have spent quite some time to re-package that dual progress dialog so it would work outside my projects - time well spent I think. But it would be nice if someone would help in trying to work out what the problem is - I am completely out of ideas which I why I came here in the first place.
-
Believe me I have tried to reduce this to a totally trivial case that causes the focus theft I've burned weeks on this problem which is why I'm passing the code over to minds who have forgotten more about Qt than I ever knew.
@Perdrix said in Focus stealing progress dialog:
Believe me I have tried to reduce this to a totally trivial case that causes the focus theft I've burned weeks on this problem which is why I'm passing the code over to minds who have forgotten more about Qt than I ever knew.
You are a seasoned developer who knows a lot about Qt, don't downplay your expertise David!
However, if the use case is too complex to boil down to 10-20 lines and it requires calling
processEvents()
to function, then there is something wrong in the application. You could doQLoggingCategory::setFilterRules("qt.widgets.focus=true")
and/or listen to theQApplication::focusChanged()
signal to find out why and where the focus is changed. -
Try running the code I pointed you to on Dropbox. The focus IS definitely stolen - that's why I am here - I tried trivial test cases like what you just posted and they caused no problems. I can't see (and I can tell you I have looked until I am blue in the face) what is wrong with the code for DSS::ProgressDlg but something is clearly causing focus to be stolen whenever the progress bars are updated.
It's truly dis-heartening when you go somewhere for help and get told that there's no problem, when clearly there is - it's just that neither you nor I yet know what code is causing it.
I've now burnt about 7 weeks on this and it's driving me nuts.
I have spent quite some time to re-package that dual progress dialog so it would work outside my projects - time well spent I think. But it would be nice if someone would help in trying to work out what the problem is - I am completely out of ideas which I why I came here in the first place.
@Perdrix said in Focus stealing progress dialog:
Try running the code I pointed you to on Dropbox.
That code doesn't compile without openmp and I don't have the time to re-arrange the code.
Just by code-reading and as a general comment: The progress setters seem to be invoked from different threads, that aren't
QThreads
, so they have no event loop running. I am not sure, whatprocessEvents()
really does in that case.
I don't use openmp. The loop inDialog::runTask()
depends on it and it's not a trivial change to remove that dependency.As a general question: Why are you struggling to boil the issue down to a simpler reproducer? Have you tried the debugging hints from my previous post?
I'd simply connect a lambda toQApplication::focusChanged()
. Call aqFatal() << "Focus thief caught red handed";
inside the lambda, when the old focus object is one of the progress bars. If it crashes too early, use a static int as a counter to crash when focus is stolen. Then you have a perfect stack trace to the point where it happened. -
I'll post a revised version with openmp removed entirely. Yes it's bad form to do long running processing on the gui thread - but that's what we have so need to call processEvents() from the dlg.
Your suggestions sound good - I wasn't aware of QLoggingCategory stuff and hadn't considered the use of QApplication::focusChanged - that's the advantage you have over me! I'll see what mileage I can get out of those.
D.
-
Hmm odder and odder:
I did this:int main(int argc, char *argv[]) { QApplication a(argc, argv); Dialog w; QObject::connect(&a, &QApplication::focusChanged, &a, [](QWidget* old, QWidget* now){ qDebug() << "Old focus widget: " << old; qDebug() << "New focus widget: " << now; } ); w.show(); return a.exec(); }
As far as Qt is concerned when I click on another application, focus leaves the "StopButton" (the one named "Cancel" and the focus widget goes to nullptr. BUT the Progress dlg still jumps to the front and steals focus from the other application the instant that the progress is updated. No focusChanged signal occurs when that happens.
Old focus widget: QPushButton(0x55d1c44c14a0, name="StopButton") New focus widget: QWidget(0x0)
What on earth is going on here? The application focus (not the individual Widget focus) IS changed when the progress dlg updates the progress bars.
-
As promised here's the code with openmp stuff removed. It's a tar.gz file setup to build on Linux.