重要

本文档涵盖 IPython 6.0 及更高版本。从 6.0 版本开始,IPython 不再支持与低于 Python 3.3 的 Python 版本(包括所有版本的 Python 2.7)的兼容性。

如果您正在寻找与 Python 2.7 兼容的 IPython 版本,请使用 IPython 5.x LTS 版本并参阅其文档(LTS 是长期支持版本)。

与 GUI 事件循环集成

当用户键入 %gui qt 时,IPython 会将自身与 Qt 事件循环集成,以便您可以同时使用 GUI 和交互式提示符。IPython 支持许多常见的 GUI 工具包,但从 IPython 3.0 开始,可以在不修改 IPython 本身的情况下集成其他事件循环。

支持的事件循环包括 qt5qt6gtk2gtk3gtk4wxosxtk。确保您指定的事件循环与您自己的代码使用的 GUI 工具包匹配。

要使 IPython GUI 事件循环集成在每次启动时自动发生,请在您的 IPython 配置文件中设置 c.InteractiveShellApp.gui 配置键(请参阅 设置可配置选项)。

如果您使用的事件循环受 IPython 支持,无论您使用终端 IPython 还是 IPython 内核,启用事件循环集成都遵循刚才描述的步骤。

但是,终端 IPython 处理事件循环的方式与 IPython 内核处理事件循环的方式非常不同,因此如果您需要与新型事件循环集成,则需要不同的步骤来与每个事件循环集成。

在终端中与新事件循环集成

版本 5.0 中已更改:有一个新的 API 用于使用 prompt_toolkit 集成事件循环。

在终端中,IPython 使用 prompt_toolkit 向用户提示输入。prompt_toolkit 提供了与外部事件循环集成的钩子。

要集成事件循环,请定义一个函数,该函数运行 GUI 事件循环,直到有输入等待 prompt_toolkit 处理。有两种方法可以检测此条件

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

定义此函数后,使用 IPython 注册该函数

IPython.terminal.pt_inputhooks.register(name, inputhook)

将函数 inputhook 注册为 GUI name 的事件循环集成。如果 name='foo',那么用户可以通过运行 %gui foo 来启用此集成。

在内核中与新的事件循环集成

内核运行自己的事件循环,因此与其他事件循环集成更简单。IPython 允许其他事件循环进行控制,但它必须定期调用 IPython.kernel.zmq.kernelbase.Kernel.do_one_iteration()

要与此集成,请编写一个函数,该函数采用一个参数(IPython 内核实例),安排事件循环调用 kernel.do_one_iteration(),至少每 kernel._poll_interval 秒调用一次,并启动事件循环。

使用 IPython.kernel.zmq.eventloops.register_integration() 装饰此函数,传入要为其注册的名称。以下是 IPython 中已包含的 Tkinter 集成的略微简化版本

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

一些事件循环可以做得更好,并集成对内核的 ZMQ 套接字上的消息的检查,从而使内核比普通轮询更具响应性。如何执行此操作超出了本文档的范围;如果您有兴趣,请查看 IPython.kernel.zmq.eventloops 中 Qt 的集成。