Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Getting unexpected results with QTimer.



  • I am hoping someone can help me with this. The code below controls an On screen display volume widget. A QTimer is started to wait 3 seconds before hiding the widget when a button is released. If a volume button is pressed again within those 3 seconds, the timer must be killed.

    If I press a button repeatedly instead of held down, something weird happens. it's as if none of the timers have been killed; If I press a button, say 5 times fast, the widget hides 3 seconds after the first button press, and repeats the print message 4 times too. In other words, 5 different timers are running. It also has consequences for the widget, it starts flickering after three seconds. When the button is held down, the loop performs flawlessly and does not initiate a timer until the button is released - Even though the autorepeats simulates a button press plus a button release. I am a bit stumped, who can help me here?

    Thank you.

    The code in question:

    class Carma(QtWidgets.QWidget):
    
        def __init__(self,init):
            super().__init__()
    
            if self.init == True:
                self.volstep = 1  # Volume step
                self.osd_delaytime = 3  # seconds
                self.mainvolume = 50  # startup volume
                self.volrate = 20  # volume change speed when using +-
                self.choice = -1
                self.vol_delay_ms = 500
                self.vol_interval_ms = 100
                self.timer = QtCore.QTimer()
                self.init_ui()
    
    
    
        def setVolume(self):
            vol_int_ms = int((1/self.volrate)*1000)
            self.buttons_src[6].setStyleSheet("border-image: url(images/bezel-button.png)")
            self.buttons_src[6].setAutoRepeat(True)
            self.buttons_src[6].setAutoRepeatDelay(self.vol_delay_ms)
            self.buttons_src[6].setAutoRepeatInterval(vol_int_ms)
            self.buttons_src[6].pressed.connect(lambda: self.voldn())
            self.buttons_src[6].clicked.connect(lambda: self.volhidetimer(2))
            self.buttons_src[7].setStyleSheet("border-image: url(images/bezel-button.png)")
            self.buttons_src[7].setAutoRepeat(True)
            self.buttons_src[7].setAutoRepeatDelay(self.vol_delay_ms)
            self.buttons_src[7].setAutoRepeatInterval(vol_int_ms)
            self.buttons_src[7].pressed.connect(lambda: self.volup())
            self.buttons_src[7].clicked.connect(lambda: self.volhidetimer(2))
            self.init = False
    
        def voldn(self):
            self.timer.stop()
            print("voldn pressed", self.mainvolume)
            self.buttons_src[6].setStyleSheet("border-image: url(images/bezel-buttonp.png)")
            if self.osd_vol_widget.isHidden():
                self.osd_vol_widget.show()
            if 1 <= self.mainvolume <= 100:
                self.mainvolume -= self.volstep
                self.osd_vol_bar.setValue(self.mainvolume)
                self.osd_vol_label.setText(str(self.mainvolume))
    
        def volup(self):
            self.timer.stop()
            print("volup pressed", self.mainvolume)
            self.buttons_src[7].setStyleSheet("border-image: url(images/bezel-buttonp.png)")
            if self.osd_vol_widget.isHidden():
                self.osd_vol_widget.show()
            if 0 <= self.mainvolume <= 99:
                self.mainvolume += self.volstep
                self.osd_vol_bar.setValue(self.mainvolume)
                self.osd_vol_label.setText(str(self.mainvolume))
    
        def volhidetimer(self, delaytime):
            if not any(self.buttons_src[i].isDown() for i in (6, 7)):
                self.buttons_src[6].setStyleSheet("border-image: url(images/bezel-button.png)")
                self.timer.singleShot(delaytime * 1000, lambda: self.volhide())
                print("Start timer volume hide")
    
        def volhide(self):
            if not any(self.buttons_src[i].isDown() for i in (6, 7)):
                self.osd_vol_widget.hide()
                print("Volume widget hidden")
    

  • Lifetime Qt Champion

    Hi,

    The singleShot method is a class function. You are creating a new independent single shot timer each time you call it.



  • @SGaist Understood. How can I prevent this?


  • Lifetime Qt Champion

    Make set your timer as single shot and just restart it.



  • @Paul_F The implementation that @SGaist points to is something like:

    self.timer = QtCore.QTimer(singleShot=True)
    self.timer.timeout.connect(self.volhide)
    

    then change to:

    self.timer.setInterval(delaytime * 1000)
    self.timer.start()
    


  • @eyllanesc Thank you, but I just sorted it.

    instead of .singleshot I used setSingleShot and .timeout.connect and started it manually. Now it works, no more flickering.



  • @SGaist Thank you, it now works flawlessly!



  • @Paul_F That's what my code does:

    self.timer = QtCore.QTimer(singleShot=True)
    

    is the same as:

    self.timer = QtCore.QTimer()
    self.timer.setSingleShot(True)
    

    And please don't abuse lambda functions.



  • @eyllanesc I tend to get errors if I don't so I just put it in. I just started three months ago with python, with only 80's basic as a base. So cut me some slack ;)


Log in to reply