Я боролся с этим в течение некоторого времени. Я попытаюсь объяснить, что я хочу сделать, может быть, вы, ребята, могли мне помочь.
Поэтому давайте скажем, что у меня есть 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, поэтому, если у кого-то есть идея - пожалуйста, поделитесь.
Благодарю.
Ответ Фердинанда хорош тем, что он избегает использования 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()
) для создания таймера с нулевым интервалом, который вызывается каждый раз, когда больше нет процессов для обработки, для запуска следующей итерации цикла. Это дает вам возможность реагировать на события 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
Я хотел бы дать свое решение этой проблемы.
У меня была аналогичная проблема, создавая цикл для съемки фотографий в реальном времени с сенсора с помощью 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
Это сработало, по крайней мере, для меня! Надеюсь, это помогло кому-то!
yield
ING, но вы должны вызватьprocessEvents()
для обновления графического интерфейса между ними. Используя только одну итерацию, вам не нужно беспокоиться об этом. Обратите внимание, что это всего лишь «доказательство концепции», задача ОП - настроить его под свои нужды.