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