1. 7f51a0332edd0c675c2d314ca3e62df7ef041281 deps/ipython (8.4.0-33-g7f51a0332)
109 lines
4.3 KiB
ReStructuredText
109 lines
4.3 KiB
ReStructuredText
================================
|
|
Integrating with GUI event loops
|
|
================================
|
|
|
|
When the user types ``%gui qt``, IPython integrates itself with the Qt event
|
|
loop, so you can use both a GUI and an interactive prompt together. IPython
|
|
supports a number of common GUI toolkits, but from IPython 3.0, it is possible
|
|
to integrate other event loops without modifying IPython itself.
|
|
|
|
Supported event loops include ``qt4``, ``qt5``, ``gtk2``, ``gtk3``, ``gtk4``,
|
|
``wx``, ``osx`` and ``tk``. Make sure the event loop you specify matches the
|
|
GUI toolkit used by your own code.
|
|
|
|
To make IPython GUI event loop integration occur automatically at every
|
|
startup, set the ``c.InteractiveShellApp.gui`` configuration key in your
|
|
IPython profile (see :ref:`setting_config`).
|
|
|
|
If the event loop you use is supported by IPython, turning on event loop
|
|
integration follows the steps just described whether you use Terminal IPython
|
|
or an IPython kernel.
|
|
|
|
However, the way Terminal IPython handles event loops is very different from
|
|
the way IPython kernel does, so if you need to integrate with a new kind of
|
|
event loop, different steps are needed to integrate with each.
|
|
|
|
Integrating with a new event loop in the terminal
|
|
-------------------------------------------------
|
|
|
|
.. versionchanged:: 5.0
|
|
|
|
There is a new API for event loop integration using prompt_toolkit.
|
|
|
|
In the terminal, IPython uses prompt_toolkit to prompt the user for input.
|
|
prompt_toolkit provides hooks to integrate with an external event loop.
|
|
|
|
To integrate an event loop, define a function which runs the GUI event loop
|
|
until there is input waiting for prompt_toolkit to process. There are two ways
|
|
to detect this condition::
|
|
|
|
# Polling for input.
|
|
def inputhook(context):
|
|
while not context.input_is_ready():
|
|
# Replace this with the appropriate call for the event loop:
|
|
iterate_loop_once()
|
|
|
|
# Using a file descriptor to notify the event loop to stop.
|
|
def inputhook2(context):
|
|
fd = context.fileno()
|
|
# Replace the functions below with those for the event loop.
|
|
add_file_reader(fd, callback=stop_the_loop)
|
|
run_the_loop()
|
|
|
|
Once you have defined this function, register it with IPython:
|
|
|
|
.. currentmodule:: IPython.terminal.pt_inputhooks
|
|
|
|
.. function:: register(name, inputhook)
|
|
|
|
Register the function *inputhook* as the event loop integration for the
|
|
GUI *name*. If ``name='foo'``, then the user can enable this integration
|
|
by running ``%gui foo``.
|
|
|
|
|
|
Integrating with a new event loop in the kernel
|
|
-----------------------------------------------
|
|
|
|
The kernel runs its own event loop, so it's simpler to integrate with others.
|
|
IPython allows the other event loop to take control, but it must call
|
|
:meth:`IPython.kernel.zmq.kernelbase.Kernel.do_one_iteration` periodically.
|
|
|
|
To integrate with this, write a function that takes a single argument,
|
|
the IPython kernel instance, arranges for your event loop to call
|
|
``kernel.do_one_iteration()`` at least every ``kernel._poll_interval`` seconds,
|
|
and starts the event loop.
|
|
|
|
Decorate this function with :func:`IPython.kernel.zmq.eventloops.register_integration`,
|
|
passing in the names you wish to register it for. Here is a slightly simplified
|
|
version of the Tkinter integration already included in IPython::
|
|
|
|
@register_integration('tk')
|
|
def loop_tk(kernel):
|
|
"""Start a kernel with the Tk event loop."""
|
|
from tkinter import Tk
|
|
|
|
# Tk uses milliseconds
|
|
poll_interval = int(1000*kernel._poll_interval)
|
|
# For Tkinter, we create a Tk object and call its withdraw method.
|
|
class Timer(object):
|
|
def __init__(self, func):
|
|
self.app = Tk()
|
|
self.app.withdraw()
|
|
self.func = func
|
|
|
|
def on_timer(self):
|
|
self.func()
|
|
self.app.after(poll_interval, self.on_timer)
|
|
|
|
def start(self):
|
|
self.on_timer() # Call it once to get things going.
|
|
self.app.mainloop()
|
|
|
|
kernel.timer = Timer(kernel.do_one_iteration)
|
|
kernel.timer.start()
|
|
|
|
Some event loops can go one better, and integrate checking for messages on the
|
|
kernel's ZMQ sockets, making the kernel more responsive than plain polling. How
|
|
to do this is outside the scope of this document; if you are interested, look at
|
|
the integration with Qt in :mod:`IPython.kernel.zmq.eventloops`.
|