##// END OF EJS Templates
Proper redirection of keystrokes to subprocesses.
Gael Varoquaux -
Show More
@@ -1,55 +1,55 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Object for encapsulating process execution by using callbacks for stdout,
3 Object for encapsulating process execution by using callbacks for stdout,
4 stderr and stdin.
4 stderr and stdin.
5 """
5 """
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18 from killable_process import Popen, PIPE
18 from killable_process import Popen, PIPE
19 from threading import Thread
19 from threading import Thread
20 from time import sleep
20 from time import sleep
21
21
22 class PipedProcess(Thread):
22 class PipedProcess(Thread):
23
23
24 def __init__(self, command_string, out_callback,
24 def __init__(self, command_string, out_callback,
25 end_callback=None,):
25 end_callback=None,):
26 self.command_string = command_string
26 self.command_string = command_string
27 self.out_callback = out_callback
27 self.out_callback = out_callback
28 self.end_callback = end_callback
28 self.end_callback = end_callback
29 Thread.__init__(self)
29 Thread.__init__(self)
30
30
31
31
32 def run(self):
32 def run(self):
33 """ Start the process and hook up the callbacks.
33 """ Start the process and hook up the callbacks.
34 """
34 """
35 process = Popen((self.command_string + ' 2>&1', ), shell=True,
35 process = Popen((self.command_string + ' 2>&1', ), shell=True,
36 universal_newlines=True,
36 universal_newlines=True,
37 stdout=PIPE, stdin=PIPE)
37 stdout=PIPE, stdin=PIPE, )
38 self.process = process
38 self.process = process
39 while True:
39 while True:
40 out_char = process.stdout.read(1)
40 out_char = process.stdout.read(1)
41 if out_char == '':
41 if out_char == '':
42 if process.poll() is not None:
42 if process.poll() is not None:
43 # The process has finished
43 # The process has finished
44 break
44 break
45 else:
45 else:
46 # The process is not giving any interesting
46 # The process is not giving any interesting
47 # output. No use polling it immediatly.
47 # output. No use polling it immediatly.
48 sleep(0.1)
48 sleep(0.1)
49 else:
49 else:
50 self.out_callback(out_char)
50 self.out_callback(out_char)
51
51
52 if self.end_callback is not None:
52 if self.end_callback is not None:
53 self.end_callback()
53 self.end_callback()
54
54
55
55
@@ -1,75 +1,77 b''
1 """
1 """
2 Entry point for a simple application giving a graphical frontend to
2 Entry point for a simple application giving a graphical frontend to
3 ipython.
3 ipython.
4 """
4 """
5
5
6 import wx
6 import wx
7 from wx_frontend import WxController
7 from wx_frontend import WxController
8 import __builtin__
8 import __builtin__
9
9
10 class IPythonXController(WxController):
10 class IPythonXController(WxController):
11 """ Sub class of WxController that adds some application-specific
11 """ Sub class of WxController that adds some application-specific
12 bindings.
12 bindings.
13 """
13 """
14
14
15 debug = False
16
15 def __init__(self, *args, **kwargs):
17 def __init__(self, *args, **kwargs):
16 WxController.__init__(self, *args, **kwargs)
18 WxController.__init__(self, *args, **kwargs)
17 self.ipython0.ask_exit = self.do_exit
19 self.ipython0.ask_exit = self.do_exit
18
20
19
21
20 def _on_key_down(self, event, skip=True):
22 def _on_key_down(self, event, skip=True):
21 # Intercept Ctrl-D to quit
23 # Intercept Ctrl-D to quit
22 if event.KeyCode == ord('D') and event.ControlDown() and \
24 if event.KeyCode == ord('D') and event.ControlDown() and \
23 self.get_current_edit_buffer()=='' and \
25 self.get_current_edit_buffer()=='' and \
24 self._input_state == 'readline':
26 self._input_state == 'readline':
25 wx.CallAfter(self.ask_exit)
27 wx.CallAfter(self.ask_exit)
26 else:
28 else:
27 WxController._on_key_down(self, event, skip=skip)
29 WxController._on_key_down(self, event, skip=skip)
28
30
29
31
30 def ask_exit(self):
32 def ask_exit(self):
31 """ Ask the user whether to exit.
33 """ Ask the user whether to exit.
32 """
34 """
33 self.write('\n')
35 self.write('\n')
34 self.capture_output()
36 self.capture_output()
35 self.ipython0.shell.exit()
37 self.ipython0.shell.exit()
36 self.release_output()
38 self.release_output()
37 wx.Yield()
39 wx.Yield()
38 if not self.ipython0.exit_now:
40 if not self.ipython0.exit_now:
39 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
41 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
40
42
41
43
42 def do_exit(self):
44 def do_exit(self):
43 """ Exits the interpreter, kills the windows.
45 """ Exits the interpreter, kills the windows.
44 """
46 """
45 WxController.do_exit(self)
47 WxController.do_exit(self)
46 self.release_output()
48 self.release_output()
47 wx.CallAfter(wx.Exit)
49 wx.CallAfter(wx.Exit)
48
50
49
51
50
52
51 class IPythonX(wx.Frame):
53 class IPythonX(wx.Frame):
52 """ Main frame of the IPythonX app.
54 """ Main frame of the IPythonX app.
53 """
55 """
54
56
55 def __init__(self, parent, id, title):
57 def __init__(self, parent, id, title):
56 wx.Frame.__init__(self, parent, id, title, size=(300,250))
58 wx.Frame.__init__(self, parent, id, title, size=(300,250))
57 self._sizer = wx.BoxSizer(wx.VERTICAL)
59 self._sizer = wx.BoxSizer(wx.VERTICAL)
58 self.shell = IPythonXController(self)
60 self.shell = IPythonXController(self)
59 self._sizer.Add(self.shell, 1, wx.EXPAND)
61 self._sizer.Add(self.shell, 1, wx.EXPAND)
60 self.SetSizer(self._sizer)
62 self.SetSizer(self._sizer)
61 self.SetAutoLayout(1)
63 self.SetAutoLayout(1)
62 self.Show(True)
64 self.Show(True)
63
65
64
66
65 def main():
67 def main():
66 app = wx.PySimpleApp()
68 app = wx.PySimpleApp()
67 frame = IPythonX(None, wx.ID_ANY, 'IPythonX')
69 frame = IPythonX(None, wx.ID_ANY, 'IPythonX')
68 frame.shell.SetFocus()
70 frame.shell.SetFocus()
69 frame.shell.app = app
71 frame.shell.app = app
70 frame.SetSize((680, 460))
72 frame.SetSize((680, 460))
71
73
72 app.MainLoop()
74 app.MainLoop()
73
75
74 if __name__ == '__main__':
76 if __name__ == '__main__':
75 main()
77 main()
@@ -1,398 +1,407 b''
1 # encoding: utf-8 -*- test-case-name:
1 # encoding: utf-8 -*- test-case-name:
2 # FIXME: Need to add tests.
2 # FIXME: Need to add tests.
3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
4
4
5 """Classes to provide a Wx frontend to the
5 """Classes to provide a Wx frontend to the
6 IPython.kernel.core.interpreter.
6 IPython.kernel.core.interpreter.
7
7
8 """
8 """
9
9
10 __docformat__ = "restructuredtext en"
10 __docformat__ = "restructuredtext en"
11
11
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13 # Copyright (C) 2008 The IPython Development Team
13 # Copyright (C) 2008 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22
22
23
23
24 import wx
24 import wx
25 import re
25 import re
26 from wx import stc
26 from wx import stc
27 from console_widget import ConsoleWidget
27 from console_widget import ConsoleWidget
28 import __builtin__
28 import __builtin__
29 from time import sleep
29 from time import sleep
30 import sys
30 import sys
31 import signal
31 import signal
32
32
33 from threading import Lock
33 from threading import Lock
34
34
35 from IPython.frontend.piped_process import PipedProcess
35 from IPython.frontend.piped_process import PipedProcess
36 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
36 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
37
37
38 #_COMMAND_BG = '#FAFAF1' # Nice green
38 #_COMMAND_BG = '#FAFAF1' # Nice green
39 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
39 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
40 _ERROR_BG = '#FFF1F1' # Nice red
40 _ERROR_BG = '#FFF1F1' # Nice red
41
41
42 _RUNNING_BUFFER_MARKER = 31
42 _RUNNING_BUFFER_MARKER = 31
43 _ERROR_MARKER = 30
43 _ERROR_MARKER = 30
44
44
45 #-------------------------------------------------------------------------------
45 #-------------------------------------------------------------------------------
46 # Classes to implement the Wx frontend
46 # Classes to implement the Wx frontend
47 #-------------------------------------------------------------------------------
47 #-------------------------------------------------------------------------------
48 class WxController(PrefilterFrontEnd, ConsoleWidget):
48 class WxController(PrefilterFrontEnd, ConsoleWidget):
49
49
50 output_prompt = \
50 output_prompt = \
51 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
51 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
52
52
53 # Print debug info on what is happening to the console.
53 # Print debug info on what is happening to the console.
54 debug = True
54 debug = True
55
55
56 # The title of the terminal, as captured through the ANSI escape
56 # The title of the terminal, as captured through the ANSI escape
57 # sequences.
57 # sequences.
58
58
59 def _set_title(self, title):
59 def _set_title(self, title):
60 return self.Parent.SetTitle(title)
60 return self.Parent.SetTitle(title)
61
61
62 def _get_title(self):
62 def _get_title(self):
63 return self.Parent.GetTitle()
63 return self.Parent.GetTitle()
64
64
65 title = property(_get_title, _set_title)
65 title = property(_get_title, _set_title)
66
66
67 #--------------------------------------------------------------------------
67 #--------------------------------------------------------------------------
68 # Private Attributes
68 # Private Attributes
69 #--------------------------------------------------------------------------
69 #--------------------------------------------------------------------------
70
70
71 # A flag governing the behavior of the input. Can be:
71 # A flag governing the behavior of the input. Can be:
72 #
72 #
73 # 'readline' for readline-like behavior with a prompt
73 # 'readline' for readline-like behavior with a prompt
74 # and an edit buffer.
74 # and an edit buffer.
75 # 'subprocess' for sending the raw input directly to a
75 # 'subprocess' for sending the raw input directly to a
76 # subprocess.
76 # subprocess.
77 # 'buffering' for buffering of the input, that will be used
77 # 'buffering' for buffering of the input, that will be used
78 # when the input state switches back to another state.
78 # when the input state switches back to another state.
79 _input_state = 'readline'
79 _input_state = 'readline'
80
80
81 # Attribute to store reference to the pipes of a subprocess, if we
81 # Attribute to store reference to the pipes of a subprocess, if we
82 # are running any.
82 # are running any.
83 _running_process = False
83 _running_process = False
84
84
85 # A queue for writing fast streams to the screen without flooding the
85 # A queue for writing fast streams to the screen without flooding the
86 # event loop
86 # event loop
87 _out_buffer = []
87 _out_buffer = []
88
88
89 # A lock to lock the _out_buffer to make sure we don't empty it
89 # A lock to lock the _out_buffer to make sure we don't empty it
90 # while it is being swapped
90 # while it is being swapped
91 _out_buffer_lock = Lock()
91 _out_buffer_lock = Lock()
92
92
93 #--------------------------------------------------------------------------
93 #--------------------------------------------------------------------------
94 # Public API
94 # Public API
95 #--------------------------------------------------------------------------
95 #--------------------------------------------------------------------------
96
96
97 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
97 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
98 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
98 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
99 *args, **kwds):
99 *args, **kwds):
100 """ Create Shell instance.
100 """ Create Shell instance.
101 """
101 """
102 ConsoleWidget.__init__(self, parent, id, pos, size, style)
102 ConsoleWidget.__init__(self, parent, id, pos, size, style)
103 PrefilterFrontEnd.__init__(self)
103 PrefilterFrontEnd.__init__(self)
104
104
105 # Marker for running buffer.
105 # Marker for running buffer.
106 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
106 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
107 background=_RUNNING_BUFFER_BG)
107 background=_RUNNING_BUFFER_BG)
108 # Marker for tracebacks.
108 # Marker for tracebacks.
109 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
109 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
110 background=_ERROR_BG)
110 background=_ERROR_BG)
111
111
112 # A time for flushing the write buffer
112 # A time for flushing the write buffer
113 BUFFER_FLUSH_TIMER_ID = 100
113 BUFFER_FLUSH_TIMER_ID = 100
114 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
114 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
115 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
115 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
116
116
117 def do_completion(self):
117 def do_completion(self):
118 """ Do code completion.
118 """ Do code completion.
119 """
119 """
120 if self.debug:
120 if self.debug:
121 print >>sys.__stdout__, "do_completion",
121 print >>sys.__stdout__, "do_completion",
122 line = self.get_current_edit_buffer()
122 line = self.get_current_edit_buffer()
123 new_line, completions = self.complete(line)
123 new_line, completions = self.complete(line)
124 if len(completions)>1:
124 if len(completions)>1:
125 self.write_completion(completions)
125 self.write_completion(completions)
126 self.replace_current_edit_buffer(new_line)
126 self.replace_current_edit_buffer(new_line)
127 if self.debug:
127 if self.debug:
128 print >>sys.__stdout__, completions
128 print >>sys.__stdout__, completions
129
129
130
130
131 def do_calltip(self):
131 def do_calltip(self):
132 if self.debug:
132 if self.debug:
133 print >>sys.__stdout__, "do_calltip"
133 print >>sys.__stdout__, "do_calltip"
134 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
134 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
135 symbol = self.get_current_edit_buffer()
135 symbol = self.get_current_edit_buffer()
136 symbol_string = separators.split(symbol)[-1]
136 symbol_string = separators.split(symbol)[-1]
137 base_symbol_string = symbol_string.split('.')[0]
137 base_symbol_string = symbol_string.split('.')[0]
138 if base_symbol_string in self.shell.user_ns:
138 if base_symbol_string in self.shell.user_ns:
139 symbol = self.shell.user_ns[base_symbol_string]
139 symbol = self.shell.user_ns[base_symbol_string]
140 elif base_symbol_string in self.shell.user_global_ns:
140 elif base_symbol_string in self.shell.user_global_ns:
141 symbol = self.shell.user_global_ns[base_symbol_string]
141 symbol = self.shell.user_global_ns[base_symbol_string]
142 elif base_symbol_string in __builtin__.__dict__:
142 elif base_symbol_string in __builtin__.__dict__:
143 symbol = __builtin__.__dict__[base_symbol_string]
143 symbol = __builtin__.__dict__[base_symbol_string]
144 else:
144 else:
145 return False
145 return False
146 for name in symbol_string.split('.')[1:] + ['__doc__']:
146 for name in symbol_string.split('.')[1:] + ['__doc__']:
147 symbol = getattr(symbol, name)
147 symbol = getattr(symbol, name)
148 try:
148 try:
149 self.AutoCompCancel()
149 self.AutoCompCancel()
150 wx.Yield()
150 wx.Yield()
151 self.CallTipShow(self.GetCurrentPos(), symbol)
151 self.CallTipShow(self.GetCurrentPos(), symbol)
152 except:
152 except:
153 # The retrieve symbol couldn't be converted to a string
153 # The retrieve symbol couldn't be converted to a string
154 pass
154 pass
155
155
156
156
157 def popup_completion(self, create=False):
157 def popup_completion(self, create=False):
158 """ Updates the popup completion menu if it exists. If create is
158 """ Updates the popup completion menu if it exists. If create is
159 true, open the menu.
159 true, open the menu.
160 """
160 """
161 if self.debug:
161 if self.debug:
162 print >>sys.__stdout__, "popup_completion",
162 print >>sys.__stdout__, "popup_completion",
163 line = self.get_current_edit_buffer()
163 line = self.get_current_edit_buffer()
164 if (self.AutoCompActive() and not line[-1] == '.') \
164 if (self.AutoCompActive() and not line[-1] == '.') \
165 or create==True:
165 or create==True:
166 suggestion, completions = self.complete(line)
166 suggestion, completions = self.complete(line)
167 offset=0
167 offset=0
168 if completions:
168 if completions:
169 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
169 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
170 residual = complete_sep.split(line)[-1]
170 residual = complete_sep.split(line)[-1]
171 offset = len(residual)
171 offset = len(residual)
172 self.pop_completion(completions, offset=offset)
172 self.pop_completion(completions, offset=offset)
173 if self.debug:
173 if self.debug:
174 print >>sys.__stdout__, completions
174 print >>sys.__stdout__, completions
175
175
176
176
177 def new_prompt(self, prompt):
177 def new_prompt(self, prompt):
178 self._input_state = 'readline'
178 self._input_state = 'readline'
179 ConsoleWidget.new_prompt(self, prompt)
179 ConsoleWidget.new_prompt(self, prompt)
180
180
181
181
182 def raw_input(self, prompt):
182 def raw_input(self, prompt):
183 """ A replacement from python's raw_input.
183 """ A replacement from python's raw_input.
184 """
184 """
185 self.new_prompt(prompt)
185 self.new_prompt(prompt)
186 self.waiting = True
186 self.waiting = True
187 self.__old_on_enter = self._on_enter
187 self.__old_on_enter = self._on_enter
188 def my_on_enter():
188 def my_on_enter():
189 self.waiting = False
189 self.waiting = False
190 self._on_enter = my_on_enter
190 self._on_enter = my_on_enter
191 # XXX: Busy waiting, ugly.
191 # XXX: Busy waiting, ugly.
192 while self.waiting:
192 while self.waiting:
193 wx.Yield()
193 wx.Yield()
194 sleep(0.1)
194 sleep(0.1)
195 self._on_enter = self.__old_on_enter
195 self._on_enter = self.__old_on_enter
196 self._input_state = 'buffering'
196 self._input_state = 'buffering'
197 return self.get_current_edit_buffer().rstrip('\n')
197 return self.get_current_edit_buffer().rstrip('\n')
198
198
199
199
200 def execute(self, python_string, raw_string=None):
200 def execute(self, python_string, raw_string=None):
201 self._input_state = 'buffering'
201 self._input_state = 'buffering'
202 self.CallTipCancel()
202 self.CallTipCancel()
203 self._cursor = wx.BusyCursor()
203 self._cursor = wx.BusyCursor()
204 if raw_string is None:
204 if raw_string is None:
205 raw_string = python_string
205 raw_string = python_string
206 end_line = self.current_prompt_line \
206 end_line = self.current_prompt_line \
207 + max(1, len(raw_string.split('\n'))-1)
207 + max(1, len(raw_string.split('\n'))-1)
208 for i in range(self.current_prompt_line, end_line):
208 for i in range(self.current_prompt_line, end_line):
209 self.MarkerAdd(i, _RUNNING_BUFFER_MARKER)
209 self.MarkerAdd(i, _RUNNING_BUFFER_MARKER)
210 # Update the display:
210 # Update the display:
211 wx.Yield()
211 wx.Yield()
212 self.GotoPos(self.GetLength())
212 self.GotoPos(self.GetLength())
213 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
213 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
214
214
215
215
216 def capture_output(self):
216 def capture_output(self):
217 self.__old_raw_input = __builtin__.raw_input
217 self.__old_raw_input = __builtin__.raw_input
218 __builtin__.raw_input = self.raw_input
218 __builtin__.raw_input = self.raw_input
219 PrefilterFrontEnd.capture_output(self)
219 PrefilterFrontEnd.capture_output(self)
220
220
221
221
222 def release_output(self):
222 def release_output(self):
223 __builtin__.raw_input = self.__old_raw_input
223 __builtin__.raw_input = self.__old_raw_input
224 PrefilterFrontEnd.capture_output(self)
224 PrefilterFrontEnd.capture_output(self)
225
225
226
226
227 def after_execute(self):
227 def after_execute(self):
228 PrefilterFrontEnd.after_execute(self)
228 PrefilterFrontEnd.after_execute(self)
229 if hasattr(self, '_cursor'):
229 if hasattr(self, '_cursor'):
230 del self._cursor
230 del self._cursor
231
231
232
232
233 def system_call(self, command_string):
233 def system_call(self, command_string):
234 self._input_state = 'subprocess'
234 self._input_state = 'subprocess'
235 self._running_process = PipedProcess(command_string,
235 self._running_process = PipedProcess(command_string,
236 out_callback=self.buffered_write,
236 out_callback=self.buffered_write,
237 end_callback = self._end_system_call)
237 end_callback = self._end_system_call)
238 self._running_process.start()
238 self._running_process.start()
239 # XXX: another one of these polling loops to have a blocking
239 # XXX: another one of these polling loops to have a blocking
240 # call
240 # call
241 wx.Yield()
241 wx.Yield()
242 while self._running_process:
242 while self._running_process:
243 wx.Yield()
243 wx.Yield()
244 sleep(0.1)
244 sleep(0.1)
245 # Be sure to flush the buffer.
245 # Be sure to flush the buffer.
246 self._buffer_flush(event=None)
246 self._buffer_flush(event=None)
247
247
248
248
249 def buffered_write(self, text):
249 def buffered_write(self, text):
250 """ A write method for streams, that caches the stream in order
250 """ A write method for streams, that caches the stream in order
251 to avoid flooding the event loop.
251 to avoid flooding the event loop.
252
252
253 This can be called outside of the main loop, in separate
253 This can be called outside of the main loop, in separate
254 threads.
254 threads.
255 """
255 """
256 self._out_buffer_lock.acquire()
256 self._out_buffer_lock.acquire()
257 self._out_buffer.append(text)
257 self._out_buffer.append(text)
258 self._out_buffer_lock.release()
258 self._out_buffer_lock.release()
259 if not self._buffer_flush_timer.IsRunning():
259 if not self._buffer_flush_timer.IsRunning():
260 wx.CallAfter(self._buffer_flush_timer.Start, 100) # milliseconds
260 wx.CallAfter(self._buffer_flush_timer.Start, 100) # milliseconds
261
261
262
262
263 def show_traceback(self):
263 def show_traceback(self):
264 start_line = self.GetCurrentLine()
264 start_line = self.GetCurrentLine()
265 PrefilterFrontEnd.show_traceback(self)
265 PrefilterFrontEnd.show_traceback(self)
266 wx.Yield()
266 wx.Yield()
267 for i in range(start_line, self.GetCurrentLine()):
267 for i in range(start_line, self.GetCurrentLine()):
268 self.MarkerAdd(i, _ERROR_MARKER)
268 self.MarkerAdd(i, _ERROR_MARKER)
269
269
270
270
271 #--------------------------------------------------------------------------
271 #--------------------------------------------------------------------------
272 # Private API
272 # Private API
273 #--------------------------------------------------------------------------
273 #--------------------------------------------------------------------------
274
274
275 def _on_key_down(self, event, skip=True):
275 def _on_key_down(self, event, skip=True):
276 """ Capture the character events, let the parent
276 """ Capture the character events, let the parent
277 widget handle them, and put our logic afterward.
277 widget handle them, and put our logic afterward.
278 """
278 """
279 print >>sys.__stderr__, event.KeyCode
280 current_line_number = self.GetCurrentLine()
279 current_line_number = self.GetCurrentLine()
281 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
280 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
282 # Capture Control-C
281 # Capture Control-C
283 if self._input_state == 'subprocess':
282 if self._input_state == 'subprocess':
284 if self.debug:
283 if self.debug:
285 print >>sys.__stderr__, 'Killing running process'
284 print >>sys.__stderr__, 'Killing running process'
286 self._running_process.process.kill()
285 self._running_process.process.kill()
287 elif self._input_state == 'buffering':
286 elif self._input_state == 'buffering':
288 if self.debug:
287 if self.debug:
289 print >>sys.__stderr__, 'Raising KeyboardException'
288 print >>sys.__stderr__, 'Raising KeyboardException'
290 raise KeyboardException
289 raise KeyboardException
291 # XXX: We need to make really sure we
290 # XXX: We need to make really sure we
292 # get back to a prompt.
291 # get back to a prompt.
293 elif self._input_state == 'subprocess' and event.KeyCode<256 \
292 elif self._input_state == 'subprocess' and (
294 and event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
293 ( event.KeyCode<256 and
294 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN, wx.MOD_SHIFT))
295 or
296 ( event.KeyCode in (ord('d'), ord('D')) and
297 event.ControlDown())):
295 # We are running a process, we redirect keys.
298 # We are running a process, we redirect keys.
296 ConsoleWidget._on_key_down(self, event, skip=skip)
299 ConsoleWidget._on_key_down(self, event, skip=skip)
297 if self.debug:
300 char = chr(event.KeyCode)
298 print >>sys.__stderr__, chr(event.KeyCode)
301 # Deal with some inconsistency in wx keycodes:
299 self._running_process.process.stdin.write(chr(event.KeyCode))
302 if char == '\r':
303 char = '\n'
304 elif not event.ShiftDown():
305 char = char.lower()
306 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
307 char = '\04'
308 self._running_process.process.stdin.write(char)
300 self._running_process.process.stdin.flush()
309 self._running_process.process.stdin.flush()
301 elif event.KeyCode in (ord('('), 57):
310 elif event.KeyCode in (ord('('), 57):
302 # Calltips
311 # Calltips
303 event.Skip()
312 event.Skip()
304 self.do_calltip()
313 self.do_calltip()
305 elif self.AutoCompActive():
314 elif self.AutoCompActive():
306 event.Skip()
315 event.Skip()
307 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
316 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
308 wx.CallAfter(self.popup_completion, create=True)
317 wx.CallAfter(self.popup_completion, create=True)
309 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
318 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
310 wx.WXK_RIGHT):
319 wx.WXK_RIGHT):
311 wx.CallAfter(self.popup_completion)
320 wx.CallAfter(self.popup_completion)
312 else:
321 else:
313 # Up history
322 # Up history
314 if event.KeyCode == wx.WXK_UP and (
323 if event.KeyCode == wx.WXK_UP and (
315 ( current_line_number == self.current_prompt_line and
324 ( current_line_number == self.current_prompt_line and
316 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
325 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
317 or event.ControlDown() ):
326 or event.ControlDown() ):
318 new_buffer = self.get_history_previous(
327 new_buffer = self.get_history_previous(
319 self.get_current_edit_buffer())
328 self.get_current_edit_buffer())
320 if new_buffer is not None:
329 if new_buffer is not None:
321 self.replace_current_edit_buffer(new_buffer)
330 self.replace_current_edit_buffer(new_buffer)
322 if self.GetCurrentLine() > self.current_prompt_line:
331 if self.GetCurrentLine() > self.current_prompt_line:
323 # Go to first line, for seemless history up.
332 # Go to first line, for seemless history up.
324 self.GotoPos(self.current_prompt_pos)
333 self.GotoPos(self.current_prompt_pos)
325 # Down history
334 # Down history
326 elif event.KeyCode == wx.WXK_DOWN and (
335 elif event.KeyCode == wx.WXK_DOWN and (
327 ( current_line_number == self.LineCount -1 and
336 ( current_line_number == self.LineCount -1 and
328 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
337 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
329 or event.ControlDown() ):
338 or event.ControlDown() ):
330 new_buffer = self.get_history_next()
339 new_buffer = self.get_history_next()
331 if new_buffer is not None:
340 if new_buffer is not None:
332 self.replace_current_edit_buffer(new_buffer)
341 self.replace_current_edit_buffer(new_buffer)
333 # Tab-completion
342 # Tab-completion
334 elif event.KeyCode == ord('\t'):
343 elif event.KeyCode == ord('\t'):
335 last_line = self.get_current_edit_buffer().split('\n')[-1]
344 last_line = self.get_current_edit_buffer().split('\n')[-1]
336 if not re.match(r'^\s*$', last_line):
345 if not re.match(r'^\s*$', last_line):
337 self.do_completion()
346 self.do_completion()
338 else:
347 else:
339 event.Skip()
348 event.Skip()
340 else:
349 else:
341 ConsoleWidget._on_key_down(self, event, skip=skip)
350 ConsoleWidget._on_key_down(self, event, skip=skip)
342
351
343
352
344 def _on_key_up(self, event, skip=True):
353 def _on_key_up(self, event, skip=True):
345 if event.KeyCode in (59, ord('.')):
354 if event.KeyCode in (59, ord('.')):
346 # Intercepting '.'
355 # Intercepting '.'
347 event.Skip()
356 event.Skip()
348 self.popup_completion(create=True)
357 self.popup_completion(create=True)
349 else:
358 else:
350 ConsoleWidget._on_key_up(self, event, skip=skip)
359 ConsoleWidget._on_key_up(self, event, skip=skip)
351
360
352
361
353 def _on_enter(self):
362 def _on_enter(self):
354 if self.debug:
363 if self.debug:
355 print >>sys.__stdout__, repr(self.get_current_edit_buffer())
364 print >>sys.__stdout__, repr(self.get_current_edit_buffer())
356 PrefilterFrontEnd._on_enter(self)
365 PrefilterFrontEnd._on_enter(self)
357
366
358
367
359 def _end_system_call(self):
368 def _end_system_call(self):
360 """ Called at the end of a system call.
369 """ Called at the end of a system call.
361 """
370 """
362 print >>sys.__stderr__, 'End of system call'
371 print >>sys.__stderr__, 'End of system call'
363 self._input_state = 'buffering'
372 self._input_state = 'buffering'
364 self._running_process = False
373 self._running_process = False
365
374
366
375
367 def _buffer_flush(self, event):
376 def _buffer_flush(self, event):
368 """ Called by the timer to flush the write buffer.
377 """ Called by the timer to flush the write buffer.
369
378
370 This is always called in the mainloop, by the wx timer.
379 This is always called in the mainloop, by the wx timer.
371 """
380 """
372 self._out_buffer_lock.acquire()
381 self._out_buffer_lock.acquire()
373 _out_buffer = self._out_buffer
382 _out_buffer = self._out_buffer
374 self._out_buffer = []
383 self._out_buffer = []
375 self._out_buffer_lock.release()
384 self._out_buffer_lock.release()
376 self.write(''.join(_out_buffer), refresh=False)
385 self.write(''.join(_out_buffer), refresh=False)
377 self._buffer_flush_timer.Stop()
386 self._buffer_flush_timer.Stop()
378
387
379
388
380 if __name__ == '__main__':
389 if __name__ == '__main__':
381 class MainWindow(wx.Frame):
390 class MainWindow(wx.Frame):
382 def __init__(self, parent, id, title):
391 def __init__(self, parent, id, title):
383 wx.Frame.__init__(self, parent, id, title, size=(300,250))
392 wx.Frame.__init__(self, parent, id, title, size=(300,250))
384 self._sizer = wx.BoxSizer(wx.VERTICAL)
393 self._sizer = wx.BoxSizer(wx.VERTICAL)
385 self.shell = WxController(self)
394 self.shell = WxController(self)
386 self._sizer.Add(self.shell, 1, wx.EXPAND)
395 self._sizer.Add(self.shell, 1, wx.EXPAND)
387 self.SetSizer(self._sizer)
396 self.SetSizer(self._sizer)
388 self.SetAutoLayout(1)
397 self.SetAutoLayout(1)
389 self.Show(True)
398 self.Show(True)
390
399
391 app = wx.PySimpleApp()
400 app = wx.PySimpleApp()
392 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
401 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
393 frame.shell.SetFocus()
402 frame.shell.SetFocus()
394 frame.SetSize((680, 460))
403 frame.SetSize((680, 460))
395 self = frame.shell
404 self = frame.shell
396
405
397 app.MainLoop()
406 app.MainLoop()
398
407
General Comments 0
You need to be logged in to leave comments. Login now