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
The IPython Console allows you to execute commands and interact with data inside IPython interpreters. To launch a new IPython instance, go to New console (default settings) under the Consoles menu, or use the keyboard shortcut Ctrl - T ( Cmd - T on macOS) when the console is focused.
Jupyter QtConsole The Qtconsole is a very lightweight application that largely feels like a terminal, but provides a number of enhancements only possible in a GUI, such as inline figures, proper multiline editing with syntax highlighting, graphical calltips, and more.
Spyder can launch new IPython instances itself, through “Open an IPython console” under the Consoles menu, the IPython Console pane menu or its context menu ( Ctrl - T by default), to take advantage of the full suite of Spyder's features.
The Qt console is a GUI application similar to IPython terminal. However, it provides a number of enhancements which are not available in text based IPython terminal. The enhance features are inline figures, multi-line editing with syntax highlighting, graphical calltips, etc.
Ok, this code seems to do the trick (i.e. it puts a non-blocking ipython interpreter in a Qt widget, which can be embedded into other widgets). Keywords passed to terminal_widget
get added to the namespace of the widget
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.utils.traitlets import TraitError from PyQt4 import QtGui, QtCore def event_loop(kernel): kernel.timer = QtCore.QTimer() kernel.timer.timeout.connect(kernel.do_one_iteration) kernel.timer.start(1000*kernel._poll_interval) def default_kernel_app(): app = IPKernelApp.instance() app.initialize(['python', '--pylab=qt']) app.kernel.eventloop = event_loop return app def default_manager(kernel): connection_file = find_connection_file(kernel.connection_file) manager = QtKernelManager(connection_file=connection_file) manager.load_connection_file() manager.start_channels() atexit.register(manager.cleanup_connection_file) return manager def console_widget(manager): try: # Ipython v0.13 widget = RichIPythonWidget(gui_completion='droplist') except TraitError: # IPython v0.12 widget = RichIPythonWidget(gui_completion=True) widget.kernel_manager = manager return widget def terminal_widget(**kwargs): kernel_app = default_kernel_app() manager = default_manager(kernel_app) widget = console_widget(manager) #update namespace kernel_app.shell.user_ns.update(kwargs) kernel_app.start() return widget app = QtGui.QApplication([]) widget = terminal_widget(testing=123) widget.show() app.exec_()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With