QThread Windows 没有响应
QThread Windows not responding
我在 Windows 上用 PyQt 写了一个 GUI 程序。我的程序中有一些昂贵的操作。虽然这些操作是 运行ning,但程序在程序栏中显示 "Not Responding"。
我想肯定是这个操作阻塞了主线程去更新UI,所以我用QThread写了多线程代码测试了一下,还是不行。
我写了一个小程序来测试,在新线程中根本没有运行运行,这是我的小测试代码:
from PyQt5.QtCore import QThread, QObject, QCoreApplication, qDebug, QTimer
class Worker(QObject):
def on_timeout(self):
qDebug('Worker.on_timeout get called from: %s' % hex(int(QThread.currentThreadId())))
if __name__ == '__main__':
import sys
app = QCoreApplication(sys.argv)
qDebug('From main thread: %s' % hex(int(QThread.currentThreadId())))
t = QThread()
qDebug(t)
worker = Worker()
timer = QTimer()
timer.timeout.connect(worker.on_timeout)
timer.start(1000)
timer.moveToThread(t)
worker.moveToThread(t)
t.start()
app.exec_()
这是输出:
From main thread: 0x634
Worker.on_timeout get called from: 0x634
您的程序有几个错误,无法产生您显示的输出。
首先,无法将线程对象传递给 qDebug
- 参数必须是字符串。如果要打印对象,请使用 qDebug(repr(obj))
- 或者更好,只需使用 print(obj)
.
其次,您不能在创建计时器的线程之外启动计时器。您的示例在主线程中建立信号连接,并在主线程中启动计时器。所以worker.on_timeout
会在主线程中被调用。但是如果你在 将它移动到工作线程之后连接并启动定时器 ,你将得到这个错误:
QObject::startTimer: Timers can only be used with threads started with
QThread
我认为使用定时器是不必要的,并且会混淆你的例子,所以最好完全不使用它。相反,您应该将工作线程的 started
信号连接到工作对象的 run
方法。要模拟长运行操作,可以使用QThread.sleep()
:
from PyQt5.QtCore import QThread, QObject, QCoreApplication
class Worker(QObject):
def run(self):
print('Worker called from: %#x' % int(QThread.currentThreadId()))
QThread.sleep(2)
print('Finished')
QCoreApplication.quit()
if __name__ == '__main__':
import sys
app = QCoreApplication(sys.argv)
print('From main thread: %#x' % int(QThread.currentThreadId()))
t = QThread()
worker = Worker()
worker.moveToThread(t)
t.started.connect(worker.run)
t.start()
app.exec_()
最后,请注意,在将工作对象移动到线程后,您应该始终建立信号连接。 this answer.
中解释了其原因
我在 Windows 上用 PyQt 写了一个 GUI 程序。我的程序中有一些昂贵的操作。虽然这些操作是 运行ning,但程序在程序栏中显示 "Not Responding"。
我想肯定是这个操作阻塞了主线程去更新UI,所以我用QThread写了多线程代码测试了一下,还是不行。
我写了一个小程序来测试,在新线程中根本没有运行运行,这是我的小测试代码:
from PyQt5.QtCore import QThread, QObject, QCoreApplication, qDebug, QTimer
class Worker(QObject):
def on_timeout(self):
qDebug('Worker.on_timeout get called from: %s' % hex(int(QThread.currentThreadId())))
if __name__ == '__main__':
import sys
app = QCoreApplication(sys.argv)
qDebug('From main thread: %s' % hex(int(QThread.currentThreadId())))
t = QThread()
qDebug(t)
worker = Worker()
timer = QTimer()
timer.timeout.connect(worker.on_timeout)
timer.start(1000)
timer.moveToThread(t)
worker.moveToThread(t)
t.start()
app.exec_()
这是输出:
From main thread: 0x634
Worker.on_timeout get called from: 0x634
您的程序有几个错误,无法产生您显示的输出。
首先,无法将线程对象传递给 qDebug
- 参数必须是字符串。如果要打印对象,请使用 qDebug(repr(obj))
- 或者更好,只需使用 print(obj)
.
其次,您不能在创建计时器的线程之外启动计时器。您的示例在主线程中建立信号连接,并在主线程中启动计时器。所以worker.on_timeout
会在主线程中被调用。但是如果你在 将它移动到工作线程之后连接并启动定时器 ,你将得到这个错误:
QObject::startTimer: Timers can only be used with threads started with QThread
我认为使用定时器是不必要的,并且会混淆你的例子,所以最好完全不使用它。相反,您应该将工作线程的 started
信号连接到工作对象的 run
方法。要模拟长运行操作,可以使用QThread.sleep()
:
from PyQt5.QtCore import QThread, QObject, QCoreApplication
class Worker(QObject):
def run(self):
print('Worker called from: %#x' % int(QThread.currentThreadId()))
QThread.sleep(2)
print('Finished')
QCoreApplication.quit()
if __name__ == '__main__':
import sys
app = QCoreApplication(sys.argv)
print('From main thread: %#x' % int(QThread.currentThreadId()))
t = QThread()
worker = Worker()
worker.moveToThread(t)
t.started.connect(worker.run)
t.start()
app.exec_()
最后,请注意,在将工作对象移动到线程后,您应该始终建立信号连接。 this answer.
中解释了其原因