##// END OF EJS Templates
Write & borrow some inputhooks for prompt_toolkit
Thomas Kluyver -
Show More
@@ -0,0 +1,16 b''
1 import importlib
2 import os
3
4 aliases = {
5 'qt4': 'qt'
6 }
7
8 def get_inputhook_func(gui):
9 if gui in aliases:
10 return get_inputhook_func(aliases[gui])
11
12 if gui == 'qt5':
13 os.environ['QT_API'] = 'pyqt5'
14
15 mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui)
16 return mod.inputhook
@@ -0,0 +1,55 b''
1 # Code borrowed from python-prompt-toolkit examples
2 # https://github.com/jonathanslenders/python-prompt-toolkit/blob/77cdcfbc7f4b4c34a9d2f9a34d422d7152f16209/examples/inputhook.py
3
4 # Copyright (c) 2014, Jonathan Slenders
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without modification,
8 # are permitted provided that the following conditions are met:
9 #
10 # * Redistributions of source code must retain the above copyright notice, this
11 # list of conditions and the following disclaimer.
12 #
13 # * Redistributions in binary form must reproduce the above copyright notice, this
14 # list of conditions and the following disclaimer in the documentation and/or
15 # other materials provided with the distribution.
16 #
17 # * Neither the name of the {organization} nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
25 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 """
33 PyGTK input hook for prompt_toolkit.
34
35 Listens on the pipe prompt_toolkit sets up for a notification that it should
36 return control to the terminal event loop.
37 """
38
39 import gtk, gobject
40
41 def inputhook(context):
42 """
43 When the eventloop of prompt-toolkit is idle, call this inputhook.
44
45 This will run the GTK main loop until the file descriptor
46 `context.fileno()` becomes ready.
47
48 :param context: An `InputHookContext` instance.
49 """
50 def _main_quit(*a, **kw):
51 gtk.main_quit()
52 return False
53
54 gobject.io_add_watch(context.fileno(), gobject.IO_IN, _main_quit)
55 gtk.main()
@@ -0,0 +1,12 b''
1 """prompt_toolkit input hook for GTK 3
2 """
3
4 from gi.repository import Gtk, GLib
5
6 def _main_quit(*args, **kwargs):
7 Gtk.main_quit()
8 return False
9
10 def inputhook(context):
11 GLib.io_add_watch(context.fileno(), GLib.IO_IN, _main_quit)
12 Gtk.main()
@@ -0,0 +1,11 b''
1 from IPython.external.qt_for_kernel import QtCore, QtGui
2
3 def inputhook(context):
4 app = QtCore.QCoreApplication.instance()
5 if not app:
6 return
7 event_loop = QtCore.QEventLoop(app)
8 notifier = QtCore.QSocketNotifier(context.fileno(), QtCore.QSocketNotifier.Read)
9 notifier.setEnabled(True)
10 notifier.activated.connect(event_loop.exit)
11 event_loop.exec_()
@@ -0,0 +1,93 b''
1 # Code borrowed from ptpython
2 # https://github.com/jonathanslenders/ptpython/blob/86b71a89626114b18898a0af463978bdb32eeb70/ptpython/eventloop.py
3
4 # Copyright (c) 2015, Jonathan Slenders
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without modification,
8 # are permitted provided that the following conditions are met:
9 #
10 # * Redistributions of source code must retain the above copyright notice, this
11 # list of conditions and the following disclaimer.
12 #
13 # * Redistributions in binary form must reproduce the above copyright notice, this
14 # list of conditions and the following disclaimer in the documentation and/or
15 # other materials provided with the distribution.
16 #
17 # * Neither the name of the {organization} nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
25 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32 """
33 Wrapper around the eventloop that gives some time to the Tkinter GUI to process
34 events when it's loaded and while we are waiting for input at the REPL. This
35 way we don't block the UI of for instance ``turtle`` and other Tk libraries.
36
37 (Normally Tkinter registeres it's callbacks in ``PyOS_InputHook`` to integrate
38 in readline. ``prompt-toolkit`` doesn't understand that input hook, but this
39 will fix it for Tk.)
40 """
41 import time
42
43 import _tkinter
44 try:
45 import tkinter
46 except ImportError:
47 import Tkinter as tkinter # Python 2
48
49 def inputhook(inputhook_context):
50 """
51 Inputhook for Tk.
52 Run the Tk eventloop until prompt-toolkit needs to process the next input.
53 """
54 # Get the current TK application.
55 root = tkinter._default_root
56
57 def wait_using_filehandler():
58 """
59 Run the TK eventloop until the file handler that we got from the
60 inputhook becomes readable.
61 """
62 # Add a handler that sets the stop flag when `prompt-toolkit` has input
63 # to process.
64 stop = [False]
65 def done(*a):
66 stop[0] = True
67
68 root.createfilehandler(inputhook_context.fileno(), _tkinter.READABLE, done)
69
70 # Run the TK event loop as long as we don't receive input.
71 while root.dooneevent(_tkinter.ALL_EVENTS):
72 if stop[0]:
73 break
74
75 root.deletefilehandler(inputhook_context.fileno())
76
77 def wait_using_polling():
78 """
79 Windows TK doesn't support 'createfilehandler'.
80 So, run the TK eventloop and poll until input is ready.
81 """
82 while not inputhook_context.input_is_ready():
83 while root.dooneevent(_tkinter.ALL_EVENTS | _tkinter.DONT_WAIT):
84 pass
85 # Sleep to make the CPU idle, but not too long, so that the UI
86 # stays responsive.
87 time.sleep(.01)
88
89 if root is not None:
90 if hasattr(root, 'createfilehandler'):
91 wait_using_filehandler()
92 else:
93 wait_using_polling()
@@ -9,7 +9,7 b' from prompt_toolkit.completion import Completer, Completion'
9 from prompt_toolkit.enums import DEFAULT_BUFFER
9 from prompt_toolkit.enums import DEFAULT_BUFFER
10 from prompt_toolkit.filters import HasFocus, HasSelection
10 from prompt_toolkit.filters import HasFocus, HasSelection
11 from prompt_toolkit.history import InMemoryHistory
11 from prompt_toolkit.history import InMemoryHistory
12 from prompt_toolkit.shortcuts import create_prompt_application
12 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop
13 from prompt_toolkit.interface import CommandLineInterface
13 from prompt_toolkit.interface import CommandLineInterface
14 from prompt_toolkit.key_binding.manager import KeyBindingManager
14 from prompt_toolkit.key_binding.manager import KeyBindingManager
15 from prompt_toolkit.key_binding.vi_state import InputMode
15 from prompt_toolkit.key_binding.vi_state import InputMode
@@ -22,6 +22,8 b' from pygments.styles import get_style_by_name'
22 from pygments.lexers import Python3Lexer, PythonLexer
22 from pygments.lexers import Python3Lexer, PythonLexer
23 from pygments.token import Token
23 from pygments.token import Token
24
24
25 from .pt_inputhooks import get_inputhook_func
26
25
27
26 class IPythonPTCompleter(Completer):
28 class IPythonPTCompleter(Completer):
27 """Adaptor to provide IPython completions to prompt_toolkit"""
29 """Adaptor to provide IPython completions to prompt_toolkit"""
@@ -40,7 +42,6 b' class IPythonPTCompleter(Completer):'
40 for m in matches:
42 for m in matches:
41 yield Completion(m, start_position=start_pos)
43 yield Completion(m, start_position=start_pos)
42
44
43
44 class PTInteractiveShell(InteractiveShell):
45 class PTInteractiveShell(InteractiveShell):
45 colors_force = True
46 colors_force = True
46
47
@@ -70,7 +71,6 b' class PTInteractiveShell(InteractiveShell):'
70 (Token.Prompt, (' ' * (width - 2)) + ': '),
71 (Token.Prompt, (' ' * (width - 2)) + ': '),
71 ]
72 ]
72
73
73
74 def init_prompt_toolkit_cli(self):
74 def init_prompt_toolkit_cli(self):
75 kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode)
75 kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode)
76 insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT)
76 insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT)
@@ -135,7 +135,8 b' class PTInteractiveShell(InteractiveShell):'
135 style=style,
135 style=style,
136 )
136 )
137
137
138 self.pt_cli = CommandLineInterface(app)
138 self.pt_cli = CommandLineInterface(app,
139 eventloop=create_eventloop(self.inputhook))
139
140
140 def __init__(self, *args, **kwargs):
141 def __init__(self, *args, **kwargs):
141 super(PTInteractiveShell, self).__init__(*args, **kwargs)
142 super(PTInteractiveShell, self).__init__(*args, **kwargs)
@@ -158,6 +159,16 b' class PTInteractiveShell(InteractiveShell):'
158 if document:
159 if document:
159 self.run_cell(document.text, store_history=True)
160 self.run_cell(document.text, store_history=True)
160
161
162 _inputhook = None
163 def inputhook(self, context):
164 if self._inputhook is not None:
165 self._inputhook(context)
166
167 def enable_gui(self, gui=None):
168 if gui:
169 self._inputhook = get_inputhook_func(gui)
170 else:
171 self._inputhook = None
161
172
162 if __name__ == '__main__':
173 if __name__ == '__main__':
163 PTInteractiveShell.instance().interact()
174 PTInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now