PYQT - How to cancel loop in my GUI using cancel button?
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!
精彩评论