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