##// END OF EJS Templates
Cleanup of the old stateMachine code...
ldufrechou -
Show More
@@ -1,472 +1,454 b''
1 #!/usr/bin/python
1 #!/usr/bin/python
2 # -*- coding: iso-8859-15 -*-
2 # -*- coding: iso-8859-15 -*-
3 '''
3 '''
4 Provides IPython remote instance.
4 Provides IPython remote instance.
5
5
6 @author: Laurent Dufrechou
6 @author: Laurent Dufrechou
7 laurent.dufrechou _at_ gmail.com
7 laurent.dufrechou _at_ gmail.com
8 @license: BSD
8 @license: BSD
9
9
10 All rights reserved. This program and the accompanying materials are made
10 All rights reserved. This program and the accompanying materials are made
11 available under the terms of the BSD which accompanies this distribution, and
11 available under the terms of the BSD which accompanies this distribution, and
12 is available at U{http://www.opensource.org/licenses/bsd-license.php}
12 is available at U{http://www.opensource.org/licenses/bsd-license.php}
13 '''
13 '''
14
14
15 __version__ = 0.9
15 __version__ = 0.9
16 __author__ = "Laurent Dufrechou"
16 __author__ = "Laurent Dufrechou"
17 __email__ = "laurent.dufrechou _at_ gmail.com"
17 __email__ = "laurent.dufrechou _at_ gmail.com"
18 __license__ = "BSD"
18 __license__ = "BSD"
19
19
20 import re
20 import re
21 import sys
21 import sys
22 import os
22 import os
23 import locale
23 import locale
24 import time
24 import time
25 import pydoc,__builtin__,site
25 import pydoc,__builtin__,site
26 from thread_ex import ThreadEx
26 from thread_ex import ThreadEx
27 from StringIO import StringIO
27 from StringIO import StringIO
28
28
29 try:
29 try:
30 import IPython
30 import IPython
31 except Exception,e:
31 except Exception,e:
32 raise "Error importing IPython (%s)" % str(e)
32 raise "Error importing IPython (%s)" % str(e)
33
33
34 ##############################################################################
34 ##############################################################################
35 class _Helper(object):
35 class _Helper(object):
36 """Redefine the built-in 'help'.
36 """Redefine the built-in 'help'.
37 This is a wrapper around pydoc.help (with a twist).
37 This is a wrapper around pydoc.help (with a twist).
38 """
38 """
39
39
40 def __init__(self,pager):
40 def __init__(self,pager):
41 self._pager = pager
41 self._pager = pager
42
42
43 def __repr__(self):
43 def __repr__(self):
44 return "Type help() for interactive help, " \
44 return "Type help() for interactive help, " \
45 "or help(object) for help about object."
45 "or help(object) for help about object."
46
46
47 def __call__(self, *args, **kwds):
47 def __call__(self, *args, **kwds):
48 class DummyWriter(object):
48 class DummyWriter(object):
49 def __init__(self,pager):
49 def __init__(self,pager):
50 self._pager = pager
50 self._pager = pager
51
51
52 def write(self,data):
52 def write(self,data):
53 self._pager(data)
53 self._pager(data)
54
54
55 import pydoc
55 import pydoc
56 pydoc.help.output = DummyWriter(self._pager)
56 pydoc.help.output = DummyWriter(self._pager)
57 pydoc.help.interact = lambda :1
57 pydoc.help.interact = lambda :1
58
58
59 return pydoc.help(*args, **kwds)
59 return pydoc.help(*args, **kwds)
60
60
61
61
62 ##############################################################################
62 ##############################################################################
63 class _CodeExecutor(ThreadEx):
63 class _CodeExecutor(ThreadEx):
64
64
65 def __init__(self, instance, after):
65 def __init__(self, instance, after):
66 ThreadEx.__init__(self)
66 ThreadEx.__init__(self)
67 self.instance = instance
67 self.instance = instance
68 self._afterExecute=after
68 self._afterExecute=after
69
69
70 def run(self):
70 def run(self):
71 try:
71 try:
72 self.instance._doc_text = None
72 self.instance._doc_text = None
73 self.instance._help_text = None
73 self.instance._help_text = None
74 self.instance._execute()
74 self.instance._execute()
75 # used for uper class to generate event after execution
75 # used for uper class to generate event after execution
76 self._afterExecute()
76 self._afterExecute()
77
77
78 except KeyboardInterrupt:
78 except KeyboardInterrupt:
79 pass
79 pass
80
80
81
81
82 ##############################################################################
82 ##############################################################################
83 class NonBlockingIPShell(object):
83 class NonBlockingIPShell(object):
84 '''
84 '''
85 Create an IPython instance, running the commands in a separate,
85 Create an IPython instance, running the commands in a separate,
86 non-blocking thread.
86 non-blocking thread.
87 This allows embedding in any GUI without blockage.
87 This allows embedding in any GUI without blockage.
88
88
89 Note: The ThreadEx class supports asynchroneous function call
89 Note: The ThreadEx class supports asynchroneous function call
90 via raise_exc()
90 via raise_exc()
91 '''
91 '''
92
92
93 def __init__(self,argv
93 def __init__(self,argv
94 =[],user_ns={},user_global_ns=None,
94 =[],user_ns={},user_global_ns=None,
95 cin=None, cout=None, cerr=None,
95 cin=None, cout=None, cerr=None,
96 ask_exit_handler=None):
96 ask_exit_handler=None):
97 '''
97 '''
98 @param argv: Command line options for IPython
98 @param argv: Command line options for IPython
99 @type argv: list
99 @type argv: list
100 @param user_ns: User namespace.
100 @param user_ns: User namespace.
101 @type user_ns: dictionary
101 @type user_ns: dictionary
102 @param user_global_ns: User global namespace.
102 @param user_global_ns: User global namespace.
103 @type user_global_ns: dictionary.
103 @type user_global_ns: dictionary.
104 @param cin: Console standard input.
104 @param cin: Console standard input.
105 @type cin: IO stream
105 @type cin: IO stream
106 @param cout: Console standard output.
106 @param cout: Console standard output.
107 @type cout: IO stream
107 @type cout: IO stream
108 @param cerr: Console standard error.
108 @param cerr: Console standard error.
109 @type cerr: IO stream
109 @type cerr: IO stream
110 @param exit_handler: Replacement for builtin exit() function
110 @param exit_handler: Replacement for builtin exit() function
111 @type exit_handler: function
111 @type exit_handler: function
112 @param time_loop: Define the sleep time between two thread's loop
112 @param time_loop: Define the sleep time between two thread's loop
113 @type int
113 @type int
114 '''
114 '''
115 #ipython0 initialisation
116 self.initIpython0(argv, user_ns, user_global_ns,
117 cin, cout, cerr,
118 ask_exit_handler)
119
120 #vars used by _execute
121 self._iter_more = 0
122 self._history_level = 0
123 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
124 self._prompt = str(self._IP.outputcache.prompt1).strip()
125
126 #thread working vars
127 self._line_to_execute = ''
128
129 #vars that will be checked by GUI loop to handle thread states...
130 #will be replaced later by PostEvent GUI funtions...
131 self._doc_text = None
132 self._help_text = None
133 self._add_button = None
134
135 def initIpython0(self, argv=[], user_ns={}, user_global_ns=None,
136 cin=None, cout=None, cerr=None,
137 ask_exit_handler=None):
115 #first we redefine in/out/error functions of IPython
138 #first we redefine in/out/error functions of IPython
116 if cin:
139 if cin:
117 IPython.Shell.Term.cin = cin
140 IPython.Shell.Term.cin = cin
118 if cout:
141 if cout:
119 IPython.Shell.Term.cout = cout
142 IPython.Shell.Term.cout = cout
120 if cerr:
143 if cerr:
121 IPython.Shell.Term.cerr = cerr
144 IPython.Shell.Term.cerr = cerr
122
145
123 # This is to get rid of the blockage that accurs during
146 # This is to get rid of the blockage that accurs during
124 # IPython.Shell.InteractiveShell.user_setup()
147 # IPython.Shell.InteractiveShell.user_setup()
125 IPython.iplib.raw_input = lambda x: None
148 IPython.iplib.raw_input = lambda x: None
126
149
127 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
150 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
128
151
129 excepthook = sys.excepthook
152 excepthook = sys.excepthook
130
153
131 self._IP = IPython.Shell.make_IPython(
154 self._IP = IPython.Shell.make_IPython(
132 argv,user_ns=user_ns,
155 argv,user_ns=user_ns,
133 user_global_ns=user_global_ns,
156 user_global_ns=user_global_ns,
134 embedded=True,
157 embedded=True,
135 shell_class=IPython.Shell.InteractiveShell)
158 shell_class=IPython.Shell.InteractiveShell)
136
159
137 #we replace IPython default encoding by wx locale encoding
160 #we replace IPython default encoding by wx locale encoding
138 loc = locale.getpreferredencoding()
161 loc = locale.getpreferredencoding()
139 if loc:
162 if loc:
140 self._IP.stdin_encoding = loc
163 self._IP.stdin_encoding = loc
141 #we replace the ipython default pager by our pager
164 #we replace the ipython default pager by our pager
142 self._IP.set_hook('show_in_pager',self._pager)
165 self._IP.set_hook('show_in_pager',self._pager)
143
166
144 #we replace the ipython default shell command caller by our shell handler
167 #we replace the ipython default shell command caller by our shell handler
145 self._IP.set_hook('shell_hook',self._shell)
168 self._IP.set_hook('shell_hook',self._shell)
146
169
147 #we replace the ipython default input command caller by our method
170 #we replace the ipython default input command caller by our method
148 IPython.iplib.raw_input_original = self._raw_input
171 IPython.iplib.raw_input_original = self._raw_input
149 #we replace the ipython default exit command by our method
172 #we replace the ipython default exit command by our method
150 self._IP.exit = self._setAskExit
173 self._IP.exit = ask_exit_handler
151 #we modify Exit and Quit Magic
152 ip = IPython.ipapi.get()
153 ip.expose_magic('Exit', self._setDoExit)
154 ip.expose_magic('Quit', self._setDoExit)
155 #we replace the help command
174 #we replace the help command
156 self._IP.user_ns['help'] = _Helper(self._pager_help)
175 self._IP.user_ns['help'] = _Helper(self._pager_help)
157
176
158 sys.excepthook = excepthook
177 sys.excepthook = excepthook
159
178
160 #vars used by _execute
161 self._iter_more = 0
162 self._history_level = 0
163 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
164 self._prompt = str(self._IP.outputcache.prompt1).strip()
165
166 #thread working vars
167 self._line_to_execute = ''
168
169 #vars that will be checked by GUI loop to handle thread states...
170 #will be replaced later by PostEvent GUI funtions...
171 self._doc_text = None
172 self._help_text = None
173 self._ask_exit = False
174 self._add_button = None
175
176 #----------------------- Thread management section ----------------------
179 #----------------------- Thread management section ----------------------
177 def doExecute(self,line):
180 def doExecute(self,line):
178 """
181 """
179 Tell the thread to process the 'line' command
182 Tell the thread to process the 'line' command
180 """
183 """
181
184
182 self._line_to_execute = line
185 self._line_to_execute = line
183 #we launch the ipython line execution in a thread to make it interruptible
186 #we launch the ipython line execution in a thread to make it interruptible
184 self.ce = _CodeExecutor(self,self._afterExecute)
187 self.ce = _CodeExecutor(self,self._afterExecute)
185 self.ce.start()
188 self.ce.start()
186
189
187 #----------------------- IPython management section ----------------------
190 #----------------------- IPython management section ----------------------
188 def getAskExit(self):
189 '''
190 returns the _ask_exit variable that can be checked by GUI to see if
191 IPython request an exit handling
192 '''
193 return self._ask_exit
194
195 def clearAskExit(self):
196 '''
197 clear the _ask_exit var when GUI as handled the request.
198 '''
199 self._ask_exit = False
200
201 def getDocText(self):
191 def getDocText(self):
202 """
192 """
203 Returns the output of the processing that need to be paged (if any)
193 Returns the output of the processing that need to be paged (if any)
204
194
205 @return: The std output string.
195 @return: The std output string.
206 @rtype: string
196 @rtype: string
207 """
197 """
208 return self._doc_text
198 return self._doc_text
209
199
210 def getHelpText(self):
200 def getHelpText(self):
211 """
201 """
212 Returns the output of the processing that need to be paged via help pager(if any)
202 Returns the output of the processing that need to be paged via help pager(if any)
213
203
214 @return: The std output string.
204 @return: The std output string.
215 @rtype: string
205 @rtype: string
216 """
206 """
217 return self._help_text
207 return self._help_text
218
208
219 def getBanner(self):
209 def getBanner(self):
220 """
210 """
221 Returns the IPython banner for useful info on IPython instance
211 Returns the IPython banner for useful info on IPython instance
222
212
223 @return: The banner string.
213 @return: The banner string.
224 @rtype: string
214 @rtype: string
225 """
215 """
226 return self._IP.BANNER
216 return self._IP.BANNER
227
217
228 def getPromptCount(self):
218 def getPromptCount(self):
229 """
219 """
230 Returns the prompt number.
220 Returns the prompt number.
231 Each time a user execute a line in the IPython shell the prompt count is increased
221 Each time a user execute a line in the IPython shell the prompt count is increased
232
222
233 @return: The prompt number
223 @return: The prompt number
234 @rtype: int
224 @rtype: int
235 """
225 """
236 return self._IP.outputcache.prompt_count
226 return self._IP.outputcache.prompt_count
237
227
238 def getPrompt(self):
228 def getPrompt(self):
239 """
229 """
240 Returns current prompt inside IPython instance
230 Returns current prompt inside IPython instance
241 (Can be In [...]: ot ...:)
231 (Can be In [...]: ot ...:)
242
232
243 @return: The current prompt.
233 @return: The current prompt.
244 @rtype: string
234 @rtype: string
245 """
235 """
246 return self._prompt
236 return self._prompt
247
237
248 def getIndentation(self):
238 def getIndentation(self):
249 """
239 """
250 Returns the current indentation level
240 Returns the current indentation level
251 Usefull to put the caret at the good start position if we want to do autoindentation.
241 Usefull to put the caret at the good start position if we want to do autoindentation.
252
242
253 @return: The indentation level.
243 @return: The indentation level.
254 @rtype: int
244 @rtype: int
255 """
245 """
256 return self._IP.indent_current_nsp
246 return self._IP.indent_current_nsp
257
247
258 def updateNamespace(self, ns_dict):
248 def updateNamespace(self, ns_dict):
259 '''
249 '''
260 Add the current dictionary to the shell namespace.
250 Add the current dictionary to the shell namespace.
261
251
262 @param ns_dict: A dictionary of symbol-values.
252 @param ns_dict: A dictionary of symbol-values.
263 @type ns_dict: dictionary
253 @type ns_dict: dictionary
264 '''
254 '''
265 self._IP.user_ns.update(ns_dict)
255 self._IP.user_ns.update(ns_dict)
266
256
267 def complete(self, line):
257 def complete(self, line):
268 '''
258 '''
269 Returns an auto completed line and/or posibilities for completion.
259 Returns an auto completed line and/or posibilities for completion.
270
260
271 @param line: Given line so far.
261 @param line: Given line so far.
272 @type line: string
262 @type line: string
273
263
274 @return: Line completed as for as possible,
264 @return: Line completed as for as possible,
275 and possible further completions.
265 and possible further completions.
276 @rtype: tuple
266 @rtype: tuple
277 '''
267 '''
278 split_line = self._complete_sep.split(line)
268 split_line = self._complete_sep.split(line)
279 possibilities = self._IP.complete(split_line[-1])
269 possibilities = self._IP.complete(split_line[-1])
280 if possibilities:
270 if possibilities:
281
271
282 def _commonPrefix(str1, str2):
272 def _commonPrefix(str1, str2):
283 '''
273 '''
284 Reduction function. returns common prefix of two given strings.
274 Reduction function. returns common prefix of two given strings.
285
275
286 @param str1: First string.
276 @param str1: First string.
287 @type str1: string
277 @type str1: string
288 @param str2: Second string
278 @param str2: Second string
289 @type str2: string
279 @type str2: string
290
280
291 @return: Common prefix to both strings.
281 @return: Common prefix to both strings.
292 @rtype: string
282 @rtype: string
293 '''
283 '''
294 for i in range(len(str1)):
284 for i in range(len(str1)):
295 if not str2.startswith(str1[:i+1]):
285 if not str2.startswith(str1[:i+1]):
296 return str1[:i]
286 return str1[:i]
297 return str1
287 return str1
298 common_prefix = reduce(_commonPrefix, possibilities)
288 common_prefix = reduce(_commonPrefix, possibilities)
299 completed = line[:-len(split_line[-1])]+common_prefix
289 completed = line[:-len(split_line[-1])]+common_prefix
300 else:
290 else:
301 completed = line
291 completed = line
302 return completed, possibilities
292 return completed, possibilities
303
293
304 def historyBack(self):
294 def historyBack(self):
305 '''
295 '''
306 Provides one history command back.
296 Provides one history command back.
307
297
308 @return: The command string.
298 @return: The command string.
309 @rtype: string
299 @rtype: string
310 '''
300 '''
311 history = ''
301 history = ''
312 #the below while loop is used to suppress empty history lines
302 #the below while loop is used to suppress empty history lines
313 while((history == '' or history == '\n') and self._history_level >0):
303 while((history == '' or history == '\n') and self._history_level >0):
314 if self._history_level>=1:
304 if self._history_level>=1:
315 self._history_level -= 1
305 self._history_level -= 1
316 history = self._getHistory()
306 history = self._getHistory()
317 return history
307 return history
318
308
319 def historyForward(self):
309 def historyForward(self):
320 '''
310 '''
321 Provides one history command forward.
311 Provides one history command forward.
322
312
323 @return: The command string.
313 @return: The command string.
324 @rtype: string
314 @rtype: string
325 '''
315 '''
326 history = ''
316 history = ''
327 #the below while loop is used to suppress empty history lines
317 #the below while loop is used to suppress empty history lines
328 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
318 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
329 if self._history_level < self._getHistoryMaxIndex():
319 if self._history_level < self._getHistoryMaxIndex():
330 self._history_level += 1
320 self._history_level += 1
331 history = self._getHistory()
321 history = self._getHistory()
332 else:
322 else:
333 if self._history_level == self._getHistoryMaxIndex():
323 if self._history_level == self._getHistoryMaxIndex():
334 history = self._getHistory()
324 history = self._getHistory()
335 self._history_level += 1
325 self._history_level += 1
336 else:
326 else:
337 history = ''
327 history = ''
338 return history
328 return history
339
329
340 def initHistoryIndex(self):
330 def initHistoryIndex(self):
341 '''
331 '''
342 set history to last command entered
332 set history to last command entered
343 '''
333 '''
344 self._history_level = self._getHistoryMaxIndex()+1
334 self._history_level = self._getHistoryMaxIndex()+1
345
335
346 #----------------------- IPython PRIVATE management section --------------
336 #----------------------- IPython PRIVATE management section --------------
347 def _afterExecute(self):
337 def _afterExecute(self):
348 '''
338 '''
349 Can be redefined to generate post event after excution is done
339 Can be redefined to generate post event after excution is done
350 '''
340 '''
351 pass
341 pass
352
342
353 def _setAskExit(self):
343 #def _askExit(self):
354 '''
344 # '''
355 set the _ask_exit variable that can be checked by GUI to see if
345 # Can be redefined to generate post event to exit the Ipython shell
356 IPython request an exit handling
346 # '''
357 '''
347 # pass
358 self._ask_exit = True
359
348
360 def _setDoExit(self, toto, arg):
361 '''
362 set the _do_exit variable that can be checked by GUI to see if
363 IPython do a direct exit of the app
364 '''
365 self._do_exit = True
366
367 def _getHistoryMaxIndex(self):
349 def _getHistoryMaxIndex(self):
368 '''
350 '''
369 returns the max length of the history buffer
351 returns the max length of the history buffer
370
352
371 @return: history length
353 @return: history length
372 @rtype: int
354 @rtype: int
373 '''
355 '''
374 return len(self._IP.input_hist_raw)-1
356 return len(self._IP.input_hist_raw)-1
375
357
376 def _getHistory(self):
358 def _getHistory(self):
377 '''
359 '''
378 Get's the command string of the current history level.
360 Get's the command string of the current history level.
379
361
380 @return: Historic command stri
362 @return: Historic command stri
381 @rtype: string
363 @rtype: string
382 '''
364 '''
383 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
365 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
384 return rv
366 return rv
385
367
386 def _pager_help(self,text):
368 def _pager_help(self,text):
387 '''
369 '''
388 This function is used as a callback replacment to IPython help pager function
370 This function is used as a callback replacment to IPython help pager function
389
371
390 It puts the 'text' value inside the self._help_text string that can be retrived via getHelpText
372 It puts the 'text' value inside the self._help_text string that can be retrived via getHelpText
391 function.
373 function.
392 '''
374 '''
393 if self._help_text == None:
375 if self._help_text == None:
394 self._help_text = text
376 self._help_text = text
395 else:
377 else:
396 self._help_text += text
378 self._help_text += text
397
379
398 def _pager(self,IP,text):
380 def _pager(self,IP,text):
399 '''
381 '''
400 This function is used as a callback replacment to IPython pager function
382 This function is used as a callback replacment to IPython pager function
401
383
402 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
384 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
403 function.
385 function.
404 '''
386 '''
405 self._doc_text = text
387 self._doc_text = text
406
388
407 def _raw_input(self, prompt=''):
389 def _raw_input(self, prompt=''):
408 '''
390 '''
409 Custom raw_input() replacement. Get's current line from console buffer.
391 Custom raw_input() replacement. Get's current line from console buffer.
410
392
411 @param prompt: Prompt to print. Here for compatability as replacement.
393 @param prompt: Prompt to print. Here for compatability as replacement.
412 @type prompt: string
394 @type prompt: string
413
395
414 @return: The current command line text.
396 @return: The current command line text.
415 @rtype: string
397 @rtype: string
416 '''
398 '''
417 return self._line_to_execute
399 return self._line_to_execute
418
400
419 def _execute(self):
401 def _execute(self):
420 '''
402 '''
421 Executes the current line provided by the shell object.
403 Executes the current line provided by the shell object.
422 '''
404 '''
423 orig_stdout = sys.stdout
405 orig_stdout = sys.stdout
424 sys.stdout = IPython.Shell.Term.cout
406 sys.stdout = IPython.Shell.Term.cout
425
407
426 try:
408 try:
427 line = self._IP.raw_input(None, self._iter_more)
409 line = self._IP.raw_input(None, self._iter_more)
428 if self._IP.autoindent:
410 if self._IP.autoindent:
429 self._IP.readline_startup_hook(None)
411 self._IP.readline_startup_hook(None)
430
412
431 except KeyboardInterrupt:
413 except KeyboardInterrupt:
432 self._IP.write('\nKeyboardInterrupt\n')
414 self._IP.write('\nKeyboardInterrupt\n')
433 self._IP.resetbuffer()
415 self._IP.resetbuffer()
434 # keep cache in sync with the prompt counter:
416 # keep cache in sync with the prompt counter:
435 self._IP.outputcache.prompt_count -= 1
417 self._IP.outputcache.prompt_count -= 1
436
418
437 if self._IP.autoindent:
419 if self._IP.autoindent:
438 self._IP.indent_current_nsp = 0
420 self._IP.indent_current_nsp = 0
439 self._iter_more = 0
421 self._iter_more = 0
440 except:
422 except:
441 self._IP.showtraceback()
423 self._IP.showtraceback()
442 else:
424 else:
443 self._iter_more = self._IP.push(line)
425 self._iter_more = self._IP.push(line)
444 if (self._IP.SyntaxTB.last_syntax_error and
426 if (self._IP.SyntaxTB.last_syntax_error and
445 self._IP.rc.autoedit_syntax):
427 self._IP.rc.autoedit_syntax):
446 self._IP.edit_syntax_error()
428 self._IP.edit_syntax_error()
447 if self._iter_more:
429 if self._iter_more:
448 self._prompt = str(self._IP.outputcache.prompt2).strip()
430 self._prompt = str(self._IP.outputcache.prompt2).strip()
449 if self._IP.autoindent:
431 if self._IP.autoindent:
450 self._IP.readline_startup_hook(self._IP.pre_readline)
432 self._IP.readline_startup_hook(self._IP.pre_readline)
451 else:
433 else:
452 self._prompt = str(self._IP.outputcache.prompt1).strip()
434 self._prompt = str(self._IP.outputcache.prompt1).strip()
453 self._IP.indent_current_nsp = 0 #we set indentation to 0
435 self._IP.indent_current_nsp = 0 #we set indentation to 0
454 sys.stdout = orig_stdout
436 sys.stdout = orig_stdout
455
437
456 def _shell(self, ip, cmd):
438 def _shell(self, ip, cmd):
457 '''
439 '''
458 Replacement method to allow shell commands without them blocking.
440 Replacement method to allow shell commands without them blocking.
459
441
460 @param ip: Ipython instance, same as self._IP
442 @param ip: Ipython instance, same as self._IP
461 @type cmd: Ipython instance
443 @type cmd: Ipython instance
462 @param cmd: Shell command to execute.
444 @param cmd: Shell command to execute.
463 @type cmd: string
445 @type cmd: string
464 '''
446 '''
465 stdin, stdout = os.popen4(cmd)
447 stdin, stdout = os.popen4(cmd)
466 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
448 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
467 #we use print command because the shell command is called inside IPython instance and thus is
449 #we use print command because the shell command is called inside IPython instance and thus is
468 #redirected to thread cout
450 #redirected to thread cout
469 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
451 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
470 print "\x01\x1b[1;36m\x02"+result
452 print "\x01\x1b[1;36m\x02"+result
471 stdout.close()
453 stdout.close()
472 stdin.close()
454 stdin.close()
@@ -1,710 +1,707 b''
1 #!/usr/bin/python
1 #!/usr/bin/python
2 # -*- coding: iso-8859-15 -*-
2 # -*- coding: iso-8859-15 -*-
3 '''
3 '''
4 Provides IPython WX console widgets.
4 Provides IPython WX console widgets.
5
5
6 @author: Laurent Dufrechou
6 @author: Laurent Dufrechou
7 laurent.dufrechou _at_ gmail.com
7 laurent.dufrechou _at_ gmail.com
8 This WX widget is based on the original work of Eitan Isaacson
8 This WX widget is based on the original work of Eitan Isaacson
9 that provided the console for the GTK toolkit.
9 that provided the console for the GTK toolkit.
10
10
11 Original work from:
11 Original work from:
12 @author: Eitan Isaacson
12 @author: Eitan Isaacson
13 @organization: IBM Corporation
13 @organization: IBM Corporation
14 @copyright: Copyright (c) 2007 IBM Corporation
14 @copyright: Copyright (c) 2007 IBM Corporation
15 @license: BSD
15 @license: BSD
16
16
17 All rights reserved. This program and the accompanying materials are made
17 All rights reserved. This program and the accompanying materials are made
18 available under the terms of the BSD which accompanies this distribution, and
18 available under the terms of the BSD which accompanies this distribution, and
19 is available at U{http://www.opensource.org/licenses/bsd-license.php}
19 is available at U{http://www.opensource.org/licenses/bsd-license.php}
20 '''
20 '''
21
21
22 __version__ = 0.8
22 __version__ = 0.8
23 __author__ = "Laurent Dufrechou"
23 __author__ = "Laurent Dufrechou"
24 __email__ = "laurent.dufrechou _at_ gmail.com"
24 __email__ = "laurent.dufrechou _at_ gmail.com"
25 __license__ = "BSD"
25 __license__ = "BSD"
26
26
27 import wx
27 import wx
28 import wx.stc as stc
28 import wx.stc as stc
29 import wx.lib.newevent
29 import wx.lib.newevent
30
30
31 import re
31 import re
32 import sys
32 import sys
33 import os
33 import os
34 import locale
34 import locale
35 import time
35 import time
36 from StringIO import StringIO
36 from StringIO import StringIO
37 try:
37 try:
38 import IPython
38 import IPython
39 except Exception,e:
39 except Exception,e:
40 raise "Error importing IPython (%s)" % str(e)
40 raise "Error importing IPython (%s)" % str(e)
41
41
42
42
43 from NonBlockingIPShell import *
43 from ipshell_nonblocking import NonBlockingIPShell
44
44
45 class WxNonBlockingIPShell(NonBlockingIPShell):
45 class WxNonBlockingIPShell(NonBlockingIPShell):
46 '''
46 '''
47 An NonBlockingIPShell Thread that is WX dependent.
47 An NonBlockingIPShell Thread that is WX dependent.
48 Thus it permits direct interaction with a WX GUI without OnIdle event state machine trick...
48 Thus it permits direct interaction with a WX GUI without OnIdle event state machine trick...
49 '''
49 '''
50 def __init__(self,wx_instance,
50 def __init__(self,wx_instance,
51 argv=[],user_ns={},user_global_ns=None,
51 argv=[],user_ns={},user_global_ns=None,
52 cin=None, cout=None, cerr=None,
52 cin=None, cout=None, cerr=None,
53 ask_exit_handler=None):
53 ask_exit_handler=None):
54
54
55 #user_ns['addGUIShortcut'] = self.addGUIShortcut
55 #user_ns['addGUIShortcut'] = self.addGUIShortcut
56 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
56 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
57 cin, cout, cerr,
57 cin, cout, cerr,
58 ask_exit_handler)
58 ask_exit_handler)
59
59
60 # This creates a new Event class and a EVT binder function
60 # This creates a new Event class and a EVT binder function
61 (self.IPythonAskExitEvent, EVT_IP_ASK_EXIT) = wx.lib.newevent.NewEvent()
61 (self.IPythonAskExitEvent, EVT_IP_ASK_EXIT) = wx.lib.newevent.NewEvent()
62 #(self.IPythonAddButtonEvent, EVT_IP_ADD_BUTTON_EXIT) = \
62 #(self.IPythonAddButtonEvent, EVT_IP_ADD_BUTTON_EXIT) = \
63 # wx.lib.newevent.NewEvent()
63 # wx.lib.newevent.NewEvent()
64 (self.IPythonExecuteDoneEvent, EVT_IP_EXECUTE_DONE) = \
64 (self.IPythonExecuteDoneEvent, EVT_IP_EXECUTE_DONE) = \
65 wx.lib.newevent.NewEvent()
65 wx.lib.newevent.NewEvent()
66
66
67 wx_instance.Bind(EVT_IP_ASK_EXIT, wx_instance.ask_exit_handler)
67 wx_instance.Bind(EVT_IP_ASK_EXIT, wx_instance.ask_exit_handler)
68 #wx_instance.Bind(EVT_IP_ADD_BUTTON_EXIT, wx_instance.add_button_handler)
68 #wx_instance.Bind(EVT_IP_ADD_BUTTON_EXIT, wx_instance.add_button_handler)
69 wx_instance.Bind(EVT_IP_EXECUTE_DONE, wx_instance.evtStateExecuteDone)
69 wx_instance.Bind(EVT_IP_EXECUTE_DONE, wx_instance.evtStateExecuteDone)
70
70
71 self.wx_instance = wx_instance
71 self.wx_instance = wx_instance
72 self._IP.ask_exit = self._askExit
72 self._IP.ask_exit = self._askExit
73 self._IP.exit = self._askExit
73
74
74 #def addGUIShortcut(self,text,func):
75 #def addGUIShortcut(self,text,func):
75 # evt = self.IPythonAddButtonEvent(
76 # evt = self.IPythonAddButtonEvent(
76 # button_info={ 'text':text,
77 # button_info={ 'text':text,
77 # 'func':self.wx_instance.doExecuteLine(func)})
78 # 'func':self.wx_instance.doExecuteLine(func)})
78 # wx.PostEvent(self.wx_instance, evt)
79 # wx.PostEvent(self.wx_instance, evt)
79
80
80 def _askExit(self):
81 def _askExit(self):
81 evt = self.IPythonAskExitEvent()
82 evt = self.IPythonAskExitEvent()
82 wx.PostEvent(self.wx_instance, evt)
83 wx.PostEvent(self.wx_instance, evt)
83
84
84 def _afterExecute(self):
85 def _afterExecute(self):
85 evt = self.IPythonExecuteDoneEvent()
86 evt = self.IPythonExecuteDoneEvent()
86 wx.PostEvent(self.wx_instance, evt)
87 wx.PostEvent(self.wx_instance, evt)
87
88
88
89
89 class WxConsoleView(stc.StyledTextCtrl):
90 class WxConsoleView(stc.StyledTextCtrl):
90 '''
91 '''
91 Specialized styled text control view for console-like workflow.
92 Specialized styled text control view for console-like workflow.
92 We use here a scintilla frontend thus it can be reused in any GUI taht supports
93 We use here a scintilla frontend thus it can be reused in any GUI taht supports
93 scintilla with less work.
94 scintilla with less work.
94
95
95 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.(with Black background)
96 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.(with Black background)
96 @type ANSI_COLORS_BLACK: dictionary
97 @type ANSI_COLORS_BLACK: dictionary
97
98
98 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.(with White background)
99 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.(with White background)
99 @type ANSI_COLORS_WHITE: dictionary
100 @type ANSI_COLORS_WHITE: dictionary
100
101
101 @ivar color_pat: Regex of terminal color pattern
102 @ivar color_pat: Regex of terminal color pattern
102 @type color_pat: _sre.SRE_Pattern
103 @type color_pat: _sre.SRE_Pattern
103 '''
104 '''
104 ANSI_STYLES_BLACK ={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
105 ANSI_STYLES_BLACK ={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
105 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
106 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
106 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
107 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
107 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
108 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
108 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
109 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
109 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
110 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
110 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
111 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
111 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
112 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
112
113
113 ANSI_STYLES_WHITE ={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
114 ANSI_STYLES_WHITE ={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
114 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
115 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
115 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
116 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
116 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
117 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
117 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
118 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
118 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
119 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
119 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
120 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
120 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
121 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
121
122
122 def __init__(self,parent,prompt,intro="",background_color="BLACK",pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
123 def __init__(self,parent,prompt,intro="",background_color="BLACK",pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
123 style=0):
124 style=0):
124 '''
125 '''
125 Initialize console view.
126 Initialize console view.
126
127
127 @param parent: Parent widget
128 @param parent: Parent widget
128 @param prompt: User specified prompt
129 @param prompt: User specified prompt
129 @type intro: string
130 @type intro: string
130 @param intro: User specified startup introduction string
131 @param intro: User specified startup introduction string
131 @type intro: string
132 @type intro: string
132 @param background_color: Can be BLACK or WHITE
133 @param background_color: Can be BLACK or WHITE
133 @type background_color: string
134 @type background_color: string
134 @param other: init param of styledTextControl (can be used as-is)
135 @param other: init param of styledTextControl (can be used as-is)
135 '''
136 '''
136 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
137 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
137
138
138 ####### Scintilla configuration ##################################################
139 ####### Scintilla configuration ##################################################
139
140
140 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside the widget
141 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside the widget
141 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
142 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
142 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
143 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
143
144
144 #we define platform specific fonts
145 #we define platform specific fonts
145 if wx.Platform == '__WXMSW__':
146 if wx.Platform == '__WXMSW__':
146 faces = { 'times': 'Times New Roman',
147 faces = { 'times': 'Times New Roman',
147 'mono' : 'Courier New',
148 'mono' : 'Courier New',
148 'helv' : 'Arial',
149 'helv' : 'Arial',
149 'other': 'Comic Sans MS',
150 'other': 'Comic Sans MS',
150 'size' : 10,
151 'size' : 10,
151 'size2': 8,
152 'size2': 8,
152 }
153 }
153 elif wx.Platform == '__WXMAC__':
154 elif wx.Platform == '__WXMAC__':
154 faces = { 'times': 'Times New Roman',
155 faces = { 'times': 'Times New Roman',
155 'mono' : 'Monaco',
156 'mono' : 'Monaco',
156 'helv' : 'Arial',
157 'helv' : 'Arial',
157 'other': 'Comic Sans MS',
158 'other': 'Comic Sans MS',
158 'size' : 10,
159 'size' : 10,
159 'size2': 8,
160 'size2': 8,
160 }
161 }
161 else:
162 else:
162 faces = { 'times': 'Times',
163 faces = { 'times': 'Times',
163 'mono' : 'Courier',
164 'mono' : 'Courier',
164 'helv' : 'Helvetica',
165 'helv' : 'Helvetica',
165 'other': 'new century schoolbook',
166 'other': 'new century schoolbook',
166 'size' : 10,
167 'size' : 10,
167 'size2': 8,
168 'size2': 8,
168 }
169 }
169
170
170 #We draw a line at position 80
171 #We draw a line at position 80
171 self.SetEdgeMode(stc.STC_EDGE_LINE)
172 self.SetEdgeMode(stc.STC_EDGE_LINE)
172 self.SetEdgeColumn(80)
173 self.SetEdgeColumn(80)
173 self.SetEdgeColour(wx.LIGHT_GREY)
174 self.SetEdgeColour(wx.LIGHT_GREY)
174
175
175 #self.SetViewWhiteSpace(True)
176 #self.SetViewWhiteSpace(True)
176 #self.SetViewEOL(True)
177 #self.SetViewEOL(True)
177 self.SetEOLMode(stc.STC_EOL_CRLF)
178 self.SetEOLMode(stc.STC_EOL_CRLF)
178 #self.SetWrapMode(stc.STC_WRAP_CHAR)
179 #self.SetWrapMode(stc.STC_WRAP_CHAR)
179 #self.SetWrapMode(stc.STC_WRAP_WORD)
180 #self.SetWrapMode(stc.STC_WRAP_WORD)
180 self.SetBufferedDraw(True)
181 self.SetBufferedDraw(True)
181 #self.SetUseAntiAliasing(True)
182 #self.SetUseAntiAliasing(True)
182 self.SetLayoutCache(stc.STC_CACHE_PAGE)
183 self.SetLayoutCache(stc.STC_CACHE_PAGE)
183
184
184 self.EnsureCaretVisible()
185 self.EnsureCaretVisible()
185
186
186 self.SetMargins(3,3) #text is moved away from border with 3px
187 self.SetMargins(3,3) #text is moved away from border with 3px
187 # Suppressing Scintilla margins
188 # Suppressing Scintilla margins
188 self.SetMarginWidth(0,0)
189 self.SetMarginWidth(0,0)
189 self.SetMarginWidth(1,0)
190 self.SetMarginWidth(1,0)
190 self.SetMarginWidth(2,0)
191 self.SetMarginWidth(2,0)
191
192
192 # make some styles
193 # make some styles
193 if background_color != "BLACK":
194 if background_color != "BLACK":
194 self.background_color = "WHITE"
195 self.background_color = "WHITE"
195 self.SetCaretForeground("BLACK")
196 self.SetCaretForeground("BLACK")
196 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
197 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
197 else:
198 else:
198 self.background_color = background_color
199 self.background_color = background_color
199 self.SetCaretForeground("WHITE")
200 self.SetCaretForeground("WHITE")
200 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
201 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
201
202
202 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s" % (self.ANSI_STYLES['0;30'][1],
203 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s" % (self.ANSI_STYLES['0;30'][1],
203 self.background_color,
204 self.background_color,
204 faces['size'], faces['mono']))
205 faces['size'], faces['mono']))
205 self.StyleClearAll()
206 self.StyleClearAll()
206 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FF0000,back:#0000FF,bold")
207 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FF0000,back:#0000FF,bold")
207 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
208 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
208
209
209 for style in self.ANSI_STYLES.values():
210 for style in self.ANSI_STYLES.values():
210 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
211 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
211
212
212 #######################################################################
213 #######################################################################
213
214
214 self.indent = 0
215 self.indent = 0
215 self.prompt_count = 0
216 self.prompt_count = 0
216 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
217 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
217
218
218 self.write(intro)
219 self.write(intro)
219 self.setPrompt(prompt)
220 self.setPrompt(prompt)
220 self.showPrompt()
221 self.showPrompt()
221
222
222 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
223 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
223 #self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
224 #self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
224
225
225 def write(self, text):
226 def write(self, text):
226 '''
227 '''
227 Write given text to buffer.
228 Write given text to buffer.
228
229
229 @param text: Text to append.
230 @param text: Text to append.
230 @type text: string
231 @type text: string
231 '''
232 '''
232 segments = self.color_pat.split(text)
233 segments = self.color_pat.split(text)
233 segment = segments.pop(0)
234 segment = segments.pop(0)
234 self.StartStyling(self.getCurrentLineEnd(),0xFF)
235 self.StartStyling(self.getCurrentLineEnd(),0xFF)
235 self.AppendText(segment)
236 self.AppendText(segment)
236
237
237 if segments:
238 if segments:
238 ansi_tags = self.color_pat.findall(text)
239 ansi_tags = self.color_pat.findall(text)
239
240
240 for tag in ansi_tags:
241 for tag in ansi_tags:
241 i = segments.index(tag)
242 i = segments.index(tag)
242 self.StartStyling(self.getCurrentLineEnd(),0xFF)
243 self.StartStyling(self.getCurrentLineEnd(),0xFF)
243 self.AppendText(segments[i+1])
244 self.AppendText(segments[i+1])
244
245
245 if tag != '0':
246 if tag != '0':
246 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
247 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
247
248
248 segments.pop(i)
249 segments.pop(i)
249
250
250 self.moveCursor(self.getCurrentLineEnd())
251 self.moveCursor(self.getCurrentLineEnd())
251
252
252 def getPromptLen(self):
253 def getPromptLen(self):
253 '''
254 '''
254 Return the length of current prompt
255 Return the length of current prompt
255 '''
256 '''
256 return len(str(self.prompt_count)) + 7
257 return len(str(self.prompt_count)) + 7
257
258
258 def setPrompt(self,prompt):
259 def setPrompt(self,prompt):
259 self.prompt = prompt
260 self.prompt = prompt
260
261
261 def setIndentation(self,indentation):
262 def setIndentation(self,indentation):
262 self.indent = indentation
263 self.indent = indentation
263
264
264 def setPromptCount(self,count):
265 def setPromptCount(self,count):
265 self.prompt_count = count
266 self.prompt_count = count
266
267
267 def showPrompt(self):
268 def showPrompt(self):
268 '''
269 '''
269 Prints prompt at start of line.
270 Prints prompt at start of line.
270
271
271 @param prompt: Prompt to print.
272 @param prompt: Prompt to print.
272 @type prompt: string
273 @type prompt: string
273 '''
274 '''
274 self.write(self.prompt)
275 self.write(self.prompt)
275 #now we update the position of end of prompt
276 #now we update the position of end of prompt
276 self.current_start = self.getCurrentLineEnd()
277 self.current_start = self.getCurrentLineEnd()
277
278
278 autoindent = self.indent*' '
279 autoindent = self.indent*' '
279 autoindent = autoindent.replace(' ','\t')
280 autoindent = autoindent.replace(' ','\t')
280 self.write(autoindent)
281 self.write(autoindent)
281
282
282 def changeLine(self, text):
283 def changeLine(self, text):
283 '''
284 '''
284 Replace currently entered command line with given text.
285 Replace currently entered command line with given text.
285
286
286 @param text: Text to use as replacement.
287 @param text: Text to use as replacement.
287 @type text: string
288 @type text: string
288 '''
289 '''
289 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
290 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
290 self.ReplaceSelection(text)
291 self.ReplaceSelection(text)
291 self.moveCursor(self.getCurrentLineEnd())
292 self.moveCursor(self.getCurrentLineEnd())
292
293
293 def getCurrentPromptStart(self):
294 def getCurrentPromptStart(self):
294 return self.current_start
295 return self.current_start
295
296
296 def getCurrentLineStart(self):
297 def getCurrentLineStart(self):
297 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
298 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
298
299
299 def getCurrentLineEnd(self):
300 def getCurrentLineEnd(self):
300 return self.GetLength()
301 return self.GetLength()
301
302
302 def getCurrentLine(self):
303 def getCurrentLine(self):
303 '''
304 '''
304 Get text in current command line.
305 Get text in current command line.
305
306
306 @return: Text of current command line.
307 @return: Text of current command line.
307 @rtype: string
308 @rtype: string
308 '''
309 '''
309 return self.GetTextRange(self.getCurrentPromptStart(),
310 return self.GetTextRange(self.getCurrentPromptStart(),
310 self.getCurrentLineEnd())
311 self.getCurrentLineEnd())
311
312
312 def showReturned(self, text):
313 def showReturned(self, text):
313 '''
314 '''
314 Show returned text from last command and print new prompt.
315 Show returned text from last command and print new prompt.
315
316
316 @param text: Text to show.
317 @param text: Text to show.
317 @type text: string
318 @type text: string
318 '''
319 '''
319 self.write('\n'+text)
320 self.write('\n'+text)
320 if text:
321 if text:
321 self.write('\n')
322 self.write('\n')
322 self.showPrompt()
323 self.showPrompt()
323
324
324 def moveCursorOnNewValidKey(self):
325 def moveCursorOnNewValidKey(self):
325 #If cursor is at wrong position put it at last line...
326 #If cursor is at wrong position put it at last line...
326 if self.GetCurrentPos() < self.getCurrentPromptStart():
327 if self.GetCurrentPos() < self.getCurrentPromptStart():
327 self.GotoPos(self.getCurrentPromptStart())
328 self.GotoPos(self.getCurrentPromptStart())
328
329
329 def removeFromTo(self,from_pos,to_pos):
330 def removeFromTo(self,from_pos,to_pos):
330 if from_pos < to_pos:
331 if from_pos < to_pos:
331 self.SetSelection(from_pos,to_pos)
332 self.SetSelection(from_pos,to_pos)
332 self.DeleteBack()
333 self.DeleteBack()
333
334
334 def removeCurrentLine(self):
335 def removeCurrentLine(self):
335 self.LineDelete()
336 self.LineDelete()
336
337
337 def moveCursor(self,position):
338 def moveCursor(self,position):
338 self.GotoPos(position)
339 self.GotoPos(position)
339
340
340 def getCursorPos(self):
341 def getCursorPos(self):
341 return self.GetCurrentPos()
342 return self.GetCurrentPos()
342
343
343 def selectFromTo(self,from_pos,to_pos):
344 def selectFromTo(self,from_pos,to_pos):
344 self.SetSelectionStart(from_pos)
345 self.SetSelectionStart(from_pos)
345 self.SetSelectionEnd(to_pos)
346 self.SetSelectionEnd(to_pos)
346
347
347 def writeHistory(self,history):
348 def writeHistory(self,history):
348 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
349 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
349 self.changeLine(history)
350 self.changeLine(history)
350
351
351 def writeCompletion(self, possibilities):
352 def writeCompletion(self, possibilities):
352 max_len = len(max(possibilities,key=len))
353 max_len = len(max(possibilities,key=len))
353 max_symbol =' '*max_len
354 max_symbol =' '*max_len
354
355
355 #now we check how much symbol we can put on a line...
356 #now we check how much symbol we can put on a line...
356 cursor_pos = self.getCursorPos()
357 cursor_pos = self.getCursorPos()
357 test_buffer = max_symbol + ' '*4
358 test_buffer = max_symbol + ' '*4
358 current_lines = self.GetLineCount()
359 current_lines = self.GetLineCount()
359
360
360 allowed_symbols = 80/len(test_buffer)
361 allowed_symbols = 80/len(test_buffer)
361 if allowed_symbols == 0:
362 if allowed_symbols == 0:
362 allowed_symbols = 1
363 allowed_symbols = 1
363
364
364 pos = 1
365 pos = 1
365 buf = ''
366 buf = ''
366 for symbol in possibilities:
367 for symbol in possibilities:
367 #buf += symbol+'\n'#*spaces)
368 #buf += symbol+'\n'#*spaces)
368 if pos<allowed_symbols:
369 if pos<allowed_symbols:
369 spaces = max_len - len(symbol) + 4
370 spaces = max_len - len(symbol) + 4
370 buf += symbol+' '*spaces
371 buf += symbol+' '*spaces
371 pos += 1
372 pos += 1
372 else:
373 else:
373 buf+=symbol+'\n'
374 buf+=symbol+'\n'
374 pos = 1
375 pos = 1
375 self.write(buf)
376 self.write(buf)
376
377
377 def _onKeypress(self, event, skip=True):
378 def _onKeypress(self, event, skip=True):
378 '''
379 '''
379 Key press callback used for correcting behavior for console-like
380 Key press callback used for correcting behavior for console-like
380 interfaces. For example 'home' should go to prompt, not to begining of
381 interfaces. For example 'home' should go to prompt, not to begining of
381 line.
382 line.
382
383
383 @param widget: Widget that key press accored in.
384 @param widget: Widget that key press accored in.
384 @type widget: gtk.Widget
385 @type widget: gtk.Widget
385 @param event: Event object
386 @param event: Event object
386 @type event: gtk.gdk.Event
387 @type event: gtk.gdk.Event
387
388
388 @return: Return True if event as been catched.
389 @return: Return True if event as been catched.
389 @rtype: boolean
390 @rtype: boolean
390 '''
391 '''
391
392
392 if event.GetKeyCode() == wx.WXK_HOME:
393 if event.GetKeyCode() == wx.WXK_HOME:
393 if event.Modifiers == wx.MOD_NONE:
394 if event.Modifiers == wx.MOD_NONE:
394 self.moveCursorOnNewValidKey()
395 self.moveCursorOnNewValidKey()
395 self.moveCursor(self.getCurrentPromptStart())
396 self.moveCursor(self.getCurrentPromptStart())
396 return True
397 return True
397 elif event.Modifiers == wx.MOD_SHIFT:
398 elif event.Modifiers == wx.MOD_SHIFT:
398 self.moveCursorOnNewValidKey()
399 self.moveCursorOnNewValidKey()
399 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
400 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
400 return True
401 return True
401 else:
402 else:
402 return False
403 return False
403
404
404 elif event.GetKeyCode() == wx.WXK_LEFT:
405 elif event.GetKeyCode() == wx.WXK_LEFT:
405 if event.Modifiers == wx.MOD_NONE:
406 if event.Modifiers == wx.MOD_NONE:
406 self.moveCursorOnNewValidKey()
407 self.moveCursorOnNewValidKey()
407
408
408 self.moveCursor(self.getCursorPos()-1)
409 self.moveCursor(self.getCursorPos()-1)
409 if self.getCursorPos() < self.getCurrentPromptStart():
410 if self.getCursorPos() < self.getCurrentPromptStart():
410 self.moveCursor(self.getCurrentPromptStart())
411 self.moveCursor(self.getCurrentPromptStart())
411 return True
412 return True
412
413
413 elif event.GetKeyCode() == wx.WXK_BACK:
414 elif event.GetKeyCode() == wx.WXK_BACK:
414 self.moveCursorOnNewValidKey()
415 self.moveCursorOnNewValidKey()
415 if self.getCursorPos() > self.getCurrentPromptStart():
416 if self.getCursorPos() > self.getCurrentPromptStart():
416 self.removeFromTo(self.getCursorPos()-1,self.getCursorPos())
417 self.removeFromTo(self.getCursorPos()-1,self.getCursorPos())
417 return True
418 return True
418
419
419 if skip:
420 if skip:
420 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
421 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
421 self.moveCursorOnNewValidKey()
422 self.moveCursorOnNewValidKey()
422
423
423 event.Skip()
424 event.Skip()
424 return True
425 return True
425 return False
426 return False
426
427
427 def OnUpdateUI(self, evt):
428 def OnUpdateUI(self, evt):
428 # check for matching braces
429 # check for matching braces
429 braceAtCaret = -1
430 braceAtCaret = -1
430 braceOpposite = -1
431 braceOpposite = -1
431 charBefore = None
432 charBefore = None
432 caretPos = self.GetCurrentPos()
433 caretPos = self.GetCurrentPos()
433
434
434 if caretPos > 0:
435 if caretPos > 0:
435 charBefore = self.GetCharAt(caretPos - 1)
436 charBefore = self.GetCharAt(caretPos - 1)
436 styleBefore = self.GetStyleAt(caretPos - 1)
437 styleBefore = self.GetStyleAt(caretPos - 1)
437
438
438 # check before
439 # check before
439 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
440 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
440 braceAtCaret = caretPos - 1
441 braceAtCaret = caretPos - 1
441
442
442 # check after
443 # check after
443 if braceAtCaret < 0:
444 if braceAtCaret < 0:
444 charAfter = self.GetCharAt(caretPos)
445 charAfter = self.GetCharAt(caretPos)
445 styleAfter = self.GetStyleAt(caretPos)
446 styleAfter = self.GetStyleAt(caretPos)
446
447
447 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
448 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
448 braceAtCaret = caretPos
449 braceAtCaret = caretPos
449
450
450 if braceAtCaret >= 0:
451 if braceAtCaret >= 0:
451 braceOpposite = self.BraceMatch(braceAtCaret)
452 braceOpposite = self.BraceMatch(braceAtCaret)
452
453
453 if braceAtCaret != -1 and braceOpposite == -1:
454 if braceAtCaret != -1 and braceOpposite == -1:
454 self.BraceBadLight(braceAtCaret)
455 self.BraceBadLight(braceAtCaret)
455 else:
456 else:
456 self.BraceHighlight(braceAtCaret, braceOpposite)
457 self.BraceHighlight(braceAtCaret, braceOpposite)
457 #pt = self.PointFromPosition(braceOpposite)
458 #pt = self.PointFromPosition(braceOpposite)
458 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
459 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
459 #print pt
460 #print pt
460 #self.Refresh(False)
461 #self.Refresh(False)
461
462
462 class WxIPythonViewPanel(wx.Panel):
463 class WxIPythonViewPanel(wx.Panel):
463 '''
464 '''
464 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
465 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
465 If you want to port this to any other GUI toolkit, just replace the WxConsoleView
466 If you want to port this to any other GUI toolkit, just replace the WxConsoleView
466 by YOURGUIConsoleView and make YOURGUIIPythonView derivate from whatever container you want.
467 by YOURGUIConsoleView and make YOURGUIIPythonView derivate from whatever container you want.
467 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
468 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
468 Any idea to make it more 'genric' welcomed.
469 Any idea to make it more 'genric' welcomed.
469 '''
470 '''
470
471
471 def __init__(self, parent, ask_exit_handler=None, intro=None,
472 def __init__(self, parent, ask_exit_handler=None, intro=None,
472 background_color="BLACK", add_button_handler=None,
473 background_color="BLACK", add_button_handler=None,
473 wx_ip_shell=None,
474 wx_ip_shell=None,
474 ):
475 ):
475 '''
476 '''
476 Initialize.
477 Initialize.
477 Instanciate an IPython thread.
478 Instanciate an IPython thread.
478 Instanciate a WxConsoleView.
479 Instanciate a WxConsoleView.
479 Redirect I/O to console.
480 Redirect I/O to console.
480 '''
481 '''
481 wx.Panel.__init__(self,parent,-1)
482 wx.Panel.__init__(self,parent,-1)
482
483
483 ### IPython non blocking shell instanciation ###
484 ### IPython non blocking shell instanciation ###
484 self.cout = StringIO()
485 self.cout = StringIO()
485
486
486 self.add_button_handler = add_button_handler
487 self.add_button_handler = add_button_handler
487 self.ask_exit_handler = ask_exit_handler
488 self.ask_exit_handler = ask_exit_handler
488
489
489 if wx_ip_shell is not None:
490 if wx_ip_shell is not None:
490 self.IP = wx_ip_shell
491 self.IP = wx_ip_shell
491 else:
492 else:
492 self.IP = WxNonBlockingIPShell(self,
493 self.IP = WxNonBlockingIPShell(self,
493 cout=self.cout,cerr=self.cout,
494 cout=self.cout,cerr=self.cout,
494 ask_exit_handler = ask_exit_handler)
495 ask_exit_handler = ask_exit_handler)
495
496
496 ### IPython wx console view instanciation ###
497 ### IPython wx console view instanciation ###
497 #If user didn't defined an intro text, we create one for him
498 #If user didn't defined an intro text, we create one for him
498 #If you really wnat an empty intrp just call wxIPythonViewPanel with intro=''
499 #If you really wnat an empty intrp just call wxIPythonViewPanel with intro=''
499 if intro == None:
500 if intro == None:
500 welcome_text = "Welcome to WxIPython Shell.\n\n"
501 welcome_text = "Welcome to WxIPython Shell.\n\n"
501 welcome_text+= self.IP.getBanner()
502 welcome_text+= self.IP.getBanner()
502 welcome_text+= "!command -> Execute command in shell\n"
503 welcome_text+= "!command -> Execute command in shell\n"
503 welcome_text+= "TAB -> Autocompletion\n"
504 welcome_text+= "TAB -> Autocompletion\n"
504
505
505 self.text_ctrl = WxConsoleView(self,
506 self.text_ctrl = WxConsoleView(self,
506 self.IP.getPrompt(),
507 self.IP.getPrompt(),
507 intro=welcome_text,
508 intro=welcome_text,
508 background_color=background_color)
509 background_color=background_color)
509
510
510 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
511 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
511
512
512 ### making the layout of the panel ###
513 ### making the layout of the panel ###
513 sizer = wx.BoxSizer(wx.VERTICAL)
514 sizer = wx.BoxSizer(wx.VERTICAL)
514 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
515 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
515 self.SetAutoLayout(True)
516 self.SetAutoLayout(True)
516 sizer.Fit(self)
517 sizer.Fit(self)
517 sizer.SetSizeHints(self)
518 sizer.SetSizeHints(self)
518 self.SetSizer(sizer)
519 self.SetSizer(sizer)
519 #and we focus on the widget :)
520 #and we focus on the widget :)
520 self.SetFocus()
521 self.SetFocus()
521
522
522 #widget state management (for key handling different cases)
523 #widget state management (for key handling different cases)
523 self.setCurrentState('IDLE')
524 self.setCurrentState('IDLE')
524 self.pager_state = 'DONE'
525 self.pager_state = 'DONE'
525
526
526 def __del__(self):
527 def __del__(self):
527 WxConsoleView.__del__()
528 WxConsoleView.__del__()
528
529
529 #---------------------------- IPython Thread Management ---------------------------------------
530 #---------------------------- IPython Thread Management ---------------------------------------
530 def stateDoExecuteLine(self):
531 def stateDoExecuteLine(self):
531 #print >>sys.__stdout__,"command:",self.getCurrentLine()
532 #print >>sys.__stdout__,"command:",self.getCurrentLine()
532 self.doExecuteLine(self.text_ctrl.getCurrentLine())
533 line=self.text_ctrl.getCurrentLine()
533
534 def doExecuteLine(self,line):
535 #print >>sys.__stdout__,"command:",line
536 self.IP.doExecute(line.replace('\t',' '*4))
534 self.IP.doExecute(line.replace('\t',' '*4))
537 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
535 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
538 self.setCurrentState('WAIT_END_OF_EXECUTION')
536 self.setCurrentState('WAIT_END_OF_EXECUTION')
539
540
537
541 def evtStateExecuteDone(self,evt):
538 def evtStateExecuteDone(self,evt):
542 self.doc = self.IP.getDocText()
539 self.doc = self.IP.getDocText()
543 self.help = self.IP.getHelpText()
540 self.help = self.IP.getHelpText()
544 if self.doc:
541 if self.doc:
545 self.pager_lines = self.doc[7:].split('\n')
542 self.pager_lines = self.doc[7:].split('\n')
546 self.pager_state = 'INIT'
543 self.pager_state = 'INIT'
547 self.setCurrentState('SHOW_DOC')
544 self.setCurrentState('SHOW_DOC')
548 self.pager(self.doc)
545 self.pager(self.doc)
549
546
550 if self.help:
547 if self.help:
551 self.pager_lines = self.help.split('\n')
548 self.pager_lines = self.help.split('\n')
552 self.pager_state = 'INIT'
549 self.pager_state = 'INIT'
553 self.setCurrentState('SHOW_DOC')
550 self.setCurrentState('SHOW_DOC')
554 self.pager(self.help)
551 self.pager(self.help)
555
552
556 else:
553 else:
557 self.stateShowPrompt()
554 self.stateShowPrompt()
558
555
559 def stateShowPrompt(self):
556 def stateShowPrompt(self):
560 self.setCurrentState('SHOW_PROMPT')
557 self.setCurrentState('SHOW_PROMPT')
561 self.text_ctrl.setPrompt(self.IP.getPrompt())
558 self.text_ctrl.setPrompt(self.IP.getPrompt())
562 self.text_ctrl.setIndentation(self.IP.getIndentation())
559 self.text_ctrl.setIndentation(self.IP.getIndentation())
563 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
560 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
564 rv = self.cout.getvalue()
561 rv = self.cout.getvalue()
565 if rv: rv = rv.strip('\n')
562 if rv: rv = rv.strip('\n')
566 self.text_ctrl.showReturned(rv)
563 self.text_ctrl.showReturned(rv)
567 self.cout.truncate(0)
564 self.cout.truncate(0)
568 self.IP.initHistoryIndex()
565 self.IP.initHistoryIndex()
569 self.setCurrentState('IDLE')
566 self.setCurrentState('IDLE')
570
567
571 def setCurrentState(self, state):
568 def setCurrentState(self, state):
572 self.cur_state = state
569 self.cur_state = state
573 self.updateStatusTracker(self.cur_state)
570 self.updateStatusTracker(self.cur_state)
574
571
575 #---------------------------- IPython pager ---------------------------------------
572 #---------------------------- IPython pager ---------------------------------------
576 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
573 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
577 if self.pager_state == 'WAITING':
574 if self.pager_state == 'WAITING':
578 #print >>sys.__stdout__,"PAGER waiting"
575 #print >>sys.__stdout__,"PAGER waiting"
579 return
576 return
580
577
581 if self.pager_state == 'INIT':
578 if self.pager_state == 'INIT':
582 #print >>sys.__stdout__,"PAGER state:",self.pager_state
579 #print >>sys.__stdout__,"PAGER state:",self.pager_state
583 self.pager_nb_lines = len(self.pager_lines)
580 self.pager_nb_lines = len(self.pager_lines)
584 self.pager_index = 0
581 self.pager_index = 0
585 self.pager_do_remove = False
582 self.pager_do_remove = False
586 self.text_ctrl.write('\n')
583 self.text_ctrl.write('\n')
587 self.pager_state = 'PROCESS_LINES'
584 self.pager_state = 'PROCESS_LINES'
588
585
589 if self.pager_state == 'PROCESS_LINES':
586 if self.pager_state == 'PROCESS_LINES':
590 #print >>sys.__stdout__,"PAGER state:",self.pager_state
587 #print >>sys.__stdout__,"PAGER state:",self.pager_state
591 if self.pager_do_remove == True:
588 if self.pager_do_remove == True:
592 self.text_ctrl.removeCurrentLine()
589 self.text_ctrl.removeCurrentLine()
593 self.pager_do_remove = False
590 self.pager_do_remove = False
594
591
595 if self.pager_nb_lines > 10:
592 if self.pager_nb_lines > 10:
596 #print >>sys.__stdout__,"PAGER processing 10 lines"
593 #print >>sys.__stdout__,"PAGER processing 10 lines"
597 if self.pager_index > 0:
594 if self.pager_index > 0:
598 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
595 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
599 else:
596 else:
600 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
597 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
601
598
602 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
599 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
603 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
600 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
604 self.pager_index += 10
601 self.pager_index += 10
605 self.pager_nb_lines -= 10
602 self.pager_nb_lines -= 10
606 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
603 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
607 self.pager_do_remove = True
604 self.pager_do_remove = True
608 self.pager_state = 'WAITING'
605 self.pager_state = 'WAITING'
609 return
606 return
610 else:
607 else:
611 #print >>sys.__stdout__,"PAGER processing last lines"
608 #print >>sys.__stdout__,"PAGER processing last lines"
612 if self.pager_nb_lines > 0:
609 if self.pager_nb_lines > 0:
613 if self.pager_index > 0:
610 if self.pager_index > 0:
614 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
611 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
615 else:
612 else:
616 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
613 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
617
614
618 self.pager_index += 1
615 self.pager_index += 1
619 self.pager_nb_lines -= 1
616 self.pager_nb_lines -= 1
620 if self.pager_nb_lines > 0:
617 if self.pager_nb_lines > 0:
621 for line in self.pager_lines[self.pager_index:]:
618 for line in self.pager_lines[self.pager_index:]:
622 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
619 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
623 self.pager_nb_lines = 0
620 self.pager_nb_lines = 0
624 self.pager_state = 'DONE'
621 self.pager_state = 'DONE'
625 self.stateShowPrompt()
622 self.stateShowPrompt()
626
623
627 #---------------------------- Key Handler --------------------------------------------
624 #---------------------------- Key Handler --------------------------------------------
628 def keyPress(self, event):
625 def keyPress(self, event):
629 '''
626 '''
630 Key press callback with plenty of shell goodness, like history,
627 Key press callback with plenty of shell goodness, like history,
631 autocompletions, etc.
628 autocompletions, etc.
632 '''
629 '''
633
630
634 if event.GetKeyCode() == ord('C'):
631 if event.GetKeyCode() == ord('C'):
635 if event.Modifiers == wx.MOD_CONTROL:
632 if event.Modifiers == wx.MOD_CONTROL:
636 if self.cur_state == 'WAIT_END_OF_EXECUTION':
633 if self.cur_state == 'WAIT_END_OF_EXECUTION':
637 #we raise an exception inside the IPython thread container
634 #we raise an exception inside the IPython thread container
638 self.IP.ce.raise_exc(KeyboardInterrupt)
635 self.IP.ce.raise_exc(KeyboardInterrupt)
639 return
636 return
640
637
641 if event.KeyCode == wx.WXK_RETURN:
638 if event.KeyCode == wx.WXK_RETURN:
642 if self.cur_state == 'IDLE':
639 if self.cur_state == 'IDLE':
643 #we change the state ot the state machine
640 #we change the state ot the state machine
644 self.setCurrentState('DO_EXECUTE_LINE')
641 self.setCurrentState('DO_EXECUTE_LINE')
645 self.stateDoExecuteLine()
642 self.stateDoExecuteLine()
646 return
643 return
647 if self.pager_state == 'WAITING':
644 if self.pager_state == 'WAITING':
648 self.pager_state = 'PROCESS_LINES'
645 self.pager_state = 'PROCESS_LINES'
649 self.pager(self.doc)
646 self.pager(self.doc)
650 return
647 return
651
648
652 if event.GetKeyCode() in [ord('q'),ord('Q')]:
649 if event.GetKeyCode() in [ord('q'),ord('Q')]:
653 if self.pager_state == 'WAITING':
650 if self.pager_state == 'WAITING':
654 self.pager_state = 'DONE'
651 self.pager_state = 'DONE'
655 self.stateShowPrompt()
652 self.stateShowPrompt()
656 return
653 return
657
654
658 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
655 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
659 if self.cur_state == 'IDLE':
656 if self.cur_state == 'IDLE':
660 if event.KeyCode == wx.WXK_UP:
657 if event.KeyCode == wx.WXK_UP:
661 history = self.IP.historyBack()
658 history = self.IP.historyBack()
662 self.text_ctrl.writeHistory(history)
659 self.text_ctrl.writeHistory(history)
663 return
660 return
664 if event.KeyCode == wx.WXK_DOWN:
661 if event.KeyCode == wx.WXK_DOWN:
665 history = self.IP.historyForward()
662 history = self.IP.historyForward()
666 self.text_ctrl.writeHistory(history)
663 self.text_ctrl.writeHistory(history)
667 return
664 return
668 if event.KeyCode == wx.WXK_TAB:
665 if event.KeyCode == wx.WXK_TAB:
669 #if line empty we disable tab completion
666 #if line empty we disable tab completion
670 if not self.text_ctrl.getCurrentLine().strip():
667 if not self.text_ctrl.getCurrentLine().strip():
671 self.text_ctrl.write('\t')
668 self.text_ctrl.write('\t')
672 return
669 return
673 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
670 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
674 if len(possibilities) > 1:
671 if len(possibilities) > 1:
675 cur_slice = self.text_ctrl.getCurrentLine()
672 cur_slice = self.text_ctrl.getCurrentLine()
676 self.text_ctrl.write('\n')
673 self.text_ctrl.write('\n')
677 self.text_ctrl.writeCompletion(possibilities)
674 self.text_ctrl.writeCompletion(possibilities)
678 self.text_ctrl.write('\n')
675 self.text_ctrl.write('\n')
679
676
680 self.text_ctrl.showPrompt()
677 self.text_ctrl.showPrompt()
681 self.text_ctrl.write(cur_slice)
678 self.text_ctrl.write(cur_slice)
682 self.text_ctrl.changeLine(completed or cur_slice)
679 self.text_ctrl.changeLine(completed or cur_slice)
683
680
684 return
681 return
685 event.Skip()
682 event.Skip()
686
683
687 #---------------------------- Hook Section --------------------------------------------
684 #---------------------------- Hook Section --------------------------------------------
688 def updateHistoryTracker(self,command_line):
685 def updateHistoryTracker(self,command_line):
689 '''
686 '''
690 Default history tracker (does nothing)
687 Default history tracker (does nothing)
691 '''
688 '''
692 pass
689 pass
693
690
694 def setHistoryTrackerHook(self,func):
691 def setHistoryTrackerHook(self,func):
695 '''
692 '''
696 Define a new history tracker
693 Define a new history tracker
697 '''
694 '''
698 self.updateHistoryTracker = func
695 self.updateHistoryTracker = func
699 def updateStatusTracker(self,status):
696 def updateStatusTracker(self,status):
700 '''
697 '''
701 Default status tracker (does nothing)
698 Default status tracker (does nothing)
702 '''
699 '''
703 pass
700 pass
704
701
705 def setStatusTrackerHook(self,func):
702 def setStatusTrackerHook(self,func):
706 '''
703 '''
707 Define a new status tracker
704 Define a new status tracker
708 '''
705 '''
709 self.updateStatusTracker = func
706 self.updateStatusTracker = func
710
707
General Comments 0
You need to be logged in to leave comments. Login now