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