I'd like to embed an IPython qt console widget in a PyQt application I am working on. The code provided below (and adapted from https://stackoverflow.com/a/9796491/1332492) Accomplishes this for IPython v0.12. However, this crashes in IPython v0.13 at the line self.heartbeat.start() with RuntimeError: threads can only be started once. Commenting out this line brings up the widget, but doesn't respond to user input.
Does anyone know how to achieve the equivalent functionality for 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_()
Traceback for 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