Embedding IPython Qt console in a PyQt application
Solution 1:
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_()
Solution 2:
The accepted answer by @ChrisB is fine for IPython version 0.13, but it doesn't work with newer versions. From the examples section of the IPython kernel repository on github, this is the way to do it in v1.x+ (currently tested with 4.0.1), which has the feature that the console and kernel are in the same process.
Here is an example, based on the official one, which gives a convenience class that can be easily plugged into an application. It's setup to work with pyqt4 and IPython 4.0.1 on Python 2.7:
(Note: you'll need to install the ipykernel and qtconsole packages)
# Set the QT API to PyQt4
import os
os.environ['QT_API'] = 'pyqt'
import sip
sip.setapi("QString", 2)
sip.setapi("QVariant", 2)
from PyQt4.QtGui import *
# Import the console machinery from ipython
from qtconsole.rich_ipython_widget import RichIPythonWidget
from qtconsole.inprocess import QtInProcessKernelManager
from IPython.lib import guisupport
class QIPythonWidget(RichIPythonWidget):
""" Convenience class for a live IPython console widget. We can replace the standard banner using the customBanner argument"""
def __init__(self,customBanner=None,*args,**kwargs):
if not customBanner is None: self.banner=customBanner
super(QIPythonWidget, self).__init__(*args,**kwargs)
self.kernel_manager = kernel_manager = QtInProcessKernelManager()
kernel_manager.start_kernel()
kernel_manager.kernel.gui = 'qt4'
self.kernel_client = kernel_client = self._kernel_manager.client()
kernel_client.start_channels()
def stop():
kernel_client.stop_channels()
kernel_manager.shutdown_kernel()
guisupport.get_app_qt4().exit()
self.exit_requested.connect(stop)
def pushVariables(self,variableDict):
""" Given a dictionary containing name / value pairs, push those variables to the IPython console widget """
self.kernel_manager.kernel.shell.push(variableDict)
def clearTerminal(self):
""" Clears the terminal """
self._control.clear()
def printText(self,text):
""" Prints some plain text to the console """
self._append_plain_text(text)
def executeCommand(self,command):
""" Execute a command in the frame of the console widget """
self._execute(command,False)
class ExampleWidget(QWidget):
""" Main GUI Widget including a button and IPython Console widget inside vertical layout """
def __init__(self, parent=None):
super(ExampleWidget, self).__init__(parent)
layout = QVBoxLayout(self)
self.button = QPushButton('Another widget')
ipyConsole = QIPythonWidget(customBanner="Welcome to the embedded ipython console\n")
layout.addWidget(self.button)
layout.addWidget(ipyConsole)
# This allows the variable foo and method print_process_id to be accessed from the ipython console
ipyConsole.pushVariables({"foo":43,"print_process_id":print_process_id})
ipyConsole.printText("The variable 'foo' and the method 'print_process_id()' are available. Use the 'whos' command for information.")
def print_process_id():
print 'Process ID is:', os.getpid()
def main():
app = QApplication([])
widget = ExampleWidget()
widget.show()
app.exec_()
if __name__ == '__main__':
main()