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