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