PYQT - 如何通过取消按钮在我的GUI中终止循环?
我在这个问题上纠结了很久。让我试着解释一下我想做的事情,也许你们能帮我。
假设我有一个图形界面,上面有一个状态标签,还有两个循环,像这样:
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()
我希望在按下“开始”按钮后,状态标签(STATUS)能更新显示结果,并且在按下“停止”按钮时能取消这两个循环。
我该如何使用线程、QEventloop或者其他方法来实现这个功能呢?我对PyQT还是个新手,所以如果有人有想法,请分享一下。
谢谢!
3 个回答
我想分享一下我对这个问题的解决方案。
我在用PyQt创建一个循环来实时从传感器拍照时,遇到了类似的问题。
我发现使用QTimer是唯一有效的解决办法,之前我尝试过使用yield和检查self.stop是否为True的方法,但都不行。
因为这个讨论已经有点过时了,所以我打算用一个和这里发布的例子非常相似的例子。
我们想要用某种信号(在这个例子中是按键)来初始化一个计数器,然后再用另一个按键来停止它。
我们将使用QTimer
对象,在timeout()
信号发出时更新计数器,这个信号是由定时器发出的。
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
这个方法对我来说是有效的!希望这能帮助到某些人!
Ferdinand的回答很好,因为它避免了使用processEvents()来创建自己的事件循环。不过,我觉得有一个更简单的办法:为什么不在按下停止按钮时设置一个标志,然后在循环中检查这个标志,如果标志被设置了就退出循环呢?可以这样做:
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()
实现这个功能最简单的方法是使用生成器和一个“空闲计时器”。
具体来说,你可以把你的循环变成一个生成器,使用 yield
这个关键词,这样你就可以通过外部调用 next()
来触发每一次循环。接着,你可以使用 Qt 的低级计时器(startTimer()
、killTimer()
和 timerEvent()
)来创建一个间隔为零的计时器,这个计时器会在没有更多事件需要处理时被调用,从而运行下一次循环。这种方式让你可以在循环中响应图形界面的事件,比如处理停止按钮的 clicked()
信号。
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