Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. QML Button onClicked() - cannot open Dialog/PopUp until function finishes?
Forum Updated to NodeBB v4.3 + New Features

QML Button onClicked() - cannot open Dialog/PopUp until function finishes?

Scheduled Pinned Locked Moved Unsolved Qt for Python
6 Posts 2 Posters 1.3k 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.
  • R Offline
    R Offline
    robliou
    wrote on last edited by robliou
    #1

    Hi!

    I have a fairly simple question that I surprisingly have not been able to find a lot of online help for.

    Basically, I have a Button in QML that onClicked(), I would like it to:

    1. open a Dialog or PopUp and then
    2. run a fairly CPU-intensive function in Python.

    However, I can never get the Dialog or PopUp to display until after the function finishes. I have tried moving the function to a new thread, but to no avail. Has anyone else run into this problem before, and have an idea how to solve? My code is as below:

    **main.qml

    RoundButton{...//Button design details
    onClicked: {
                        progressBarDialog.open() //this is the name of the Dialog window I'm trying to open as soon as the button is pressed
                        var checkedTuple=['algoOne', 'algoTwo', 'algoThree']
                        bridge.start_run_function(checkedTuple) //this calls my threaded function under the Bridge class
                        mainLoader.source = "pages/report.qml"; // Load new page after function runs
                    }
            }
    

    **main.py #this file houses all my python functions under the Bridge class

    @QmlElement
    class Bridge(QObject):
    
    @Slot(str, result=str) //creates a new thread to run the function
        def start_run_function(self,checkedTuple):
            self.running = False
    
            if not self.running:
                self.running = True
    
            thread = threading.Thread(target=self.runFunction(checkedTuple))
            thread.start()
    
        @Slot(str, result=str)
        def runFunction(self, checkedTuple):
    //complicated algorithm that takes a minute to run, but works fine. Ultimately I want this function to run after the Dialog/PopUp is first displayed/visible.
    

    This issue occurs whether or not I use a new thread when running runFunction() . Please note that I was able to get the pop-up window to display correctly first using QT Widgets, but that is a very different system from QML.

    Thanks in advance,

    jeremy_kJ 1 Reply Last reply
    0
    • R robliou

      Hi!

      I have a fairly simple question that I surprisingly have not been able to find a lot of online help for.

      Basically, I have a Button in QML that onClicked(), I would like it to:

      1. open a Dialog or PopUp and then
      2. run a fairly CPU-intensive function in Python.

      However, I can never get the Dialog or PopUp to display until after the function finishes. I have tried moving the function to a new thread, but to no avail. Has anyone else run into this problem before, and have an idea how to solve? My code is as below:

      **main.qml

      RoundButton{...//Button design details
      onClicked: {
                          progressBarDialog.open() //this is the name of the Dialog window I'm trying to open as soon as the button is pressed
                          var checkedTuple=['algoOne', 'algoTwo', 'algoThree']
                          bridge.start_run_function(checkedTuple) //this calls my threaded function under the Bridge class
                          mainLoader.source = "pages/report.qml"; // Load new page after function runs
                      }
              }
      

      **main.py #this file houses all my python functions under the Bridge class

      @QmlElement
      class Bridge(QObject):
      
      @Slot(str, result=str) //creates a new thread to run the function
          def start_run_function(self,checkedTuple):
              self.running = False
      
              if not self.running:
                  self.running = True
      
              thread = threading.Thread(target=self.runFunction(checkedTuple))
              thread.start()
      
          @Slot(str, result=str)
          def runFunction(self, checkedTuple):
      //complicated algorithm that takes a minute to run, but works fine. Ultimately I want this function to run after the Dialog/PopUp is first displayed/visible.
      

      This issue occurs whether or not I use a new thread when running runFunction() . Please note that I was able to get the pop-up window to display correctly first using QT Widgets, but that is a very different system from QML.

      Thanks in advance,

      jeremy_kJ Offline
      jeremy_kJ Offline
      jeremy_k
      wrote on last edited by
      #2

      @robliou said in QML Button onClicked() - cannot open Dialog/PopUp until function finishes?:

      Hi!

      I have a fairly simple question that I surprisingly have not been able to find a lot of online help for.

      Basically, I have a Button in QML that onClicked(), I would like it to:

      1. open a Dialog or PopUp and then
      2. run a fairly CPU-intensive function in Python.

      However, I can never get the Dialog or PopUp to display until after the function finishes. I have tried moving the function to a new thread, but to no avail. Has anyone else run into this problem before, and have an idea how to solve? My code is as below:

      So far, this sounds like the expected behavior. The Qt event loop is blocked as long as application code is running in the UI thread. There are caveats with nested event loops or explicit processing, but those involve more caveats that are easiest to handle by avoiding entirely.

      Further, presuming cpython in early 2024, the global interpreter lock (GIL) implies that if one thread is executing python code, no other thread is.

      **main.py #this file houses all my python functions under the Bridge class

      @QmlElement
      class Bridge(QObject):
      
      @Slot(str, result=str) //creates a new thread to run the function
          def start_run_function(self,checkedTuple):
              self.running = False
      
              if not self.running:
                  self.running = True
      

      I was going to ignore the code entirely, but this sticks out as at least partially misplaced. Perhaps self.running = False is supposed to be in the constructor.

      Please note that I was able to get the pop-up window to display correctly first using QT Widgets, but that is a very different system from QML.

      This is the interesting part. Is the Widgets implementation more or less free of python code? If so, this is the expected behavior. If not, or perhaps regardless, a comparison could be fruitful.

      Asking a question about code? http://eel.is/iso-c++/testcase/

      R 1 Reply Last reply
      0
      • jeremy_kJ jeremy_k

        @robliou said in QML Button onClicked() - cannot open Dialog/PopUp until function finishes?:

        Hi!

        I have a fairly simple question that I surprisingly have not been able to find a lot of online help for.

        Basically, I have a Button in QML that onClicked(), I would like it to:

        1. open a Dialog or PopUp and then
        2. run a fairly CPU-intensive function in Python.

        However, I can never get the Dialog or PopUp to display until after the function finishes. I have tried moving the function to a new thread, but to no avail. Has anyone else run into this problem before, and have an idea how to solve? My code is as below:

        So far, this sounds like the expected behavior. The Qt event loop is blocked as long as application code is running in the UI thread. There are caveats with nested event loops or explicit processing, but those involve more caveats that are easiest to handle by avoiding entirely.

        Further, presuming cpython in early 2024, the global interpreter lock (GIL) implies that if one thread is executing python code, no other thread is.

        **main.py #this file houses all my python functions under the Bridge class

        @QmlElement
        class Bridge(QObject):
        
        @Slot(str, result=str) //creates a new thread to run the function
            def start_run_function(self,checkedTuple):
                self.running = False
        
                if not self.running:
                    self.running = True
        

        I was going to ignore the code entirely, but this sticks out as at least partially misplaced. Perhaps self.running = False is supposed to be in the constructor.

        Please note that I was able to get the pop-up window to display correctly first using QT Widgets, but that is a very different system from QML.

        This is the interesting part. Is the Widgets implementation more or less free of python code? If so, this is the expected behavior. If not, or perhaps regardless, a comparison could be fruitful.

        R Offline
        R Offline
        robliou
        wrote on last edited by robliou
        #3

        @jeremy_k

        Hello Jeremy, thank you for taking the time to reply.
        So far, this sounds like the expected behavior. The Qt event loop is blocked as long as application code is running in the UI thread.
        OK, makes sense, but surely when i run progressBarDialog.open() this isn't blocking the GUI event loop, or is it? Isn't opening a dialog/ popup just a local event?

        Even if it is blocking, I am opening this dialog window before running the function, so why doesn't it just show up first before the function runs?

        Secondly, the fact that I used QThread to run run_function should mean that a new thread is now running this function, so there should be no interference with the GUI event loop?

        I was going to ignore the code entirely, but this sticks out as at least partially misplaced
        You are correct, this belongs in the constructor. I just temporarily put it here for testing/expediency purposes, but I don't think it affects the outcome of our output here.

        This is the interesting part. Is the Widgets implementation more or less free of python code? If so, this is the expected behavior. If not, or perhaps regardless, a comparison could be fruitful.

        I took another look at my QT Widgets code on how I achieved proper functioning.

        In my Widgets implementation, I used a combination of Python and QT Widgets. Here is the process I used to get the progress bar to display correctly upon clicking a button:

        1. in my main.py file, I created a text_browser that contains the progress bar
        2. created a function called show_progress_bar that renders the text_browser visible when called (i.e. self.show_text_browser())
        3. created a function called submit_clicked() that, when the button is clicked, first:
          a) runs show_progress_bar to make the progress bar visible, then
          b) calls run_function which, in a new thread, runs the complex Python function

        This code seems to work perfectly, with the progress bar accurately picking up signals that are emitted as run_function proceeds.

        Unfortunately, I am trying to achieve the same effect with QML but frustratingly cannot seem to do it. Any ideas or assistance you could provide would be much appreciated.

        Thank you,

        R jeremy_kJ 2 Replies Last reply
        0
        • R robliou

          @jeremy_k

          Hello Jeremy, thank you for taking the time to reply.
          So far, this sounds like the expected behavior. The Qt event loop is blocked as long as application code is running in the UI thread.
          OK, makes sense, but surely when i run progressBarDialog.open() this isn't blocking the GUI event loop, or is it? Isn't opening a dialog/ popup just a local event?

          Even if it is blocking, I am opening this dialog window before running the function, so why doesn't it just show up first before the function runs?

          Secondly, the fact that I used QThread to run run_function should mean that a new thread is now running this function, so there should be no interference with the GUI event loop?

          I was going to ignore the code entirely, but this sticks out as at least partially misplaced
          You are correct, this belongs in the constructor. I just temporarily put it here for testing/expediency purposes, but I don't think it affects the outcome of our output here.

          This is the interesting part. Is the Widgets implementation more or less free of python code? If so, this is the expected behavior. If not, or perhaps regardless, a comparison could be fruitful.

          I took another look at my QT Widgets code on how I achieved proper functioning.

          In my Widgets implementation, I used a combination of Python and QT Widgets. Here is the process I used to get the progress bar to display correctly upon clicking a button:

          1. in my main.py file, I created a text_browser that contains the progress bar
          2. created a function called show_progress_bar that renders the text_browser visible when called (i.e. self.show_text_browser())
          3. created a function called submit_clicked() that, when the button is clicked, first:
            a) runs show_progress_bar to make the progress bar visible, then
            b) calls run_function which, in a new thread, runs the complex Python function

          This code seems to work perfectly, with the progress bar accurately picking up signals that are emitted as run_function proceeds.

          Unfortunately, I am trying to achieve the same effect with QML but frustratingly cannot seem to do it. Any ideas or assistance you could provide would be much appreciated.

          Thank you,

          R Offline
          R Offline
          robliou
          wrote on last edited by
          #4

          @jeremy_k Wondering if anyone else has any ideas on this? I tried implementing my multithreading using this method:

          https://www.pythonguis.com/tutorials/multithreading-pyside6-applications-qthreadpool/

          and the worker threads seem to run OK (as in, they run successfully from the Worker thread), but they still seem to be blocking on my GUI event loop for some reason?

          1 Reply Last reply
          0
          • R Offline
            R Offline
            robliou
            wrote on last edited by robliou
            #5

            I suspect that QML, not Python, is causing the problem. The GUI locks when you try to both do something in the main GUI event loop (i.e. open a QML popup window) and also run a Python function, as triggered from a QML event (i.e. clicking a button). QML seems to automatically lock the main event loop, even if the Python function is called using a separate thread.

            Meanwhile, if one were to do all of this completely within Python (ie using Python + QT Widgets + Qthread), the problem goes away and everything works fine.

            Isn't that strange?

            1 Reply Last reply
            0
            • R robliou

              @jeremy_k

              Hello Jeremy, thank you for taking the time to reply.
              So far, this sounds like the expected behavior. The Qt event loop is blocked as long as application code is running in the UI thread.
              OK, makes sense, but surely when i run progressBarDialog.open() this isn't blocking the GUI event loop, or is it? Isn't opening a dialog/ popup just a local event?

              Even if it is blocking, I am opening this dialog window before running the function, so why doesn't it just show up first before the function runs?

              Secondly, the fact that I used QThread to run run_function should mean that a new thread is now running this function, so there should be no interference with the GUI event loop?

              I was going to ignore the code entirely, but this sticks out as at least partially misplaced
              You are correct, this belongs in the constructor. I just temporarily put it here for testing/expediency purposes, but I don't think it affects the outcome of our output here.

              This is the interesting part. Is the Widgets implementation more or less free of python code? If so, this is the expected behavior. If not, or perhaps regardless, a comparison could be fruitful.

              I took another look at my QT Widgets code on how I achieved proper functioning.

              In my Widgets implementation, I used a combination of Python and QT Widgets. Here is the process I used to get the progress bar to display correctly upon clicking a button:

              1. in my main.py file, I created a text_browser that contains the progress bar
              2. created a function called show_progress_bar that renders the text_browser visible when called (i.e. self.show_text_browser())
              3. created a function called submit_clicked() that, when the button is clicked, first:
                a) runs show_progress_bar to make the progress bar visible, then
                b) calls run_function which, in a new thread, runs the complex Python function

              This code seems to work perfectly, with the progress bar accurately picking up signals that are emitted as run_function proceeds.

              Unfortunately, I am trying to achieve the same effect with QML but frustratingly cannot seem to do it. Any ideas or assistance you could provide would be much appreciated.

              Thank you,

              jeremy_kJ Offline
              jeremy_kJ Offline
              jeremy_k
              wrote on last edited by
              #6

              @robliou said in QML Button onClicked() - cannot open Dialog/PopUp until function finishes?:

              @jeremy_k

              Hello Jeremy, thank you for taking the time to reply.
              So far, this sounds like the expected behavior. The Qt event loop is blocked as long as application code is running in the UI thread.
              OK, makes sense, but surely when i run progressBarDialog.open() this isn't blocking the GUI event loop, or is it? Isn't opening a dialog/ popup just a local event?

              I don't know what's meant by a local event. In my toy test on macOs, Dialog.open() followed by a long-running operation still shows the dialog before becoming unresponsive. I don't know that this is required by the interface. It's not inconceivable to me that some windowing systems might require a working event loop to show a window.

              toy.qml:

              import QtQuick.Window
              import QtQuick.Dialogs
              
              Window {
                  visible: true
              
                  MessageDialog { id: dialog }
              
                  MouseArea {
                      anchors.fill: parent
                      onClicked: {
                          console.log("pre");
                          dialog.open();
                          for (var i = 0; i < 500000000; i++)
                                  ;
                          console.log("post");
                      }
                  }
              }
              

              Secondly, the fact that I used QThread to run run_function should mean that a new thread is now running this function, so there should be no interference with the GUI event loop?

              It shouldn't interfere with the C++ event loop implementation. There will be contention for any python code run in an event handler, slot, etc.

              This is the interesting part. Is the Widgets implementation more or less free of python code? If so, this is the expected behavior. If not, or perhaps regardless, a comparison could be fruitful.

              I took another look at my QT Widgets code on how I achieved proper functioning.

              I'm lost in the description. If you can share a terse, brief example, that might help. If not, it sounds like you understand the Python and Qt widgets fundamentals.

              @robliou said in QML Button onClicked() - cannot open Dialog/PopUp until function finishes?:

              I suspect that QML, not Python, is causing the problem. The GUI locks when you try to both do something in the main GUI event loop (i.e. open a QML popup window) and also run a Python function, as triggered from a QML event (i.e. clicking a button). QML seems to automatically lock the main event loop, even if the Python function is called using a separate thread.

              Native code that is run from python (PySide and PyQt included) need to explicitly drop the global interpreter lock to allow other python threads to execute. It's possible that you've found a defect where this release is missing. The QML engine itself isn't likely to be involved. While a signal handler/QML event is running, that QML engine is blocking its thread's event loop.

              If you can put together a minimal example, that would help for a bug report. And as usual, try the latest release to verify that the issue hasn't already been addressed.

              Asking a question about code? http://eel.is/iso-c++/testcase/

              1 Reply Last reply
              0

              • Login

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