我想在我正在开发的 PyQt 应用程序中嵌入一个 IPython qt 控制台小部件。下面提供的代码(并改编自https://stackoverflow.com/a/9796491/1332492)为 IPython v0.12 实现了这一点。但是,这在 IPython v0.13 中self.heartbeat.start()
与RuntimeError: threads can only be started once
. 注释掉这一行会打开小部件,但不会响应用户输入。
有谁知道如何实现 IPython v0.13 的等效功能?
"""
Adapted from
https://stackoverflow.com/a/9796491/1332492
"""
import os
import atexit
from IPython.zmq.ipkernel import IPKernelApp
from IPython.lib.kernel import find_connection_file
from IPython.frontend.qt.kernelmanager import QtKernelManager
from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.config.application import catch_config_error
from PyQt4 import QtCore
class IPythonLocalKernelApp(IPKernelApp):
DEFAULT_INSTANCE_ARGS = ['']
@catch_config_error
def initialize(self, argv=None):
super(IPythonLocalKernelApp, self).initialize(argv)
self.kernel.eventloop = self.loop_qt4_nonblocking
def loop_qt4_nonblocking(self, kernel):
"""Non-blocking version of the ipython qt4 kernel loop"""
kernel.timer = QtCore.QTimer()
kernel.timer.timeout.connect(kernel.do_one_iteration)
kernel.timer.start(1000*kernel._poll_interval)
def start(self, argv=DEFAULT_INSTANCE_ARGS):
"""Starts IPython kernel app
argv: arguments passed to kernel
"""
self.initialize(argv)
self.heartbeat.start()
if self.poller is not None:
self.poller.start()
self.kernel.start()
class IPythonConsoleQtWidget(RichIPythonWidget):
_connection_file = None
def __init__(self, *args, **kw):
RichIPythonWidget.__init__(self, *args, **kw)
self._existing = True
self._may_close = False
self._confirm_exit = False
def _init_kernel_manager(self):
km = QtKernelManager(connection_file=self._connection_file, config=self.config)
km.load_connection_file()
km.start_channels(hb=self._heartbeat)
self.kernel_manager = km
atexit.register(self.kernel_manager.cleanup_connection_file)
def connect_kernel(self, connection_file, heartbeat=False):
self._heartbeat = heartbeat
if os.path.exists(connection_file):
self._connection_file = connection_file
else:
self._connection_file = find_connection_file(connection_file)
self._init_kernel_manager()
def main(**kwargs):
kernelapp = IPythonLocalKernelApp.instance()
kernelapp.start()
widget = IPythonConsoleQtWidget()
widget.connect_kernel(connection_file=kernelapp.connection_file)
widget.show()
return widget
if __name__ == "__main__":
from PyQt4.QtGui import QApplication
app = QApplication([''])
main()
app.exec_()
v0.13 的回溯
RuntimeError Traceback (most recent call last)
/Users/beaumont/terminal.py in <module>()
80 from PyQt4.QtGui import QApplication
81 app = QApplication([''])
---> 82 main()
global main = <function main at 0x106d0c848>
83 app.exec_()
/Users/beaumont/terminal.py in main(**kwargs={})
69 def main(**kwargs):
70 kernelapp = IPythonLocalKernelApp.instance()
---> 71 kernelapp.start()
kernelapp.start = <bound method IPythonLocalKernelApp.start of <__main__.IPythonLocalKernelApp object at 0x106d10590>>
72
73 widget = IPythonConsoleQtWidget()
/Users/beaumont/terminal.py in start(self=<__main__.IPythonLocalKernelApp object>, argv=[''])
33 """
34 self.initialize(argv)
---> 35 self.heartbeat.start()
self.heartbeat.start = <bound method Heartbeat.start of <Heartbeat(Thread-1, started daemon 4458577920)>>
36
37 if self.poller is not None:
/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/threading.pyc in start(self=<Heartbeat(Thread-1, started daemon 4458577920)>)
487 raise RuntimeError("thread.__init__() not called")
488 if self.__started.is_set():
--> 489 raise RuntimeError("threads can only be started once")
global RuntimeError = undefined
490 if __debug__:
491 self._note("%s.start(): starting thread", self)
RuntimeError: threads can only be started once