Show More
@@ -0,0 +1,103 b'' | |||||
|
1 | ================================ | |||
|
2 | Integrating with GUI event loops | |||
|
3 | ================================ | |||
|
4 | ||||
|
5 | When the user types ``%gui qt``, IPython integrates itself with the Qt event | |||
|
6 | loop, so you can use both a GUI and an interactive prompt together. IPython | |||
|
7 | supports a number of common GUI toolkits, but from IPython 3.0, it is possible | |||
|
8 | to integrate other event loops without modifying IPython itself. | |||
|
9 | ||||
|
10 | Terminal IPython handles event loops very differently from the IPython kernel, | |||
|
11 | so different steps are needed to integrate with each. | |||
|
12 | ||||
|
13 | Event loops in the terminal | |||
|
14 | --------------------------- | |||
|
15 | ||||
|
16 | In the terminal, IPython uses a blocking Python function to wait for user input. | |||
|
17 | However, the Python C API provides a hook, :c:func:`PyOS_InputHook`, which is | |||
|
18 | called frequently while waiting for input. This can be set to a function which | |||
|
19 | briefly runs the event loop and then returns. | |||
|
20 | ||||
|
21 | IPython provides Python level wrappers for setting and resetting this hook. To | |||
|
22 | use them, subclass :class:`IPython.lib.inputhook.InputHookBase`, and define | |||
|
23 | an ``enable(app=None)`` method, which initialises the event loop and calls | |||
|
24 | ``self.manager.set_inputhook(f)`` with a function which will briefly run the | |||
|
25 | event loop before exiting. Decorate the class with a call to | |||
|
26 | :func:`IPython.lib.inputhook.register`:: | |||
|
27 | ||||
|
28 | from IPython.lib.inputhook import register, InputHookBase | |||
|
29 | ||||
|
30 | @register('clutter') | |||
|
31 | class ClutterInputHook(InputHookBase): | |||
|
32 | def enable(self, app=None): | |||
|
33 | self.manager.set_inputhook(inputhook_clutter) | |||
|
34 | ||||
|
35 | You can also optionally define a ``disable()`` method, taking no arguments, if | |||
|
36 | there are extra steps needed to clean up. IPython will take care of resetting | |||
|
37 | the hook, whether or not you provide a disable method. | |||
|
38 | ||||
|
39 | The simplest way to define the hook function is just to run one iteration of the | |||
|
40 | event loop, or to run until no events are pending. Most event loops provide some | |||
|
41 | mechanism to do one of these things. However, the GUI may lag slightly, | |||
|
42 | because the hook is only called every 0.1 seconds. Alternatively, the hook can | |||
|
43 | keep running the event loop until there is input ready on stdin. IPython | |||
|
44 | provides a function to facilitate this: | |||
|
45 | ||||
|
46 | .. currentmodule:: IPython.lib.inputhook | |||
|
47 | ||||
|
48 | .. function:: stdin_ready() | |||
|
49 | ||||
|
50 | Returns True if there is something ready to read on stdin. | |||
|
51 | ||||
|
52 | If this is the case, the hook function should return immediately. | |||
|
53 | ||||
|
54 | This is implemented for Windows and POSIX systems - on other platforms, it | |||
|
55 | always returns True, so that the hook always gives Python a chance to check | |||
|
56 | for input. | |||
|
57 | ||||
|
58 | ||||
|
59 | Event loops in the kernel | |||
|
60 | ------------------------- | |||
|
61 | ||||
|
62 | The kernel runs its own event loop, so it's simpler to integrate with others. | |||
|
63 | IPython allows the other event loop to take control, but it must call | |||
|
64 | :meth:`IPython.kernel.zmq.kernelbase.Kernel.do_one_iteration` periodically. | |||
|
65 | ||||
|
66 | To integrate with this, write a function that takes a single argument, | |||
|
67 | the IPython kernel instance, arranges for your event loop to call | |||
|
68 | ``kernel.do_one_iteration()`` at least every ``kernel._poll_interval`` seconds, | |||
|
69 | and starts the event loop. | |||
|
70 | ||||
|
71 | Decorate this function with :func:`IPython.kernel.zmq.eventloops.register_integration`, | |||
|
72 | passing in the names you wish to register it for. Here is a slightly simplified | |||
|
73 | version of the Tkinter integration already included in IPython:: | |||
|
74 | ||||
|
75 | @register_integration('tk') | |||
|
76 | def loop_tk(kernel): | |||
|
77 | """Start a kernel with the Tk event loop.""" | |||
|
78 | from tkinter import Tk | |||
|
79 | ||||
|
80 | # Tk uses milliseconds | |||
|
81 | poll_interval = int(1000*kernel._poll_interval) | |||
|
82 | # For Tkinter, we create a Tk object and call its withdraw method. | |||
|
83 | class Timer(object): | |||
|
84 | def __init__(self, func): | |||
|
85 | self.app = Tk() | |||
|
86 | self.app.withdraw() | |||
|
87 | self.func = func | |||
|
88 | ||||
|
89 | def on_timer(self): | |||
|
90 | self.func() | |||
|
91 | self.app.after(poll_interval, self.on_timer) | |||
|
92 | ||||
|
93 | def start(self): | |||
|
94 | self.on_timer() # Call it once to get things going. | |||
|
95 | self.app.mainloop() | |||
|
96 | ||||
|
97 | kernel.timer = Timer(kernel.do_one_iteration) | |||
|
98 | kernel.timer.start() | |||
|
99 | ||||
|
100 | Some event loops can go one better, and integrate checking for messages on the | |||
|
101 | kernel's ZMQ sockets, making the kernel more responsive than plain polling. How | |||
|
102 | to do this is outside the scope of this document; if you are interested, look at | |||
|
103 | the integration with Qt in :mod:`IPython.kernel.zmq.eventloops`. |
@@ -0,0 +1,4 b'' | |||||
|
1 | * It's now possible to provide mechanisms to integrate IPython with other event | |||
|
2 | loops, in addition to the ones we already support. This lets you run GUI code | |||
|
3 | in IPython with an interactive prompt, and to embed the IPython | |||
|
4 | kernel in GUI applications. See :doc:`/config/eventloops` for details. |
@@ -190,13 +190,12 b' class InputHookManager(object):' | |||||
190 | the names with which to register this GUI integration. The classes |
|
190 | the names with which to register this GUI integration. The classes | |
191 | themselves should subclass :class:`InputHookBase`. |
|
191 | themselves should subclass :class:`InputHookBase`. | |
192 |
|
192 | |||
193 |
|
|
193 | :: | |
194 | -------- |
|
|||
195 |
|
194 | |||
196 | @inputhook_manager.register('qt') |
|
195 | @inputhook_manager.register('qt') | |
197 | class QtInputHook(InputHookBase): |
|
196 | class QtInputHook(InputHookBase): | |
198 | def enable(self, app=None): |
|
197 | def enable(self, app=None): | |
199 | ... |
|
198 | ... | |
200 | """ |
|
199 | """ | |
201 | def decorator(cls): |
|
200 | def decorator(cls): | |
202 | inst = cls(self) |
|
201 | inst = cls(self) |
General Comments 0
You need to be logged in to leave comments.
Login now