I want to put an ipython qtconsole in an MDI pyqt application as a subwindow, and then create other subwindows from the qtconsole. So the embedded qtconsole needs to be able to access the namespace of the application. According to this page from the ipython docs, an InProcessKernel would be the best solution. I ran this example script (reproduced below) from a python terminal (if I run it from ipython, I get a MultipleInstanceError). But forget about creating subwindows for now, first I need to figure out how to pass objects into the embedded qtconsole.
if __name__ == __main__
?I mostly just want to be able to pass the QMainWindow instance into the embedded qtconsole, because creating subwindows requires passing this object (something like window.mdiArea.addSubWindow()
). It sort of works in a hackish way if I run the script twice.
By the way, the internal_ipkernel
module used in one of the other official ipython examples (ipkernel_qtapp.py) seems to be missing from the latest versions of ipython.
Running Python 2.7, IPython 2.2/2.3 and Windows 8.1.
from __future__ import print_function
import os
from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
from IPython.qt.inprocess import QtInProcessKernelManager
from IPython.lib import guisupport
def print_process_id():
print('Process ID is:', os.getpid())
def main():
# Print the ID of the main process
print_process_id()
app = guisupport.get_app_qt4()
# Create an in-process kernel
# >>> print_process_id()
# will print the same process ID as the main process
kernel_manager = QtInProcessKernelManager()
kernel_manager.start_kernel()
kernel = kernel_manager.kernel
kernel.gui = 'qt4'
kernel.shell.push({'foo': 43, 'print_process_id': print_process_id})
kernel_client = kernel_manager.client()
kernel_client.start_channels()
def stop():
kernel_client.stop_channels()
kernel_manager.shutdown_kernel()
app.exit()
control = RichIPythonWidget()
control.kernel_manager = kernel_manager
control.kernel_client = kernel_client
control.exit_requested.connect(stop)
control.show()
guisupport.start_event_loop_qt4(app)
if __name__ == '__main__':
test = 'hello'
main()
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)
.