对象线程中的 PySide 插槽不是 运行
PySide slot not run in object's thread
我正在尝试 运行 在 PySide 的单独线程中执行一项长任务,以便主线程可以继续处理 GUI 事件。我读过正确的方法是:
- 将任务封装在
QObject
子类中,其中工作在 run()
方法中完成,完成后发出 finished
信号。
- 创建一个新的
QThread
并使用 QObject.moveToThread()
. 设置任务对象的线程关联性
- 将线程的
start
信号连接到任务的 run()
方法。
- 使用
QThread.start()
启动线程。
但是,我 运行 遇到了一个奇怪的问题,如果 run()
方法被包装在一个槽中,函数将在主线程中 运行 而不是对象所属的线程。如果我将其保留为标准 Python 方法,一切正常。
这是我创建的一个最小示例:
#!/usr/bin/env python
import sys
from PySide import QtCore, QtGui
class Task(QtCore.QObject):
"""Does some work and emits a signal when done."""
finished = QtCore.Signal(object)
def run1(self):
"""Runs task and emits finished() signal when done."""
try:
# Try running the task
result = self._run()
except:
self.finished.emit(None)
else:
self.finished.emit(result)
@QtCore.Slot()
def run2(self):
"""Same as run1, but wrapped in a slot."""
self.run1()
def _run(self):
"""Override in subclass"""
pass
class TestTask(Task):
"""Prints thread ID."""
def __init__(self, name):
super().__init__()
self.name = name
def _run(self):
print('{} thread ID:'.format(self.name), QtCore.QThread.currentThreadId())
return 'success'
def main():
gui = QtGui.QApplication([])
print('Main thread ID: ', QtCore.QThread.currentThreadId())
# thread1 calls task1.run1()
task1 = TestTask('task1')
task1.finished.connect(lambda r: print('Task 1 finished:', r))
thread1 = QtCore.QThread()
task1.moveToThread(thread1)
thread1.started.connect(task1.run1)
# thread2 calls task2.run2()
task2 = TestTask('task2')
task1.finished.connect(lambda r: print('Task 2 finished:', r))
thread2 = QtCore.QThread()
task2.moveToThread(thread2)
thread2.started.connect(task2.run2)
# Start both threads
thread1.start()
thread2.start()
# Run event loop (doesn't actually return)
sys.exit(gui.exec_())
if __name__ == '__main__':
main()
这会产生以下输出:
Main thread ID: 139962303178496
task1 thread ID: 139961642776320
task2 thread ID: 139962303178496
Task 2 finished success
Task 1 finished success
将 run()
保留为标准 Python 方法并不是什么大问题,但我想知道为什么会这样。这是 QT4.8 和 PySide 1.2.4。
这可能是由 PySide 中的一个可能错误引起的。该问题似乎是由继承具有装饰槽的 base-class 引起的。如果将此槽移动到 subclass,问题就消失了:
class TestTask(Task):
...
@QtCore.Slot()
def run2(self):
"""Same as run1, but wrapped in a slot."""
self.run1()
(PS:作为另一个数据点,值得注意的是您的原始示例在 PyQt4 中运行良好。
更新:
据推测,这是由 PySide 中的一个已知错误引起的:请参阅 PYSIDE-249。
我正在尝试 运行 在 PySide 的单独线程中执行一项长任务,以便主线程可以继续处理 GUI 事件。我读过正确的方法是:
- 将任务封装在
QObject
子类中,其中工作在run()
方法中完成,完成后发出finished
信号。 - 创建一个新的
QThread
并使用QObject.moveToThread()
. 设置任务对象的线程关联性
- 将线程的
start
信号连接到任务的run()
方法。 - 使用
QThread.start()
启动线程。
但是,我 运行 遇到了一个奇怪的问题,如果 run()
方法被包装在一个槽中,函数将在主线程中 运行 而不是对象所属的线程。如果我将其保留为标准 Python 方法,一切正常。
这是我创建的一个最小示例:
#!/usr/bin/env python
import sys
from PySide import QtCore, QtGui
class Task(QtCore.QObject):
"""Does some work and emits a signal when done."""
finished = QtCore.Signal(object)
def run1(self):
"""Runs task and emits finished() signal when done."""
try:
# Try running the task
result = self._run()
except:
self.finished.emit(None)
else:
self.finished.emit(result)
@QtCore.Slot()
def run2(self):
"""Same as run1, but wrapped in a slot."""
self.run1()
def _run(self):
"""Override in subclass"""
pass
class TestTask(Task):
"""Prints thread ID."""
def __init__(self, name):
super().__init__()
self.name = name
def _run(self):
print('{} thread ID:'.format(self.name), QtCore.QThread.currentThreadId())
return 'success'
def main():
gui = QtGui.QApplication([])
print('Main thread ID: ', QtCore.QThread.currentThreadId())
# thread1 calls task1.run1()
task1 = TestTask('task1')
task1.finished.connect(lambda r: print('Task 1 finished:', r))
thread1 = QtCore.QThread()
task1.moveToThread(thread1)
thread1.started.connect(task1.run1)
# thread2 calls task2.run2()
task2 = TestTask('task2')
task1.finished.connect(lambda r: print('Task 2 finished:', r))
thread2 = QtCore.QThread()
task2.moveToThread(thread2)
thread2.started.connect(task2.run2)
# Start both threads
thread1.start()
thread2.start()
# Run event loop (doesn't actually return)
sys.exit(gui.exec_())
if __name__ == '__main__':
main()
这会产生以下输出:
Main thread ID: 139962303178496
task1 thread ID: 139961642776320
task2 thread ID: 139962303178496
Task 2 finished success
Task 1 finished success
将 run()
保留为标准 Python 方法并不是什么大问题,但我想知道为什么会这样。这是 QT4.8 和 PySide 1.2.4。
这可能是由 PySide 中的一个可能错误引起的。该问题似乎是由继承具有装饰槽的 base-class 引起的。如果将此槽移动到 subclass,问题就消失了:
class TestTask(Task):
...
@QtCore.Slot()
def run2(self):
"""Same as run1, but wrapped in a slot."""
self.run1()
(PS:作为另一个数据点,值得注意的是您的原始示例在 PyQt4 中运行良好。
更新:
据推测,这是由 PySide 中的一个已知错误引起的:请参阅 PYSIDE-249。