Embedding Ipython Qt Console in a Pyqt Application

Embedding IPython Qt console in a PyQt application

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_()

Can't quit PyQt5 application with embedded iPython Qtconsole

You have to use a Kernel Manager, like this:
Embedding IPython Qt console in a PyQt application

Here is an working example for PyQt5:

import os
os.environ['QT_API'] = 'pyqt5'

from PyQt5 import QtWidgets
from PyQt5 import QtGui
# ipython won't work if this is not correctly installed. And the error message will be misleading
from PyQt5 import QtSvg

# Import the console machinery from ipython
from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.qt.inprocess import QtInProcessKernelManager

def get_app_qt5(*args, **kwargs):
"""Create a new qt5 app or return an existing one."""
app = QtWidgets.QApplication.instance()
if app is None:
if not args:
args = ([''],)
app = QtWidgets.QApplication(*args, **kwargs)
return app

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):
super(QIPythonWidget, self).__init__(*args,**kwargs)
if customBanner!=None: self.banner=customBanner
self.kernel_manager = kernel_manager = QtInProcessKernelManager()
kernel_manager.start_kernel()
kernel_manager.kernel.gui = 'qt'
self.kernel_client = kernel_client = self._kernel_manager.client()
kernel_client.start_channels()

def stop():
kernel_client.stop_channels()
kernel_manager.shutdown_kernel()
get_app_qt5().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(QtWidgets.QMainWindow):
""" Main GUI Window including a button and IPython Console widget inside vertical layout """
def __init__(self, parent=None):
super(ExampleWidget, self).__init__(parent)
self.setWindowTitle('iPython in PyQt5 app example')
self.mainWidget = QtWidgets.QWidget(self)
self.setCentralWidget(self.mainWidget)
layout = QtWidgets.QVBoxLayout(self.mainWidget)
self.button = QtWidgets.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.\n\nTo push variables run this before starting the UI:\n ipyConsole.pushVariables({\"foo\":43,\"print_process_id\":print_process_id})")
self.setGeometry(300, 300, 800, 600)

def print_process_id():
print('Process ID is:', os.getpid())

def main():
app = get_app_qt5()
widget = ExampleWidget()
widget.show()
app.exec_()

if __name__ == '__main__':
main()

Embedding Ipython into a PyQt4 App

I went down the same path, but ended up using IPython dev as the embedding solution is cleaner and has no nasty input() / help() bugs.

Here is a work-around for 0.13.x: https://stackoverflow.com/a/12375397/532513 -- problem is, if you use help() for example, everything freezes.

In the development IPython, it's all much simpler. Here's a working example:

from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.frontend.qt.inprocess import QtInProcessKernelManager
from IPython.lib import guisupport
from PyQt4.QtGui import QApplication

app = QApplication(sys.argv)

kernel_manager = QtInProcessKernelManager()
kernel_manager.start_kernel()
kernel = kernel_manager.kernel
kernel.gui = 'qt4'

kernel_client = kernel_manager.client()
kernel_client.start_channels()

def stop():
kernel_client.stop_channels()
kernel_manager.shutdown_kernel()
# here you should exit your application with a suitable call
sys.exit()

widget = RichIPythonWidget()
widget.kernel_manager = kernel_manager
widget.kernel_client = kernel_client
widget.exit_requested.connect(stop)
widget.setWindowTitle("IPython shell")

ipython_widget = widget
ipython_widget.show()

app.exec_()
sys.exit()

embedding ipython qtconsole and passing objects

You can use this to start an ipython console in a given qt widget:

from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.qt.inprocess import QtInProcessKernelManager

def put_ipy(parent):
kernel_manager = QtInProcessKernelManager()
kernel_manager.start_kernel()
kernel = kernel_manager.kernel
kernel.gui = 'qt4'

kernel_client = kernel_manager.client()
kernel_client.start_channels()
kernel_client.namespace = parent

def stop():
kernel_client.stop_channels()
kernel_manager.shutdown_kernel()

layout = QtGui.QVBoxLayout(parent)
widget = RichIPythonWidget(parent=parent)
layout.addWidget(widget)
widget.kernel_manager = kernel_manager
widget.kernel_client = kernel_client
widget.exit_requested.connect(stop)
ipython_widget = widget
ipython_widget.show()
kernel.shell.push({'widget':widget,'kernel':kernel, 'parent':parent})
return {'widget':widget,'kernel':kernel}

To pop up windows from the console, you can run

app = QtGui.QApplication([])
win = QtGui.QWidget(None)
win.show()
put_ipy(win)

but this will swap the original python interpreter with an empty ipy one, with only the variables defined that you passed (widget, kernel, parent here), i.e., both the console and the command line have the same kernels, and the original one is blocked.

You can get around such behaviour by doing the above within a qt application with another main window and set of interactions. To pass variables to the kernel, use (as above) kernel.shell.push(dict).



Related Topics



Leave a reply



Submit