##// END OF EJS Templates
[wxipython shell] Patch from cody precod to correct 'enter' key behaviour under MacOSX
ldufrechou -
r1158:c525ef31 merge
parent child Browse files
Show More
@@ -1,752 +1,751 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3 '''
4 4 Provides IPython WX console widgets.
5 5
6 6 @author: Laurent Dufrechou
7 7 laurent.dufrechou _at_ gmail.com
8 8 This WX widget is based on the original work of Eitan Isaacson
9 9 that provided the console for the GTK toolkit.
10 10
11 11 Original work from:
12 12 @author: Eitan Isaacson
13 13 @organization: IBM Corporation
14 14 @copyright: Copyright (c) 2007 IBM Corporation
15 15 @license: BSD
16 16
17 17 All rights reserved. This program and the accompanying materials are made
18 18 available under the terms of the BSD which accompanies this distribution, and
19 19 is available at U{http://www.opensource.org/licenses/bsd-license.php}
20 20 '''
21 21
22 22 __version__ = 0.8
23 23 __author__ = "Laurent Dufrechou"
24 24 __email__ = "laurent.dufrechou _at_ gmail.com"
25 25 __license__ = "BSD"
26 26
27 27 import wx
28 28 import wx.stc as stc
29 29 import wx.lib.newevent
30 30
31 31 import re
32 32 import sys
33 33 import locale
34 34 from StringIO import StringIO
35 35 try:
36 36 import IPython
37 37 except Exception,e:
38 38 raise "Error importing IPython (%s)" % str(e)
39 39
40 40 from ipshell_nonblocking import NonBlockingIPShell
41 41
42 42
43 43 class WxNonBlockingIPShell(NonBlockingIPShell):
44 44 '''
45 45 An NonBlockingIPShell Thread that is WX dependent.
46 46 '''
47 47 def __init__(self, parent,
48 48 argv=[],user_ns={},user_global_ns=None,
49 49 cin=None, cout=None, cerr=None,
50 50 ask_exit_handler=None):
51 51
52 52 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
53 53 cin, cout, cerr,
54 54 ask_exit_handler)
55 55
56 56 self.parent = parent
57 57
58 58 self.ask_exit_callback = ask_exit_handler
59 59 self._IP.exit = self._askExit
60 60
61 61 def addGUIShortcut(self,text,func):
62 62 wx.CallAfter(self.parent.add_button_handler,
63 63 button_info={ 'text':text,
64 64 'func':self.parent.doExecuteLine(func)})
65 65
66 66 def _askExit(self):
67 67 wx.CallAfter(self.ask_exit_callback, ())
68 68
69 69 def _afterExecute(self):
70 70 wx.CallAfter(self.parent.evtStateExecuteDone, ())
71 71
72 72
73 73 class WxConsoleView(stc.StyledTextCtrl):
74 74 '''
75 75 Specialized styled text control view for console-like workflow.
76 76 We use here a scintilla frontend thus it can be reused in any GUI that
77 77 supports scintilla with less work.
78 78
79 79 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.
80 80 (with Black background)
81 81 @type ANSI_COLORS_BLACK: dictionary
82 82
83 83 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.
84 84 (with White background)
85 85 @type ANSI_COLORS_WHITE: dictionary
86 86
87 87 @ivar color_pat: Regex of terminal color pattern
88 88 @type color_pat: _sre.SRE_Pattern
89 89 '''
90 90 ANSI_STYLES_BLACK={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
91 91 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
92 92 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
93 93 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
94 94 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
95 95 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
96 96 '1;34': [12,'LIGHT BLUE'], '1;35':
97 97 [13,'MEDIUM VIOLET RED'],
98 98 '1;36': [14,'LIGHT STEEL BLUE'],'1;37': [15,'YELLOW']}
99 99
100 100 ANSI_STYLES_WHITE={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
101 101 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
102 102 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
103 103 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
104 104 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
105 105 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
106 106 '1;34': [12,'LIGHT BLUE'], '1;35':
107 107 [13,'MEDIUM VIOLET RED'],
108 108 '1;36': [14,'LIGHT STEEL BLUE'],'1;37': [15,'YELLOW']}
109 109
110 110 def __init__(self,parent,prompt,intro="",background_color="BLACK",
111 111 pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
112 112 style=0):
113 113 '''
114 114 Initialize console view.
115 115
116 116 @param parent: Parent widget
117 117 @param prompt: User specified prompt
118 118 @type intro: string
119 119 @param intro: User specified startup introduction string
120 120 @type intro: string
121 121 @param background_color: Can be BLACK or WHITE
122 122 @type background_color: string
123 123 @param other: init param of styledTextControl (can be used as-is)
124 124 '''
125 125 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
126 126
127 127 ####### Scintilla configuration ###################################
128 128
129 129 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside
130 130 # the widget
131 131 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
132 132 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
133 133
134 134 #we define platform specific fonts
135 135 if wx.Platform == '__WXMSW__':
136 136 faces = { 'times': 'Times New Roman',
137 137 'mono' : 'Courier New',
138 138 'helv' : 'Arial',
139 139 'other': 'Comic Sans MS',
140 140 'size' : 10,
141 141 'size2': 8,
142 142 }
143 143 elif wx.Platform == '__WXMAC__':
144 144 faces = { 'times': 'Times New Roman',
145 145 'mono' : 'Monaco',
146 146 'helv' : 'Arial',
147 147 'other': 'Comic Sans MS',
148 148 'size' : 10,
149 149 'size2': 8,
150 150 }
151 151 else:
152 152 faces = { 'times': 'Times',
153 153 'mono' : 'Courier',
154 154 'helv' : 'Helvetica',
155 155 'other': 'new century schoolbook',
156 156 'size' : 10,
157 157 'size2': 8,
158 158 }
159 159
160 160 #We draw a line at position 80
161 161 self.SetEdgeMode(stc.STC_EDGE_LINE)
162 162 self.SetEdgeColumn(80)
163 163 self.SetEdgeColour(wx.LIGHT_GREY)
164 164
165 165 #self.SetViewWhiteSpace(True)
166 166 #self.SetViewEOL(True)
167 167 self.SetEOLMode(stc.STC_EOL_CRLF)
168 168 #self.SetWrapMode(stc.STC_WRAP_CHAR)
169 169 #self.SetWrapMode(stc.STC_WRAP_WORD)
170 170 self.SetBufferedDraw(True)
171 171 #self.SetUseAntiAliasing(True)
172 172 self.SetLayoutCache(stc.STC_CACHE_PAGE)
173 173
174 174 self.EnsureCaretVisible()
175 175
176 176 self.SetMargins(3,3) #text is moved away from border with 3px
177 177 # Suppressing Scintilla margins
178 178 self.SetMarginWidth(0,0)
179 179 self.SetMarginWidth(1,0)
180 180 self.SetMarginWidth(2,0)
181 181
182 182 # make some styles
183 183 if background_color != "BLACK":
184 184 self.background_color = "WHITE"
185 185 self.SetCaretForeground("BLACK")
186 186 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
187 187 else:
188 188 self.background_color = background_color
189 189 self.SetCaretForeground("WHITE")
190 190 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
191 191
192 192 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
193 193 "fore:%s,back:%s,size:%d,face:%s"
194 194 % (self.ANSI_STYLES['0;30'][1],
195 195 self.background_color,
196 196 faces['size'], faces['mono']))
197 197 self.StyleClearAll()
198 198 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
199 199 "fore:#FF0000,back:#0000FF,bold")
200 200 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
201 201 "fore:#000000,back:#FF0000,bold")
202 202
203 203 for style in self.ANSI_STYLES.values():
204 204 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
205 205
206 206 #######################################################################
207 207
208 208 self.indent = 0
209 209 self.prompt_count = 0
210 210 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
211 211
212 212 self.write(intro)
213 213 self.setPrompt(prompt)
214 214 self.showPrompt()
215 215
216 216 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
217 217
218 218 def asyncWrite(self, text):
219 219 '''
220 220 Write given text to buffer in an asynchroneous way.
221 221 It is used from another thread to be able to acces the GUI.
222 222 @param text: Text to append
223 223 @type text: string
224 224 '''
225 225 try:
226 226 #print >>sys.__stdout__,'entering'
227 227 wx.MutexGuiEnter()
228 228 #print >>sys.__stdout__,'locking the GUI'
229 229
230 230 #be sure not to be interrutpted before the MutexGuiLeave!
231 231 self.write(text)
232 232 #print >>sys.__stdout__,'done'
233 233
234 234 except KeyboardInterrupt:
235 235 #print >>sys.__stdout__,'got keyboard interrupt'
236 236 wx.MutexGuiLeave()
237 237 #print >>sys.__stdout__,'interrupt unlock the GUI'
238 238 raise KeyboardInterrupt
239 239 wx.MutexGuiLeave()
240 240 #print >>sys.__stdout__,'normal unlock the GUI'
241 241
242 242
243 243 def write(self, text):
244 244 '''
245 245 Write given text to buffer.
246 246
247 247 @param text: Text to append.
248 248 @type text: string
249 249 '''
250 250 segments = self.color_pat.split(text)
251 251 segment = segments.pop(0)
252 252 self.StartStyling(self.getCurrentLineEnd(),0xFF)
253 253 self.AppendText(segment)
254 254
255 255 if segments:
256 256 ansi_tags = self.color_pat.findall(text)
257 257
258 258 for tag in ansi_tags:
259 259 i = segments.index(tag)
260 260 self.StartStyling(self.getCurrentLineEnd(),0xFF)
261 261 self.AppendText(segments[i+1])
262 262
263 263 if tag != '0':
264 264 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
265 265
266 266 segments.pop(i)
267 267
268 268 self.moveCursor(self.getCurrentLineEnd())
269 269
270 270 def getPromptLen(self):
271 271 '''
272 272 Return the length of current prompt
273 273 '''
274 274 return len(str(self.prompt_count)) + 7
275 275
276 276 def setPrompt(self,prompt):
277 277 self.prompt = prompt
278 278
279 279 def setIndentation(self,indentation):
280 280 self.indent = indentation
281 281
282 282 def setPromptCount(self,count):
283 283 self.prompt_count = count
284 284
285 285 def showPrompt(self):
286 286 '''
287 287 Prints prompt at start of line.
288 288
289 289 @param prompt: Prompt to print.
290 290 @type prompt: string
291 291 '''
292 292 self.write(self.prompt)
293 293 #now we update the position of end of prompt
294 294 self.current_start = self.getCurrentLineEnd()
295 295
296 296 autoindent = self.indent*' '
297 297 autoindent = autoindent.replace(' ','\t')
298 298 self.write(autoindent)
299 299
300 300 def changeLine(self, text):
301 301 '''
302 302 Replace currently entered command line with given text.
303 303
304 304 @param text: Text to use as replacement.
305 305 @type text: string
306 306 '''
307 307 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
308 308 self.ReplaceSelection(text)
309 309 self.moveCursor(self.getCurrentLineEnd())
310 310
311 311 def getCurrentPromptStart(self):
312 312 return self.current_start
313 313
314 314 def getCurrentLineStart(self):
315 315 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
316 316
317 317 def getCurrentLineEnd(self):
318 318 return self.GetLength()
319 319
320 320 def getCurrentLine(self):
321 321 '''
322 322 Get text in current command line.
323 323
324 324 @return: Text of current command line.
325 325 @rtype: string
326 326 '''
327 327 return self.GetTextRange(self.getCurrentPromptStart(),
328 328 self.getCurrentLineEnd())
329 329
330 330 def moveCursorOnNewValidKey(self):
331 331 #If cursor is at wrong position put it at last line...
332 332 if self.GetCurrentPos() < self.getCurrentPromptStart():
333 333 self.GotoPos(self.getCurrentPromptStart())
334 334
335 335 def removeFromTo(self,from_pos,to_pos):
336 336 if from_pos < to_pos:
337 337 self.SetSelection(from_pos,to_pos)
338 338 self.DeleteBack()
339 339
340 340 def removeCurrentLine(self):
341 341 self.LineDelete()
342 342
343 343 def moveCursor(self,position):
344 344 self.GotoPos(position)
345 345
346 346 def getCursorPos(self):
347 347 return self.GetCurrentPos()
348 348
349 349 def selectFromTo(self,from_pos,to_pos):
350 350 self.SetSelectionStart(from_pos)
351 351 self.SetSelectionEnd(to_pos)
352 352
353 353 def writeHistory(self,history):
354 354 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
355 355 self.changeLine(history)
356 356
357 357 def writeCompletion(self, possibilities):
358 358 max_len = len(max(possibilities,key=len))
359 359 max_symbol =' '*max_len
360 360
361 361 #now we check how much symbol we can put on a line...
362 362 cursor_pos = self.getCursorPos()
363 363 test_buffer = max_symbol + ' '*4
364 364 current_lines = self.GetLineCount()
365 365
366 366 allowed_symbols = 80/len(test_buffer)
367 367 if allowed_symbols == 0:
368 368 allowed_symbols = 1
369 369
370 370 pos = 1
371 371 buf = ''
372 372 for symbol in possibilities:
373 373 #buf += symbol+'\n'#*spaces)
374 374 if pos<allowed_symbols:
375 375 spaces = max_len - len(symbol) + 4
376 376 buf += symbol+' '*spaces
377 377 pos += 1
378 378 else:
379 379 buf+=symbol+'\n'
380 380 pos = 1
381 381 self.write(buf)
382 382
383 383 def _onKeypress(self, event, skip=True):
384 384 '''
385 385 Key press callback used for correcting behavior for console-like
386 386 interfaces. For example 'home' should go to prompt, not to begining of
387 387 line.
388 388
389 389 @param widget: Widget that key press accored in.
390 390 @type widget: gtk.Widget
391 391 @param event: Event object
392 392 @type event: gtk.gdk.Event
393 393
394 394 @return: Return True if event as been catched.
395 395 @rtype: boolean
396 396 '''
397
398 397 if event.GetKeyCode() == wx.WXK_HOME:
399 398 if event.Modifiers == wx.MOD_NONE:
400 399 self.moveCursorOnNewValidKey()
401 400 self.moveCursor(self.getCurrentPromptStart())
402 401 return True
403 402 elif event.Modifiers == wx.MOD_SHIFT:
404 403 self.moveCursorOnNewValidKey()
405 404 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
406 405 return True
407 406 else:
408 407 return False
409 408
410 409 elif event.GetKeyCode() == wx.WXK_LEFT:
411 410 if event.Modifiers == wx.MOD_NONE:
412 411 self.moveCursorOnNewValidKey()
413 412
414 413 self.moveCursor(self.getCursorPos()-1)
415 414 if self.getCursorPos() < self.getCurrentPromptStart():
416 415 self.moveCursor(self.getCurrentPromptStart())
417 416 return True
418 417
419 418 elif event.GetKeyCode() == wx.WXK_BACK:
420 419 self.moveCursorOnNewValidKey()
421 420 if self.getCursorPos() > self.getCurrentPromptStart():
422 421 event.Skip()
423 422 return True
424 423
425 424 if skip:
426 425 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
427 426 self.moveCursorOnNewValidKey()
428 427
429 428 event.Skip()
430 429 return True
431 430 return False
432 431
433 432 def OnUpdateUI(self, evt):
434 433 # check for matching braces
435 434 braceAtCaret = -1
436 435 braceOpposite = -1
437 436 charBefore = None
438 437 caretPos = self.GetCurrentPos()
439 438
440 439 if caretPos > 0:
441 440 charBefore = self.GetCharAt(caretPos - 1)
442 441 styleBefore = self.GetStyleAt(caretPos - 1)
443 442
444 443 # check before
445 444 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
446 445 braceAtCaret = caretPos - 1
447 446
448 447 # check after
449 448 if braceAtCaret < 0:
450 449 charAfter = self.GetCharAt(caretPos)
451 450 styleAfter = self.GetStyleAt(caretPos)
452 451
453 452 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
454 453 braceAtCaret = caretPos
455 454
456 455 if braceAtCaret >= 0:
457 456 braceOpposite = self.BraceMatch(braceAtCaret)
458 457
459 458 if braceAtCaret != -1 and braceOpposite == -1:
460 459 self.BraceBadLight(braceAtCaret)
461 460 else:
462 461 self.BraceHighlight(braceAtCaret, braceOpposite)
463 462 #pt = self.PointFromPosition(braceOpposite)
464 463 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
465 464 #print pt
466 465 #self.Refresh(False)
467 466
468 467 class IPShellWidget(wx.Panel):
469 468 '''
470 469 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
471 470 If you want to port this to any other GUI toolkit, just replace the
472 471 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
473 472 from whatever container you want. I've choosed to derivate from a wx.Panel
474 473 because it seems to be more useful
475 474 Any idea to make it more 'generic' welcomed.
476 475 '''
477 476
478 477 def __init__(self, parent, intro=None,
479 478 background_color="BLACK", add_button_handler=None,
480 479 wx_ip_shell=None, user_ns={},user_global_ns=None,
481 480 ):
482 481 '''
483 482 Initialize.
484 483 Instanciate an IPython thread.
485 484 Instanciate a WxConsoleView.
486 485 Redirect I/O to console.
487 486 '''
488 wx.Panel.__init__(self,parent,-1)
487 wx.Panel.__init__(self,parent,wx.ID_ANY)
489 488
490 489 ### IPython non blocking shell instanciation ###
491 490 self.cout = StringIO()
492 491 self.add_button_handler = add_button_handler
493 492
494 493 if wx_ip_shell is not None:
495 494 self.IP = wx_ip_shell
496 495 else:
497 496 self.IP = WxNonBlockingIPShell(self,
498 497 cout = self.cout, cerr = self.cout,
499 498 ask_exit_handler = self.askExitCallback)
500 499
501 500 ### IPython wx console view instanciation ###
502 501 #If user didn't defined an intro text, we create one for him
503 502 #If you really wnat an empty intrp just call wxIPythonViewPanel
504 503 #with intro=''
505 504 if intro is None:
506 505 welcome_text = "Welcome to WxIPython Shell.\n\n"
507 506 welcome_text+= self.IP.getBanner()
508 507 welcome_text+= "!command -> Execute command in shell\n"
509 508 welcome_text+= "TAB -> Autocompletion\n"
510 509 else:
511 510 welcome_text = intro
512 511
513 512 self.text_ctrl = WxConsoleView(self,
514 513 self.IP.getPrompt(),
515 514 intro=welcome_text,
516 515 background_color=background_color)
517 516
518 517 self.cout.write = self.text_ctrl.asyncWrite
519 518
520 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
519 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress)
521 520
522 521 ### making the layout of the panel ###
523 522 sizer = wx.BoxSizer(wx.VERTICAL)
524 523 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
525 524 self.SetAutoLayout(True)
526 525 sizer.Fit(self)
527 526 sizer.SetSizeHints(self)
528 527 self.SetSizer(sizer)
529 528 #and we focus on the widget :)
530 529 self.SetFocus()
531 530
532 531 #widget state management (for key handling different cases)
533 532 self.setCurrentState('IDLE')
534 533 self.pager_state = 'DONE'
535 534 self.raw_input_current_line = 0
536 535
537 536 def askExitCallback(self, event):
538 537 self.askExitHandler(event)
539 538
540 539 #---------------------- IPython Thread Management ------------------------
541 540 def stateDoExecuteLine(self):
542 541 lines=self.text_ctrl.getCurrentLine()
543 542 self.text_ctrl.write('\n')
544 543 lines_to_execute = lines.replace('\t',' '*4)
545 544 lines_to_execute = lines_to_execute.replace('\r\n','\n')
546 545 self.IP.doExecute(lines.encode('cp1252'))
547 546 self.updateHistoryTracker(lines)
548 547 self.setCurrentState('WAIT_END_OF_EXECUTION')
549 548
550 549 def evtStateExecuteDone(self,evt):
551 550 self.doc = self.IP.getDocText()
552 551 self.help = self.IP.getHelpText()
553 552 if self.doc:
554 553 self.pager_lines = self.doc[7:].split('\n')
555 554 self.pager_state = 'INIT'
556 555 self.setCurrentState('SHOW_DOC')
557 556 self.pager(self.doc)
558 557 elif self.help:
559 558 self.pager_lines = self.help.split('\n')
560 559 self.pager_state = 'INIT'
561 560 self.setCurrentState('SHOW_DOC')
562 561 self.pager(self.help)
563 562 else:
564 563 self.stateShowPrompt()
565 564
566 565 def stateShowPrompt(self):
567 566 self.setCurrentState('SHOW_PROMPT')
568 567 self.text_ctrl.setPrompt(self.IP.getPrompt())
569 568 self.text_ctrl.setIndentation(self.IP.getIndentation())
570 569 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
571 570 self.text_ctrl.showPrompt()
572 571 self.IP.initHistoryIndex()
573 572 self.setCurrentState('IDLE')
574 573
575 574 def setCurrentState(self, state):
576 575 self.cur_state = state
577 576 self.updateStatusTracker(self.cur_state)
578 577
579 578 def pager(self,text):
580 579
581 580 if self.pager_state == 'INIT':
582 581 #print >>sys.__stdout__,"PAGER state:",self.pager_state
583 582 self.pager_nb_lines = len(self.pager_lines)
584 583 self.pager_index = 0
585 584 self.pager_do_remove = False
586 585 self.text_ctrl.write('\n')
587 586 self.pager_state = 'PROCESS_LINES'
588 587
589 588 if self.pager_state == 'PROCESS_LINES':
590 589 #print >>sys.__stdout__,"PAGER state:",self.pager_state
591 590 if self.pager_do_remove == True:
592 591 self.text_ctrl.removeCurrentLine()
593 592 self.pager_do_remove = False
594 593
595 594 if self.pager_nb_lines > 10:
596 595 #print >>sys.__stdout__,"PAGER processing 10 lines"
597 596 if self.pager_index > 0:
598 597 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
599 598 else:
600 599 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
601 600
602 601 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
603 602 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
604 603 self.pager_index += 10
605 604 self.pager_nb_lines -= 10
606 605 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
607 606 self.pager_do_remove = True
608 607 self.pager_state = 'WAITING'
609 608 return
610 609 else:
611 610 #print >>sys.__stdout__,"PAGER processing last lines"
612 611 if self.pager_nb_lines > 0:
613 612 if self.pager_index > 0:
614 613 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
615 614 else:
616 615 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
617 616
618 617 self.pager_index += 1
619 618 self.pager_nb_lines -= 1
620 619 if self.pager_nb_lines > 0:
621 620 for line in self.pager_lines[self.pager_index:]:
622 621 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
623 622 self.pager_nb_lines = 0
624 623 self.pager_state = 'DONE'
625 624 self.stateShowPrompt()
626 625
627 626 #------------------------ Key Handler ------------------------------------
628 627 def keyPress(self, event):
629 628 '''
630 629 Key press callback with plenty of shell goodness, like history,
631 630 autocompletions, etc.
632 631 '''
633
634 632 if event.GetKeyCode() == ord('C'):
635 633 if event.Modifiers == wx.MOD_CONTROL:
636 634 if self.cur_state == 'WAIT_END_OF_EXECUTION':
637 635 #we raise an exception inside the IPython thread container
638 636 self.IP.ce.raise_exc(KeyboardInterrupt)
639 637 return
640 638
641 639 if event.KeyCode == wx.WXK_RETURN:
642 640 if self.cur_state == 'IDLE':
643 641 #we change the state ot the state machine
644 642 self.setCurrentState('DO_EXECUTE_LINE')
645 643 self.stateDoExecuteLine()
646 644 return
645
647 646 if self.pager_state == 'WAITING':
648 647 self.pager_state = 'PROCESS_LINES'
649 648 self.pager(self.doc)
650 649 return
651 650
652 651 if self.cur_state == 'WAITING_USER_INPUT':
653 652 line=self.text_ctrl.getCurrentLine()
654 653 self.text_ctrl.write('\n')
655 654 self.setCurrentState('WAIT_END_OF_EXECUTION')
656 655 return
657 656
658 657 if event.GetKeyCode() in [ord('q'),ord('Q')]:
659 658 if self.pager_state == 'WAITING':
660 659 self.pager_state = 'DONE'
661 660 self.text_ctrl.write('\n')
662 661 self.stateShowPrompt()
663 662 return
664 663
665 664 if self.cur_state == 'WAITING_USER_INPUT':
666 665 event.Skip()
667 666
668 667 if self.cur_state == 'IDLE':
669 668 if event.KeyCode == wx.WXK_UP:
670 669 history = self.IP.historyBack()
671 670 self.text_ctrl.writeHistory(history)
672 671 return
673 672 if event.KeyCode == wx.WXK_DOWN:
674 673 history = self.IP.historyForward()
675 674 self.text_ctrl.writeHistory(history)
676 675 return
677 676 if event.KeyCode == wx.WXK_TAB:
678 677 #if line empty we disable tab completion
679 678 if not self.text_ctrl.getCurrentLine().strip():
680 679 self.text_ctrl.write('\t')
681 680 return
682 681 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
683 682 if len(possibilities) > 1:
684 683 cur_slice = self.text_ctrl.getCurrentLine()
685 684 self.text_ctrl.write('\n')
686 685 self.text_ctrl.writeCompletion(possibilities)
687 686 self.text_ctrl.write('\n')
688 687
689 688 self.text_ctrl.showPrompt()
690 689 self.text_ctrl.write(cur_slice)
691 690 self.text_ctrl.changeLine(completed or cur_slice)
692 691
693 692 return
694 693 event.Skip()
695 694
696 695 #------------------------ Hook Section -----------------------------------
697 696 def updateHistoryTracker(self,command_line):
698 697 '''
699 698 Default history tracker (does nothing)
700 699 '''
701 700 pass
702 701
703 702 def setHistoryTrackerHook(self,func):
704 703 '''
705 704 Define a new history tracker
706 705 '''
707 706 self.updateHistoryTracker = func
708 707
709 708 def updateStatusTracker(self,status):
710 709 '''
711 710 Default status tracker (does nothing)
712 711 '''
713 712 pass
714 713
715 714 def setStatusTrackerHook(self,func):
716 715 '''
717 716 Define a new status tracker
718 717 '''
719 718 self.updateStatusTracker = func
720 719
721 720 def askExitHandler(self, event):
722 721 '''
723 722 Default exit handler
724 723 '''
725 724 self.text_ctrl.write('\nExit callback has not been set.')
726 725
727 726 def setAskExitHandler(self, func):
728 727 '''
729 728 Define an exit handler
730 729 '''
731 730 self.askExitHandler = func
732 731
733 732 if __name__ == '__main__':
734 733 # Some simple code to test the shell widget.
735 734 class MainWindow(wx.Frame):
736 735 def __init__(self, parent, id, title):
737 736 wx.Frame.__init__(self, parent, id, title, size=(300,250))
738 737 self._sizer = wx.BoxSizer(wx.VERTICAL)
739 738 self.shell = IPShellWidget(self)
740 739 self._sizer.Add(self.shell, 1, wx.EXPAND)
741 740 self.SetSizer(self._sizer)
742 741 self.SetAutoLayout(1)
743 742 self.Show(True)
744 743
745 744 app = wx.PySimpleApp()
746 745 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
747 746 frame.SetSize((780, 460))
748 747 shell = frame.shell
749 748
750 749 app.MainLoop()
751 750
752 751
General Comments 0
You need to be logged in to leave comments. Login now