#python #multithreading #qthread #pyside2
#python #многопоточность #qthread #pyside2
Вопрос:
Я хочу использовать QThread в Pyside2 для передачи данных между дочерними потоками. Я хочу написать приложение, которое считывает последовательные данные в readThread (дочерний поток), записывает данные, прочитанные в writeThread (дочерний поток), и отображает последовательные данные в функции plot (основной поток). Я написал демо для этого, и код выглядит следующим образом:
import threading
from PySide2.QtWidgets import QApplication, QPushButton, QWidget, QVBoxLayout, QLCDNumber, QMessageBox
from PySide2.QtCore import *
import sys
import os
# add environment path of pyside2
envpath = r'E:anacondaenvstensorflowLibsite-packagesPySide2pluginsplatforms'
os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = envpath
sec = 0 # counter
# child thread, representing the process of read serial data
class ReadThread(QThread):
timer = Signal() # create a signal
end = Signal() # create a signal
def run(self):
print("ReadThread1", threading.current_thread()) # print thread ID
while True:
self.sleep(1) # sleep 1s
print("ReadThread2", threading.current_thread())
if sec == 2:
self.end.emit() # emit end signal
break
self.timer.emit()
# chiald thead2, represents write data to txt file
class WriteThread(QThread):
def run(self):
print("WriteThread", threading.current_thread()) # print thread ID
class Counter(QWidget):
def __init__(self, parent=None):
super(Counter, self).__init__(parent)
self.setWindowTitle("qthreading test")
self.resize(300, 120)
# add a layout
layout = QVBoxLayout()
self.lcdNumber = QLCDNumber()
layout.addWidget(self.lcdNumber)
# add a pushbutton
button = QPushButton('start')
layout.addWidget(button)
self.readThread = ReadThread() # workthread
self.writeThread = WriteThread() # writeThread
self.readThread.timer.connect(self.plot)
self.readThread.timer.connect(self.writeThread.run) #I want to connect readThread and writeThread
self.readThread.end.connect(self.end)
button.clicked.connect(self.work)
self.setLayout(layout)
# The callback function of readThread (Main Thread), representing the process of plot serial data
def plot(self):
global sec
sec = 1
self.lcdNumber.display(sec)
print("countTime", threading.current_thread())
def end(self):
QMessageBox.information(self, "message", "stop")
# start readTread and WriteThread when button is clicked
def work(self):
self.readThread.start()
self.writeThread.start()
if __name__ == "__main__":
app = QApplication(sys.argv)
form = Counter()
form.show()
sys.exit(app.exec_())
Когда я запустил программу, результат был следующим:
**ReadThread1 <_DummyThread(Dummy-1, started daemon 7556)>
WriteThread <_DummyThread(Dummy-2, started daemon 2552)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 7556)>
WriteThread <_MainThread(MainThread, started 7536)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 7556)>
WriteThread <_MainThread(MainThread, started 7536)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 7556)>**
Как вы можете видеть, сначала и readThread, и writeThread находятся в дочернем потоке, но при отправке сигнала () поток записи запускается в основном потоке, в то время как поток чтения находится в дочернем потоке. Я не знаю, что не так с моими кодами. Почему операция не выполняется в дочернем потоке, когда writeThread отвечает на signal() ? Было бы неплохо, если бы вы могли изменить его непосредственно в моем коде.
Ответ №1:
В вашей логике есть основная ошибка: вы не должны напрямую вызывать метод run QThread, поскольку он косвенно вызывается методом start, поэтому вы замечаете, что вначале метод «run» выполняется во вторичном потоке, а затем, когда вы вызываете его напрямую, он будет выполненвыполняется в потоке, к которому принадлежит QObject (WriteThread), который является основным потоком.
Если вы хотите выполнить задачу во вторичном потоке в зависимости от сигнала, вы должны создать QObject и переместить его в новый поток:
class ReadThread(QThread):
timer = Signal() # create a signal
end = Signal() # create a signal
def run(self):
print("ReadThread1", threading.current_thread()) # print thread ID
while True:
self.sleep(1) # sleep 1s
print("ReadThread2", threading.current_thread())
if sec == 2:
self.end.emit() # emit end signal
break
self.timer.emit()
class WriteWorker(QObject):
def run_task(self):
print("WriteThread", threading.current_thread())
class Counter(QWidget):
def __init__(self, parent=None):
super(Counter, self).__init__(parent)
self.setWindowTitle("qthreading test")
self.resize(300, 120)
# add a layout
layout = QVBoxLayout()
self.lcdNumber = QLCDNumber()
layout.addWidget(self.lcdNumber)
# add a pushbutton
button = QPushButton("start")
layout.addWidget(button)
self.readThread = ReadThread() # workthread
self.writeThread = QThread() # writeThread
self.readThread.timer.connect(self.plot)
self.worker = WriteWorker()
self.worker.moveToThread(self.writeThread)
self.readThread.timer.connect(self.worker.run_task)
self.readThread.end.connect(self.end)
button.clicked.connect(self.work)
self.setLayout(layout)
def plot(self):
global sec
sec = 1
self.lcdNumber.display(sec)
print("countTime", threading.current_thread())
def end(self):
QMessageBox.information(self, "message", "stop")
def work(self):
self.readThread.start()
self.writeThread.start()
if __name__ == "__main__":
app = QApplication(sys.argv)
form = Counter()
form.show()
ret = app.exec_()
form.writeThread.quit()
form.writeThread.wait()
sys.exit(ret)
Вывод:
ReadThread1 <_DummyThread(Dummy-1, started daemon 140088050697792)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 140088050697792)>
WriteThread <_DummyThread(Dummy-2, started daemon 140088042305088)>
countTime <_MainThread(MainThread, started 140088409069376)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 140088050697792)>
countTime <_MainThread(MainThread, started 140088409069376)>
WriteThread <_DummyThread(Dummy-2, started daemon 140088042305088)>
ReadThread2 <_DummyThread(Dummy-1, started daemon 140088050697792)>