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. Multithreading puzzle
Forum Updated to NodeBB v4.3 + New Features

Multithreading puzzle

Scheduled Pinned Locked Moved Unsolved Qt for Python
8 Posts 3 Posters 530 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.
  • P Offline
    P Offline
    Paul_F
    wrote on last edited by Paul_F
    #1

    Hi all,

    I am relatively new to Python and Qt. I have recently (3 months ago) started learning python, and I am getting the hang of it.

    I am not one to read the manual beginning to end; I sort of dove off in the deep end. My first objective is to create a car media player, with all common functions like radio, media player, bluetooth, etc. My progress so far is that I have built the GUI in PyQt5(just moved to Qt6), programmed the Qpushbuttons etc, and I have made a start on the volume control. This is where I am getting stumped. I plan to use classic On-screen-display for the volume and other direct influence settings (like brightness). I have managed to get the volume to neatly decrease and increase, but when I release the button, I need the source buttons to immediately be re-enabled and listen for presses, while in a parallel thread a method is called to wait 3 seconds, and then remove the volume widget from screen (like in older tvs). I have studied QThread and tried a few times, but it keeps botching and I have had numerous different errors. I have included the code without the attempt for multithreading and I have annotated important parts:

    class Carma(QtWidgets.QWidget):
    
        def __init__(self,init):
            super().__init__()
    
            # Setup main components and variables
            # -----------------------------------
            # *snipped out widget code, not important for this question
    
            self.init=init
            setSource()
    
    
        def src_button_active(self ,num):
            self.buttons_src[num].setStyleSheet("border-image: url(images/bezel-buttonp.png)") # changes image for button animation
            self.buttons_src[1].released.connect(lambda: self.radio(1))
            self.buttons_src[2].released.connect(lambda: self.mediaplayer(2))
            self.buttons_src[3].released.connect(lambda: self.btplayer(3))
            self.buttons_src[4].released.connect(lambda: self.equaliser(4))
            self.buttons_src[5].released.connect(lambda: self.settings(5))
    
        def setSource(self):    #set the source buttons to listen
            for x in range (1 ,6):
                self.buttons_src[x].pressed.connect(lambda: self.src_button_active(x))
            if self.init == True:
                self.setVolume()
    
        def setVolume(self): # set the volume buttons to listen
            self.buttons_src[6].setStyleSheet("border-image: url(images/bezel-button.png)") # resets button animation
            self.buttons_src[6].pressed.connect(lambda: self.voldn())
            self.buttons_src[6].released.connect(lambda: self.volhide(2))                   # or should I use clicked here?
            self.buttons_src[7].setStyleSheet("border-image: url(images/bezel-button.png)")
            self.buttons_src[7].pressed.connect(lambda: self.volup())
            self.buttons_src[7].released.connect(lambda: self.volhide(2))
            self.init = False
    
        def volhide(self,waittime): # This should be done parallel with the return to setVolume after volume button has been released. Has to terminate itself too.
            self.buttons_src[6].setStyleSheet("border-image: url(images/bezel-button.png")
            self.buttons_src[7].setStyleSheet("border-image: url(images/bezel-button.png")
            print ("volume hide waiting time")     #just debugging info
            time.sleep(waittime)
            self.osd_vol_widget.hide()
            print ("Volume widget hidden")
    
        def voldn(self):
            print("voldn pressed",self.volume)
            self.buttons_src[6].setStyleSheet("border-image: url(images/bezel-buttonp.png")
            if self.osd_vol_widget.isHidden():
                self.osd_vol_widget.show()
            self.volume -= self.volstep
            self.osd_vol_bar.setValue(self.volume)
            time.sleep(1/self.volrate)
    
        def volup(self):
            print("volup pressed",self.volume)
            self.buttons_src[7].setStyleSheet("border-image: url(images/bezel-buttonp.png")
            if self.osd_vol_widget.isHidden():
                self.osd_vol_widget.show()
            self.volume += self.volstep
            self.osd_vol_bar.setValue(self.volume)
            time.sleep(1/self.volrate)
    
    
    

    I would like to request that a volunteer mute the code to make volhide a parallel thread. It will also help me greatly in understanding Qt multithreading. I have succeeded in multithreading non-Qt code, i can drive 5 SPI displays simultaneously now.

    And of course, I appreciate any and all input on this matter. Thank you in advance.

    JonBJ 1 Reply Last reply
    0
    • P Paul_F

      Hi all,

      I am relatively new to Python and Qt. I have recently (3 months ago) started learning python, and I am getting the hang of it.

      I am not one to read the manual beginning to end; I sort of dove off in the deep end. My first objective is to create a car media player, with all common functions like radio, media player, bluetooth, etc. My progress so far is that I have built the GUI in PyQt5(just moved to Qt6), programmed the Qpushbuttons etc, and I have made a start on the volume control. This is where I am getting stumped. I plan to use classic On-screen-display for the volume and other direct influence settings (like brightness). I have managed to get the volume to neatly decrease and increase, but when I release the button, I need the source buttons to immediately be re-enabled and listen for presses, while in a parallel thread a method is called to wait 3 seconds, and then remove the volume widget from screen (like in older tvs). I have studied QThread and tried a few times, but it keeps botching and I have had numerous different errors. I have included the code without the attempt for multithreading and I have annotated important parts:

      class Carma(QtWidgets.QWidget):
      
          def __init__(self,init):
              super().__init__()
      
              # Setup main components and variables
              # -----------------------------------
              # *snipped out widget code, not important for this question
      
              self.init=init
              setSource()
      
      
          def src_button_active(self ,num):
              self.buttons_src[num].setStyleSheet("border-image: url(images/bezel-buttonp.png)") # changes image for button animation
              self.buttons_src[1].released.connect(lambda: self.radio(1))
              self.buttons_src[2].released.connect(lambda: self.mediaplayer(2))
              self.buttons_src[3].released.connect(lambda: self.btplayer(3))
              self.buttons_src[4].released.connect(lambda: self.equaliser(4))
              self.buttons_src[5].released.connect(lambda: self.settings(5))
      
          def setSource(self):    #set the source buttons to listen
              for x in range (1 ,6):
                  self.buttons_src[x].pressed.connect(lambda: self.src_button_active(x))
              if self.init == True:
                  self.setVolume()
      
          def setVolume(self): # set the volume buttons to listen
              self.buttons_src[6].setStyleSheet("border-image: url(images/bezel-button.png)") # resets button animation
              self.buttons_src[6].pressed.connect(lambda: self.voldn())
              self.buttons_src[6].released.connect(lambda: self.volhide(2))                   # or should I use clicked here?
              self.buttons_src[7].setStyleSheet("border-image: url(images/bezel-button.png)")
              self.buttons_src[7].pressed.connect(lambda: self.volup())
              self.buttons_src[7].released.connect(lambda: self.volhide(2))
              self.init = False
      
          def volhide(self,waittime): # This should be done parallel with the return to setVolume after volume button has been released. Has to terminate itself too.
              self.buttons_src[6].setStyleSheet("border-image: url(images/bezel-button.png")
              self.buttons_src[7].setStyleSheet("border-image: url(images/bezel-button.png")
              print ("volume hide waiting time")     #just debugging info
              time.sleep(waittime)
              self.osd_vol_widget.hide()
              print ("Volume widget hidden")
      
          def voldn(self):
              print("voldn pressed",self.volume)
              self.buttons_src[6].setStyleSheet("border-image: url(images/bezel-buttonp.png")
              if self.osd_vol_widget.isHidden():
                  self.osd_vol_widget.show()
              self.volume -= self.volstep
              self.osd_vol_bar.setValue(self.volume)
              time.sleep(1/self.volrate)
      
          def volup(self):
              print("volup pressed",self.volume)
              self.buttons_src[7].setStyleSheet("border-image: url(images/bezel-buttonp.png")
              if self.osd_vol_widget.isHidden():
                  self.osd_vol_widget.show()
              self.volume += self.volstep
              self.osd_vol_bar.setValue(self.volume)
              time.sleep(1/self.volrate)
      
      
      

      I would like to request that a volunteer mute the code to make volhide a parallel thread. It will also help me greatly in understanding Qt multithreading. I have succeeded in multithreading non-Qt code, i can drive 5 SPI displays simultaneously now.

      And of course, I appreciate any and all input on this matter. Thank you in advance.

      JonBJ Offline
      JonBJ Offline
      JonB
      wrote on last edited by
      #2

      @Paul_F
      Like every beginner here, who mostly seem to try to use threads from the outset, I would say: why are you wanting to use a thread? They are complex, and really often not needed, especially by beginners. And they always cause problems.

      while in a parallel thread a method is called to wait 3 seconds, and then remove the volume widget from screen (like in older tvs).

      Why any thread here? Use a QTimer. And in any case you will soon find that since Qt does not allow access to anything in the main UI thread from any other thread, without falling over in a big heap, you couldn't do what you said from another thread anyway....

      P 1 Reply Last reply
      2
      • JonBJ JonB

        @Paul_F
        Like every beginner here, who mostly seem to try to use threads from the outset, I would say: why are you wanting to use a thread? They are complex, and really often not needed, especially by beginners. And they always cause problems.

        while in a parallel thread a method is called to wait 3 seconds, and then remove the volume widget from screen (like in older tvs).

        Why any thread here? Use a QTimer. And in any case you will soon find that since Qt does not allow access to anything in the main UI thread from any other thread, without falling over in a big heap, you couldn't do what you said from another thread anyway....

        P Offline
        P Offline
        Paul_F
        wrote on last edited by
        #3

        @JonB Yes, I have already experienced that problem about the main UI, and if QTimer is the solution I will go with that.

        Do you have any suggestions for QTimer?

        Thank you.

        JonBJ 1 Reply Last reply
        0
        • P Paul_F

          @JonB Yes, I have already experienced that problem about the main UI, and if QTimer is the solution I will go with that.

          Do you have any suggestions for QTimer?

          Thank you.

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by JonB
          #4

          @Paul_F said in Multithreading puzzle:

          Do you have any suggestions for QTimer?

          Like what? Use it to implement exactly what you wrote. Use QTimer::singleShot(), set it off for 3 seconds from whatever moment you want it to start (some button being pressed or whatever), have its slot remove the widget.

          BTW, don't ever use Python's time.sleep() in your UI code as you presently have.

          1 Reply Last reply
          1
          • P Offline
            P Offline
            Paul_F
            wrote on last edited by
            #5

            Thnx again.

            I am learning. Can you explain why my use of time.sleep is unwanted?

            JonBJ 1 Reply Last reply
            0
            • SGaistS Offline
              SGaistS Offline
              SGaist
              Lifetime Qt Champion
              wrote on last edited by
              #6

              Hi,

              Because it will block the event loop and this freeze your application.

              Interested in AI ? www.idiap.ch
              Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

              1 Reply Last reply
              1
              • P Paul_F

                Thnx again.

                I am learning. Can you explain why my use of time.sleep is unwanted?

                JonBJ Offline
                JonBJ Offline
                JonB
                wrote on last edited by JonB
                #7

                @Paul_F
                You have slots calling time.sleep(). While that is executing the Qt event loop won't run. Your UI will be blocked/unresponsive when the button is pressed causing this code to run. A QTimer will not do that. This is an event-driven approach.

                P 1 Reply Last reply
                1
                • JonBJ JonB

                  @Paul_F
                  You have slots calling time.sleep(). While that is executing the Qt event loop won't run. Your UI will be blocked/unresponsive when the button is pressed causing this code to run. A QTimer will not do that. This is an event-driven approach.

                  P Offline
                  P Offline
                  Paul_F
                  wrote on last edited by
                  #8

                  @JonB

                  Your explanation means a lot to me, thank you very much. I understand now. It is purely a Qt event loop thing. The time.sleep command was originally incorporated in methods I tried to call with Qthread, but I removed that code and left the time.sleep in. That probably explains it.

                  1 Reply Last reply
                  1

                  • Login

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