##// END OF EJS Templates
Code refactoring to be pep8 complaint with functions names.
laurent.dufrechou@gmail.com -
Show More
@@ -1,533 +1,533
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3 '''
4 4 Provides IPython remote instance.
5 5
6 6 @author: Laurent Dufrechou
7 7 laurent.dufrechou _at_ gmail.com
8 8 @license: BSD
9 9
10 10 All rights reserved. This program and the accompanying materials are made
11 11 available under the terms of the BSD which accompanies this distribution, and
12 12 is available at U{http://www.opensource.org/licenses/bsd-license.php}
13 13 '''
14 14
15 15 __version__ = 0.9
16 16 __author__ = "Laurent Dufrechou"
17 17 __email__ = "laurent.dufrechou _at_ gmail.com"
18 18 __license__ = "BSD"
19 19
20 20 import re
21 21 import sys
22 22 import os
23 23 import locale
24 24 from thread_ex import ThreadEx
25 25
26 26 try:
27 27 import IPython
28 28 except Exception,e:
29 29 print "Error importing IPython (%s)" % str(e)
30 30 raise Exception, e
31 31
32 32 ##############################################################################
33 33 class _Helper(object):
34 34 """Redefine the built-in 'help'.
35 35 This is a wrapper around pydoc.help (with a twist).
36 36 """
37 37
38 38 def __init__(self, pager):
39 39 self._pager = pager
40 40
41 41 def __repr__(self):
42 42 return "Type help() for interactive help, " \
43 43 "or help(object) for help about object."
44 44
45 45 def __call__(self, *args, **kwds):
46 46 class DummyWriter(object):
47 47 '''Dumy class to handle help output'''
48 48 def __init__(self, pager):
49 49 self._pager = pager
50 50
51 51 def write(self, data):
52 52 '''hook to fill self._pager'''
53 53 self._pager(data)
54 54
55 55 import pydoc
56 56 pydoc.help.output = DummyWriter(self._pager)
57 57 pydoc.help.interact = lambda :1
58 58
59 59 return pydoc.help(*args, **kwds)
60 60
61 61
62 62 ##############################################################################
63 63 class _CodeExecutor(ThreadEx):
64 64 ''' Thread that execute ipython code '''
65 65 def __init__(self, instance):
66 66 ThreadEx.__init__(self)
67 67 self.instance = instance
68 68
69 69 def run(self):
70 70 '''Thread main loop'''
71 71 try:
72 72 self.instance._doc_text = None
73 73 self.instance._help_text = None
74 74 self.instance._execute()
75 75 # used for uper class to generate event after execution
76 self.instance._afterExecute()
76 self.instance._after_execute()
77 77
78 78 except KeyboardInterrupt:
79 79 pass
80 80
81 81
82 82 ##############################################################################
83 83 class NonBlockingIPShell(object):
84 84 '''
85 85 Create an IPython instance, running the commands in a separate,
86 86 non-blocking thread.
87 87 This allows embedding in any GUI without blockage.
88 88
89 89 Note: The ThreadEx class supports asynchroneous function call
90 90 via raise_exc()
91 91 '''
92 92
93 93 def __init__(self, argv=[], user_ns={}, user_global_ns=None,
94 94 cin=None, cout=None, cerr=None,
95 95 ask_exit_handler=None):
96 96 '''
97 97 @param argv: Command line options for IPython
98 98 @type argv: list
99 99 @param user_ns: User namespace.
100 100 @type user_ns: dictionary
101 101 @param user_global_ns: User global namespace.
102 102 @type user_global_ns: dictionary.
103 103 @param cin: Console standard input.
104 104 @type cin: IO stream
105 105 @param cout: Console standard output.
106 106 @type cout: IO stream
107 107 @param cerr: Console standard error.
108 108 @type cerr: IO stream
109 109 @param exit_handler: Replacement for builtin exit() function
110 110 @type exit_handler: function
111 111 @param time_loop: Define the sleep time between two thread's loop
112 112 @type int
113 113 '''
114 114 #ipython0 initialisation
115 115 self._IP = None
116 self.initIpython0(argv, user_ns, user_global_ns,
116 self.init_ipython0(argv, user_ns, user_global_ns,
117 117 cin, cout, cerr,
118 118 ask_exit_handler)
119 119
120 120 #vars used by _execute
121 121 self._iter_more = 0
122 122 self._history_level = 0
123 123 self._complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
124 124 self._prompt = str(self._IP.outputcache.prompt1).strip()
125 125
126 126 #thread working vars
127 127 self._line_to_execute = ''
128 128 self._threading = True
129 129
130 130 #vars that will be checked by GUI loop to handle thread states...
131 131 #will be replaced later by PostEvent GUI funtions...
132 132 self._doc_text = None
133 133 self._help_text = None
134 134 self._add_button = None
135 135
136 def initIpython0(self, argv=[], user_ns={}, user_global_ns=None,
136 def init_ipython0(self, argv=[], user_ns={}, user_global_ns=None,
137 137 cin=None, cout=None, cerr=None,
138 138 ask_exit_handler=None):
139 139 ''' Initialize an ipython0 instance '''
140 140
141 141 #first we redefine in/out/error functions of IPython
142 142 #BUG: we've got a limitation form ipython0 there
143 143 #only one instance can be instanciated else tehre will be
144 144 #cin/cout/cerr clash...
145 145 if cin:
146 146 IPython.genutils.Term.cin = cin
147 147 if cout:
148 148 IPython.genutils.Term.cout = cout
149 149 if cerr:
150 150 IPython.genutils.Term.cerr = cerr
151 151
152 152 excepthook = sys.excepthook
153 153
154 154 #Hack to save sys.displayhook, because ipython seems to overwrite it...
155 155 self.sys_displayhook_ori = sys.displayhook
156 156
157 157 self._IP = IPython.Shell.make_IPython(
158 158 argv,user_ns=user_ns,
159 159 user_global_ns=user_global_ns,
160 160 embedded=True,
161 161 shell_class=IPython.Shell.InteractiveShell)
162 162
163 163 #we save ipython0 displayhook and we restore sys.displayhook
164 164 self.displayhook = sys.displayhook
165 165 sys.displayhook = self.sys_displayhook_ori
166 166
167 167 #we replace IPython default encoding by wx locale encoding
168 168 loc = locale.getpreferredencoding()
169 169 if loc:
170 170 self._IP.stdin_encoding = loc
171 171 #we replace the ipython default pager by our pager
172 172 self._IP.set_hook('show_in_pager', self._pager)
173 173
174 174 #we replace the ipython default shell command caller by our shell handler
175 175 self._IP.set_hook('shell_hook', self._shell)
176 176
177 177 #we replace the ipython default input command caller by our method
178 178 IPython.iplib.raw_input_original = self._raw_input
179 179 #we replace the ipython default exit command by our method
180 180 self._IP.exit = ask_exit_handler
181 181 #we replace the help command
182 182 self._IP.user_ns['help'] = _Helper(self._pager_help)
183 183
184 184 #we disable cpase magic... until we found a way to use it properly.
185 185 #import IPython.ipapi
186 186 ip = IPython.ipapi.get()
187 def bypassMagic(self, arg):
187 def bypass_magic(self, arg):
188 188 print '%this magic is currently disabled.'
189 ip.expose_magic('cpaste', bypassMagic)
189 ip.expose_magic('cpaste', bypass_magic)
190 190
191 def resetMagic(self, arg):
191 def reset_magic(self, arg):
192 192 """Resets the namespace by removing all names defined by the user.
193 193
194 194 Input/Output history are left around in case you need them."""
195 195
196 196 ans = True ##todo find away to ask the user...
197 197 ##seems hard to do it cleanly...
198 198 if not ans:
199 199 print 'Nothing done.'
200 200 return
201 201 user_ns = self.shell.user_ns
202 202 for i in self.magic_who_ls():
203 203 del(user_ns[i])
204 204
205 205 # Also flush the private list of module references kept for script
206 206 # execution protection
207 207 self.shell._user_main_modules[:] = []
208 208
209 ip.expose_magic('reset', resetMagic)
209 ip.expose_magic('reset', reset_magic)
210 210
211 211 sys.excepthook = excepthook
212 212
213 213 #----------------------- Thread management section ----------------------
214 def doExecute(self, line):
214 def do_execute(self, line):
215 215 """
216 216 Tell the thread to process the 'line' command
217 217 """
218 218
219 219 self._line_to_execute = line
220 220
221 221 if self._threading:
222 222 #we launch the ipython line execution in a thread to make it interruptible
223 223 #with include it in self namespace to be able to call ce.raise_exc(KeyboardInterrupt)
224 224 self.ce = _CodeExecutor(self)
225 225 self.ce.start()
226 226 else:
227 227 try:
228 228 self._doc_text = None
229 229 self._help_text = None
230 230 self._execute()
231 231 # used for uper class to generate event after execution
232 self._afterExecute()
232 self._after_execute()
233 233
234 234 except KeyboardInterrupt:
235 235 pass
236 236
237 237 #----------------------- IPython management section ----------------------
238 def getThreading(self):
238 def get_threading(self):
239 239 """
240 240 Returns threading status, is set to True, then each command sent to
241 241 the interpreter will be executed in a separated thread allowing,
242 242 for example, breaking a long running commands.
243 243 Disallowing it, permits better compatibilty with instance that is embedding
244 244 IPython instance.
245 245
246 246 @return: Execution method
247 247 @rtype: bool
248 248 """
249 249 return self._threading
250 250
251 def setThreading(self, state):
251 def set_threading(self, state):
252 252 """
253 253 Sets threading state, if set to True, then each command sent to
254 254 the interpreter will be executed in a separated thread allowing,
255 255 for example, breaking a long running commands.
256 256 Disallowing it, permits better compatibilty with instance that is embedding
257 257 IPython instance.
258 258
259 259 @param state: Sets threading state
260 260 @type bool
261 261 """
262 262 self._threading = state
263 263
264 def getDocText(self):
264 def get_doc_text(self):
265 265 """
266 266 Returns the output of the processing that need to be paged (if any)
267 267
268 268 @return: The std output string.
269 269 @rtype: string
270 270 """
271 271 return self._doc_text
272 272
273 def getHelpText(self):
273 def get_help_text(self):
274 274 """
275 275 Returns the output of the processing that need to be paged via help pager(if any)
276 276
277 277 @return: The std output string.
278 278 @rtype: string
279 279 """
280 280 return self._help_text
281 281
282 def getBanner(self):
282 def get_banner(self):
283 283 """
284 284 Returns the IPython banner for useful info on IPython instance
285 285
286 286 @return: The banner string.
287 287 @rtype: string
288 288 """
289 289 return self._IP.BANNER
290 290
291 def getPromptCount(self):
291 def get_prompt_count(self):
292 292 """
293 293 Returns the prompt number.
294 294 Each time a user execute a line in the IPython shell the prompt count is increased
295 295
296 296 @return: The prompt number
297 297 @rtype: int
298 298 """
299 299 return self._IP.outputcache.prompt_count
300 300
301 def getPrompt(self):
301 def get_prompt(self):
302 302 """
303 303 Returns current prompt inside IPython instance
304 304 (Can be In [...]: ot ...:)
305 305
306 306 @return: The current prompt.
307 307 @rtype: string
308 308 """
309 309 return self._prompt
310 310
311 def getIndentation(self):
311 def get_indentation(self):
312 312 """
313 313 Returns the current indentation level
314 314 Usefull to put the caret at the good start position if we want to do autoindentation.
315 315
316 316 @return: The indentation level.
317 317 @rtype: int
318 318 """
319 319 return self._IP.indent_current_nsp
320 320
321 def updateNamespace(self, ns_dict):
321 def update_namespace(self, ns_dict):
322 322 '''
323 323 Add the current dictionary to the shell namespace.
324 324
325 325 @param ns_dict: A dictionary of symbol-values.
326 326 @type ns_dict: dictionary
327 327 '''
328 328 self._IP.user_ns.update(ns_dict)
329 329
330 330 def complete(self, line):
331 331 '''
332 332 Returns an auto completed line and/or posibilities for completion.
333 333
334 334 @param line: Given line so far.
335 335 @type line: string
336 336
337 337 @return: Line completed as for as possible,
338 338 and possible further completions.
339 339 @rtype: tuple
340 340 '''
341 341 split_line = self._complete_sep.split(line)
342 342 possibilities = self._IP.complete(split_line[-1])
343 343 if possibilities:
344 344
345 def _commonPrefix(str1, str2):
345 def _common_prefix(str1, str2):
346 346 '''
347 347 Reduction function. returns common prefix of two given strings.
348 348
349 349 @param str1: First string.
350 350 @type str1: string
351 351 @param str2: Second string
352 352 @type str2: string
353 353
354 354 @return: Common prefix to both strings.
355 355 @rtype: string
356 356 '''
357 357 for i in range(len(str1)):
358 358 if not str2.startswith(str1[:i+1]):
359 359 return str1[:i]
360 360 return str1
361 common_prefix = reduce(_commonPrefix, possibilities)
361 common_prefix = reduce(_common_prefix, possibilities)
362 362 completed = line[:-len(split_line[-1])]+common_prefix
363 363 else:
364 364 completed = line
365 365 return completed, possibilities
366 366
367 def historyBack(self):
367 def history_back(self):
368 368 '''
369 369 Provides one history command back.
370 370
371 371 @return: The command string.
372 372 @rtype: string
373 373 '''
374 374 history = ''
375 375 #the below while loop is used to suppress empty history lines
376 376 while((history == '' or history == '\n') and self._history_level >0):
377 377 if self._history_level >= 1:
378 378 self._history_level -= 1
379 history = self._getHistory()
379 history = self._get_history()
380 380 return history
381 381
382 def historyForward(self):
382 def history_forward(self):
383 383 '''
384 384 Provides one history command forward.
385 385
386 386 @return: The command string.
387 387 @rtype: string
388 388 '''
389 389 history = ''
390 390 #the below while loop is used to suppress empty history lines
391 391 while((history == '' or history == '\n') \
392 and self._history_level <= self._getHistoryMaxIndex()):
393 if self._history_level < self._getHistoryMaxIndex():
392 and self._history_level <= self._get_history_max_index()):
393 if self._history_level < self._get_history_max_index():
394 394 self._history_level += 1
395 history = self._getHistory()
395 history = self._get_history()
396 396 else:
397 if self._history_level == self._getHistoryMaxIndex():
398 history = self._getHistory()
397 if self._history_level == self._get_history_max_index():
398 history = self._get_history()
399 399 self._history_level += 1
400 400 else:
401 401 history = ''
402 402 return history
403 403
404 def initHistoryIndex(self):
404 def init_history_index(self):
405 405 '''
406 406 set history to last command entered
407 407 '''
408 self._history_level = self._getHistoryMaxIndex()+1
408 self._history_level = self._get_history_max_index()+1
409 409
410 410 #----------------------- IPython PRIVATE management section --------------
411 def _afterExecute(self):
411 def _after_execute(self):
412 412 '''
413 413 Can be redefined to generate post event after excution is done
414 414 '''
415 415 pass
416 416
417 #def _askExit(self):
417 #def _ask_exit(self):
418 418 # '''
419 419 # Can be redefined to generate post event to exit the Ipython shell
420 420 # '''
421 421 # pass
422 422
423 def _getHistoryMaxIndex(self):
423 def _get_history_max_index(self):
424 424 '''
425 425 returns the max length of the history buffer
426 426
427 427 @return: history length
428 428 @rtype: int
429 429 '''
430 430 return len(self._IP.input_hist_raw)-1
431 431
432 def _getHistory(self):
432 def _get_history(self):
433 433 '''
434 434 Get's the command string of the current history level.
435 435
436 436 @return: Historic command stri
437 437 @rtype: string
438 438 '''
439 439 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
440 440 return rv
441 441
442 442 def _pager_help(self, text):
443 443 '''
444 444 This function is used as a callback replacment to IPython help pager function
445 445
446 446 It puts the 'text' value inside the self._help_text string that can be retrived via
447 getHelpText function.
447 get_help_text function.
448 448 '''
449 449 if self._help_text == None:
450 450 self._help_text = text
451 451 else:
452 452 self._help_text += text
453 453
454 454 def _pager(self, IP, text):
455 455 '''
456 456 This function is used as a callback replacment to IPython pager function
457 457
458 458 It puts the 'text' value inside the self._doc_text string that can be retrived via
459 getDocText function.
459 get_doc_text function.
460 460 '''
461 461 self._doc_text = text
462 462
463 463 def _raw_input(self, prompt=''):
464 464 '''
465 465 Custom raw_input() replacement. Get's current line from console buffer.
466 466
467 467 @param prompt: Prompt to print. Here for compatability as replacement.
468 468 @type prompt: string
469 469
470 470 @return: The current command line text.
471 471 @rtype: string
472 472 '''
473 473 return self._line_to_execute
474 474
475 475 def _execute(self):
476 476 '''
477 477 Executes the current line provided by the shell object.
478 478 '''
479 479
480 480 orig_stdout = sys.stdout
481 481 sys.stdout = IPython.Shell.Term.cout
482 482 #self.sys_displayhook_ori = sys.displayhook
483 483 #sys.displayhook = self.displayhook
484 484
485 485 try:
486 486 line = self._IP.raw_input(None, self._iter_more)
487 487 if self._IP.autoindent:
488 488 self._IP.readline_startup_hook(None)
489 489
490 490 except KeyboardInterrupt:
491 491 self._IP.write('\nKeyboardInterrupt\n')
492 492 self._IP.resetbuffer()
493 493 # keep cache in sync with the prompt counter:
494 494 self._IP.outputcache.prompt_count -= 1
495 495
496 496 if self._IP.autoindent:
497 497 self._IP.indent_current_nsp = 0
498 498 self._iter_more = 0
499 499 except:
500 500 self._IP.showtraceback()
501 501 else:
502 502 self._IP.write(str(self._IP.outputcache.prompt_out).strip())
503 503 self._iter_more = self._IP.push(line)
504 504 if (self._IP.SyntaxTB.last_syntax_error and self._IP.rc.autoedit_syntax):
505 505 self._IP.edit_syntax_error()
506 506 if self._iter_more:
507 507 self._prompt = str(self._IP.outputcache.prompt2).strip()
508 508 if self._IP.autoindent:
509 509 self._IP.readline_startup_hook(self._IP.pre_readline)
510 510 else:
511 511 self._prompt = str(self._IP.outputcache.prompt1).strip()
512 512 self._IP.indent_current_nsp = 0 #we set indentation to 0
513 513
514 514 sys.stdout = orig_stdout
515 515 #sys.displayhook = self.sys_displayhook_ori
516 516
517 517 def _shell(self, ip, cmd):
518 518 '''
519 519 Replacement method to allow shell commands without them blocking.
520 520
521 521 @param ip: Ipython instance, same as self._IP
522 522 @type cmd: Ipython instance
523 523 @param cmd: Shell command to execute.
524 524 @type cmd: string
525 525 '''
526 526 stdin, stdout = os.popen4(cmd)
527 527 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
528 528 #we use print command because the shell command is called
529 529 #inside IPython instance and thus is redirected to thread cout
530 530 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
531 531 print "\x01\x1b[1;36m\x02"+result
532 532 stdout.close()
533 533 stdin.close()
@@ -1,915 +1,915
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.9
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
30 30 import re
31 31 from StringIO import StringIO
32 32
33 33 import sys
34 34 import codecs
35 35 import locale
36 36 for enc in (locale.getpreferredencoding(),
37 37 sys.getfilesystemencoding(),
38 38 sys.getdefaultencoding()):
39 39 try:
40 40 codecs.lookup(enc)
41 41 ENCODING = enc
42 42 break
43 43 except LookupError:
44 44 pass
45 45 else:
46 46 ENCODING = 'utf-8'
47 47
48 48 from ipshell_nonblocking import NonBlockingIPShell
49 49
50 50 class WxNonBlockingIPShell(NonBlockingIPShell):
51 51 '''
52 52 An NonBlockingIPShell Thread that is WX dependent.
53 53 '''
54 54 def __init__(self, parent,
55 55 argv=[],user_ns={},user_global_ns=None,
56 56 cin=None, cout=None, cerr=None,
57 57 ask_exit_handler=None):
58 58
59 59 NonBlockingIPShell.__init__(self, argv, user_ns, user_global_ns,
60 60 cin, cout, cerr,
61 61 ask_exit_handler)
62 62
63 63 self.parent = parent
64 64
65 65 self.ask_exit_callback = ask_exit_handler
66 self._IP.exit = self._askExit
66 self._IP.exit = self._ask_exit
67 67
68 68 def addGUIShortcut(self, text, func):
69 69 wx.CallAfter(self.parent.add_button_handler,
70 70 button_info={ 'text':text,
71 71 'func':self.parent.doExecuteLine(func)})
72 72
73 def _askExit(self):
73 def _ask_exit(self):
74 74 wx.CallAfter(self.ask_exit_callback, ())
75 75
76 def _afterExecute(self):
76 def _after_execute(self):
77 77 wx.CallAfter(self.parent.evtStateExecuteDone, ())
78 78
79 79
80 80 class WxConsoleView(stc.StyledTextCtrl):
81 81 '''
82 82 Specialized styled text control view for console-like workflow.
83 83 We use here a scintilla frontend thus it can be reused in any GUI that
84 84 supports scintilla with less work.
85 85
86 86 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.
87 87 (with Black background)
88 88 @type ANSI_COLORS_BLACK: dictionary
89 89
90 90 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.
91 91 (with White background)
92 92 @type ANSI_COLORS_WHITE: dictionary
93 93
94 94 @ivar color_pat: Regex of terminal color pattern
95 95 @type color_pat: _sre.SRE_Pattern
96 96 '''
97 97 ANSI_STYLES_BLACK = {'0;30': [0, 'WHITE'], '0;31': [1, 'RED'],
98 98 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
99 99 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
100 100 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
101 101 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
102 102 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
103 103 '1;34': [12, 'LIGHT BLUE'], '1;35':
104 104 [13, 'MEDIUM VIOLET RED'],
105 105 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
106 106
107 107 ANSI_STYLES_WHITE = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
108 108 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
109 109 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
110 110 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
111 111 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
112 112 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
113 113 '1;34': [12, 'LIGHT BLUE'], '1;35':
114 114 [13, 'MEDIUM VIOLET RED'],
115 115 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
116 116
117 117 def __init__(self, parent, prompt, intro="", background_color="BLACK",
118 118 pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
119 119 style=0, autocomplete_mode = 'IPYTHON'):
120 120 '''
121 121 Initialize console view.
122 122
123 123 @param parent: Parent widget
124 124 @param prompt: User specified prompt
125 125 @type intro: string
126 126 @param intro: User specified startup introduction string
127 127 @type intro: string
128 128 @param background_color: Can be BLACK or WHITE
129 129 @type background_color: string
130 130 @param other: init param of styledTextControl (can be used as-is)
131 131 @param autocomplete_mode: Can be 'IPYTHON' or 'STC'
132 132 'IPYTHON' show autocompletion the ipython way
133 133 'STC" show it scintilla text control way
134 134 '''
135 135 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
136 136
137 137 ####### Scintilla configuration ###################################
138 138
139 139 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside
140 140 # the widget
141 141 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
142 142 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
143 143
144 144 #We draw a line at position 80
145 145 self.SetEdgeMode(stc.STC_EDGE_LINE)
146 146 self.SetEdgeColumn(80)
147 147 self.SetEdgeColour(wx.LIGHT_GREY)
148 148
149 149 #self.SetViewWhiteSpace(True)
150 150 #self.SetViewEOL(True)
151 151 self.SetEOLMode(stc.STC_EOL_CRLF)
152 152 #self.SetWrapMode(stc.STC_WRAP_CHAR)
153 153 #self.SetWrapMode(stc.STC_WRAP_WORD)
154 154 self.SetBufferedDraw(True)
155 155 #self.SetUseAntiAliasing(True)
156 156 self.SetLayoutCache(stc.STC_CACHE_PAGE)
157 157 self.SetUndoCollection(False)
158 158 self.SetUseTabs(True)
159 159 self.SetIndent(4)
160 160 self.SetTabWidth(4)
161 161
162 162 self.EnsureCaretVisible()
163 163
164 164 self.SetMargins(3, 3) #text is moved away from border with 3px
165 165 # Suppressing Scintilla margins
166 166 self.SetMarginWidth(0, 0)
167 167 self.SetMarginWidth(1, 0)
168 168 self.SetMarginWidth(2, 0)
169 169
170 170 self.background_color = background_color
171 171 self.buildStyles()
172 172
173 173 self.indent = 0
174 174 self.prompt_count = 0
175 175 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
176 176
177 177 self.write(intro)
178 178 self.setPrompt(prompt)
179 179 self.showPrompt()
180 180
181 181 self.autocomplete_mode = autocomplete_mode
182 182
183 183 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress)
184 184
185 185 def buildStyles(self):
186 186 #we define platform specific fonts
187 187 if wx.Platform == '__WXMSW__':
188 188 faces = { 'times': 'Times New Roman',
189 189 'mono' : 'Courier New',
190 190 'helv' : 'Arial',
191 191 'other': 'Comic Sans MS',
192 192 'size' : 10,
193 193 'size2': 8,
194 194 }
195 195 elif wx.Platform == '__WXMAC__':
196 196 faces = { 'times': 'Times New Roman',
197 197 'mono' : 'Monaco',
198 198 'helv' : 'Arial',
199 199 'other': 'Comic Sans MS',
200 200 'size' : 10,
201 201 'size2': 8,
202 202 }
203 203 else:
204 204 faces = { 'times': 'Times',
205 205 'mono' : 'Courier',
206 206 'helv' : 'Helvetica',
207 207 'other': 'new century schoolbook',
208 208 'size' : 10,
209 209 'size2': 8,
210 210 }
211 211
212 212 # make some styles
213 213 if self.background_color != "BLACK":
214 214 self.background_color = "WHITE"
215 215 self.SetCaretForeground("BLACK")
216 216 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
217 217 else:
218 218 self.SetCaretForeground("WHITE")
219 219 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
220 220
221 221 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
222 222 "fore:%s,back:%s,size:%d,face:%s"
223 223 % (self.ANSI_STYLES['0;30'][1],
224 224 self.background_color,
225 225 faces['size'], faces['mono']))
226 226 self.StyleClearAll()
227 227 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
228 228 "fore:#FF0000,back:#0000FF,bold")
229 229 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
230 230 "fore:#000000,back:#FF0000,bold")
231 231
232 232 for style in self.ANSI_STYLES.values():
233 233 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
234 234
235 235 #######################################################################
236 236
237 237 def setBackgroundColor(self, color):
238 238 self.background_color = color
239 239 self.buildStyles()
240 240
241 241 def getBackgroundColor(self, color):
242 242 return self.background_color
243 243
244 244 def asyncWrite(self, text):
245 245 '''
246 246 Write given text to buffer in an asynchroneous way.
247 247 It is used from another thread to be able to acces the GUI.
248 248 @param text: Text to append
249 249 @type text: string
250 250 '''
251 251 try:
252 252 wx.MutexGuiEnter()
253 253
254 254 #be sure not to be interrutpted before the MutexGuiLeave!
255 255 self.write(text)
256 256
257 257 except KeyboardInterrupt:
258 258 wx.MutexGuiLeave()
259 259 raise KeyboardInterrupt
260 260 wx.MutexGuiLeave()
261 261
262 262
263 263 def write(self, text):
264 264 '''
265 265 Write given text to buffer.
266 266
267 267 @param text: Text to append.
268 268 @type text: string
269 269 '''
270 270 segments = self.color_pat.split(text)
271 271 segment = segments.pop(0)
272 272 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
273 273 self.AppendText(segment)
274 274
275 275 if segments:
276 276 ansi_tags = self.color_pat.findall(text)
277 277
278 278 for tag in ansi_tags:
279 279 i = segments.index(tag)
280 280 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
281 281 self.AppendText(segments[i+1])
282 282
283 283 if tag != '0':
284 284 self.SetStyling(len(segments[i+1]), self.ANSI_STYLES[tag][0])
285 285
286 286 segments.pop(i)
287 287
288 288 self.moveCursor(self.getCurrentLineEnd())
289 289
290 290 def getPromptLen(self):
291 291 '''
292 292 Return the length of current prompt
293 293 '''
294 294 return len(str(self.prompt_count)) + 7
295 295
296 296 def setPrompt(self, prompt):
297 297 self.prompt = prompt
298 298
299 299 def setIndentation(self, indentation):
300 300 self.indent = indentation
301 301
302 302 def setPromptCount(self, count):
303 303 self.prompt_count = count
304 304
305 305 def showPrompt(self):
306 306 '''
307 307 Prints prompt at start of line.
308 308
309 309 @param prompt: Prompt to print.
310 310 @type prompt: string
311 311 '''
312 312 self.write(self.prompt)
313 313 #now we update the position of end of prompt
314 314 self.current_start = self.getCurrentLineEnd()
315 315
316 316 autoindent = self.indent*' '
317 317 autoindent = autoindent.replace(' ','\t')
318 318 self.write(autoindent)
319 319
320 320 def changeLine(self, text):
321 321 '''
322 322 Replace currently entered command line with given text.
323 323
324 324 @param text: Text to use as replacement.
325 325 @type text: string
326 326 '''
327 327 self.SetSelection(self.getCurrentPromptStart(), self.getCurrentLineEnd())
328 328 self.ReplaceSelection(text)
329 329 self.moveCursor(self.getCurrentLineEnd())
330 330
331 331 def getCurrentPromptStart(self):
332 332 return self.current_start
333 333
334 334 def getCurrentLineStart(self):
335 335 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
336 336
337 337 def getCurrentLineEnd(self):
338 338 return self.GetLength()
339 339
340 340 def getCurrentLine(self):
341 341 '''
342 342 Get text in current command line.
343 343
344 344 @return: Text of current command line.
345 345 @rtype: string
346 346 '''
347 347 return self.GetTextRange(self.getCurrentPromptStart(),
348 348 self.getCurrentLineEnd())
349 349
350 350 def moveCursorOnNewValidKey(self):
351 351 #If cursor is at wrong position put it at last line...
352 352 if self.GetCurrentPos() < self.getCurrentPromptStart():
353 353 self.GotoPos(self.getCurrentPromptStart())
354 354
355 355 def removeFromTo(self, from_pos, to_pos):
356 356 if from_pos < to_pos:
357 357 self.SetSelection(from_pos, to_pos)
358 358 self.DeleteBack()
359 359
360 360 def removeCurrentLine(self):
361 361 self.LineDelete()
362 362
363 363 def moveCursor(self, position):
364 364 self.GotoPos(position)
365 365
366 366 def getCursorPos(self):
367 367 return self.GetCurrentPos()
368 368
369 369 def selectFromTo(self, from_pos, to_pos):
370 370 self.SetSelectionStart(from_pos)
371 371 self.SetSelectionEnd(to_pos)
372 372
373 373 def writeHistory(self, history):
374 374 self.removeFromTo(self.getCurrentPromptStart(), self.getCurrentLineEnd())
375 375 self.changeLine(history)
376 376
377 377 def setCompletionMethod(self, completion):
378 378 if completion in ['IPYTHON', 'STC']:
379 379 self.autocomplete_mode = completion
380 380 else:
381 381 raise AttributeError
382 382
383 383 def getCompletionMethod(self, completion):
384 384 return self.autocomplete_mode
385 385
386 386 def writeCompletion(self, possibilities):
387 387 if self.autocomplete_mode == 'IPYTHON':
388 388 max_len = len(max(possibilities, key=len))
389 389 max_symbol = ' '*max_len
390 390
391 391 #now we check how much symbol we can put on a line...
392 392 test_buffer = max_symbol + ' '*4
393 393
394 394 allowed_symbols = 80/len(test_buffer)
395 395 if allowed_symbols == 0:
396 396 allowed_symbols = 1
397 397
398 398 pos = 1
399 399 buf = ''
400 400 for symbol in possibilities:
401 401 #buf += symbol+'\n'#*spaces)
402 402 if pos < allowed_symbols:
403 403 spaces = max_len - len(symbol) + 4
404 404 buf += symbol+' '*spaces
405 405 pos += 1
406 406 else:
407 407 buf += symbol+'\n'
408 408 pos = 1
409 409 self.write(buf)
410 410 else:
411 411 possibilities.sort() # Python sorts are case sensitive
412 412 self.AutoCompSetIgnoreCase(False)
413 413 self.AutoCompSetAutoHide(False)
414 414 #let compute the length ot last word
415 415 splitter = [' ', '(', '[', '{','=']
416 416 last_word = self.getCurrentLine()
417 417 for breaker in splitter:
418 418 last_word = last_word.split(breaker)[-1]
419 419 self.AutoCompShow(len(last_word), " ".join(possibilities))
420 420
421 421 def _onKeypress(self, event, skip=True):
422 422 '''
423 423 Key press callback used for correcting behavior for console-like
424 424 interfaces. For example 'home' should go to prompt, not to begining of
425 425 line.
426 426
427 427 @param widget: Widget that key press accored in.
428 428 @type widget: gtk.Widget
429 429 @param event: Event object
430 430 @type event: gtk.gdk.Event
431 431
432 432 @return: Return True if event as been catched.
433 433 @rtype: boolean
434 434 '''
435 435
436 436 if not self.AutoCompActive():
437 437 if event.GetKeyCode() == wx.WXK_HOME:
438 438 if event.Modifiers == wx.MOD_NONE:
439 439 self.moveCursorOnNewValidKey()
440 440 self.moveCursor(self.getCurrentPromptStart())
441 441 return True
442 442 elif event.Modifiers == wx.MOD_SHIFT:
443 443 self.moveCursorOnNewValidKey()
444 444 self.selectFromTo(self.getCurrentPromptStart(), self.getCursorPos())
445 445 return True
446 446 else:
447 447 return False
448 448
449 449 elif event.GetKeyCode() == wx.WXK_LEFT:
450 450 if event.Modifiers == wx.MOD_NONE:
451 451 self.moveCursorOnNewValidKey()
452 452
453 453 self.moveCursor(self.getCursorPos()-1)
454 454 if self.getCursorPos() < self.getCurrentPromptStart():
455 455 self.moveCursor(self.getCurrentPromptStart())
456 456 return True
457 457
458 458 elif event.GetKeyCode() == wx.WXK_BACK:
459 459 self.moveCursorOnNewValidKey()
460 460 if self.getCursorPos() > self.getCurrentPromptStart():
461 461 event.Skip()
462 462 return True
463 463
464 464 if skip:
465 465 if event.GetKeyCode() not in [wx.WXK_PAGEUP, wx.WXK_PAGEDOWN]\
466 466 and event.Modifiers == wx.MOD_NONE:
467 467 self.moveCursorOnNewValidKey()
468 468
469 469 event.Skip()
470 470 return True
471 471 return False
472 472 else:
473 473 event.Skip()
474 474
475 475 def OnUpdateUI(self, evt):
476 476 # check for matching braces
477 477 braceAtCaret = -1
478 478 braceOpposite = -1
479 479 charBefore = None
480 480 caretPos = self.GetCurrentPos()
481 481
482 482 if caretPos > 0:
483 483 charBefore = self.GetCharAt(caretPos - 1)
484 484 styleBefore = self.GetStyleAt(caretPos - 1)
485 485
486 486 # check before
487 487 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
488 488 braceAtCaret = caretPos - 1
489 489
490 490 # check after
491 491 if braceAtCaret < 0:
492 492 charAfter = self.GetCharAt(caretPos)
493 493 styleAfter = self.GetStyleAt(caretPos)
494 494
495 495 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
496 496 braceAtCaret = caretPos
497 497
498 498 if braceAtCaret >= 0:
499 499 braceOpposite = self.BraceMatch(braceAtCaret)
500 500
501 501 if braceAtCaret != -1 and braceOpposite == -1:
502 502 self.BraceBadLight(braceAtCaret)
503 503 else:
504 504 self.BraceHighlight(braceAtCaret, braceOpposite)
505 505 #pt = self.PointFromPosition(braceOpposite)
506 506 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
507 507 #print pt
508 508 #self.Refresh(False)
509 509
510 510 class IPShellWidget(wx.Panel):
511 511 '''
512 512 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
513 513 If you want to port this to any other GUI toolkit, just replace the
514 514 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
515 515 from whatever container you want. I've choosed to derivate from a wx.Panel
516 516 because it seems to be more useful
517 517 Any idea to make it more 'generic' welcomed.
518 518 '''
519 519
520 520 def __init__(self, parent, intro=None,
521 521 background_color="BLACK", add_button_handler=None,
522 522 wx_ip_shell=None, user_ns={},user_global_ns=None,
523 523 ):
524 524 '''
525 525 Initialize.
526 526 Instanciate an IPython thread.
527 527 Instanciate a WxConsoleView.
528 528 Redirect I/O to console.
529 529 '''
530 530 wx.Panel.__init__(self,parent,wx.ID_ANY)
531 531
532 532 self.parent = parent
533 533 ### IPython non blocking shell instanciation ###
534 534 self.cout = StringIO()
535 535 self.add_button_handler = add_button_handler
536 536
537 537 if wx_ip_shell is not None:
538 538 self.IP = wx_ip_shell
539 539 else:
540 540 self.IP = WxNonBlockingIPShell(self,
541 541 cout = self.cout, cerr = self.cout,
542 542 ask_exit_handler = self.askExitCallback)
543 543
544 544 ### IPython wx console view instanciation ###
545 545 #If user didn't defined an intro text, we create one for him
546 546 #If you really wnat an empty intro just call wxIPythonViewPanel
547 547 #with intro=''
548 548 if intro is None:
549 549 welcome_text = "Welcome to WxIPython Shell.\n\n"
550 welcome_text+= self.IP.getBanner()
550 welcome_text+= self.IP.get_banner()
551 551 welcome_text+= "!command -> Execute command in shell\n"
552 552 welcome_text+= "TAB -> Autocompletion\n"
553 553 else:
554 554 welcome_text = intro
555 555
556 556 self.text_ctrl = WxConsoleView(self,
557 self.IP.getPrompt(),
557 self.IP.get_prompt(),
558 558 intro=welcome_text,
559 559 background_color=background_color)
560 560
561 561 option_text = wx.StaticText(self, -1, "Options:")
562 562 self.completion_option = wx.CheckBox(self, -1, "Scintilla Completion")
563 563 self.completion_option.SetToolTip(wx.ToolTip(
564 564 "Selects the completion type:\nEither Ipython default style or Scintilla one"))
565 565 #self.completion_option.SetValue(False)
566 566 self.background_option = wx.CheckBox(self, -1, "White Background")
567 567 self.background_option.SetToolTip(wx.ToolTip(
568 568 "Selects the back ground color: BLACK or WHITE"))
569 569 #self.background_option.SetValue(False)
570 570 self.threading_option = wx.CheckBox(self, -1, "Execute in thread")
571 571 self.threading_option.SetToolTip(wx.ToolTip(
572 572 "Use threading: infinite loop don't freeze the GUI and commands can be breaked\nNo threading: maximum compatibility"))
573 573 #self.threading_option.SetValue(False)
574 574
575 575 self.options={'completion':{'value':'IPYTHON',
576 576 'checkbox':self.completion_option,'STC':True,'IPYTHON':False,
577 577 'setfunc':self.text_ctrl.setCompletionMethod},
578 578 'background_color':{'value':'BLACK',
579 579 'checkbox':self.background_option,'WHITE':True,'BLACK':False,
580 580 'setfunc':self.text_ctrl.setBackgroundColor},
581 581 'threading':{'value':'True',
582 582 'checkbox':self.threading_option,'True':True,'False':False,
583 'setfunc':self.IP.setThreading},
583 'setfunc':self.IP.set_threading},
584 584 }
585 585
586 586 #self.cout.write dEfault option is asynchroneous because default sate is threading ON
587 587 self.cout.write = self.text_ctrl.asyncWrite
588 588 #we reloard options
589 589 self.reloadOptions(self.options)
590 590
591 591 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress)
592 592 self.completion_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionCompletion)
593 593 self.background_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionBackgroundColor)
594 594 self.threading_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionThreading)
595 595
596 596 ### making the layout of the panel ###
597 597 sizer = wx.BoxSizer(wx.VERTICAL)
598 598 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
599 599 option_sizer = wx.BoxSizer(wx.HORIZONTAL)
600 600 sizer.Add(option_sizer, 0)
601 601 option_sizer.AddMany([(10, 20),
602 602 (option_text, 0, wx.ALIGN_CENTER_VERTICAL),
603 603 (5, 5),
604 604 (self.completion_option, 0, wx.ALIGN_CENTER_VERTICAL),
605 605 (8, 8),
606 606 (self.background_option, 0, wx.ALIGN_CENTER_VERTICAL),
607 607 (8, 8),
608 608 (self.threading_option, 0, wx.ALIGN_CENTER_VERTICAL)
609 609 ])
610 610 self.SetAutoLayout(True)
611 611 sizer.Fit(self)
612 612 sizer.SetSizeHints(self)
613 613 self.SetSizer(sizer)
614 614 #and we focus on the widget :)
615 615 self.SetFocus()
616 616
617 617 #widget state management (for key handling different cases)
618 618 self.setCurrentState('IDLE')
619 619 self.pager_state = 'DONE'
620 620 self.raw_input_current_line = 0
621 621
622 622 def askExitCallback(self, event):
623 623 self.askExitHandler(event)
624 624
625 625 #---------------------- IPython Thread Management ------------------------
626 626 def stateDoExecuteLine(self):
627 627 lines=self.text_ctrl.getCurrentLine()
628 628 self.text_ctrl.write('\n')
629 629 lines_to_execute = lines.replace('\t',' '*4)
630 630 lines_to_execute = lines_to_execute.replace('\r','')
631 self.IP.doExecute(lines_to_execute.encode(ENCODING))
631 self.IP.do_execute(lines_to_execute.encode(ENCODING))
632 632 self.updateHistoryTracker(lines)
633 if(self.text_ctrl.getCursorPos()!=0):
634 self.text_ctrl.removeCurrentLine()
635 633 self.setCurrentState('WAIT_END_OF_EXECUTION')
636 634
637 635 def evtStateExecuteDone(self,evt):
638 self.doc = self.IP.getDocText()
639 self.help = self.IP.getHelpText()
636 if(self.text_ctrl.getCursorPos()!=0):
637 self.text_ctrl.removeCurrentLine()
638 self.doc = self.IP.get_doc_text()
639 self.help = self.IP.get_help_text()
640 640 if self.doc:
641 641 self.pager_lines = self.doc[7:].split('\n')
642 642 self.pager_state = 'INIT'
643 643 self.setCurrentState('SHOW_DOC')
644 644 self.pager(self.doc)
645 645 elif self.help:
646 646 self.pager_lines = self.help.split('\n')
647 647 self.pager_state = 'INIT'
648 648 self.setCurrentState('SHOW_DOC')
649 649 self.pager(self.help)
650 650 else:
651 651 self.stateShowPrompt()
652 652
653 653 def stateShowPrompt(self):
654 654 self.setCurrentState('SHOW_PROMPT')
655 self.text_ctrl.setPrompt(self.IP.getPrompt())
656 self.text_ctrl.setIndentation(self.IP.getIndentation())
657 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
655 self.text_ctrl.setPrompt(self.IP.get_prompt())
656 self.text_ctrl.setIndentation(self.IP.get_indentation())
657 self.text_ctrl.setPromptCount(self.IP.get_prompt_count())
658 658 self.text_ctrl.showPrompt()
659 self.IP.initHistoryIndex()
659 self.IP.init_history_index()
660 660 self.setCurrentState('IDLE')
661 661
662 662 def setCurrentState(self, state):
663 663 self.cur_state = state
664 664 self.updateStatusTracker(self.cur_state)
665 665
666 666 def pager(self,text):
667 667
668 668 if self.pager_state == 'INIT':
669 669 #print >>sys.__stdout__,"PAGER state:",self.pager_state
670 670 self.pager_nb_lines = len(self.pager_lines)
671 671 self.pager_index = 0
672 672 self.pager_do_remove = False
673 673 self.text_ctrl.write('\n')
674 674 self.pager_state = 'PROCESS_LINES'
675 675
676 676 if self.pager_state == 'PROCESS_LINES':
677 677 #print >>sys.__stdout__,"PAGER state:",self.pager_state
678 678 if self.pager_do_remove == True:
679 679 self.text_ctrl.removeCurrentLine()
680 680 self.pager_do_remove = False
681 681
682 682 if self.pager_nb_lines > 10:
683 683 #print >>sys.__stdout__,"PAGER processing 10 lines"
684 684 if self.pager_index > 0:
685 685 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
686 686 else:
687 687 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
688 688
689 689 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
690 690 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
691 691 self.pager_index += 10
692 692 self.pager_nb_lines -= 10
693 693 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
694 694 self.pager_do_remove = True
695 695 self.pager_state = 'WAITING'
696 696 return
697 697 else:
698 698 #print >>sys.__stdout__,"PAGER processing last lines"
699 699 if self.pager_nb_lines > 0:
700 700 if self.pager_index > 0:
701 701 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
702 702 else:
703 703 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
704 704
705 705 self.pager_index += 1
706 706 self.pager_nb_lines -= 1
707 707 if self.pager_nb_lines > 0:
708 708 for line in self.pager_lines[self.pager_index:]:
709 709 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
710 710 self.pager_nb_lines = 0
711 711 self.pager_state = 'DONE'
712 712 self.stateShowPrompt()
713 713
714 714 #------------------------ Key Handler ------------------------------------
715 715 def keyPress(self, event):
716 716 '''
717 717 Key press callback with plenty of shell goodness, like history,
718 718 autocompletions, etc.
719 719 '''
720 720 if event.GetKeyCode() == ord('C'):
721 721 if event.Modifiers == wx.MOD_CONTROL or event.Modifiers == wx.MOD_ALT:
722 722 if self.cur_state == 'WAIT_END_OF_EXECUTION':
723 723 #we raise an exception inside the IPython thread container
724 724 self.IP.ce.raise_exc(KeyboardInterrupt)
725 725 return
726 726
727 727 #let this before 'wx.WXK_RETURN' because we have to put 'IDLE'
728 728 #mode if AutoComp has been set as inactive
729 729 if self.cur_state == 'COMPLETING':
730 730 if not self.text_ctrl.AutoCompActive():
731 731 self.cur_state = 'IDLE'
732 732 else:
733 733 event.Skip()
734 734
735 735 if event.KeyCode == wx.WXK_RETURN:
736 736 if self.cur_state == 'IDLE':
737 737 #we change the state ot the state machine
738 738 self.setCurrentState('DO_EXECUTE_LINE')
739 739 self.stateDoExecuteLine()
740 740 return
741 741
742 742 if self.pager_state == 'WAITING':
743 743 self.pager_state = 'PROCESS_LINES'
744 744 self.pager(self.doc)
745 745 return
746 746
747 747 if self.cur_state == 'WAITING_USER_INPUT':
748 748 line=self.text_ctrl.getCurrentLine()
749 749 self.text_ctrl.write('\n')
750 750 self.setCurrentState('WAIT_END_OF_EXECUTION')
751 751 return
752 752
753 753 if event.GetKeyCode() in [ord('q'),ord('Q')]:
754 754 if self.pager_state == 'WAITING':
755 755 self.pager_state = 'DONE'
756 756 self.text_ctrl.write('\n')
757 757 self.stateShowPrompt()
758 758 return
759 759
760 760 if self.cur_state == 'WAITING_USER_INPUT':
761 761 event.Skip()
762 762
763 763 if self.cur_state == 'IDLE':
764 764 if event.KeyCode == wx.WXK_UP:
765 history = self.IP.historyBack()
765 history = self.IP.history_back()
766 766 self.text_ctrl.writeHistory(history)
767 767 return
768 768 if event.KeyCode == wx.WXK_DOWN:
769 history = self.IP.historyForward()
769 history = self.IP.history_forward()
770 770 self.text_ctrl.writeHistory(history)
771 771 return
772 772 if event.KeyCode == wx.WXK_TAB:
773 773 #if line empty we disable tab completion
774 774 if not self.text_ctrl.getCurrentLine().strip():
775 775 self.text_ctrl.write('\t')
776 776 return
777 777 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
778 778 if len(possibilities) > 1:
779 779 if self.text_ctrl.autocomplete_mode == 'IPYTHON':
780 780 cur_slice = self.text_ctrl.getCurrentLine()
781 781 self.text_ctrl.write('\n')
782 782 self.text_ctrl.writeCompletion(possibilities)
783 783 self.text_ctrl.write('\n')
784 784
785 785 self.text_ctrl.showPrompt()
786 786 self.text_ctrl.write(cur_slice)
787 787 self.text_ctrl.changeLine(completed or cur_slice)
788 788 else:
789 789 self.cur_state = 'COMPLETING'
790 790 self.text_ctrl.writeCompletion(possibilities)
791 791 else:
792 792 self.text_ctrl.changeLine(completed or cur_slice)
793 793 return
794 794 event.Skip()
795 795
796 796 #------------------------ Option Section ---------------------------------
797 797 def evtCheckOptionCompletion(self, event):
798 798 if event.IsChecked():
799 799 self.options['completion']['value']='STC'
800 800 else:
801 801 self.options['completion']['value']='IPYTHON'
802 802 self.text_ctrl.setCompletionMethod(self.options['completion']['value'])
803 803 self.updateOptionTracker('completion',
804 804 self.options['completion']['value'])
805 805 self.text_ctrl.SetFocus()
806 806
807 807 def evtCheckOptionBackgroundColor(self, event):
808 808 if event.IsChecked():
809 809 self.options['background_color']['value']='WHITE'
810 810 else:
811 811 self.options['background_color']['value']='BLACK'
812 812 self.text_ctrl.setBackgroundColor(self.options['background_color']['value'])
813 813 self.updateOptionTracker('background_color',
814 814 self.options['background_color']['value'])
815 815 self.text_ctrl.SetFocus()
816 816
817 817 def evtCheckOptionThreading(self, event):
818 818 if event.IsChecked():
819 819 self.options['threading']['value']='True'
820 self.IP.setThreading(True)
820 self.IP.set_threading(True)
821 821 self.cout.write = self.text_ctrl.asyncWrite
822 822 else:
823 823 self.options['threading']['value']='False'
824 self.IP.setThreading(False)
824 self.IP.set_threading(False)
825 825 self.cout.write = self.text_ctrl.write
826 826 self.updateOptionTracker('threading',
827 827 self.options['threading']['value'])
828 828 self.text_ctrl.SetFocus()
829 829
830 830 def getOptions(self):
831 831 return self.options
832 832
833 833 def reloadOptions(self,options):
834 834 self.options = options
835 835 for key in self.options.keys():
836 836 value = self.options[key]['value']
837 837 self.options[key]['checkbox'].SetValue(self.options[key][value])
838 838 self.options[key]['setfunc'](value)
839 839
840 840 if self.options['threading']['value']=='True':
841 self.IP.setThreading(True)
841 self.IP.set_threading(True)
842 842 self.cout.write = self.text_ctrl.asyncWrite
843 843 else:
844 self.IP.setThreading(False)
844 self.IP.set_threading(False)
845 845 self.cout.write = self.text_ctrl.write
846 846
847 847 #------------------------ Hook Section -----------------------------------
848 848 def updateOptionTracker(self,name,value):
849 849 '''
850 850 Default history tracker (does nothing)
851 851 '''
852 852 pass
853 853
854 854 def setOptionTrackerHook(self,func):
855 855 '''
856 856 Define a new history tracker
857 857 '''
858 858 self.updateOptionTracker = func
859 859
860 860 def updateHistoryTracker(self,command_line):
861 861 '''
862 862 Default history tracker (does nothing)
863 863 '''
864 864 pass
865 865
866 866 def setHistoryTrackerHook(self,func):
867 867 '''
868 868 Define a new history tracker
869 869 '''
870 870 self.updateHistoryTracker = func
871 871
872 872 def updateStatusTracker(self,status):
873 873 '''
874 874 Default status tracker (does nothing)
875 875 '''
876 876 pass
877 877
878 878 def setStatusTrackerHook(self,func):
879 879 '''
880 880 Define a new status tracker
881 881 '''
882 882 self.updateStatusTracker = func
883 883
884 884 def askExitHandler(self, event):
885 885 '''
886 886 Default exit handler
887 887 '''
888 888 self.text_ctrl.write('\nExit callback has not been set.')
889 889
890 890 def setAskExitHandler(self, func):
891 891 '''
892 892 Define an exit handler
893 893 '''
894 894 self.askExitHandler = func
895 895
896 896 if __name__ == '__main__':
897 897 # Some simple code to test the shell widget.
898 898 class MainWindow(wx.Frame):
899 899 def __init__(self, parent, id, title):
900 900 wx.Frame.__init__(self, parent, id, title, size=(300,250))
901 901 self._sizer = wx.BoxSizer(wx.VERTICAL)
902 902 self.shell = IPShellWidget(self)
903 903 self._sizer.Add(self.shell, 1, wx.EXPAND)
904 904 self.SetSizer(self._sizer)
905 905 self.SetAutoLayout(1)
906 906 self.Show(True)
907 907
908 908 app = wx.PySimpleApp()
909 909 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
910 910 frame.SetSize((780, 460))
911 911 shell = frame.shell
912 912
913 913 app.MainLoop()
914 914
915 915
General Comments 0
You need to be logged in to leave comments. Login now