在嵌入式PyQt应用中使用QThread
我写了一个用C++开发的Qt应用程序,这个程序嵌入了Python解释器,并使用PyQt来创建可以编写脚本的用户界面,用于数据分析。Python代码会对数据进行一些计算,然后返回一个QWidget(里面包含各种图表等),这个QWidget会被插入到主应用程序中。
我想从Python中创建一个新的QThread,这样控制权就能返回给C++应用程序,这样就不会因为重的计算而阻塞运行图形界面的主线程。问题是,一旦控制权返回给C++应用程序,线程似乎就会“睡着”,直到再次调用Python解释器,比如在matplotlib图表上发生鼠标悬停事件。我怀疑这可能和Python的全局解释器锁(GIL)有关。
我该如何让从Python创建的QThread在控制权返回到嵌入的应用程序后继续运行呢?
我使用以下代码进行测试。嵌入的应用程序调用test_thread(),并将返回的容器添加到QTabwidget中。这个线程只有在我在matplotlib图表上生成鼠标事件时才会执行。
import time
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import pyqtSignal, pyqtSlot
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
app = QtGui.QApplication.instance()
class Worker(QtCore.QObject):
finished = pyqtSignal()
def __init__(self):
QtCore.QObject.__init__(self)
def do_work(self):
for i in range(0, 10):
print 'worker running'
time.sleep(1)
self.finished.emit()
class Controller(QtCore.QObject):
def __init__(self):
QtCore.QObject.__init__(self)
self.thread = QtCore.QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.worker.finished.connect(self.thread.quit)
self.thread.started.connect(self.worker.do_work)
self.thread.finished.connect(app.exit)
def start_thread(self):
self.thread.start()
class Container(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.controller = Controller()
self.controller.start_thread()
self.fig = Figure(dpi=72, facecolor=(1, 1, 1), edgecolor=(0, 0, 0))
self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.canvas)
self.setLayout(self.layout)
def test_thread():
container = Container()
return container
1 个回答
CPython是单线程的,这主要是因为有个叫做GIL(全局解释器锁)的东西。简单来说,你不能在Python中同时运行多个线程。
https://wiki.python.org/moin/GlobalInterpreterLock
这里不是Qt在限制你,而是Python在限制你。解决这个问题的办法是为你的“线程”启动一个独立的Python进程,然后使用进程间通信(比如套接字、共享内存等)来让这些进程互相交流。Qt提供了这些进程间通信的接口。你只需要把进程间的通信数据进行序列化。
这段话是基于你在问关于并发的事情。如果你问的是其他问题,我会修改我的回答来澄清。
我如何让从Python创建的QThread在控制权返回给嵌入应用程序后继续运行?
这完全取决于嵌入的应用程序。Python解释器必须被允许继续运行。如果嵌入的应用程序不允许Python继续运行,那么任何试图绕过这个限制的做法都会导致问题。