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

发布于 2024-12-01 14:52:15 字数 530 浏览 0 评论 0原文

我已经为此苦苦挣扎了一段时间。我会尽力解释我想做什么,也许你们可以帮助我。

假设我有带有状态标签的 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()               

我希望在按下“开始”(完成)后使用结果更新标签(状态),并且我想在按下“停止”按钮时取消循环。

如何使用线程、QEventloop 或任何其他方式(如果存在)来实现这一点。我几乎是 PyQT 的初学者,所以如果有人有任何想法 - 请分享。

谢谢。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

蒗幽 2024-12-08 14:52:15

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

这个想法是使用 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

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
简单气质女生网名 2024-12-08 14:52:15

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

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()
鹿港小镇 2024-12-08 14:52:15

我想针对这个问题给出我的解决方案。

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

这有效,至少对我来说!
希望这对某人有帮助!

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!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文