Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Focus stealing progress dialog
Forum Updated to NodeBB v4.3 + New Features

Focus stealing progress dialog

Scheduled Pinned Locked Moved Solved General and Desktop
31 Posts 4 Posters 1.1k Views 5 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • P Offline
    P Offline
    Perdrix
    wrote on 30 Apr 2025, 07:25 last edited by Perdrix
    #9

    @SGaist I'm creating a stand-alone Creator project for this problem, and I am almost there, but can't work out how to pass the /openmp:experimental command line flag to MS Visual C++

    How do I configure the project to do this?
    D

    1 Reply Last reply
    0
    • P Offline
      P Offline
      Perdrix
      wrote on 1 May 2025, 08:52 last edited by Perdrix 5 Jan 2025, 12:11
      #10

      @SGaist The Creator Project is here:
      https://www.dropbox.com/scl/fi/h929bym63f2sf1y3904fw/DSS_ProgressDlg.zip?rlkey=vhhntwxapk347ipc6ng2hkf59&dl=0

      It'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

      1 Reply Last reply
      0
      • P Offline
        P Offline
        Perdrix
        wrote on 1 May 2025, 12:47 last edited by
        #11

        @Axel-Spoerl This may interest you ...

        1 Reply Last reply
        0
        • A Offline
          A Offline
          Axel Spoerl
          Moderators
          wrote on 1 May 2025, 14:24 last edited by
          #12

          Thanks @Perdrix for pinging.
          Generally, a QProgressBar 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 call processEvents() 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...

          Software Engineer
          The Qt Company, Oslo

          1 Reply Last reply
          0
          • P Offline
            P Offline
            Perdrix
            wrote on 1 May 2025, 15:50 last edited by
            #13

            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

            C 1 Reply Last reply 1 May 2025, 15:59
            0
            • P Perdrix
              1 May 2025, 15:50

              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

              C Offline
              C Offline
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on 1 May 2025, 15:59 last edited by
              #14

              @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.

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              1 Reply Last reply
              1
              • P Offline
                P Offline
                Perdrix
                wrote on 1 May 2025, 16:05 last edited by Perdrix 5 Jan 2025, 16:06
                #15

                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...

                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  Axel Spoerl
                  Moderators
                  wrote on 1 May 2025, 20:43 last edited by
                  #16
                  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.

                  Software Engineer
                  The Qt Company, Oslo

                  1 Reply Last reply
                  1
                  • P Offline
                    P Offline
                    Perdrix
                    wrote on 2 May 2025, 08:36 last edited by
                    #17

                    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);
                    	}
                    
                    1 Reply Last reply
                    0
                    • P Offline
                      P Offline
                      Perdrix
                      wrote on 2 May 2025, 08:38 last edited by
                      #18

                      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.

                      A 1 Reply Last reply 2 May 2025, 11:45
                      0
                      • A Offline
                        A Offline
                        Axel Spoerl
                        Moderators
                        wrote on 2 May 2025, 09:12 last edited by Axel Spoerl 5 Feb 2025, 09:28
                        #19

                        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"
                        
                        

                        Software Engineer
                        The Qt Company, Oslo

                        1 Reply Last reply
                        1
                        • P Offline
                          P Offline
                          Perdrix
                          wrote on 2 May 2025, 11:45 last edited by Perdrix 5 Feb 2025, 11:52
                          #20

                          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.

                          A 1 Reply Last reply 2 May 2025, 13:28
                          0
                          • P Perdrix
                            2 May 2025, 08:38

                            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.

                            A Offline
                            A Offline
                            Axel Spoerl
                            Moderators
                            wrote on 2 May 2025, 11:45 last edited by
                            #21

                            @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 do QLoggingCategory::setFilterRules("qt.widgets.focus=true") and/or listen to the QApplication::focusChanged() signal to find out why and where the focus is changed.

                            Software Engineer
                            The Qt Company, Oslo

                            1 Reply Last reply
                            1
                            • P Perdrix
                              2 May 2025, 11:45

                              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.

                              A Offline
                              A Offline
                              Axel Spoerl
                              Moderators
                              wrote on 2 May 2025, 13:28 last edited by Axel Spoerl 5 Feb 2025, 15:09
                              #22

                              @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, what processEvents() really does in that case.
                              I don't use openmp. The loop in Dialog::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 to QApplication::focusChanged(). Call a qFatal() << "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.

                              Software Engineer
                              The Qt Company, Oslo

                              1 Reply Last reply
                              1
                              • P Offline
                                P Offline
                                Perdrix
                                wrote on 2 May 2025, 16:15 last edited by Perdrix 5 Feb 2025, 16:24
                                #23

                                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.

                                1 Reply Last reply
                                0
                                • P Offline
                                  P Offline
                                  Perdrix
                                  wrote on 2 May 2025, 17:39 last edited by Perdrix 5 Feb 2025, 17:41
                                  #24

                                  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.

                                  1 Reply Last reply
                                  0
                                  • P Offline
                                    P Offline
                                    Perdrix
                                    wrote on 2 May 2025, 17:47 last edited by
                                    #25

                                    As promised here's the code with openmp stuff removed. It's a tar.gz file setup to build on Linux.

                                    https://www.dropbox.com/scl/fi/ullkvjzhr4qod54wuaako/DSS_ProgressDlg.tar.gz?rlkey=hncz5km5tnxt9vy3axzw3koxy&st=iq8zaagq&dl=0

                                    1 Reply Last reply
                                    0
                                    • A Offline
                                      A Offline
                                      Axel Spoerl
                                      Moderators
                                      wrote on 2 May 2025, 18:19 last edited by
                                      #26

                                      Aha, in that case it's all about the definition of the word "focus".
                                      When QApplication::focusWidget() == nullptr, the application hasn't got focus in the Qt universe.
                                      The application won't receive user input events (e.g. mouse, keyboard).
                                      When updating of the progress bars brings the window to the front, this has nothing to do with focus. It's a change in Z-order, which is sole business of the Window manager. Qt has no say here. My window manager (openSuSE / X11 / KDE Plasma 6) doesn't show that behavior.
                                      Qt doesn't even know the Z order. That's why e.g. QApplication::widgetAt(const QPoint &) relies on the platform layer. On X11, it will ask the X server, for the window under the point.

                                      => On which system do you observe the Z order change?

                                      Software Engineer
                                      The Qt Company, Oslo

                                      1 Reply Last reply
                                      0
                                      • P Offline
                                        P Offline
                                        Perdrix
                                        wrote on 2 May 2025, 18:25 last edited by Perdrix 5 Feb 2025, 18:43
                                        #27

                                        This happens on Lubuntu 22.04 and on macOS (at least on Ventura and later).

                                        I think it also happens on some levels of W11.

                                        It's more than just Z-Order - if I were typing in the other window the keystrokes are lost after the dlg is updated.

                                        What I don't understand is why doesn't this happen with QProgressDialog, but does with our dialogue.

                                        1 Reply Last reply
                                        0
                                        • A Offline
                                          A Offline
                                          Axel Spoerl
                                          Moderators
                                          wrote on 2 May 2025, 18:59 last edited by Axel Spoerl 5 Feb 2025, 19:00
                                          #28

                                          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.
                                          Calling raise() 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
                                          }
                                          ``

                                          Software Engineer
                                          The Qt Company, Oslo

                                          1 Reply Last reply
                                          2

                                          18/31

                                          2 May 2025, 08:38

                                          • Login

                                          • Login or register to search.
                                          18 out of 31
                                          • First post
                                            18/31
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved