На самом деле ответ представляет собой комбинацию того, что three_pineapples и OP опубликовали в своих ответах:
- Как отметил three_pineapples, основная проблема заключается в том, что controlSocket(), который вызывается циклом обработки событий потока, когда выдается сигнал запуска, не возвращается до тех пор, пока не будет вызван метод stop(). Однако в дизайне OP метод stop() может вызываться Qt только в том случае, если поток сокета может обрабатывать события (поскольку именно так отправляются сигналы между потоками через циклы событий). Это невозможно, пока controlSocket() занят зацикливанием и спящим режимом.
- Чтобы поток завершился, его цикл обработки событий должен быть остановлен.
Первую проблему необходимо устранить, либо разрешив потоку обрабатывать события во время цикла, либо используя таймер вместо цикла. Обработка событий показана в этом фрагменте кода на основе кода OP:
import time
from PyQt5.QtWidgets import QMainWindow, QWidget, QPushButton, QApplication, QHBoxLayout
from PyQt5.QtCore import QThread, QObject, pyqtSignal
class MyClass(QWidget):
def __init__(self, parent=None):
super().__init__()
self.resize(250, 150)
self.setWindowTitle('Simple')
# Setup thread for the socket control
self.socketController = SocketController()
self.simulThread = QThread()
self.socketController.moveToThread(self.simulThread)
self.simulThread.started.connect(self.socketController.controlSocket)
self.simulThread.finished.connect(self.deadThread)
# Bind controls to events
self.buttonConnect = QPushButton('connect')
self.buttonConnect.clicked.connect(self.simulThread.start)
self.buttonDisconnect = QPushButton('disconnect')
self.buttonDisconnect.clicked.connect(self.socketController.stop)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(self.buttonConnect)
hbox.addWidget(self.buttonDisconnect)
self.setLayout(hbox)
def deadThread(self, data):
print("THREAD IS DEAD.")
class SocketController(QObject):
finished = pyqtSignal()
def __init__(self):
print('initialized')
super().__init__()
self.run = True
def controlSocket(self):
# setup the socket
print('control socket starting')
while self.run:
# do socket stuff
app.processEvents()
print('control socket iterating')
time.sleep(1)
# close the socket
self.finished.emit()
print('control socket done')
def stop(self):
print('stop pending')
self.run = False
app = QApplication([])
mw = MyClass()
mw.move(300, 300)
mw.show()
app.exec()
QTimer немного сложнее, но идея состоит в том, чтобы controlSocket() выполнял только одну итерацию цикла и многократно вызывал его через QTimer:
QTimer.singleShot(0, self.controlSocket)
а также
def controlSocket(self):
# do socket stuff, then:
if self.run:
QTimer.singleShot(1000, self.controlSocket)
else:
self.finished.emit()
Любой из вышеперечисленных подходов устраняет первую проблему и позволяет SocketController
прекратить выполнение своей работы, связанной с сокетом.
Вторая проблема заключается в том, что даже после того, как контроллер сокета выполнил свою работу, цикл потока все еще выполняется (эти два цикла независимы). Чтобы цикл обработки событий завершился, его необходимо остановить с помощью метода quit() потока. Уход и остановку следует выполнять вместе:
class MyClass(QWidget):
def __init__(self, parent=None):
...
self.buttonDisconnect = QPushButton('disconnect')
self.buttonDisconnect.clicked.connect(self.stop)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(self.buttonConnect)
hbox.addWidget(self.buttonDisconnect)
self.setLayout(hbox)
def stop(self):
self.simulThread.quit()
self.socketController.stop()
person
Oliver
schedule
05.01.2016