PyQt5: QSlider valueChanged event with delay
Asked Answered
I

2

5

I'm using QSlider in my GUI application in order to perform a heavy task after value changed in the QSlider. I'm doing that as follows.

self.slider.valueChanged.connect(self.value_changed)  # Inside __init__() function

def value_changed(self):  # Inside the class
    # Do the heavy task

But I can't change the value of the slider smoothly because the heavy task is running every time I change the value.

What I need is to run the heavy task after the value changed but only if the value of the slider is not changing for a while.

I can't figure out how to do this in python. Any help..?

Infanticide answered 1/4, 2017 at 3:28 Comment(1)
You can explain better, maybe you need to use QThreadCooperative
A
3

You can use startTimer/killTimer to delay your task:

class Foo(QWidget):
    def __init__(self):
        super().__init__()
        self.timer_id = -1
        self.slider = QSlider(self)
        self.slider.setMinimum(0)
        self.slider.setMaximum(100)
        self.slider.valueChanged.connect(self.value_changed)

    def timerEvent(self, event):
        self.killTimer(self.timer_id)
        self.timer_id = -1
        heavy_task()

    def value_changed(self):
        if self.timer_id != -1:
            self.killTimer(self.timer_id)

        self.timer_id = self.startTimer(3000)

so, as can you see we restart timer every time when user something change, so if 3000 millseconds not expires since last change heavy_task not run,

but any way it will be running in main thread, so for some time interface freeze for user, so you should use QThread in timerEvent to not have interface that not freeze during heavy_task execution.

Ashurbanipal answered 1/4, 2017 at 4:8 Comment(0)
D
6

I am no expert, and I arrive long after the battle, but i needed a thing like that too, and i don't understand a thing about the timers. It worked fine for one slider, but i needed 3. So I came up with this solution : when the slider is pressed, I disconnect the valueChanged slot, and when the slider is released, I reconnect it and I throw a valueChanged signal, like this :

    self.sldAZap.valueChanged.connect(self.sliderChanged)
    self.sldAZap.sliderPressed.connect(self.sldDisconnect)
    self.sldAZap.sliderReleased.connect(self.sldReconnect)

def sldDisconnect(self):
    self.sender().valueChanged.disconnect()

def sldReconnect(self):
    self.sender().valueChanged.connect(self.sliderChanged)
    self.sender().valueChanged.emit(self.sender().value())

def sliderChanged(self):
    print(self.sender().objectName() + " : " + str(self.sender().value())

With this solution, there is no delay between the moment the mouse is released and the execution of the code, and the code is executed just one time.

I hope I am clear enough and it may help someone.

Dint answered 22/7, 2018 at 20:31 Comment(0)
A
3

You can use startTimer/killTimer to delay your task:

class Foo(QWidget):
    def __init__(self):
        super().__init__()
        self.timer_id = -1
        self.slider = QSlider(self)
        self.slider.setMinimum(0)
        self.slider.setMaximum(100)
        self.slider.valueChanged.connect(self.value_changed)

    def timerEvent(self, event):
        self.killTimer(self.timer_id)
        self.timer_id = -1
        heavy_task()

    def value_changed(self):
        if self.timer_id != -1:
            self.killTimer(self.timer_id)

        self.timer_id = self.startTimer(3000)

so, as can you see we restart timer every time when user something change, so if 3000 millseconds not expires since last change heavy_task not run,

but any way it will be running in main thread, so for some time interface freeze for user, so you should use QThread in timerEvent to not have interface that not freeze during heavy_task execution.

Ashurbanipal answered 1/4, 2017 at 4:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.