开发者

PYQT - How to cancel loop in my GUI using cancel button?

开发者 https://www.devze.com 2023-03-31 18:23 出处:网络
I have been strugling with this for some time. I will try to explain what i want to do , maybe you guys could help me.

I have been strugling with this for some time. I will try to explain what i want to do , maybe you guys could help me.

So lets say I have GUI with status label on it and Two loops that look like this:

for _a in range(3000):
     self.changeLabel('_a= '+ str(_a))

for _b in range(5000):
     self.changeLabel('_b=' + str(_b))

def changeLabel(self,_text):
     self.ui.STATUS.setText(_text)   <---ui is a GUI where label is placed. 
     APP.processEvents()               

I want a label (STATUS) to be updated with a results after START being pressed (done), and i开发者_高级运维 want to cancel loops when STOP button is being pressed.

How to achieve this using Threads, QEventloop or any other way (if exists). I am pretty much beginner with PyQT so if someone have any idea - please share.

Thanks.


The easiest way to achieve this is by using generators, and an "idle timer".

The idea is to turn your loop into a generator using the yield keyword, so that you can trigger each iteration from outside using next(). Then you use Qt's low-level timer (startTimer(), killTimer(), and timerEvent()) to create a timer with interval zero, that is called every time there are no more events to process, to run the next loop iteration. This gives you the opportunity to react to GUI events during your loop, e.g., to handle the stop button clicked() signal.

class MyWidget(QWidget):  # Or whatever kind of widget you are creating

    def __init__(self, parent, **kwargs):
        super(MyWidget, self).__init__(parent, **kwargs)
        # ... Create your widgets, connect signals and slots, etc.
        self._generator = None
        self._timerId = None

    def loopGenerator(self):
        # Put the code of your loop here
        for a in range(3000):
            self.ui.STATUS.setText("a=" + a)
            # No processEvents() needed, just "pause" the loop using yield
            yield

    def start(self):  # Connect to Start-button clicked()
        self.stop()  # Stop any existing timer
        self._generator = self.loopGenerator()  # Start the loop
        self._timerId = self.startTimer(0)   # This is the idle timer

    def stop(self):  # Connect to Stop-button clicked()
        if self._timerId is not None:
            self.killTimer(self._timerId)
        self._generator = None
        self._timerId = None

    def timerEvent(self, event):
        # This is called every time the GUI is idle.
        if self._generator is None:
            return
        try:
            next(self._generator)  # Run the next iteration
        except StopIteration:
            self.stop()  # Iteration has finshed, kill the timer


Ferdinand's answer is nice in that it avoids the use of processEvents() to make your own event loop. However, I think there's a much simpler solution: why not just set a flag when the stop button is pressed and exit the loop if the flag has been set? Something like:

def stopClicked(self):
    self.stop = True

for _a in range(3000):
    self.changeLabel('_a= '+ str(_a))    
    if self.stop:
        self.stop = False
        break

def changeLabel(self,_text):
    self.ui.STATUS.setText(_text)   <---ui is a GUI where label is placed. 
    APP.processEvents()


I would like to give my solution to this problem.

I had a similar issue while creating a loop for taking real time photos from a sensor using PyQt.

I found that using QTimer was the only working solution for me, having tried the yield one and the check for self.stop is True.

Since the thread is very outdated, I'm going to use another example which is very similar to the one published here.

We want to init a counter with some kind of signal (in this case a keystroke) and then we want to stop it with another keystroke.

We are going to use the QTimer object, upgrading a counter during the timeout()signal which is emitted by the Timer.

class MyExample(QObject):

    timer = QTimer()
    cont = 0

    def __init__(self):

        super(QObject, self).__init__()

        # !!! IMPORTANT PART !!!
        # Here we connect the timeout of the timer to the count
        # function!
        self.timer.timeout.connect(self.cont)

    def keyEvent(self, e):

        # Here we connect the keystroke to the event 
        # on the object!
        if e.key() == Qt.Key_B:

            self.start()

        elif e.key() == Qt.Key_S:

            self.stop()

    def start(self):
        # Number of milliseconds the timer waits until the timeout
        self.timer.start(1000)

    def stop(self):
        self.timer.stop()

    def count(self):
        # Increase the counter on timeout
        self.cont = self.cont + 1
        print self.cont

This worked, at least for me! Hope this helped someone!

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号