##// END OF EJS Templates
Make code execution more robust.
Gael Varoquaux -
Show More
@@ -1,149 +1,158 b''
1 1 """
2 2 Base front end class for all line-oriented frontends.
3 3
4 4 Currently this focuses on synchronous frontends.
5 5 """
6 6 __docformat__ = "restructuredtext en"
7 7
8 8 #-------------------------------------------------------------------------------
9 9 # Copyright (C) 2008 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-------------------------------------------------------------------------------
14 14
15 15 #-------------------------------------------------------------------------------
16 16 # Imports
17 17 #-------------------------------------------------------------------------------
18 18 import re
19 19
20 20 import IPython
21 21
22 22
23 23 from frontendbase import FrontEndBase
24 24 from IPython.kernel.core.interpreter import Interpreter
25 25
26 26 #-------------------------------------------------------------------------------
27 27 # Base class for the line-oriented front ends
28 28 #-------------------------------------------------------------------------------
29 29 class LineFrontEndBase(FrontEndBase):
30 30
31 31 # We need to keep the prompt number, to be able to increment
32 32 # it when there is an exception.
33 33 prompt_number = 1
34 34
35 35
36 36 #--------------------------------------------------------------------------
37 37 # Public API
38 38 #--------------------------------------------------------------------------
39 39
40 40 def __init__(self, shell=None, history=None):
41 41 if shell is None:
42 42 shell = Interpreter()
43 43 FrontEndBase.__init__(self, shell=shell, history=history)
44 44
45 45 #FIXME: print banner.
46 46 banner = """IPython1 %s -- An enhanced Interactive Python.""" \
47 47 % IPython.__version__
48 48
49 49
50 50 def complete(self, token):
51 51 """Complete token in engine's user_ns
52 52
53 53 Parameters
54 54 ----------
55 55 token : string
56 56
57 57 Result
58 58 ------
59 59 Deferred result of
60 60 IPython.kernel.engineservice.IEngineBase.complete
61 61 """
62 62
63 63 return self.shell.complete(token)
64 64
65 65
66 66 def render_result(self, result):
67 67 if 'stdout' in result and result['stdout']:
68 68 self.write('\n' + result['stdout'])
69 69 if 'display' in result and result['display']:
70 70 self.write("%s%s\n" % (
71 71 self.output_prompt % result['number'],
72 72 result['display']['pprint']
73 73 ) )
74 74
75 75
76 76 def render_error(self, failure):
77 77 self.insert_text('\n\n'+str(failure)+'\n\n')
78 78 return failure
79 79
80 80
81 81 def prefilter_input(self, string):
82 82 string = string.replace('\r\n', '\n')
83 83 string = string.replace('\t', 4*' ')
84 84 # Clean the trailing whitespace
85 85 string = '\n'.join(l.rstrip() for l in string.split('\n'))
86 86 return string
87 87
88 88
89 89 def is_complete(self, string):
90 90 if ( len(self.get_current_edit_buffer().split('\n'))>1
91 91 and not re.findall(r"\n[\t ]*$", string)):
92 92 return False
93 93 else:
94 94 return FrontEndBase.is_complete(self, string)
95 95
96 96
97 97 def execute(self, python_string, raw_string=None):
98 98 """ Send the python_string to the interpreter, stores the
99 99 raw_string in the history and starts a new prompt.
100 100 """
101 101 if raw_string is None:
102 102 raw_string = string
103 103 # Create a false result, in case there is an exception
104 result = dict(number=self.prompt_number)
104 self.last_result = dict(number=self.prompt_number)
105 105 try:
106 106 self.history.input_cache[-1] = raw_string
107 107 result = self.shell.execute(python_string)
108 self.last_result = result
108 109 self.render_result(result)
109 except Exception, e:
110 except:
110 111 self.show_traceback()
111 112 finally:
112 self.prompt_number += 1
113 self.new_prompt(self.prompt % (result['number'] + 1))
114 # Start a new empty history entry
115 self._add_history(None, '')
116 # The result contains useful information that can be used
117 # elsewhere.
118 self.last_result = result
113 self.after_execute()
114
115
116 def after_execute(self):
117 """ All the operations required after an execution to put the
118 terminal back in a shape where it is usable.
119 """
120 self.prompt_number += 1
121 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
122 # Start a new empty history entry
123 self._add_history(None, '')
124 # The result contains useful information that can be used
125 # elsewhere.
119 126
120 127
121 128 def _on_enter(self):
122 129 """ Called when the return key is pressed in a line editing
123 130 buffer.
124 131 """
125 132 current_buffer = self.get_current_edit_buffer()
126 133 cleaned_buffer = self.prefilter_input(current_buffer)
127 if self.is_complete(cleaned_buffer):
134 if self.is_complete(cleaned_buffer + '\n'):
135 # The '\n' is important in case prefiltering empties the
136 # line, to get a new prompt.
128 137 self.execute(cleaned_buffer, raw_string=current_buffer)
129 138 else:
130 139 if len(current_buffer.split('\n'))>1:
131 self.write('\n' + self._get_indent_string(current_buffer))
140 self.write(self._get_indent_string(current_buffer))
132 141 else:
133 self.write('\n\t')
142 self.write('\t')
134 143
135 144
136 145 #--------------------------------------------------------------------------
137 146 # Private API
138 147 #--------------------------------------------------------------------------
139 148
140 149 def _get_indent_string(self, string):
141 print >>sys.__stderr__, string.split('\n')
150 string = string.replace('\t', ' '*4)
142 151 string = string.split('\n')[-1]
143 152 indent_chars = len(string) - len(string.lstrip())
144 153 indent_string = '\t'*(indent_chars // 4) + \
145 154 ' '*(indent_chars % 4)
146 155
147 156 return indent_string
148 157
149 158
@@ -1,410 +1,411 b''
1 1 # encoding: utf-8
2 2 """
3 3 A Wx widget to act as a console and input commands.
4 4
5 5 This widget deals with prompts and provides an edit buffer
6 6 restricted to after the last prompt.
7 7 """
8 8
9 9 __docformat__ = "restructuredtext en"
10 10
11 11 #-------------------------------------------------------------------------------
12 12 # Copyright (C) 2008 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is
15 15 # in the file COPYING, distributed as part of this software.
16 16 #-------------------------------------------------------------------------------
17 17
18 18 #-------------------------------------------------------------------------------
19 19 # Imports
20 20 #-------------------------------------------------------------------------------
21 21
22 22 import wx
23 23 import wx.stc as stc
24 24
25 25 from wx.py import editwindow
26 26
27 27 import re
28 28
29 29 # FIXME: Need to provide an API for non user-generated display on the
30 30 # screen: this should not be editable by the user.
31 31
32 32 if wx.Platform == '__WXMSW__':
33 33 _DEFAULT_SIZE = 80
34 34 else:
35 35 _DEFAULT_SIZE = 10
36 36
37 37 _DEFAULT_STYLE = {
38 38 'stdout' : 'fore:#0000FF',
39 39 'stderr' : 'fore:#007f00',
40 40 'trace' : 'fore:#FF0000',
41 41
42 42 'default' : 'size:%d' % _DEFAULT_SIZE,
43 43 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
44 44 'bracebad' : 'fore:#000000,back:#FF0000,bold',
45 45
46 46 # properties for the various Python lexer styles
47 47 'comment' : 'fore:#007F00',
48 48 'number' : 'fore:#007F7F',
49 49 'string' : 'fore:#7F007F,italic',
50 50 'char' : 'fore:#7F007F,italic',
51 51 'keyword' : 'fore:#00007F,bold',
52 52 'triple' : 'fore:#7F0000',
53 53 'tripledouble': 'fore:#7F0000',
54 54 'class' : 'fore:#0000FF,bold,underline',
55 55 'def' : 'fore:#007F7F,bold',
56 56 'operator' : 'bold',
57 57
58 58 }
59 59
60 60 # new style numbers
61 61 _STDOUT_STYLE = 15
62 62 _STDERR_STYLE = 16
63 63 _TRACE_STYLE = 17
64 64
65 65
66 66 #-------------------------------------------------------------------------------
67 67 # The console widget class
68 68 #-------------------------------------------------------------------------------
69 69 class ConsoleWidget(editwindow.EditWindow):
70 70 """ Specialized styled text control view for console-like workflow.
71 71
72 72 This widget is mainly interested in dealing with the prompt and
73 73 keeping the cursor inside the editing line.
74 74 """
75 75
76 76 title = 'Console'
77 77
78 78 style = _DEFAULT_STYLE.copy()
79 79
80 80 # Translation table from ANSI escape sequences to color. Override
81 81 # this to specify your colors.
82 82 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
83 83 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
84 84 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
85 85 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
86 86 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
87 87 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
88 88 '1;34': [12, 'LIGHT BLUE'], '1;35':
89 89 [13, 'MEDIUM VIOLET RED'],
90 90 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
91 91
92 92 # The color of the carret (call _apply_style() after setting)
93 93 carret_color = 'BLACK'
94 94
95 95
96 96 #--------------------------------------------------------------------------
97 97 # Public API
98 98 #--------------------------------------------------------------------------
99 99
100 100 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
101 101 size=wx.DefaultSize, style=0,
102 102 autocomplete_mode='IPYTHON'):
103 103 """ Autocomplete_mode: Can be 'IPYTHON' or 'STC'
104 104 'IPYTHON' show autocompletion the ipython way
105 105 'STC" show it scintilla text control way
106 106 """
107 107 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
108 108 self.configure_scintilla()
109 109
110 110 # FIXME: we need to retrieve this from the interpreter.
111 111 self.prompt = \
112 112 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
113 113 self.new_prompt(self.prompt % 1)
114 114
115 115 self.autocomplete_mode = autocomplete_mode
116 116
117 117 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
118 118 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
119 119
120 120
121 121 def configure_scintilla(self):
122 122 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
123 123 # the widget
124 124 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
125 125 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
126 126 # Also allow Ctrl Shift "=" for poor non US keyboard users.
127 127 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
128 128 stc.STC_CMD_ZOOMIN)
129 129
130 130 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
131 131 # stc.STC_CMD_PAGEUP)
132 132
133 133 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
134 134 # stc.STC_CMD_PAGEDOWN)
135 135
136 136 # Keys: we need to clear some of the keys the that don't play
137 137 # well with a console.
138 138 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
139 139 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
140 140 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
141 141
142 142
143 143 self.SetEOLMode(stc.STC_EOL_CRLF)
144 144 self.SetWrapMode(stc.STC_WRAP_CHAR)
145 145 self.SetWrapMode(stc.STC_WRAP_WORD)
146 146 self.SetBufferedDraw(True)
147 147 self.SetUseAntiAliasing(True)
148 148 self.SetLayoutCache(stc.STC_CACHE_PAGE)
149 149 self.SetUndoCollection(False)
150 150 self.SetUseTabs(True)
151 151 self.SetIndent(4)
152 152 self.SetTabWidth(4)
153 153
154 154 self.EnsureCaretVisible()
155 155
156 156 self.SetMargins(3, 3) #text is moved away from border with 3px
157 157 # Suppressing Scintilla margins
158 158 self.SetMarginWidth(0, 0)
159 159 self.SetMarginWidth(1, 0)
160 160 self.SetMarginWidth(2, 0)
161 161
162 162 self._apply_style()
163 163
164 164 # Xterm escape sequences
165 165 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
166 166 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
167 167
168 168 #self.SetEdgeMode(stc.STC_EDGE_LINE)
169 169 #self.SetEdgeColumn(80)
170 170
171 171 # styles
172 172 p = self.style
173 173 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
174 174 self.StyleClearAll()
175 175 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
176 176 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
177 177 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
178 178
179 179 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
180 180 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
181 181 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
182 182 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
183 183 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
184 184 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
185 185 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
186 186 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
187 187 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
188 188 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
189 189 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
190 190 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
191 191 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
192 192
193 193
194 194 def write(self, text):
195 195 """ Write given text to buffer, while translating the ansi escape
196 196 sequences.
197 197 """
198 198 title = self.title_pat.split(text)
199 199 if len(title)>0:
200 200 self.title = title[-1]
201 201
202 202 text = self.title_pat.sub('', text)
203 203 segments = self.color_pat.split(text)
204 204 segment = segments.pop(0)
205 205 self.StartStyling(self.GetLength(), 0xFF)
206 206 self.AppendText(segment)
207 207
208 208 if segments:
209 209 ansi_tags = self.color_pat.findall(text)
210 210
211 211 for tag in ansi_tags:
212 212 i = segments.index(tag)
213 213 self.StartStyling(self.GetLength(), 0xFF)
214 214 self.AppendText(segments[i+1])
215 215
216 216 if tag != '0':
217 217 self.SetStyling(len(segments[i+1]),
218 218 self.ANSI_STYLES[tag][0])
219 219
220 220 segments.pop(i)
221 221
222 222 self.GotoPos(self.GetLength())
223 223
224 224
225 225 def new_prompt(self, prompt):
226 226 """ Prints a prompt at start of line, and move the start of the
227 227 current block there.
228 228
229 229 The prompt can be give with ascii escape sequences.
230 230 """
231 231 self.write(prompt)
232 232 # now we update our cursor giving end of prompt
233 233 self.current_prompt_pos = self.GetLength()
234 234 self.current_prompt_line = self.GetCurrentLine()
235 235 wx.Yield()
236 236 self.EnsureCaretVisible()
237 237
238 238
239 239 def replace_current_edit_buffer(self, text):
240 240 """ Replace currently entered command line with given text.
241 241 """
242 242 self.SetSelection(self.current_prompt_pos, self.GetLength())
243 243 self.ReplaceSelection(text)
244 244 self.GotoPos(self.GetLength())
245 245
246 246
247 247 def get_current_edit_buffer(self):
248 248 """ Returns the text in current edit buffer.
249 249 """
250 250 return self.GetTextRange(self.current_prompt_pos,
251 251 self.GetLength())
252 252
253 253
254 254 #--------------------------------------------------------------------------
255 255 # Private API
256 256 #--------------------------------------------------------------------------
257 257
258 258 def _apply_style(self):
259 259 """ Applies the colors for the different text elements and the
260 260 carret.
261 261 """
262 262 self.SetCaretForeground(self.carret_color)
263 263
264 264 self.StyleClearAll()
265 265 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
266 266 "fore:#FF0000,back:#0000FF,bold")
267 267 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
268 268 "fore:#000000,back:#FF0000,bold")
269 269
270 270 for style in self.ANSI_STYLES.values():
271 271 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
272 272
273 273
274 274 def writeCompletion(self, possibilities):
275 275 if self.autocomplete_mode == 'IPYTHON':
276 276 max_len = len(max(possibilities, key=len))
277 277 max_symbol = ' '*max_len
278 278
279 279 #now we check how much symbol we can put on a line...
280 280 test_buffer = max_symbol + ' '*4
281 281
282 282 allowed_symbols = 80/len(test_buffer)
283 283 if allowed_symbols == 0:
284 284 allowed_symbols = 1
285 285
286 286 pos = 1
287 287 buf = ''
288 288 for symbol in possibilities:
289 289 #buf += symbol+'\n'#*spaces)
290 290 if pos < allowed_symbols:
291 291 spaces = max_len - len(symbol) + 4
292 292 buf += symbol+' '*spaces
293 293 pos += 1
294 294 else:
295 295 buf += symbol+'\n'
296 296 pos = 1
297 297 self.write(buf)
298 298 else:
299 299 possibilities.sort() # Python sorts are case sensitive
300 300 self.AutoCompSetIgnoreCase(False)
301 301 self.AutoCompSetAutoHide(False)
302 302 #let compute the length ot text)last word
303 303 splitter = [' ', '(', '[', '{']
304 304 last_word = self.get_current_edit_buffer()
305 305 for breaker in splitter:
306 306 last_word = last_word.split(breaker)[-1]
307 307 self.AutoCompShow(len(last_word), " ".join(possibilities))
308 308
309 309
310 310 def scroll_to_bottom(self):
311 311 maxrange = self.GetScrollRange(wx.VERTICAL)
312 312 self.ScrollLines(maxrange)
313 313
314 314
315 315 def _on_enter(self):
316 316 """ Called when the return key is hit.
317 317 """
318 318 pass
319 319
320 320
321 321 def _on_key_down(self, event, skip=True):
322 322 """ Key press callback used for correcting behavior for
323 323 console-like interfaces: the cursor is constraint to be after
324 324 the last prompt.
325 325
326 326 Return True if event as been catched.
327 327 """
328 328 catched = False
329 329 # Intercept some specific keys.
330 330 if event.KeyCode == ord('L') and event.ControlDown() :
331 331 catched = True
332 332 self.scroll_to_bottom()
333 333 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
334 334 catched = True
335 335 self.ScrollPages(-1)
336 336 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
337 337 catched = True
338 338 self.ScrollPages(1)
339 339
340 340 if self.AutoCompActive():
341 341 event.Skip()
342 342 else:
343 343 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
344 344 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
345 345 catched = True
346 self.write('\n')
346 347 self._on_enter()
347 348
348 349 elif event.KeyCode == wx.WXK_HOME:
349 350 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
350 351 self.GotoPos(self.current_prompt_pos)
351 352 catched = True
352 353
353 354 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
354 355 # FIXME: This behavior is not ideal: if the selection
355 356 # is already started, it will jump.
356 357 self.SetSelectionStart(self.current_prompt_pos)
357 358 self.SetSelectionEnd(self.GetCurrentPos())
358 359 catched = True
359 360
360 361 elif event.KeyCode == wx.WXK_UP:
361 362 if self.GetCurrentLine() > self.current_prompt_line:
362 363 if self.GetCurrentLine() == self.current_prompt_line + 1 \
363 364 and self.GetColumn(self.GetCurrentPos()) < \
364 365 self.GetColumn(self.current_prompt_pos):
365 366 self.GotoPos(self.current_prompt_pos)
366 367 else:
367 368 event.Skip()
368 369 catched = True
369 370
370 371 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
371 372 if self.GetCurrentPos() > self.current_prompt_pos:
372 373 event.Skip()
373 374 catched = True
374 375
375 376 if skip and not catched:
376 377 event.Skip()
377 378
378 379 return catched
379 380
380 381
381 382 def _on_key_up(self, event, skip=True):
382 383 """ If cursor is outside the editing region, put it back.
383 384 """
384 385 event.Skip()
385 386 if self.GetCurrentPos() < self.current_prompt_pos:
386 387 self.GotoPos(self.current_prompt_pos)
387 388
388 389
389 390
390 391
391 392 if __name__ == '__main__':
392 393 # Some simple code to test the console widget.
393 394 class MainWindow(wx.Frame):
394 395 def __init__(self, parent, id, title):
395 396 wx.Frame.__init__(self, parent, id, title, size=(300,250))
396 397 self._sizer = wx.BoxSizer(wx.VERTICAL)
397 398 self.console_widget = ConsoleWidget(self)
398 399 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
399 400 self.SetSizer(self._sizer)
400 401 self.SetAutoLayout(1)
401 402 self.Show(True)
402 403
403 404 app = wx.PySimpleApp()
404 405 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
405 406 w.SetSize((780, 460))
406 407 w.Show()
407 408
408 409 app.MainLoop()
409 410
410 411
@@ -1,106 +1,106 b''
1 1 # encoding: utf-8 -*- test-case-name:
2 2 # FIXME: Need to add tests.
3 3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
4 4
5 5 """Classes to provide a Wx frontend to the
6 6 IPython.kernel.core.interpreter.
7 7
8 8 """
9 9
10 10 __docformat__ = "restructuredtext en"
11 11
12 12 #-------------------------------------------------------------------------------
13 13 # Copyright (C) 2008 The IPython Development Team
14 14 #
15 15 # Distributed under the terms of the BSD License. The full license is in
16 16 # the file COPYING, distributed as part of this software.
17 17 #-------------------------------------------------------------------------------
18 18
19 19 #-------------------------------------------------------------------------------
20 20 # Imports
21 21 #-------------------------------------------------------------------------------
22 22
23 23
24 24 import wx
25 25 from console_widget import ConsoleWidget
26 26
27 27 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
28 28
29 29 #-------------------------------------------------------------------------------
30 30 # Classes to implement the Wx frontend
31 31 #-------------------------------------------------------------------------------
32 32 class IPythonWxController(PrefilterFrontEnd, ConsoleWidget):
33 33
34 34 output_prompt = \
35 '\n\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
35 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
36 36
37 37 #--------------------------------------------------------------------------
38 38 # Public API
39 39 #--------------------------------------------------------------------------
40 40
41 41 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
42 42 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
43 43 *args, **kwds):
44 44 """ Create Shell instance.
45 45 """
46 46 ConsoleWidget.__init__(self, parent, id, pos, size, style)
47 47 PrefilterFrontEnd.__init__(self)
48 48
49 49 # Capture Character keys
50 50 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
51 51
52 52 #--------------------------------------------------------------------------
53 53 # Private API
54 54 #--------------------------------------------------------------------------
55 55
56 56
57 57 def _on_key_down(self, event, skip=True):
58 58 """ Capture the character events, let the parent
59 59 widget handle them, and put our logic afterward.
60 60 """
61 61 current_line_number = self.GetCurrentLine()
62 62 # Up history
63 63 if event.KeyCode == wx.WXK_UP and (
64 64 ( current_line_number == self.current_prompt_line and
65 65 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
66 66 or event.ControlDown() ):
67 67 new_buffer = self.get_history_previous(
68 68 self.get_current_edit_buffer())
69 69 if new_buffer is not None:
70 70 self.replace_current_edit_buffer(new_buffer)
71 71 if self.GetCurrentLine() > self.current_prompt_line:
72 72 # Go to first line, for seemless history up.
73 73 self.GotoPos(self.current_prompt_pos)
74 74 # Down history
75 75 elif event.KeyCode == wx.WXK_DOWN and (
76 76 ( current_line_number == self.LineCount -1 and
77 77 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
78 78 or event.ControlDown() ):
79 79 new_buffer = self.get_history_next()
80 80 if new_buffer is not None:
81 81 self.replace_current_edit_buffer(new_buffer)
82 82 else:
83 83 ConsoleWidget._on_key_down(self, event, skip=skip)
84 84
85 85
86 86
87 87
88 88 if __name__ == '__main__':
89 89 class MainWindow(wx.Frame):
90 90 def __init__(self, parent, id, title):
91 91 wx.Frame.__init__(self, parent, id, title, size=(300,250))
92 92 self._sizer = wx.BoxSizer(wx.VERTICAL)
93 93 self.shell = IPythonWxController(self)
94 94 self._sizer.Add(self.shell, 1, wx.EXPAND)
95 95 self.SetSizer(self._sizer)
96 96 self.SetAutoLayout(1)
97 97 self.Show(True)
98 98
99 99 app = wx.PySimpleApp()
100 100 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
101 101 frame.shell.SetFocus()
102 102 frame.SetSize((660, 460))
103 103 self = frame.shell
104 104
105 105 app.MainLoop()
106 106
General Comments 0
You need to be logged in to leave comments. Login now