PYQT - 如何使用取消按钮取消GUI中的循环?

时间:2011-08-27 17:14:43

标签: python multithreading pyqt qeventloop

我一直在讨价还价。我会尝试解释我想做什么,也许你们可以帮助我。

所以我想说我的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()               

我希望在START按下(完成)后用标签(STATUS)更新结果,并且我想在按下STOP按钮时取消循环。

如何使用Threads,QEventloop或任何其他方式(如果存在)实现此目的。我几乎是PyQT的初学者,所以如果有人有任何想法 - 请分享。

感谢。

3 个答案:

答案 0 :(得分:1)

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

我们的想法是使用yield关键字将您的循环转换为生成器,以便您可以使用next()从外部触发每次迭代。然后使用Qt的低级计时器(startTimer()killTimer()timerEvent())创建一个间隔为零的计时器,每次没有更多要处理的事件时调用该计时器,运行下一个循环迭代。这使您有机会在循环期间对GUI事件做出反应,例如,处理停止按钮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

答案 1 :(得分:1)

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 :(得分:-1)

我想解决这个问题。

在创建使用PyQt从传感器拍摄实时照片的循环时,我遇到了类似的问题。

我发现使用QTimer对我来说是唯一可行的解​​决方案,尝试过yield 1并且检查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

这至少对我来说很有用! 希望这有助于某人!