PYQT - 如何通过取消按钮在我的GUI中终止循环?

1 投票
3 回答
7815 浏览
提问于 2025-04-17 00:27

我在这个问题上纠结了很久。让我试着解释一下我想做的事情,也许你们能帮我。

假设我有一个图形界面,上面有一个状态标签,还有两个循环,像这样:

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 个回答

-1

我想分享一下我对这个问题的解决方案。

我在用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

这个方法对我来说是有效的!希望这能帮助到某些人!

2

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()
2

实现这个功能最简单的方法是使用生成器和一个“空闲计时器”。

具体来说,你可以把你的循环变成一个生成器,使用 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

撰写回答