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. Prevent blocking main eventLoop via QueuedConnection?
Forum Updated to NodeBB v4.3 + New Features

Prevent blocking main eventLoop via QueuedConnection?

Scheduled Pinned Locked Moved Solved General and Desktop
5 Posts 2 Posters 494 Views 1 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.
  • enelE Offline
    enelE Offline
    enel
    wrote on last edited by
    #1

    I have a ComboBox, which should trigger longer lasting work when activated.
    My goal is to update the GUI, showing some "Loading" information and close the ComboBox, the do some work, while the UI may stay unresponsive in the meantime.

    I can't get the ComboBox to close and update the GUI elements the way I expect it: from what I have read so far, using Signals&Slots with QueuedConnection should provide what I want.
    But it seems, that I need to add many qApp->processEvent() calls while doing work to get an early update in the GUI.
    Is there another way (not using Threads or Timers) to finish updating the GUI and start work afterwards? Also, I am seeking for a better understanding why QueuedConnection does not help in this case? Is there any literature which explains the connection between Renderer / QML / Main threads and their synchronization regarding event handling?
    Is it correct, saying that Signals&Slots with QueuedConnection do not differ from QMetaObject::invokeMethod with QueuedConnection (as used in the commented code below) regarding the event posting and handling?

    Application

    class Application : public QObject
    {
      Q_OBJECT
      QML_NAMED_ELEMENT(App)
    
    public:
      Application(QObject *parent = nullptr) {
        QObject::connect(this, &Application::startWork, this, &Application::_doWork, Qt::QueuedConnection);
        QObject::connect(this, &Application::doneLoading, this, &Application::finish);
      }
      ~Application() {};
    
      Q_INVOKABLE void doWork() {  }
      Q_INVOKABLE bool isLoading() const { return m_isLoading; }
      Q_PROPERTY(bool isLoading READ isLoading NOTIFY isLoadingChanged)
    
    signals:
      void startWork();
      void doneLoading();
      void isLoadingChanged();
    
    private slots:
      //void _doWork() { setIsLoading(true); QMetaObject::invokeMethod(this, &Application::work, Qt::QueuedConnection); }
      void _doWork() { setIsLoading(true); work(); }
      void finish() { setIsLoading(false); }
    
    private:
        bool m_isLoading = false;
    
        void setIsLoading(bool value) {
          if (value == m_isLoading)
            return;
    
          m_isLoading = value;
          emit isLoadingChanged();
        }
    
        void work() {
          // GUI is never updated
          qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
          std::this_thread::sleep_for(std::chrono::milliseconds(4000));
    
          // GUI will update after first or second iteration
          //for (auto i = 0; i < 4; ++i) {
          //  qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
          //  std::this_thread::sleep_for(std::chrono::milliseconds(1000));
          //  qDebug("...");
          //}
    
          emit doneLoading();
        }
    };
    

    QML part:

    Window {
        id: root
        width: 400
        height: 300
        visible: true
        title: qsTr("Test")
    
        RowLayout {
            id: row
            anchors.centerIn: parent
    
            Label { id: lblState; text: "Normal" }
    
            ComboBox {
                id: cmb
                model: ["First", "Second", "Third"]
    
                onActivated: {
                    app.startWork()
                    console.log("Combo activated -> work requested")
                }
            }
    
            states : [
                State { name: "normal"; when: !app.isLoading },
                State { name: "loading"; when: app.isLoading }
            ]
    
            onStateChanged: {
                console.log("Loading: " + app.isLoading)
                lblState.text = app.isLoading ? "Loading" : "Normal"
            }
        }
    }
    
    jsulmJ 1 Reply Last reply
    0
    • enelE enel

      I have a ComboBox, which should trigger longer lasting work when activated.
      My goal is to update the GUI, showing some "Loading" information and close the ComboBox, the do some work, while the UI may stay unresponsive in the meantime.

      I can't get the ComboBox to close and update the GUI elements the way I expect it: from what I have read so far, using Signals&Slots with QueuedConnection should provide what I want.
      But it seems, that I need to add many qApp->processEvent() calls while doing work to get an early update in the GUI.
      Is there another way (not using Threads or Timers) to finish updating the GUI and start work afterwards? Also, I am seeking for a better understanding why QueuedConnection does not help in this case? Is there any literature which explains the connection between Renderer / QML / Main threads and their synchronization regarding event handling?
      Is it correct, saying that Signals&Slots with QueuedConnection do not differ from QMetaObject::invokeMethod with QueuedConnection (as used in the commented code below) regarding the event posting and handling?

      Application

      class Application : public QObject
      {
        Q_OBJECT
        QML_NAMED_ELEMENT(App)
      
      public:
        Application(QObject *parent = nullptr) {
          QObject::connect(this, &Application::startWork, this, &Application::_doWork, Qt::QueuedConnection);
          QObject::connect(this, &Application::doneLoading, this, &Application::finish);
        }
        ~Application() {};
      
        Q_INVOKABLE void doWork() {  }
        Q_INVOKABLE bool isLoading() const { return m_isLoading; }
        Q_PROPERTY(bool isLoading READ isLoading NOTIFY isLoadingChanged)
      
      signals:
        void startWork();
        void doneLoading();
        void isLoadingChanged();
      
      private slots:
        //void _doWork() { setIsLoading(true); QMetaObject::invokeMethod(this, &Application::work, Qt::QueuedConnection); }
        void _doWork() { setIsLoading(true); work(); }
        void finish() { setIsLoading(false); }
      
      private:
          bool m_isLoading = false;
      
          void setIsLoading(bool value) {
            if (value == m_isLoading)
              return;
      
            m_isLoading = value;
            emit isLoadingChanged();
          }
      
          void work() {
            // GUI is never updated
            qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
            std::this_thread::sleep_for(std::chrono::milliseconds(4000));
      
            // GUI will update after first or second iteration
            //for (auto i = 0; i < 4; ++i) {
            //  qApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers);
            //  std::this_thread::sleep_for(std::chrono::milliseconds(1000));
            //  qDebug("...");
            //}
      
            emit doneLoading();
          }
      };
      

      QML part:

      Window {
          id: root
          width: 400
          height: 300
          visible: true
          title: qsTr("Test")
      
          RowLayout {
              id: row
              anchors.centerIn: parent
      
              Label { id: lblState; text: "Normal" }
      
              ComboBox {
                  id: cmb
                  model: ["First", "Second", "Third"]
      
                  onActivated: {
                      app.startWork()
                      console.log("Combo activated -> work requested")
                  }
              }
      
              states : [
                  State { name: "normal"; when: !app.isLoading },
                  State { name: "loading"; when: app.isLoading }
              ]
      
              onStateChanged: {
                  console.log("Loading: " + app.isLoading)
                  lblState.text = app.isLoading ? "Loading" : "Normal"
              }
          }
      }
      
      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @enel said in Prevent blocking main eventLoop via QueuedConnection?:

      QueuedConnection does not help in this case?

      Because it only helps when using signals/slots across different threads. As soon as your slot is started in GUI thread you GUI is blocked. If you don't want to use threads then the only way I see is to execute qApp->processEvents regularly while doing your heavy work.

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • enelE Offline
        enelE Offline
        enel
        wrote on last edited by
        #3

        Thanks for clarification!

        woboq blog mentions (Qt Event Loop): "A QueuedConnection will post an event to the event loop to eventually be handled. ... If the receiver is in the same thread, the event will be processed later, as the event loop iterates." This led to my expectation, that the event is eventually handled in the next iteration, therefore closing the Combo could be done before.

        jsulmJ 1 Reply Last reply
        0
        • enelE enel

          Thanks for clarification!

          woboq blog mentions (Qt Event Loop): "A QueuedConnection will post an event to the event loop to eventually be handled. ... If the receiver is in the same thread, the event will be processed later, as the event loop iterates." This led to my expectation, that the event is eventually handled in the next iteration, therefore closing the Combo could be done before.

          jsulmJ Offline
          jsulmJ Offline
          jsulm
          Lifetime Qt Champion
          wrote on last edited by
          #4

          @enel said in Prevent blocking main eventLoop via QueuedConnection?:

          that the event is eventually handled in the next iteration

          But if the next iteration is delayed because of blocked event loop it will happen later.

          https://forum.qt.io/topic/113070/qt-code-of-conduct

          enelE 1 Reply Last reply
          1
          • jsulmJ jsulm

            @enel said in Prevent blocking main eventLoop via QueuedConnection?:

            that the event is eventually handled in the next iteration

            But if the next iteration is delayed because of blocked event loop it will happen later.

            enelE Offline
            enelE Offline
            enel
            wrote on last edited by
            #5

            @jsulm said in Prevent blocking main eventLoop via QueuedConnection?:

            But if the next iteration is delayed because of blocked event loop it will happen later.

            Got it! Thanks.

            1 Reply Last reply
            0
            • enelE enel has marked this topic as solved on

            • Login

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