#python #pyqt #pyqt5 #qthread #qtimer
#python #pyqt #pyqt5 #qthread #qtimer
Вопрос:
Я использую PyQt 5 для приложения с графическим интерфейсом, и у меня возникла проблема с потоками.
Существует кнопка закрытия, и как только она нажата, запускается QTimer, а затем он ожидает в цикле while, который зависит от значения переменной, в которой увеличивается в обработчике QTimer.
Проблема в том, что таймер не запускается. Интересно, что если я прокомментирую цикл while, таймер работает правильно.
Вот код:
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QHBoxLayout
import sys
import time
class MyClass(QMainWindow):
myTimer = None
myCounter = 0
def __init__(self):
QMainWindow.__init__(self)
myButton = QPushButton(text='Close')
myButton.clicked.connect(self.myButtonClicked)
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
ly = QHBoxLayout()
ly.addWidget(myButton)
centralWidget.setLayout(ly)
self.myTimer = QTimer()
self.myTimer .timeout.connect(self.__myTimerHandler)
def myButtonClicked(self):
self.myCounter= 0
self.myTimer.start(1000)
print('Loop Start')
while self.myCounter < 10:
self.__DoNothing()
print('Loop END')
def __DoNothing(self):
print('Nothing')
#time.sleep(2)
def __myTimerHandler(self):
self.myCounter = self.myCounter 1
print('Counter:' str(self.myCounter))
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWin = MyClass()
mainWin.show()
sys.exit( app.exec_() )
Вывод:
Loop Start
Nothing
Nothing
Nothing
Nothing
Nothing
Nothing
Nothing
Как я уже объяснял, с этим кодом QTimer (мой таймер) не запускается. Но если я прокомментирую цикл while, QTimer запускается правильно, и обработчик вызывается каждую секунду.
Комментирование строки time.sleep в функции DoNothing не помогает.
Я могу себе представить, что это что-то о многопоточности и доступе к одной и той же переменной, но я понятия не имею, как это решить.
Комментарии:
1. В основном потоке Qt не должно происходить никакой функции блокировки или цикла : ваш таймер запущен, но он не может быть обработан, поскольку цикл событий Qt заблокирован в
while
. Просто проверьте переменную в__myTimerHandler
.2. @musicamante Спасибо за комментарий, теперь я понимаю, что это проблема, но идея этого заключалась в том, чтобы дождаться определенного количества повторений вызова обработчика, а затем выполнить другие действия, например, перед закрытием окна выполнить обратный отсчет от 20 до 0. а затем закройте окноокно.
3. Я не понимаю, в чем проблема: у вас уже работает счетчик, если вы хотите, чтобы он повторялся 20 раз, а затем останавливался (или делал что-либо еще), просто добавьте
if self.myCounter >= 20: ...
4. Я пытаюсь сказать, что в том виде, в котором вы предлагаете, я должен написать эту строку условия if в __myTimerHandler, однако я хотел бы сделать это в функции myButtonClicked . Еще одним преимуществом этой реализации является то, что вы можете использовать этот таймер в качестве задержки в нескольких функциях. Например, если вы хотите иметь определенную временную задержку между двумя строками кодов.
5. @Majidkhalili вставил
QApplication.processEvents()
ваш цикл while иtime.sleep(1)
вошел__DoNothing
. Это позволит обрабатывать события таймера во время выполнения кода, который в противном случае блокировал бы цикл событий.
Ответ №1:
Как упоминалось в комментариях @musicamante, «В основном потоке Qt никогда не должно происходить никакой функции блокировки или цикла». И в коде, приведенном в вопросе, поток фактически запускается, но из-за цикла блокировки обработчик не получает возможности быть вызванным.
Как упоминалось в комментариях @ekhumoro, решение заключается в обработке событий в цикле ожидания с использованием «QApplication.processEvents()». А также добавление функции ожидания в функцию «__DoNothing».
Таким образом, окончательное решение будет таким, как показано ниже:
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QHBoxLayout
import sys
import time
class MyClass(QMainWindow):
myTimer = None
myCounter = 0
def __init__(self):
QMainWindow.__init__(self)
myButton = QPushButton(text='Close')
myButton.clicked.connect(self.myButtonClicked)
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
ly = QHBoxLayout()
ly.addWidget(myButton)
centralWidget.setLayout(ly)
self.myTimer = QTimer()
self.myTimer .timeout.connect(self.__myTimerHandler)
def myButtonClicked(self):
self.myCounter= 0
self.myTimer.start(1000)
print('Loop Start')
while self.myCounter < 10:
self.__DoNothing()
QApplication.processEvents()
print('Loop END')
def __DoNothing(self):
print('Nothing')
time.sleep(0.250)
def __myTimerHandler(self):
self.myCounter = self.myCounter 1
print('Counter:' str(self.myCounter))
if __name__ == "__main__":
app = QApplication(sys.argv)
mainWin = MyClass()
mainWin.show()
sys.exit( app.exec_() )
И результат будет точно таким, как ожидалось, как показано ниже:
Loop Start
Nothing
Nothing
Nothing
Nothing
Counter:1
Nothing
Nothing
Nothing
Nothing
Counter:2
Nothing
Nothing
Nothing
Nothing
Counter:3
Nothing
Nothing
Nothing
Loop Start
Nothing
Nothing
Nothing
Nothing
Counter:1
Nothing
Nothing
Nothing
Nothing
Counter:2
Nothing
Nothing
Nothing
Nothing
Nothing
Counter:3
Nothing
Nothing
Nothing
Counter:4
Nothing
Nothing
Nothing
Nothing
Counter:5
Nothing
Nothing
Nothing
Nothing
Counter:6
Nothing
Nothing
Nothing
Nothing
Counter:7
Nothing
Nothing
Nothing
Nothing
Counter:8
Nothing
Nothing
Nothing
Nothing
Counter:9
Nothing
Nothing
Nothing
Nothing
Counter:10
Loop END
Loop END
Очевидно, что разница во времени между двумя обратными вызовами счетчиков не обязательно одинакова.