Focus stealing progress dialog
-
wrote 27 days ago last edited by Perdrix
I have a progress dialogue as shown above. Whenever it updates its contents, it steals Focus which is most anti-social.
I changed the code to add:
// // Set the window so that it does not take focus // setAttribute(Qt::WA_ShowWithoutActivating);
as it appeared that this might prevent that happening, but unfortunately it still misbehaves.
What do I need to change to stop this focus thief :)?
Thanks
David -
It happens in the
ProcessDialog
class, because it's implemented that way.
The methods below are called on each update of the progress bar.
While the method name suggests, that they are called only once at the start, they are actually hit on every update.
Callingraise()
raises them.void ProgressDlg::applyStart1Text(const QString& strText) { ui->ProcessText1->setText(strText); raise(); // <----- HERE } void ProgressDlg::applyStart2Text(const QString& strText) { ui->ProcessText2->setText(strText); setProgress2Range(0, m_total2); if (m_total2 == 0) { setItemVisibility(true, false); } else { setItemVisibility(true, true); applyProgress2(0); } raise(); // <----- AND HERE } ``
-
Hi,
You seem to have multiple progress bars so likely a custom dialog, am I correct ?
Do you update only the progress bar or is something else happening ?
Did you made Cancel the default button ? -
wrote 27 days ago last edited by Perdrix
Yes, very much a custom dialogue. And yes, the Cancel button has the "autoDefault" attribute set. The code that updates the progress bars is driven by two member functions, Progress1, and Progress2:
void ProgressDlg::Progress1(const QString& text, int achieved) { QMetaObject::invokeMethod(this, "slotProgress1", Qt::AutoConnection, Q_ARG(const QString&, text), Q_ARG(int, achieved)); } void ProgressDlg::Progress2(const QString& text, int achieved) { QMetaObject::invokeMethod(this, "slotProgress2", Qt::AutoConnection, Q_ARG(const QString&, text), Q_ARG(int, achieved)); }
which can be driven from non-UI threads. Those slots just do some book-keeping, update the text fields and the sliders, and finally invoke QCoreApplication::processEvents(); which I am told was found necessary to ensure the progress dialogue was updated properly (no I didn't write this code). They do call raise(), but do not call (e.g.) setFocus(). The only time setFocus() is used is when the dialogue is initially created.
-
Would it be possible to share the implementation of the dialog ?
-
wrote 26 days ago last edited by Perdrix
@SGaist Certainly:
https://www.dropbox.com/scl/fo/yzyyeijwip3pil9poly4p/ALZGlamwAtpuC5NrqMfkTUg?rlkey=4r29ofptuf5zzqdia027av71s&dl=1PS Removing autoDefault from the Cancel button changed nothing.
PPS Same problem on both Windows and macOS
PPPS If a shared debug session would help you will be welcome to connect to my system using e.g. Chrome Remote Desktop -
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 ?
-
wrote 24 days ago last edited by
I thought that I had sent you enough to build it - with judicious removal of pch,h and adding includes for Qt and stdlib stuff.
-
wrote 23 days ago last edited by
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.
-
wrote 22 days ago last edited by Perdrix 5 Jan 2025, 12:11
@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
-
wrote 22 days ago last edited by
@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...
-
wrote 22 days ago last edited by
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. -
wrote 22 days ago last edited by Perdrix 5 Jan 2025, 16:06
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. -
wrote 21 days ago last edited by
I did say a few minor changes to remove openmp - I wanted to be SURE that you guys had all the code
Just change that mf to read:void ProgressBase::UpdateProcessorsUsed() { applyProcessorsUsed(1); }
-
wrote 21 days ago last edited by
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.
-
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"
-
wrote 21 days ago last edited by Perdrix 5 Feb 2025, 11:52
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.
1/31