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