我正在使用python和PySide开发GUI应用程序。我需要在一个单独的线程中运行长时间的回溯任务。对于线程,我决定根据"正确"的方法使用QThread,而不从if(请参阅http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/)。所以,为了在后台运行我的长任务,我只需将线程的信号"启动"连接到一个执行长任务的方法。
由于某种原因,我的程序没有按预期运行。我花了几个小时才发现,如果我将插槽名称重命名为"public"(删除下划线),一切都会按预期开始工作。我想知道私人插槽出了什么问题?尽管我可以看到它是在不同于主线程的线程中执行的(见下面的程序输出),但由于某些原因,主事件循环在专用插槽进行时似乎不起作用。
下面是一个重现问题的例子:
from PySide.QtCore import QThread, QObject, QCoreApplication, QTimer
import time
import sys
class BackgroundWorker(QObject):
def __init__(self):
QObject.__init__(self)
def __process(self):
self.process()
def process(self):
print 'Background thread:', QThread.currentThread()
for i in xrange(0, 5):
print 'In background thread', i
time.sleep(1)
self.thread.quit()
def start(self):
self.thread = QThread()
self.moveToThread(self.thread)
#self.thread.started.connect(self.process) # <---- Case 1: This works as expected
self.thread.started.connect(self.__process) # <---- Case 2: Main event loop freezes.
self.thread.finished.connect(app.quit)
self.thread.start()
def main_thread():
print 'Main thread:', QThread.currentThread()
for i in xrange(0, 5):
print 'In main thread', i
time.sleep(1)
if __name__ == '__main__':
app = QCoreApplication(sys.argv)
bw = BackgroundWorker()
QTimer.singleShot(0, bw.start)
QTimer.singleShot(100, main_thread)
app.exec_()
在方法"BackgroundWorker.start"的上述代码中,有两行用于将线程"started"信号连接到插槽情况1'线路按预期工作(连接了公共插槽),但情况2'线路-未连接(连接了专用插槽)。
情况1的程序输出:
Background thread: <PySide.QtCore.QThread object at 0x02161738>
In background thread 0
Main thread: <PySide.QtCore.QThread object at 0x021616E8>
In main thread 0
In background thread 1
In main thread 1
In background thread 2
In main thread 2
In background thread 3
In main thread 3
In background thread 4
In main thread 4
情况2的程序输出:
Background thread: <PySide.QtCore.QThread object at 0x021916E8>
In background thread 0
In background thread 1
In background thread 2
In background thread 3
In background thread 4
Main thread: <PySide.QtCore.QThread object at 0x02191788>
In main thread 0
In main thread 1
In main thread 2
In main thread 3
In main thread 4
我怀疑这在某种程度上与PySide中的信号/插槽实现有关,但我还没有找到任何错误报告或与我观察到的行为有关的东西。
PySide版本是1.2.2,python版本是2.6.6
当您在python中连接QObject的方法时,它会自动将其元对象注册为槽。但是,以双下划线开头的方法不会发生这种情况。
你可以在一个简单的例子中看到这一点:
from PySide.QtCore import *
class Test(QObject):
sig = Signal()
def __test(self):
pass
def test(self):
pass
t = Test()
print(t.metaObject().methodCount()) # prints 5
t.sig.connect(t.test)
print(t.metaObject().methodCount()) # prints 6
t.sig.connect(t._Test__test) # name mangling is applied to the __test method
print(t.metaObject().methodCount()) # still prints 6!
这意味着这样的方法被视为正常函数,当连接到信号时,它将始终在主事件线程中运行。如果你真的想使用一个以__
开头的方法名,你需要明确地将其装饰为一个槽,那么它应该可以工作:
from PySide.QtCore import QThread, QObject, QCoreApplication, QTimer, Slot
import time
import sys
class BackgroundWorker(QObject):
def __init__(self):
QObject.__init__(self)
@Slot()
def __process(self):
self.process()
...