PYQT - Как отменить цикл в моем графическом интерфейсе с помощью кнопки отмены?

1

Я боролся с этим в течение некоторого времени. Я попытаюсь объяснить, что я хочу сделать, может быть, вы, ребята, могли мне помочь.

Поэтому давайте скажем, что у меня есть 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) обновлялась с результатами после нажатия кнопки СТАРТ (завершена), и я хочу отменить циклы при нажатии кнопки STOP.

Как достичь этого с помощью Threads, QEventloop или любым другим способом (если существует). Я в значительной степени новичок с PyQT, поэтому, если у кого-то есть идея - пожалуйста, поделитесь.

Благодарю.

Теги:
multithreading
pyqt
qeventloop

3 ответа

1

Ответ Фердинанда хорош тем, что он избегает использования 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()
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
  • 0
    1. Не проще ли использовать QTimer с интервалом 0, а не низкоуровневые функции таймера QObject? 2. Это будет много событий для небольшой обработки. Может быть, делать от 10 до 100 итераций на событие вместо одной?
  • 0
    @Macke: Думаю, вы тоже можете использовать QTimer . Не понимаю, как это могло бы облегчить ситуацию. И да, вы могли бы сделать несколько итераций , прежде чем yield ING, но вы должны вызвать processEvents() для обновления графического интерфейса между ними. Используя только одну итерацию, вам не нужно беспокоиться об этом. Обратите внимание, что это всего лишь «доказательство концепции», задача ОП - настроить его под свои нужды.
Показать ещё 5 комментариев
-2

Я хотел бы дать свое решение этой проблемы.

У меня была аналогичная проблема, создавая цикл для съемки фотографий в реальном времени с сенсора с помощью PyQt.

Я обнаружил, что использование QTimer было единственным рабочим решением для меня, попробовав выход, и проверка на 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

Это сработало, по крайней мере, для меня! Надеюсь, это помогло кому-то!

  • 0
    Это не относится к опубликованному вопросу. QTimer, возможно, был единственным решением для вас, но это не делает его решением для OP. Это должен быть комментарий.
  • 0
    Извините, я думал, что это было альтернативное решение проблемы, потому что это было единственное рабочее решение для меня.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню