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