我有这段代码(如果你有pyqt5,你应该可以自己运行):
import sys
import time
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
class Worker(QObject):
def __init__(self):
super().__init__()
self.thread = None
class Tab(QObject):
def __init__(self, _main):
super().__init__()
self._main = _main
class WorkerOne(Worker):
finished = pyqtSignal()
def __init__(self):
super().__init__()
@pyqtSlot(str)
def print_name(self, name):
for _ in range(100):
print("Hello there, {0}!".format(name))
time.sleep(1)
self.finished.emit()
self.thread.quit()
class SomeTabController(Tab):
def __init__(self, _main):
super().__init__(_main)
self.threads = {}
self._main.button_start_thread.clicked.connect(self.start_thread)
# Workers
self.worker1 = WorkerOne()
#self.worker2 = WorkerTwo()
#self.worker3 = WorkerThree()
#self.worker4 = WorkerFour()
def _threaded_call(self, worker, fn, *args, signals=None, slots=None):
thread = QThread()
thread.setObjectName('thread_' + worker.__class__.__name__)
# store because garbage collection
self.threads[worker] = thread
# give worker thread so it can be quit()
worker.thread = thread
# objects stay on threads after thread.quit()
# need to move back to main thread to recycle the same Worker.
# Error is thrown about Worker having thread (0x0) if you don't do this
worker.moveToThread(QThread.currentThread())
# move to newly created thread
worker.moveToThread(thread)
# Can now apply cross-thread signals/slots
#worker.signals.connect(self.slots)
if signals:
for signal, slot in signals.items():
try:
signal.disconnect()
except TypeError: # Signal has no slots to disconnect
pass
signal.connect(slot)
#self.signals.connect(worker.slots)
if slots:
for slot, signal in slots.items():
try:
signal.disconnect()
except TypeError: # Signal has no slots to disconnect
pass
signal.connect(slot)
thread.started.connect(lambda: fn(*args)) # fn needs to be slot
thread.start()
@pyqtSlot()
def _receive_signal(self):
print("Signal received.")
@pyqtSlot(bool)
def start_thread(self):
name = "Bob"
signals = {self.worker1.finished: self._receive_signal}
self._threaded_call(self.worker1, self.worker1.print_name, name,
signals=signals)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Thread Example")
form_layout = QVBoxLayout()
self.setLayout(form_layout)
self.resize(400, 400)
self.button_start_thread = QPushButton()
self.button_start_thread.setText("Start thread.")
form_layout.addWidget(self.button_start_thread)
self.controller = SomeTabController(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
_main = MainWindow()
_main.show()
sys.exit(app.exec_())
但是WorkerOne
仍会阻止我的GUI线程,当WorkerOne.print_name
正在运行时,窗口没有响应。
我最近一直在研究很多关于QThreads的事情,我不确定为什么这根据我已经完成的研究而没有工作。
是什么给出的?
答案 0 :(得分:2)
问题是由与lambda方法的连接引起的,因为这个lambda不是Worker的一部分,因此它不会在新线程上运行。解决方案是使用functools.partial
:
from functools import partial
...
thread.started.connect(partial(fn, *args))
完整代码:
import sys
import time
from functools import partial
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
class Worker(QObject):
def __init__(self):
super().__init__()
self.thread = None
class Tab(QObject):
def __init__(self, _main):
super().__init__()
self._main = _main
class WorkerOne(Worker):
finished = pyqtSignal()
def __init__(self):
super().__init__()
@pyqtSlot(str)
def print_name(self, name):
for _ in range(100):
print("Hello there, {0}!".format(name))
time.sleep(1)
self.finished.emit()
self.thread.quit()
class SomeTabController(Tab):
def __init__(self, _main):
super().__init__(_main)
self.threads = {}
self._main.button_start_thread.clicked.connect(self.start_thread)
# Workers
self.worker1 = WorkerOne()
#self.worker2 = WorkerTwo()
#self.worker3 = WorkerThree()
#self.worker4 = WorkerFour()
def _threaded_call(self, worker, fn, *args, signals=None, slots=None):
thread = QThread()
thread.setObjectName('thread_' + worker.__class__.__name__)
# store because garbage collection
self.threads[worker] = thread
# give worker thread so it can be quit()
worker.thread = thread
# objects stay on threads after thread.quit()
# need to move back to main thread to recycle the same Worker.
# Error is thrown about Worker having thread (0x0) if you don't do this
worker.moveToThread(QThread.currentThread())
# move to newly created thread
worker.moveToThread(thread)
# Can now apply cross-thread signals/slots
#worker.signals.connect(self.slots)
if signals:
for signal, slot in signals.items():
try:
signal.disconnect()
except TypeError: # Signal has no slots to disconnect
pass
signal.connect(slot)
#self.signals.connect(worker.slots)
if slots:
for slot, signal in slots.items():
try:
signal.disconnect()
except TypeError: # Signal has no slots to disconnect
pass
signal.connect(slot)
thread.started.connect(partial(fn, *args)) # fn needs to be slot
thread.start()
@pyqtSlot()
def _receive_signal(self):
print("Signal received.")
@pyqtSlot(bool)
def start_thread(self):
name = "Bob"
signals = {self.worker1.finished: self._receive_signal}
self._threaded_call(self.worker1, self.worker1.print_name, name,
signals=signals)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Thread Example")
form_layout = QVBoxLayout()
self.setLayout(form_layout)
self.resize(400, 400)
self.button_start_thread = QPushButton()
self.button_start_thread.setText("Start thread.")
form_layout.addWidget(self.button_start_thread)
self.controller = SomeTabController(self)
if __name__ == '__main__':
app = QApplication(sys.argv)
_main = MainWindow()
_main.show()
sys.exit(app.exec_())
答案 1 :(得分:0)
为了避免阻止显示图像的幻灯片功能的gui,我运行以下命令 简单的代码。
等待一秒钟的后台线程类,然后通知gui等待完成。
class Waiter(QThread):
result = pyqtSignal(object)
def __init__(self):
QtCore.QThread.__init__(self)
def run(self):
while self.isRunning:
self.sleep(1)
self.result.emit("waited for 1s")
然后在主窗口中,连接幻灯片放映开始和停止按钮以启动和停止主应用程序的方法,并将nextImage函数连接到Waiter Thread发出的信号。
self.actionstopSlideShow.triggered.connect(self.stopSlideShow)
self.actionslideShowStart.triggered.connect(self.startSlideShow)
self.waitthread = Waiter()
self.waitthread.result.connect(self.nextImage)
然后,主应用程序的两种方法允许启动和停止时间行情记录器
def startSlideShow(self):
"""Start background thread that waits one second,
on wait result trigger next image
use thread otherwise gui freezes and stop button cannot be pressed
"""
self.waitthread.start()
def stopSlideShow(self):
self.waitthread.terminate()
self.waitthread.wait()
到目前为止,我在pyqt5中从QThread继承子类没有问题,gui更改全部在主(gui)线程内处理。