##// END OF EJS Templates
Correct erroneous behaviour when doing:...
ldufrechou -
Show More
@@ -1,1 +1,2 b''
1 [LF]
1 2 *.py
@@ -1,715 +1,715 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 write(self, text):
219 219 '''
220 220 Write given text to buffer.
221 221
222 222 @param text: Text to append.
223 223 @type text: string
224 224 '''
225 225 segments = self.color_pat.split(text)
226 226 segment = segments.pop(0)
227 227 self.StartStyling(self.getCurrentLineEnd(),0xFF)
228 228 self.AppendText(segment)
229 229
230 230 if segments:
231 231 ansi_tags = self.color_pat.findall(text)
232 232
233 233 for tag in ansi_tags:
234 234 i = segments.index(tag)
235 235 self.StartStyling(self.getCurrentLineEnd(),0xFF)
236 236 self.AppendText(segments[i+1])
237 237
238 238 if tag != '0':
239 239 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
240 240
241 241 segments.pop(i)
242 242
243 243 self.moveCursor(self.getCurrentLineEnd())
244 244
245 245 def getPromptLen(self):
246 246 '''
247 247 Return the length of current prompt
248 248 '''
249 249 return len(str(self.prompt_count)) + 7
250 250
251 251 def setPrompt(self,prompt):
252 252 self.prompt = prompt
253 253
254 254 def setIndentation(self,indentation):
255 255 self.indent = indentation
256 256
257 257 def setPromptCount(self,count):
258 258 self.prompt_count = count
259 259
260 260 def showPrompt(self):
261 261 '''
262 262 Prints prompt at start of line.
263 263
264 264 @param prompt: Prompt to print.
265 265 @type prompt: string
266 266 '''
267 267 self.write(self.prompt)
268 268 #now we update the position of end of prompt
269 269 self.current_start = self.getCurrentLineEnd()
270 270
271 271 autoindent = self.indent*' '
272 272 autoindent = autoindent.replace(' ','\t')
273 273 self.write(autoindent)
274 274
275 275 def changeLine(self, text):
276 276 '''
277 277 Replace currently entered command line with given text.
278 278
279 279 @param text: Text to use as replacement.
280 280 @type text: string
281 281 '''
282 282 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
283 283 self.ReplaceSelection(text)
284 284 self.moveCursor(self.getCurrentLineEnd())
285 285
286 286 def getCurrentPromptStart(self):
287 287 return self.current_start
288 288
289 289 def getCurrentLineStart(self):
290 290 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
291 291
292 292 def getCurrentLineEnd(self):
293 293 return self.GetLength()
294 294
295 295 def getCurrentLine(self):
296 296 '''
297 297 Get text in current command line.
298 298
299 299 @return: Text of current command line.
300 300 @rtype: string
301 301 '''
302 302 return self.GetTextRange(self.getCurrentPromptStart(),
303 303 self.getCurrentLineEnd())
304 304
305 305 def showReturned(self, text):
306 306 '''
307 307 Show returned text from last command and print new prompt.
308 308
309 309 @param text: Text to show.
310 310 @type text: string
311 311 '''
312 312 self.write('\n'+text)
313 313 if text:
314 314 self.write('\n')
315 315 self.showPrompt()
316 316
317 317 def moveCursorOnNewValidKey(self):
318 318 #If cursor is at wrong position put it at last line...
319 319 if self.GetCurrentPos() < self.getCurrentPromptStart():
320 320 self.GotoPos(self.getCurrentPromptStart())
321 321
322 322 def removeFromTo(self,from_pos,to_pos):
323 323 if from_pos < to_pos:
324 324 self.SetSelection(from_pos,to_pos)
325 325 self.DeleteBack()
326 326
327 327 def removeCurrentLine(self):
328 328 self.LineDelete()
329 329
330 330 def moveCursor(self,position):
331 331 self.GotoPos(position)
332 332
333 333 def getCursorPos(self):
334 334 return self.GetCurrentPos()
335 335
336 336 def selectFromTo(self,from_pos,to_pos):
337 337 self.SetSelectionStart(from_pos)
338 338 self.SetSelectionEnd(to_pos)
339 339
340 340 def writeHistory(self,history):
341 341 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
342 342 self.changeLine(history)
343 343
344 344 def writeCompletion(self, possibilities):
345 345 max_len = len(max(possibilities,key=len))
346 346 max_symbol =' '*max_len
347 347
348 348 #now we check how much symbol we can put on a line...
349 349 cursor_pos = self.getCursorPos()
350 350 test_buffer = max_symbol + ' '*4
351 351 current_lines = self.GetLineCount()
352 352
353 353 allowed_symbols = 80/len(test_buffer)
354 354 if allowed_symbols == 0:
355 355 allowed_symbols = 1
356 356
357 357 pos = 1
358 358 buf = ''
359 359 for symbol in possibilities:
360 360 #buf += symbol+'\n'#*spaces)
361 361 if pos<allowed_symbols:
362 362 spaces = max_len - len(symbol) + 4
363 363 buf += symbol+' '*spaces
364 364 pos += 1
365 365 else:
366 366 buf+=symbol+'\n'
367 367 pos = 1
368 368 self.write(buf)
369 369
370 370 def _onKeypress(self, event, skip=True):
371 371 '''
372 372 Key press callback used for correcting behavior for console-like
373 373 interfaces. For example 'home' should go to prompt, not to begining of
374 374 line.
375 375
376 376 @param widget: Widget that key press accored in.
377 377 @type widget: gtk.Widget
378 378 @param event: Event object
379 379 @type event: gtk.gdk.Event
380 380
381 381 @return: Return True if event as been catched.
382 382 @rtype: boolean
383 383 '''
384 384
385 385 if event.GetKeyCode() == wx.WXK_HOME:
386 386 if event.Modifiers == wx.MOD_NONE:
387 387 self.moveCursorOnNewValidKey()
388 388 self.moveCursor(self.getCurrentPromptStart())
389 389 return True
390 390 elif event.Modifiers == wx.MOD_SHIFT:
391 391 self.moveCursorOnNewValidKey()
392 392 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
393 393 return True
394 394 else:
395 395 return False
396 396
397 397 elif event.GetKeyCode() == wx.WXK_LEFT:
398 398 if event.Modifiers == wx.MOD_NONE:
399 399 self.moveCursorOnNewValidKey()
400 400
401 401 self.moveCursor(self.getCursorPos()-1)
402 402 if self.getCursorPos() < self.getCurrentPromptStart():
403 403 self.moveCursor(self.getCurrentPromptStart())
404 404 return True
405 405
406 406 elif event.GetKeyCode() == wx.WXK_BACK:
407 407 self.moveCursorOnNewValidKey()
408 408 if self.getCursorPos() > self.getCurrentPromptStart():
409 409 self.removeFromTo(self.getCursorPos()-1,self.getCursorPos())
410 410 return True
411 411
412 412 if skip:
413 413 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
414 414 self.moveCursorOnNewValidKey()
415 415
416 416 event.Skip()
417 417 return True
418 418 return False
419 419
420 420 def OnUpdateUI(self, evt):
421 421 # check for matching braces
422 422 braceAtCaret = -1
423 423 braceOpposite = -1
424 424 charBefore = None
425 425 caretPos = self.GetCurrentPos()
426 426
427 427 if caretPos > 0:
428 428 charBefore = self.GetCharAt(caretPos - 1)
429 429 styleBefore = self.GetStyleAt(caretPos - 1)
430 430
431 431 # check before
432 432 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
433 433 braceAtCaret = caretPos - 1
434 434
435 435 # check after
436 436 if braceAtCaret < 0:
437 437 charAfter = self.GetCharAt(caretPos)
438 438 styleAfter = self.GetStyleAt(caretPos)
439 439
440 440 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
441 441 braceAtCaret = caretPos
442 442
443 443 if braceAtCaret >= 0:
444 444 braceOpposite = self.BraceMatch(braceAtCaret)
445 445
446 446 if braceAtCaret != -1 and braceOpposite == -1:
447 447 self.BraceBadLight(braceAtCaret)
448 448 else:
449 449 self.BraceHighlight(braceAtCaret, braceOpposite)
450 450 #pt = self.PointFromPosition(braceOpposite)
451 451 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
452 452 #print pt
453 453 #self.Refresh(False)
454 454
455 455 class IPShellWidget(wx.Panel):
456 456 '''
457 457 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
458 458 If you want to port this to any other GUI toolkit, just replace the
459 459 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
460 460 from whatever container you want. I've choosed to derivate from a wx.Panel
461 461 because it seems to be more useful
462 462 Any idea to make it more 'generic' welcomed.
463 463 '''
464 464
465 465 def __init__(self, parent, ask_exit_handler=None, intro=None,
466 466 background_color="BLACK", add_button_handler=None,
467 467 wx_ip_shell=None,
468 468 ):
469 469 '''
470 470 Initialize.
471 471 Instanciate an IPython thread.
472 472 Instanciate a WxConsoleView.
473 473 Redirect I/O to console.
474 474 '''
475 475 wx.Panel.__init__(self,parent,-1)
476 476
477 477 ### IPython non blocking shell instanciation ###
478 478 self.cout = StringIO()
479 479
480 480 self.add_button_handler = add_button_handler
481 481 self.ask_exit_handler = ask_exit_handler
482 482
483 483 if wx_ip_shell is not None:
484 484 self.IP = wx_ip_shell
485 485 else:
486 486 self.IP = WxNonBlockingIPShell(self,
487 487 cout=self.cout,cerr=self.cout,
488 488 ask_exit_handler = ask_exit_handler)
489 489
490 490 ### IPython wx console view instanciation ###
491 491 #If user didn't defined an intro text, we create one for him
492 492 #If you really wnat an empty intrp just call wxIPythonViewPanel
493 493 #with intro=''
494 494 if intro == None:
495 495 welcome_text = "Welcome to WxIPython Shell.\n\n"
496 496 welcome_text+= self.IP.getBanner()
497 497 welcome_text+= "!command -> Execute command in shell\n"
498 498 welcome_text+= "TAB -> Autocompletion\n"
499 499
500 500 self.text_ctrl = WxConsoleView(self,
501 501 self.IP.getPrompt(),
502 502 intro=welcome_text,
503 503 background_color=background_color)
504 504
505 505 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
506 506
507 507 ### making the layout of the panel ###
508 508 sizer = wx.BoxSizer(wx.VERTICAL)
509 509 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
510 510 self.SetAutoLayout(True)
511 511 sizer.Fit(self)
512 512 sizer.SetSizeHints(self)
513 513 self.SetSizer(sizer)
514 514 #and we focus on the widget :)
515 515 self.SetFocus()
516 516
517 517 #widget state management (for key handling different cases)
518 518 self.setCurrentState('IDLE')
519 519 self.pager_state = 'DONE'
520 520
521 521 #---------------------- IPython Thread Management ------------------------
522 522 def stateDoExecuteLine(self):
523 523 #print >>sys.__stdout__,"command:",self.getCurrentLine()
524 524 line=self.text_ctrl.getCurrentLine()
525 self.IP.doExecute(line.replace('\t',' '*4))
525 self.IP.doExecute((line.replace('\t',' '*4)).encode('cp1252'))
526 526 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
527 527 self.setCurrentState('WAIT_END_OF_EXECUTION')
528 528
529 529 def evtStateExecuteDone(self,evt):
530 530 self.doc = self.IP.getDocText()
531 531 self.help = self.IP.getHelpText()
532 532 if self.doc:
533 533 self.pager_lines = self.doc[7:].split('\n')
534 534 self.pager_state = 'INIT'
535 535 self.setCurrentState('SHOW_DOC')
536 536 self.pager(self.doc)
537 537 elif self.help:
538 538 self.pager_lines = self.help.split('\n')
539 539 self.pager_state = 'INIT'
540 540 self.setCurrentState('SHOW_DOC')
541 541 self.pager(self.help)
542 542 else:
543 543 self.stateShowPrompt()
544 544
545 545 def stateShowPrompt(self):
546 546 self.setCurrentState('SHOW_PROMPT')
547 547 self.text_ctrl.setPrompt(self.IP.getPrompt())
548 548 self.text_ctrl.setIndentation(self.IP.getIndentation())
549 549 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
550 550 rv = self.cout.getvalue()
551 551 if rv: rv = rv.strip('\n')
552 552 self.text_ctrl.showReturned(rv)
553 553 self.cout.truncate(0)
554 554 self.IP.initHistoryIndex()
555 555 self.setCurrentState('IDLE')
556 556
557 557 def setCurrentState(self, state):
558 558 self.cur_state = state
559 559 self.updateStatusTracker(self.cur_state)
560 560
561 561 #---------------------------- IPython pager ---------------------------------------
562 562 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
563 563
564 564 if self.pager_state == 'INIT':
565 565 #print >>sys.__stdout__,"PAGER state:",self.pager_state
566 566 self.pager_nb_lines = len(self.pager_lines)
567 567 self.pager_index = 0
568 568 self.pager_do_remove = False
569 569 self.text_ctrl.write('\n')
570 570 self.pager_state = 'PROCESS_LINES'
571 571
572 572 if self.pager_state == 'PROCESS_LINES':
573 573 #print >>sys.__stdout__,"PAGER state:",self.pager_state
574 574 if self.pager_do_remove == True:
575 575 self.text_ctrl.removeCurrentLine()
576 576 self.pager_do_remove = False
577 577
578 578 if self.pager_nb_lines > 10:
579 579 #print >>sys.__stdout__,"PAGER processing 10 lines"
580 580 if self.pager_index > 0:
581 581 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
582 582 else:
583 583 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
584 584
585 585 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
586 586 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
587 587 self.pager_index += 10
588 588 self.pager_nb_lines -= 10
589 589 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
590 590 self.pager_do_remove = True
591 591 self.pager_state = 'WAITING'
592 592 return
593 593 else:
594 594 #print >>sys.__stdout__,"PAGER processing last lines"
595 595 if self.pager_nb_lines > 0:
596 596 if self.pager_index > 0:
597 597 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
598 598 else:
599 599 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
600 600
601 601 self.pager_index += 1
602 602 self.pager_nb_lines -= 1
603 603 if self.pager_nb_lines > 0:
604 604 for line in self.pager_lines[self.pager_index:]:
605 605 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
606 606 self.pager_nb_lines = 0
607 607 self.pager_state = 'DONE'
608 608 self.stateShowPrompt()
609 609
610 610 #------------------------ Key Handler ------------------------------------
611 611 def keyPress(self, event):
612 612 '''
613 613 Key press callback with plenty of shell goodness, like history,
614 614 autocompletions, etc.
615 615 '''
616 616
617 617 if event.GetKeyCode() == ord('C'):
618 618 if event.Modifiers == wx.MOD_CONTROL:
619 619 if self.cur_state == 'WAIT_END_OF_EXECUTION':
620 620 #we raise an exception inside the IPython thread container
621 621 self.IP.ce.raise_exc(KeyboardInterrupt)
622 622 return
623 623
624 624 if event.KeyCode == wx.WXK_RETURN:
625 625 if self.cur_state == 'IDLE':
626 626 #we change the state ot the state machine
627 627 self.setCurrentState('DO_EXECUTE_LINE')
628 628 self.stateDoExecuteLine()
629 629 return
630 630 if self.pager_state == 'WAITING':
631 631 self.pager_state = 'PROCESS_LINES'
632 632 self.pager(self.doc)
633 633 return
634 634
635 635 if event.GetKeyCode() in [ord('q'),ord('Q')]:
636 636 if self.pager_state == 'WAITING':
637 637 self.pager_state = 'DONE'
638 638 self.stateShowPrompt()
639 639 return
640 640
641 641 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
642 642 if self.cur_state == 'IDLE':
643 643 if event.KeyCode == wx.WXK_UP:
644 644 history = self.IP.historyBack()
645 645 self.text_ctrl.writeHistory(history)
646 646 return
647 647 if event.KeyCode == wx.WXK_DOWN:
648 648 history = self.IP.historyForward()
649 649 self.text_ctrl.writeHistory(history)
650 650 return
651 651 if event.KeyCode == wx.WXK_TAB:
652 652 #if line empty we disable tab completion
653 653 if not self.text_ctrl.getCurrentLine().strip():
654 654 self.text_ctrl.write('\t')
655 655 return
656 656 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
657 657 if len(possibilities) > 1:
658 658 cur_slice = self.text_ctrl.getCurrentLine()
659 659 self.text_ctrl.write('\n')
660 660 self.text_ctrl.writeCompletion(possibilities)
661 661 self.text_ctrl.write('\n')
662 662
663 663 self.text_ctrl.showPrompt()
664 664 self.text_ctrl.write(cur_slice)
665 665 self.text_ctrl.changeLine(completed or cur_slice)
666 666
667 667 return
668 668 event.Skip()
669 669
670 670 #------------------------ Hook Section -----------------------------------
671 671 def updateHistoryTracker(self,command_line):
672 672 '''
673 673 Default history tracker (does nothing)
674 674 '''
675 675 pass
676 676
677 677 def setHistoryTrackerHook(self,func):
678 678 '''
679 679 Define a new history tracker
680 680 '''
681 681 self.updateHistoryTracker = func
682 682
683 683 def updateStatusTracker(self,status):
684 684 '''
685 685 Default status tracker (does nothing)
686 686 '''
687 687 pass
688 688
689 689 def setStatusTrackerHook(self,func):
690 690 '''
691 691 Define a new status tracker
692 692 '''
693 693 self.updateStatusTracker = func
694 694
695 695
696 696 if __name__ == '__main__':
697 697 # Some simple code to test the shell widget.
698 698 class MainWindow(wx.Frame):
699 699 def __init__(self, parent, id, title):
700 700 wx.Frame.__init__(self, parent, id, title, size=(300,250))
701 701 self._sizer = wx.BoxSizer(wx.VERTICAL)
702 702 self.shell = IPShellWidget(self)
703 703 self._sizer.Add(self.shell, 1, wx.EXPAND)
704 704 self.SetSizer(self._sizer)
705 705 self.SetAutoLayout(1)
706 706 self.Show(True)
707 707
708 708 app = wx.PySimpleApp()
709 709 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
710 710 frame.SetSize((780, 460))
711 711 shell = frame.shell
712 712
713 713 app.MainLoop()
714 714
715 715
General Comments 0
You need to be logged in to leave comments. Login now