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