##// END OF EJS Templates
Fix: output prompt is now displayed correclty.
laurent dufrechou -
Show More
@@ -1,532 +1,533 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._afterExecute()
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.initIpython0(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 initIpython0(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 187 def bypassMagic(self, arg):
188 188 print '%this magic is currently disabled.'
189 189 ip.expose_magic('cpaste', bypassMagic)
190 190
191 191 def resetMagic(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 209 ip.expose_magic('reset', resetMagic)
210 210
211 211 sys.excepthook = excepthook
212 212
213 213 #----------------------- Thread management section ----------------------
214 214 def doExecute(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 232 self._afterExecute()
233 233
234 234 except KeyboardInterrupt:
235 235 pass
236 236
237 237 #----------------------- IPython management section ----------------------
238 238 def getThreading(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 251 def setThreading(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 264 def getDocText(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 273 def getHelpText(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 282 def getBanner(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 291 def getPromptCount(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 301 def getPrompt(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 311 def getIndentation(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 321 def updateNamespace(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 345 def _commonPrefix(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 361 common_prefix = reduce(_commonPrefix, 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 367 def historyBack(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 379 history = self._getHistory()
380 380 return history
381 381
382 382 def historyForward(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 392 and self._history_level <= self._getHistoryMaxIndex()):
393 393 if self._history_level < self._getHistoryMaxIndex():
394 394 self._history_level += 1
395 395 history = self._getHistory()
396 396 else:
397 397 if self._history_level == self._getHistoryMaxIndex():
398 398 history = self._getHistory()
399 399 self._history_level += 1
400 400 else:
401 401 history = ''
402 402 return history
403 403
404 404 def initHistoryIndex(self):
405 405 '''
406 406 set history to last command entered
407 407 '''
408 408 self._history_level = self._getHistoryMaxIndex()+1
409 409
410 410 #----------------------- IPython PRIVATE management section --------------
411 411 def _afterExecute(self):
412 412 '''
413 413 Can be redefined to generate post event after excution is done
414 414 '''
415 415 pass
416 416
417 417 #def _askExit(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 423 def _getHistoryMaxIndex(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 432 def _getHistory(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 447 getHelpText 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 459 getDocText 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 self._IP.write(str(self._IP.outputcache.prompt_out).strip())
502 503 self._iter_more = self._IP.push(line)
503 504 if (self._IP.SyntaxTB.last_syntax_error and self._IP.rc.autoedit_syntax):
504 505 self._IP.edit_syntax_error()
505 506 if self._iter_more:
506 507 self._prompt = str(self._IP.outputcache.prompt2).strip()
507 508 if self._IP.autoindent:
508 509 self._IP.readline_startup_hook(self._IP.pre_readline)
509 510 else:
510 511 self._prompt = str(self._IP.outputcache.prompt1).strip()
511 512 self._IP.indent_current_nsp = 0 #we set indentation to 0
512 513
513 514 sys.stdout = orig_stdout
514 515 #sys.displayhook = self.sys_displayhook_ori
515 516
516 517 def _shell(self, ip, cmd):
517 518 '''
518 519 Replacement method to allow shell commands without them blocking.
519 520
520 521 @param ip: Ipython instance, same as self._IP
521 522 @type cmd: Ipython instance
522 523 @param cmd: Shell command to execute.
523 524 @type cmd: string
524 525 '''
525 526 stdin, stdout = os.popen4(cmd)
526 527 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
527 528 #we use print command because the shell command is called
528 529 #inside IPython instance and thus is redirected to thread cout
529 530 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
530 531 print "\x01\x1b[1;36m\x02"+result
531 532 stdout.close()
532 533 stdin.close()
@@ -1,913 +1,915 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._askExit
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 73 def _askExit(self):
74 74 wx.CallAfter(self.ask_exit_callback, ())
75 75
76 76 def _afterExecute(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 550 welcome_text+= self.IP.getBanner()
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 557 self.IP.getPrompt(),
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 583 'setfunc':self.IP.setThreading},
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 631 self.IP.doExecute(lines_to_execute.encode(ENCODING))
632 632 self.updateHistoryTracker(lines)
633 if(self.text_ctrl.getCursorPos()!=0):
634 self.text_ctrl.removeCurrentLine()
633 635 self.setCurrentState('WAIT_END_OF_EXECUTION')
634 636
635 637 def evtStateExecuteDone(self,evt):
636 638 self.doc = self.IP.getDocText()
637 639 self.help = self.IP.getHelpText()
638 640 if self.doc:
639 641 self.pager_lines = self.doc[7:].split('\n')
640 642 self.pager_state = 'INIT'
641 643 self.setCurrentState('SHOW_DOC')
642 644 self.pager(self.doc)
643 645 elif self.help:
644 646 self.pager_lines = self.help.split('\n')
645 647 self.pager_state = 'INIT'
646 648 self.setCurrentState('SHOW_DOC')
647 649 self.pager(self.help)
648 650 else:
649 651 self.stateShowPrompt()
650 652
651 653 def stateShowPrompt(self):
652 654 self.setCurrentState('SHOW_PROMPT')
653 655 self.text_ctrl.setPrompt(self.IP.getPrompt())
654 656 self.text_ctrl.setIndentation(self.IP.getIndentation())
655 657 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
656 658 self.text_ctrl.showPrompt()
657 659 self.IP.initHistoryIndex()
658 660 self.setCurrentState('IDLE')
659 661
660 662 def setCurrentState(self, state):
661 663 self.cur_state = state
662 664 self.updateStatusTracker(self.cur_state)
663 665
664 666 def pager(self,text):
665 667
666 668 if self.pager_state == 'INIT':
667 669 #print >>sys.__stdout__,"PAGER state:",self.pager_state
668 670 self.pager_nb_lines = len(self.pager_lines)
669 671 self.pager_index = 0
670 672 self.pager_do_remove = False
671 673 self.text_ctrl.write('\n')
672 674 self.pager_state = 'PROCESS_LINES'
673 675
674 676 if self.pager_state == 'PROCESS_LINES':
675 677 #print >>sys.__stdout__,"PAGER state:",self.pager_state
676 678 if self.pager_do_remove == True:
677 679 self.text_ctrl.removeCurrentLine()
678 680 self.pager_do_remove = False
679 681
680 682 if self.pager_nb_lines > 10:
681 683 #print >>sys.__stdout__,"PAGER processing 10 lines"
682 684 if self.pager_index > 0:
683 685 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
684 686 else:
685 687 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
686 688
687 689 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
688 690 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
689 691 self.pager_index += 10
690 692 self.pager_nb_lines -= 10
691 693 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
692 694 self.pager_do_remove = True
693 695 self.pager_state = 'WAITING'
694 696 return
695 697 else:
696 698 #print >>sys.__stdout__,"PAGER processing last lines"
697 699 if self.pager_nb_lines > 0:
698 700 if self.pager_index > 0:
699 701 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
700 702 else:
701 703 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
702 704
703 705 self.pager_index += 1
704 706 self.pager_nb_lines -= 1
705 707 if self.pager_nb_lines > 0:
706 708 for line in self.pager_lines[self.pager_index:]:
707 709 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
708 710 self.pager_nb_lines = 0
709 711 self.pager_state = 'DONE'
710 712 self.stateShowPrompt()
711 713
712 714 #------------------------ Key Handler ------------------------------------
713 715 def keyPress(self, event):
714 716 '''
715 717 Key press callback with plenty of shell goodness, like history,
716 718 autocompletions, etc.
717 719 '''
718 720 if event.GetKeyCode() == ord('C'):
719 721 if event.Modifiers == wx.MOD_CONTROL or event.Modifiers == wx.MOD_ALT:
720 722 if self.cur_state == 'WAIT_END_OF_EXECUTION':
721 723 #we raise an exception inside the IPython thread container
722 724 self.IP.ce.raise_exc(KeyboardInterrupt)
723 725 return
724 726
725 727 #let this before 'wx.WXK_RETURN' because we have to put 'IDLE'
726 728 #mode if AutoComp has been set as inactive
727 729 if self.cur_state == 'COMPLETING':
728 730 if not self.text_ctrl.AutoCompActive():
729 731 self.cur_state = 'IDLE'
730 732 else:
731 733 event.Skip()
732 734
733 735 if event.KeyCode == wx.WXK_RETURN:
734 736 if self.cur_state == 'IDLE':
735 737 #we change the state ot the state machine
736 738 self.setCurrentState('DO_EXECUTE_LINE')
737 739 self.stateDoExecuteLine()
738 740 return
739 741
740 742 if self.pager_state == 'WAITING':
741 743 self.pager_state = 'PROCESS_LINES'
742 744 self.pager(self.doc)
743 745 return
744 746
745 747 if self.cur_state == 'WAITING_USER_INPUT':
746 748 line=self.text_ctrl.getCurrentLine()
747 749 self.text_ctrl.write('\n')
748 750 self.setCurrentState('WAIT_END_OF_EXECUTION')
749 751 return
750 752
751 753 if event.GetKeyCode() in [ord('q'),ord('Q')]:
752 754 if self.pager_state == 'WAITING':
753 755 self.pager_state = 'DONE'
754 756 self.text_ctrl.write('\n')
755 757 self.stateShowPrompt()
756 758 return
757 759
758 760 if self.cur_state == 'WAITING_USER_INPUT':
759 761 event.Skip()
760 762
761 763 if self.cur_state == 'IDLE':
762 764 if event.KeyCode == wx.WXK_UP:
763 765 history = self.IP.historyBack()
764 766 self.text_ctrl.writeHistory(history)
765 767 return
766 768 if event.KeyCode == wx.WXK_DOWN:
767 769 history = self.IP.historyForward()
768 770 self.text_ctrl.writeHistory(history)
769 771 return
770 772 if event.KeyCode == wx.WXK_TAB:
771 773 #if line empty we disable tab completion
772 774 if not self.text_ctrl.getCurrentLine().strip():
773 775 self.text_ctrl.write('\t')
774 776 return
775 777 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
776 778 if len(possibilities) > 1:
777 779 if self.text_ctrl.autocomplete_mode == 'IPYTHON':
778 780 cur_slice = self.text_ctrl.getCurrentLine()
779 781 self.text_ctrl.write('\n')
780 782 self.text_ctrl.writeCompletion(possibilities)
781 783 self.text_ctrl.write('\n')
782 784
783 785 self.text_ctrl.showPrompt()
784 786 self.text_ctrl.write(cur_slice)
785 787 self.text_ctrl.changeLine(completed or cur_slice)
786 788 else:
787 789 self.cur_state = 'COMPLETING'
788 790 self.text_ctrl.writeCompletion(possibilities)
789 791 else:
790 792 self.text_ctrl.changeLine(completed or cur_slice)
791 793 return
792 794 event.Skip()
793 795
794 796 #------------------------ Option Section ---------------------------------
795 797 def evtCheckOptionCompletion(self, event):
796 798 if event.IsChecked():
797 799 self.options['completion']['value']='STC'
798 800 else:
799 801 self.options['completion']['value']='IPYTHON'
800 802 self.text_ctrl.setCompletionMethod(self.options['completion']['value'])
801 803 self.updateOptionTracker('completion',
802 804 self.options['completion']['value'])
803 805 self.text_ctrl.SetFocus()
804 806
805 807 def evtCheckOptionBackgroundColor(self, event):
806 808 if event.IsChecked():
807 809 self.options['background_color']['value']='WHITE'
808 810 else:
809 811 self.options['background_color']['value']='BLACK'
810 812 self.text_ctrl.setBackgroundColor(self.options['background_color']['value'])
811 813 self.updateOptionTracker('background_color',
812 814 self.options['background_color']['value'])
813 815 self.text_ctrl.SetFocus()
814 816
815 817 def evtCheckOptionThreading(self, event):
816 818 if event.IsChecked():
817 819 self.options['threading']['value']='True'
818 820 self.IP.setThreading(True)
819 821 self.cout.write = self.text_ctrl.asyncWrite
820 822 else:
821 823 self.options['threading']['value']='False'
822 824 self.IP.setThreading(False)
823 825 self.cout.write = self.text_ctrl.write
824 826 self.updateOptionTracker('threading',
825 827 self.options['threading']['value'])
826 828 self.text_ctrl.SetFocus()
827 829
828 830 def getOptions(self):
829 831 return self.options
830 832
831 833 def reloadOptions(self,options):
832 834 self.options = options
833 835 for key in self.options.keys():
834 836 value = self.options[key]['value']
835 837 self.options[key]['checkbox'].SetValue(self.options[key][value])
836 838 self.options[key]['setfunc'](value)
837 839
838 840 if self.options['threading']['value']=='True':
839 841 self.IP.setThreading(True)
840 842 self.cout.write = self.text_ctrl.asyncWrite
841 843 else:
842 844 self.IP.setThreading(False)
843 845 self.cout.write = self.text_ctrl.write
844 846
845 847 #------------------------ Hook Section -----------------------------------
846 848 def updateOptionTracker(self,name,value):
847 849 '''
848 850 Default history tracker (does nothing)
849 851 '''
850 852 pass
851 853
852 854 def setOptionTrackerHook(self,func):
853 855 '''
854 856 Define a new history tracker
855 857 '''
856 858 self.updateOptionTracker = func
857 859
858 860 def updateHistoryTracker(self,command_line):
859 861 '''
860 862 Default history tracker (does nothing)
861 863 '''
862 864 pass
863 865
864 866 def setHistoryTrackerHook(self,func):
865 867 '''
866 868 Define a new history tracker
867 869 '''
868 870 self.updateHistoryTracker = func
869 871
870 872 def updateStatusTracker(self,status):
871 873 '''
872 874 Default status tracker (does nothing)
873 875 '''
874 876 pass
875 877
876 878 def setStatusTrackerHook(self,func):
877 879 '''
878 880 Define a new status tracker
879 881 '''
880 882 self.updateStatusTracker = func
881 883
882 884 def askExitHandler(self, event):
883 885 '''
884 886 Default exit handler
885 887 '''
886 888 self.text_ctrl.write('\nExit callback has not been set.')
887 889
888 890 def setAskExitHandler(self, func):
889 891 '''
890 892 Define an exit handler
891 893 '''
892 894 self.askExitHandler = func
893 895
894 896 if __name__ == '__main__':
895 897 # Some simple code to test the shell widget.
896 898 class MainWindow(wx.Frame):
897 899 def __init__(self, parent, id, title):
898 900 wx.Frame.__init__(self, parent, id, title, size=(300,250))
899 901 self._sizer = wx.BoxSizer(wx.VERTICAL)
900 902 self.shell = IPShellWidget(self)
901 903 self._sizer.Add(self.shell, 1, wx.EXPAND)
902 904 self.SetSizer(self._sizer)
903 905 self.SetAutoLayout(1)
904 906 self.Show(True)
905 907
906 908 app = wx.PySimpleApp()
907 909 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
908 910 frame.SetSize((780, 460))
909 911 shell = frame.shell
910 912
911 913 app.MainLoop()
912 914
913 915
General Comments 0
You need to be logged in to leave comments. Login now