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