##// END OF EJS Templates
Cleaned up version with initial WX callback (callafter) support
ldufrechou -
Show More
@@ -0,0 +1,420 b''
1 #!/usr/bin/python
2 # -*- coding: iso-8859-15 -*-
3 '''
4 Provides IPython remote instance.
5
6 @author: Laurent Dufrechou
7 laurent.dufrechou _at_ gmail.com
8 @license: BSD
9
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
12 is available at U{http://www.opensource.org/licenses/bsd-license.php}
13 '''
14
15 __version__ = 0.9
16 __author__ = "Laurent Dufrechou"
17 __email__ = "laurent.dufrechou _at_ gmail.com"
18 __license__ = "BSD"
19
20 import re
21 import sys
22 import os
23 import locale
24 import time
25 from ThreadEx import Thread
26 from StringIO import StringIO
27
28 try:
29 import IPython
30 except Exception,e:
31 raise "Error importing IPython (%s)" % str(e)
32
33 class IterableIPShell(Thread):
34 '''
35 Create an IPython instance inside a dedicated thread.
36 Does not start a blocking event loop, instead allow single iterations.
37 This allows embedding in any GUI without blockage.
38 The thread is a slave one, in that it doesn't interact directly with the GUI.
39 Note Thread class comes from ThreadEx that supports asynchroneous function call
40 via raise_exc()
41 '''
42
43 def __init__(self,argv=[],user_ns={},user_global_ns=None,
44 cin=None, cout=None, cerr=None,
45 exit_handler=None,time_loop = 0.1):
46 '''
47 @param argv: Command line options for IPython
48 @type argv: list
49 @param user_ns: User namespace.
50 @type user_ns: dictionary
51 @param user_global_ns: User global namespace.
52 @type user_global_ns: dictionary.
53 @param cin: Console standard input.
54 @type cin: IO stream
55 @param cout: Console standard output.
56 @type cout: IO stream
57 @param cerr: Console standard error.
58 @type cerr: IO stream
59 @param exit_handler: Replacement for builtin exit() function
60 @type exit_handler: function
61 @param time_loop: Define the sleep time between two thread's loop
62 @type int
63 '''
64 Thread.__init__(self)
65
66 #first we redefine in/out/error functions of IPython
67 if cin:
68 IPython.Shell.Term.cin = cin
69 if cout:
70 IPython.Shell.Term.cout = cout
71 if cerr:
72 IPython.Shell.Term.cerr = cerr
73
74 # This is to get rid of the blockage that accurs during
75 # IPython.Shell.InteractiveShell.user_setup()
76 IPython.iplib.raw_input = lambda x: None
77
78 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
79
80 excepthook = sys.excepthook
81
82 self._IP = IPython.Shell.make_IPython(
83 argv,user_ns=user_ns,
84 user_global_ns=user_global_ns,
85 embedded=True,
86 shell_class=IPython.Shell.InteractiveShell)
87
88 #we replace IPython default encoding by wx locale encoding
89 loc = locale.getpreferredencoding()
90 if loc:
91 self._IP.stdin_encoding = loc
92 #we replace the ipython default pager by our pager
93 self._IP.set_hook('show_in_pager',self._pager)
94
95 #we replace the ipython default shell command caller by our shell handler
96 self._IP.set_hook('shell_hook',self._shell)
97
98 #we replace the ipython default input command caller by our method
99 IPython.iplib.raw_input_original = self._raw_input
100 #we replace the ipython default exit command by our method
101 self._IP.exit = self._setAskExit
102
103 sys.excepthook = excepthook
104
105 self._iter_more = 0
106 self._history_level = 0
107 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
108 self._prompt = str(self._IP.outputcache.prompt1).strip()
109
110 #thread working vars
111 self._terminate = False
112 self._time_loop = time_loop
113 self._has_doc = False
114 self._do_execute = False
115 self._line_to_execute = ''
116
117 #vars that will be checked by GUI loop to handle thread states...
118 #will be replaced later by PostEvent GUI funtions...
119 self._doc_text = None
120 self._ask_exit = False
121 self._add_button = None
122
123 #----------------------- Thread management section ----------------------
124 def run (self):
125 """
126 Thread main loop
127 The thread will run until self._terminate will be set to True via shutdown() function
128 Command processing can be interrupted with Instance.raise_exc(KeyboardInterrupt) call in the
129 GUI thread.
130 """
131 while(not self._terminate):
132 try:
133 if self._do_execute:
134 self._doc_text = None
135 self._execute()
136 self._do_execute = False
137 self._afterExecute() #used for uper class to generate event after execution
138
139 except KeyboardInterrupt:
140 pass
141
142 time.sleep(self._time_loop)
143
144 def shutdown(self):
145 """
146 Shutdown the tread
147 """
148 self._terminate = True
149
150 def doExecute(self,line):
151 """
152 Tell the thread to process the 'line' command
153 """
154 self._do_execute = True
155 self._line_to_execute = line
156
157 def isExecuteDone(self):
158 """
159 Returns the processing state
160 """
161 return not self._do_execute
162
163 #----------------------- IPython management section ----------------------
164 def getAskExit(self):
165 '''
166 returns the _ask_exit variable that can be checked by GUI to see if
167 IPython request an exit handling
168 '''
169 return self._ask_exit
170
171 def clearAskExit(self):
172 '''
173 clear the _ask_exit var when GUI as handled the request.
174 '''
175 self._ask_exit = False
176
177 def getDocText(self):
178 """
179 Returns the output of the processing that need to be paged (if any)
180
181 @return: The std output string.
182 @rtype: string
183 """
184 return self._doc_text
185
186 def getBanner(self):
187 """
188 Returns the IPython banner for useful info on IPython instance
189
190 @return: The banner string.
191 @rtype: string
192 """
193 return self._IP.BANNER
194
195 def getPromptCount(self):
196 """
197 Returns the prompt number.
198 Each time a user execute a line in the IPython shell the prompt count is increased
199
200 @return: The prompt number
201 @rtype: int
202 """
203 return self._IP.outputcache.prompt_count
204
205 def getPrompt(self):
206 """
207 Returns current prompt inside IPython instance
208 (Can be In [...]: ot ...:)
209
210 @return: The current prompt.
211 @rtype: string
212 """
213 return self._prompt
214
215 def getIndentation(self):
216 """
217 Returns the current indentation level
218 Usefull to put the caret at the good start position if we want to do autoindentation.
219
220 @return: The indentation level.
221 @rtype: int
222 """
223 return self._IP.indent_current_nsp
224
225 def updateNamespace(self, ns_dict):
226 '''
227 Add the current dictionary to the shell namespace.
228
229 @param ns_dict: A dictionary of symbol-values.
230 @type ns_dict: dictionary
231 '''
232 self._IP.user_ns.update(ns_dict)
233
234 def complete(self, line):
235 '''
236 Returns an auto completed line and/or posibilities for completion.
237
238 @param line: Given line so far.
239 @type line: string
240
241 @return: Line completed as for as possible,
242 and possible further completions.
243 @rtype: tuple
244 '''
245 split_line = self._complete_sep.split(line)
246 possibilities = self._IP.complete(split_line[-1])
247 if possibilities:
248
249 def _commonPrefix(str1, str2):
250 '''
251 Reduction function. returns common prefix of two given strings.
252
253 @param str1: First string.
254 @type str1: string
255 @param str2: Second string
256 @type str2: string
257
258 @return: Common prefix to both strings.
259 @rtype: string
260 '''
261 for i in range(len(str1)):
262 if not str2.startswith(str1[:i+1]):
263 return str1[:i]
264 return str1
265 common_prefix = reduce(_commonPrefix, possibilities)
266 completed = line[:-len(split_line[-1])]+common_prefix
267 else:
268 completed = line
269 return completed, possibilities
270
271 def historyBack(self):
272 '''
273 Provides one history command back.
274
275 @return: The command string.
276 @rtype: string
277 '''
278 history = ''
279 #the below while loop is used to suppress empty history lines
280 while((history == '' or history == '\n') and self._history_level >0):
281 if self._history_level>=1:
282 self._history_level -= 1
283 history = self._getHistory()
284 return history
285
286 def historyForward(self):
287 '''
288 Provides one history command forward.
289
290 @return: The command string.
291 @rtype: string
292 '''
293 history = ''
294 #the below while loop is used to suppress empty history lines
295 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
296 if self._history_level < self._getHistoryMaxIndex():
297 self._history_level += 1
298 history = self._getHistory()
299 else:
300 if self._history_level == self._getHistoryMaxIndex():
301 history = self._getHistory()
302 self._history_level += 1
303 else:
304 history = ''
305 return history
306
307 def initHistoryIndex(self):
308 '''
309 set history to last command entered
310 '''
311 self._history_level = self._getHistoryMaxIndex()+1
312
313 #----------------------- IPython PRIVATE management section ----------------------
314 def _afterExecute(self):
315 '''
316 Can be redefined to generate post event after excution is done
317 '''
318 pass
319
320 def _setAskExit(self):
321 '''
322 set the _ask_exit variable that can be cjhecked by GUI to see if
323 IPython request an exit handling
324 '''
325 self._ask_exit = True
326
327 def _getHistoryMaxIndex(self):
328 '''
329 returns the max length of the history buffer
330
331 @return: history length
332 @rtype: int
333 '''
334 return len(self._IP.input_hist_raw)-1
335
336 def _getHistory(self):
337 '''
338 Get's the command string of the current history level.
339
340 @return: Historic command string.
341 @rtype: string
342 '''
343 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
344 return rv
345
346 def _pager(self,IP,text):
347 '''
348 This function is used as a callback replacment to IPython pager function
349
350 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
351 function.
352 '''
353 self._doc_text = text
354
355 def _raw_input(self, prompt=''):
356 '''
357 Custom raw_input() replacement. Get's current line from console buffer.
358
359 @param prompt: Prompt to print. Here for compatability as replacement.
360 @type prompt: string
361
362 @return: The current command line text.
363 @rtype: string
364 '''
365 return self._line_to_execute
366
367 def _execute(self):
368 '''
369 Executes the current line provided by the shell object.
370 '''
371 orig_stdout = sys.stdout
372 sys.stdout = IPython.Shell.Term.cout
373
374 try:
375 line = self._IP.raw_input(None, self._iter_more)
376 if self._IP.autoindent:
377 self._IP.readline_startup_hook(None)
378
379 except KeyboardInterrupt:
380 self._IP.write('\nKeyboardInterrupt\n')
381 self._IP.resetbuffer()
382 # keep cache in sync with the prompt counter:
383 self._IP.outputcache.prompt_count -= 1
384
385 if self._IP.autoindent:
386 self._IP.indent_current_nsp = 0
387 self._iter_more = 0
388 except:
389 self._IP.showtraceback()
390 else:
391 self._iter_more = self._IP.push(line)
392 if (self._IP.SyntaxTB.last_syntax_error and
393 self._IP.rc.autoedit_syntax):
394 self._IP.edit_syntax_error()
395 if self._iter_more:
396 self._prompt = str(self._IP.outputcache.prompt2).strip()
397 if self._IP.autoindent:
398 self._IP.readline_startup_hook(self._IP.pre_readline)
399 else:
400 self._prompt = str(self._IP.outputcache.prompt1).strip()
401 self._IP.indent_current_nsp = 0 #we set indentation to 0
402 sys.stdout = orig_stdout
403
404 def _shell(self, ip, cmd):
405 '''
406 Replacement method to allow shell commands without them blocking.
407
408 @param ip: Ipython instance, same as self._IP
409 @type cmd: Ipython instance
410 @param cmd: Shell command to execute.
411 @type cmd: string
412 '''
413 stdin, stdout = os.popen4(cmd)
414 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
415 #we use print command because the shell command is called inside IPython instance and thus is
416 #redirected to thread cout
417 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
418 print "\x01\x1b[1;36m\x02"+result
419 stdout.close()
420 stdin.close()
@@ -1,45 +1,45 b''
1 import threading
1 import threading
2 import inspect
2 import inspect
3 import ctypes
3 import ctypes
4
4
5
5
6 def _async_raise(tid, exctype):
6 def _async_raise(tid, exctype):
7 """raises the exception, performs cleanup if needed"""
7 """raises the exception, performs cleanup if needed"""
8 if not inspect.isclass(exctype):
8 if not inspect.isclass(exctype):
9 raise TypeError("Only types can be raised (not instances)")
9 raise TypeError("Only types can be raised (not instances)")
10 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
10 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
11 if res == 0:
11 if res == 0:
12 raise ValueError("invalid thread id")
12 raise ValueError("invalid thread id")
13 elif res != 1:
13 elif res != 1:
14 # """if it returns a number greater than one, you're in trouble,
14 # """if it returns a number greater than one, you're in trouble,
15 # and you should call it again with exc=NULL to revert the effect"""
15 # and you should call it again with exc=NULL to revert the effect"""
16 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
16 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
17 raise SystemError("PyThreadState_SetAsyncExc failed")
17 raise SystemError("PyThreadState_SetAsyncExc failed")
18
18
19
19
20 class Thread(threading.Thread):
20 class Thread(threading.Thread):
21 def _get_my_tid(self):
21 def _get_my_tid(self):
22 """determines this (self's) thread id"""
22 """determines this (self's) thread id"""
23 if not self.isAlive():
23 if not self.isAlive():
24 raise threading.ThreadError("the thread is not active")
24 raise threading.ThreadError("the thread is not active")
25
25
26 # do we have it cached?
26 # do we have it cached?
27 if hasattr(self, "_thread_id"):
27 if hasattr(self, "_thread_id"):
28 return self._thread_id
28 return self._thread_id
29
29
30 # no, look for it in the _active dict
30 # no, look for it in the _active dict
31 for tid, tobj in threading._active.items():
31 for tid, tobj in threading._active.items():
32 if tobj is self:
32 if tobj is self:
33 self._thread_id = tid
33 self._thread_id = tid
34 return tid
34 return tid
35
35
36 raise AssertionError("could not determine the thread's id")
36 raise AssertionError("could not determine the thread's id")
37
37
38 def raise_exc(self, exctype):
38 def raise_exc(self, exctype):
39 """raises the given exception type in the context of this thread"""
39 """raises the given exception type in the context of this thread"""
40 _async_raise(self._get_my_tid(), exctype)
40 _async_raise(self._get_my_tid(), exctype)
41
41
42 def kill(self):
42 def kill(self):
43 """raises SystemExit in the context of the given thread, which should
43 """raises SystemExit in the context of the given thread, which should
44 cause the thread to exit silently (unless caught)"""
44 cause the thread to exit silently (unless caught)"""
45 self.raise_exc(SystemExit)
45 self.raise_exc(SystemExit)
This diff has been collapsed as it changes many lines, (544 lines changed) Show them Hide them
@@ -33,391 +33,56 b' 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
39 try:
38 try:
40 import IPython
39 import IPython
41 except Exception,e:
40 except Exception,e:
42 raise "Error importing IPython (%s)" % str(e)
41 raise "Error importing IPython (%s)" % str(e)
43
42
44 class IterableIPShell(Thread):
43
44 from ipython_interactive_shell import *
45
46 class WxIterableIPShell(IterableIPShell):
45 '''
47 '''
46 Create an IPython instance inside a dedicated thread.
48 An IterableIPShell Thread that is WX dependent.
47 Does not start a blocking event loop, instead allow single iterations.
49 Thus it permits direct interaction with a WX GUI without OnIdle event state machine trick...
48 This allows embedding in any GUI without blockage.
49 The thread is a slave one, in that it doesn't interact directly with the GUI.
50 Note Thread class comes from ThreadEx that supports asynchroneous function call
51 via raise_exc()
52 '''
50 '''
53
51 def __init__(self,wx_instance,
54 def __init__(self,argv=[],user_ns=None,user_global_ns=None,
52 argv=[],user_ns={},user_global_ns=None,
55 cin=None, cout=None, cerr=None,
53 cin=None, cout=None, cerr=None,
56 exit_handler=None,time_loop = 0.1):
54 exit_handler=None,time_loop = 0.1):
57 '''
58 @param argv: Command line options for IPython
59 @type argv: list
60 @param user_ns: User namespace.
61 @type user_ns: dictionary
62 @param user_global_ns: User global namespace.
63 @type user_global_ns: dictionary.
64 @param cin: Console standard input.
65 @type cin: IO stream
66 @param cout: Console standard output.
67 @type cout: IO stream
68 @param cerr: Console standard error.
69 @type cerr: IO stream
70 @param exit_handler: Replacement for builtin exit() function
71 @type exit_handler: function
72 @param time_loop: Define the sleep time between two thread's loop
73 @type int
74 '''
75 Thread.__init__(self)
76
77 #first we redefine in/out/error functions of IPython
78 if cin:
79 IPython.Shell.Term.cin = cin
80 if cout:
81 IPython.Shell.Term.cout = cout
82 if cerr:
83 IPython.Shell.Term.cerr = cerr
84
85 # This is to get rid of the blockage that accurs during
86 # IPython.Shell.InteractiveShell.user_setup()
87 IPython.iplib.raw_input = lambda x: None
88
89 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
90
91 excepthook = sys.excepthook
92 self._IP = IPython.Shell.make_IPython(
93 argv,user_ns=user_ns,
94 user_global_ns=user_global_ns,
95 embedded=True,
96 shell_class=IPython.Shell.InteractiveShell)
97
98 #we replace IPython default encoding by wx locale encoding
99 loc = locale.getpreferredencoding()
100 if loc:
101 self._IP.stdin_encoding = loc
102 #we replace the ipython default pager by our pager
103 self._IP.set_hook('show_in_pager',self._pager)
104
105 #we replace the ipython default shell command caller by our shell handler
106 self._IP.set_hook('shell_hook',self._shell)
107
108 #we replace the ipython default input command caller by our method
109 IPython.iplib.raw_input_original = self._raw_input
110 #we replace the ipython default exit command by our method
111 self._IP.exit = self._setAskExit
112
113 sys.excepthook = excepthook
114
115 self._iter_more = 0
116 self._history_level = 0
117 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
118 self._prompt = str(self._IP.outputcache.prompt1).strip()
119
120 #thread working vars
121 self._terminate = False
122 self._time_loop = time_loop
123 self._has_doc = False
124 self._do_execute = False
125 self._line_to_execute = ''
126 self._doc_text = None
127 self._ask_exit = False
128
129 #----------------------- Thread management section ----------------------
130 def run (self):
131 """
132 Thread main loop
133 The thread will run until self._terminate will be set to True via shutdown() function
134 Command processing can be interrupted with Instance.raise_exc(KeyboardInterrupt) call in the
135 GUI thread.
136 """
137 while(not self._terminate):
138 try:
139 if self._do_execute:
140 self._doc_text = None
141 self._execute()
142 self._do_execute = False
143
144 except KeyboardInterrupt:
145 pass
146
147 time.sleep(self._time_loop)
148
149 def shutdown(self):
150 """
151 Shutdown the tread
152 """
153 self._terminate = True
154
155 def doExecute(self,line):
156 """
157 Tell the thread to process the 'line' command
158 """
159 self._do_execute = True
160 self._line_to_execute = line
161
162 def isExecuteDone(self):
163 """
164 Returns the processing state
165 """
166 return not self._do_execute
167
168 #----------------------- IPython management section ----------------------
169 def getAskExit(self):
170 '''
171 returns the _ask_exit variable that can be checked by GUI to see if
172 IPython request an exit handling
173 '''
174 return self._ask_exit
175
176 def clearAskExit(self):
177 '''
178 clear the _ask_exit var when GUI as handled the request.
179 '''
180 self._ask_exit = False
181
182 def getDocText(self):
183 """
184 Returns the output of the processing that need to be paged (if any)
185
186 @return: The std output string.
187 @rtype: string
188 """
189 return self._doc_text
190
55
191 def getBanner(self):
56 user_ns['addGUIShortcut'] = self.addGUIShortcut
192 """
57 IterableIPShell.__init__(self,argv,user_ns,user_global_ns,
193 Returns the IPython banner for useful info on IPython instance
58 cin, cout, cerr,
194
59 exit_handler,time_loop)
195 @return: The banner string.
196 @rtype: string
197 """
198 return self._IP.BANNER
199
200 def getPromptCount(self):
201 """
202 Returns the prompt number.
203 Each time a user execute a line in the IPython shell the prompt count is increased
204
205 @return: The prompt number
206 @rtype: int
207 """
208 return self._IP.outputcache.prompt_count
209
210 def getPrompt(self):
211 """
212 Returns current prompt inside IPython instance
213 (Can be In [...]: ot ...:)
214
215 @return: The current prompt.
216 @rtype: string
217 """
218 return self._prompt
219
220 def getIndentation(self):
221 """
222 Returns the current indentation level
223 Usefull to put the caret at the good start position if we want to do autoindentation.
224
225 @return: The indentation level.
226 @rtype: int
227 """
228 return self._IP.indent_current_nsp
229
230 def updateNamespace(self, ns_dict):
231 '''
232 Add the current dictionary to the shell namespace.
233
234 @param ns_dict: A dictionary of symbol-values.
235 @type ns_dict: dictionary
236 '''
237 self._IP.user_ns.update(ns_dict)
238
239 def complete(self, line):
240 '''
241 Returns an auto completed line and/or posibilities for completion.
242
243 @param line: Given line so far.
244 @type line: string
245
246 @return: Line completed as for as possible,
247 and possible further completions.
248 @rtype: tuple
249 '''
250 split_line = self._complete_sep.split(line)
251 possibilities = self._IP.complete(split_line[-1])
252 if possibilities:
253
254 def _commonPrefix(str1, str2):
255 '''
256 Reduction function. returns common prefix of two given strings.
257
258 @param str1: First string.
259 @type str1: string
260 @param str2: Second string
261 @type str2: string
262
263 @return: Common prefix to both strings.
264 @rtype: string
265 '''
266 for i in range(len(str1)):
267 if not str2.startswith(str1[:i+1]):
268 return str1[:i]
269 return str1
270 common_prefix = reduce(_commonPrefix, possibilities)
271 completed = line[:-len(split_line[-1])]+common_prefix
272 else:
273 completed = line
274 return completed, possibilities
275
276 def historyBack(self):
277 '''
278 Provides one history command back.
279
280 @return: The command string.
281 @rtype: string
282 '''
283 history = ''
284 #the below while loop is used to suppress empty history lines
285 while((history == '' or history == '\n') and self._history_level >0):
286 if self._history_level>=1:
287 self._history_level -= 1
288 history = self._getHistory()
289 return history
290
291 def historyForward(self):
292 '''
293 Provides one history command forward.
294
295 @return: The command string.
296 @rtype: string
297 '''
298 history = ''
299 #the below while loop is used to suppress empty history lines
300 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
301 if self._history_level < self._getHistoryMaxIndex():
302 self._history_level += 1
303 history = self._getHistory()
304 else:
305 if self._history_level == self._getHistoryMaxIndex():
306 history = self._getHistory()
307 self._history_level += 1
308 else:
309 history = ''
310 return history
311
60
312 def initHistoryIndex(self):
61 # This creates a new Event class and a EVT binder function
313 '''
62 (self.IPythonAskExitEvent, EVT_IP_ASK_EXIT) = wx.lib.newevent.NewEvent()
314 set history to last command entered
63 (self.IPythonAddButtonEvent, EVT_IP_ADD_BUTTON_EXIT) = wx.lib.newevent.NewEvent()
315 '''
64 (self.IPythonExecuteDoneEvent, EVT_IP_EXECUTE_DONE) = wx.lib.newevent.NewEvent()
316 self._history_level = self._getHistoryMaxIndex()+1
317
65
318 #----------------------- IPython PRIVATE management section ----------------------
66 wx_instance.Bind(EVT_IP_ASK_EXIT, wx_instance.exit_handler)
319 def _setAskExit(self):
67 wx_instance.Bind(EVT_IP_ADD_BUTTON_EXIT, wx_instance.add_button_handler)
320 '''
68 wx_instance.Bind(EVT_IP_EXECUTE_DONE, wx_instance.evtStateExecuteDone)
321 set the _ask_exit variable that can be cjhecked by GUI to see if
322 IPython request an exit handling
323 '''
324 self._ask_exit = True
325
326 def _getHistoryMaxIndex(self):
327 '''
328 returns the max length of the history buffer
329
69
330 @return: history length
70 self.wx_instance = wx_instance
331 @rtype: int
71 self._IP.exit = self._AskExit
332 '''
333 return len(self._IP.input_hist_raw)-1
334
72
335 def _getHistory(self):
73 def addGUIShortcut(self,text,func):
336 '''
74 evt = self.IPythonAddButtonEvent(button_info={'text':text,'func':self.wx_instance.doExecuteLine(func)})
337 Get's the command string of the current history level.
75 wx.PostEvent(self.wx_instance, evt)
338
76
339 @return: Historic command string.
77 def _AskExit(self):
340 @rtype: string
78 evt = self.IPythonAskExitEvent()
341 '''
79 wx.PostEvent(self.wx_instance, evt)
342 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
343 return rv
344
345 def _pager(self,IP,text):
346 '''
347 This function is used as a callback replacment to IPython pager function
348
349 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
350 function.
351 '''
352 self._doc_text = text
353
354 def _raw_input(self, prompt=''):
355 '''
356 Custom raw_input() replacement. Get's current line from console buffer.
357
358 @param prompt: Prompt to print. Here for compatability as replacement.
359 @type prompt: string
360
80
361 @return: The current command line text.
81 def _afterExecute(self):
362 @rtype: string
82 evt = self.IPythonExecuteDoneEvent()
363 '''
83 wx.PostEvent(self.wx_instance, evt)
364 return self._line_to_execute
365
84
366 def _execute(self):
367 '''
368 Executes the current line provided by the shell object.
369 '''
370 orig_stdout = sys.stdout
371 sys.stdout = IPython.Shell.Term.cout
372
85
373 try:
374 line = self._IP.raw_input(None, self._iter_more)
375 if self._IP.autoindent:
376 self._IP.readline_startup_hook(None)
377
378 except KeyboardInterrupt:
379 self._IP.write('\nKeyboardInterrupt\n')
380 self._IP.resetbuffer()
381 # keep cache in sync with the prompt counter:
382 self._IP.outputcache.prompt_count -= 1
383
384 if self._IP.autoindent:
385 self._IP.indent_current_nsp = 0
386 self._iter_more = 0
387 except:
388 self._IP.showtraceback()
389 else:
390 self._iter_more = self._IP.push(line)
391 if (self._IP.SyntaxTB.last_syntax_error and
392 self._IP.rc.autoedit_syntax):
393 self._IP.edit_syntax_error()
394 if self._iter_more:
395 self._prompt = str(self._IP.outputcache.prompt2).strip()
396 if self._IP.autoindent:
397 self._IP.readline_startup_hook(self._IP.pre_readline)
398 else:
399 self._prompt = str(self._IP.outputcache.prompt1).strip()
400 self._IP.indent_current_nsp = 0 #we set indentation to 0
401 sys.stdout = orig_stdout
402
403 def _shell(self, ip, cmd):
404 '''
405 Replacement method to allow shell commands without them blocking.
406
407 @param ip: Ipython instance, same as self._IP
408 @type cmd: Ipython instance
409 @param cmd: Shell command to execute.
410 @type cmd: string
411 '''
412 stdin, stdout = os.popen4(cmd)
413 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
414 #we use print command because the shell command is called inside IPython instance and thus is
415 #redirected to thread cout
416 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
417 print "\x01\x1b[1;36m\x02"+result
418 stdout.close()
419 stdin.close()
420
421 class WxConsoleView(stc.StyledTextCtrl):
86 class WxConsoleView(stc.StyledTextCtrl):
422 '''
87 '''
423 Specialized styled text control view for console-like workflow.
88 Specialized styled text control view for console-like workflow.
@@ -799,7 +464,8 b' class WxIPythonViewPanel(wx.Panel):'
799 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
464 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
800 Any idea to make it more 'genric' welcomed.
465 Any idea to make it more 'genric' welcomed.
801 '''
466 '''
802 def __init__(self,parent,exit_handler=None,intro=None,background_color="BLACK"):
467 def __init__(self,parent,exit_handler=None,intro=None,
468 background_color="BLACK",add_button_handler=None):
803 '''
469 '''
804 Initialize.
470 Initialize.
805 Instanciate an IPython thread.
471 Instanciate an IPython thread.
@@ -810,9 +476,14 b' class WxIPythonViewPanel(wx.Panel):'
810
476
811 ### IPython thread instanciation ###
477 ### IPython thread instanciation ###
812 self.cout = StringIO()
478 self.cout = StringIO()
813 self.IP = IterableIPShell(cout=self.cout,cerr=self.cout,
479
814 exit_handler = exit_handler,
480 self.add_button_handler = add_button_handler
815 time_loop = 0.1)
481 self.exit_handler = exit_handler
482
483 self.IP = WxIterableIPShell(self,
484 cout=self.cout,cerr=self.cout,
485 exit_handler = exit_handler,
486 time_loop = 0.1)
816 self.IP.start()
487 self.IP.start()
817
488
818 ### IPython wx console view instanciation ###
489 ### IPython wx console view instanciation ###
@@ -859,57 +530,98 b' class WxIPythonViewPanel(wx.Panel):'
859 #wx.CallAfter(self.runStateMachine)
530 #wx.CallAfter(self.runStateMachine)
860
531
861 # This creates a new Event class and a EVT binder function
532 # This creates a new Event class and a EVT binder function
862 (self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
533 #(self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
863
534 #(self.AddButtonEvent, EVT_ADDBUTTON_EXIT) = wx.lib.newevent.NewEvent()
864 self.Bind(wx.EVT_IDLE, self.runStateMachine)
865 self.Bind(EVT_ASK_EXIT, exit_handler)
866
535
536
537 #self.Bind(wx.EVT_IDLE, self.runStateMachine)
538
867 def __del__(self):
539 def __del__(self):
868 self.IP.shutdown()
540 self.IP.shutdown()
869 self.IP.join()
541 self.IP.join()
870 WxConsoleView.__del__()
542 WxConsoleView.__del__()
871
543
872 #---------------------------- IPython Thread Management ---------------------------------------
544 #---------------------------- IPython Thread Management ---------------------------------------
873 def runStateMachine(self,event):
545 def stateDoExecuteLine(self):
874 #print >>self.sys_stdout,"state:",self.cur_state
546 #print >>self.sys_stdout,"command:",self.getCurrentLine()
875 self.updateStatusTracker(self.cur_state)
547 self.doExecuteLine(self.text_ctrl.getCurrentLine())
876
548
877 if self.cur_state == 'DO_EXECUTE_LINE':
549 def doExecuteLine(self,line):
878 #print >>self.sys_stdout,"command:",self.getCurrentLine()
550 print >>sys.__stdout__,"command:",line
879 self.IP.doExecute(self.text_ctrl.getCurrentLine().replace('\t',' '*4))
551 self.IP.doExecute(line.replace('\t',' '*4))
880 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
552 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
881 self.cur_state = 'WAIT_END_OF_EXECUTION'
553 self.cur_state = 'WAIT_END_OF_EXECUTION'
882
554
883 if self.cur_state == 'WAIT_END_OF_EXECUTION':
555
884 if self.IP.isExecuteDone():
556 def evtStateExecuteDone(self,evt):
885 self.doc = self.IP.getDocText()
557 self.doc = self.IP.getDocText()
886 if self.IP.getAskExit():
558 if self.doc:
887 evt = self.AskExitEvent()
559 self.pager_state = 'INIT'
888 wx.PostEvent(self, evt)
560 self.cur_state = 'SHOW_DOC'
889 self.IP.clearAskExit()
890 if self.doc:
891 self.pager_state = 'INIT'
892 self.cur_state = 'SHOW_DOC'
893 else:
894 self.cur_state = 'SHOW_PROMPT'
895
896 if self.cur_state == 'SHOW_PROMPT':
897 self.text_ctrl.setPrompt(self.IP.getPrompt())
898 self.text_ctrl.setIndentation(self.IP.getIndentation())
899 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
900 rv = self.cout.getvalue()
901 if rv: rv = rv.strip('\n')
902 self.text_ctrl.showReturned(rv)
903 self.cout.truncate(0)
904 self.IP.initHistoryIndex()
905 self.cur_state = 'IDLE'
906
907 if self.cur_state == 'SHOW_DOC':
908 self.pager(self.doc)
561 self.pager(self.doc)
909 if self.pager_state == 'DONE':
562 #if self.pager_state == 'DONE':
910 self.cur_state = 'SHOW_PROMPT'
911
563
912 event.Skip()
564 else:
565 self.stateShowPrompt()
566
567 def stateShowPrompt(self):
568 self.cur_state = 'SHOW_PROMPT'
569 self.text_ctrl.setPrompt(self.IP.getPrompt())
570 self.text_ctrl.setIndentation(self.IP.getIndentation())
571 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
572 rv = self.cout.getvalue()
573 if rv: rv = rv.strip('\n')
574 self.text_ctrl.showReturned(rv)
575 self.cout.truncate(0)
576 self.IP.initHistoryIndex()
577 self.cur_state = 'IDLE'
578
579 ## def runStateMachine(self,event):
580 ## #print >>self.sys_stdout,"state:",self.cur_state
581 ## self.updateStatusTracker(self.cur_state)
582 ##
583 ## #if self.cur_state == 'DO_EXECUTE_LINE':
584 ## # self.doExecuteLine()
585 ##
586 ## if self.cur_state == 'WAIT_END_OF_EXECUTION':
587 ## if self.IP.isExecuteDone():
588 ## #self.button = self.IP.getAddButton()
589 ## #if self.IP.getAskExit():
590 ## # evt = self.AskExitEvent()
591 ## # wx.PostEvent(self, evt)
592 ## # self.IP.clearAskExit()
593 ## self.doc = self.IP.getDocText()
594 ## if self.doc:
595 ## self.pager_state = 'INIT'
596 ## self.cur_state = 'SHOW_DOC'
597 ## #if self.button:
598 ## #self.IP.doExecute('print "cool"')#self.button['func'])
599 ## #self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
600 ##
601 ## # self.button['func']='print "cool!"'
602 ## # self.add_button_handler(self.button)
603 ## # self.IP.shortcutProcessed()
604 ##
605 ## else:
606 ## self.cur_state = 'SHOW_PROMPT'
607 ##
608 ## if self.cur_state == 'SHOW_PROMPT':
609 ## self.text_ctrl.setPrompt(self.IP.getPrompt())
610 ## self.text_ctrl.setIndentation(self.IP.getIndentation())
611 ## self.text_ctrl.setPromptCount(self.IP.getPromptCount())
612 ## rv = self.cout.getvalue()
613 ## if rv: rv = rv.strip('\n')
614 ## self.text_ctrl.showReturned(rv)
615 ## self.cout.truncate(0)
616 ## self.IP.initHistoryIndex()
617 ## self.cur_state = 'IDLE'
618 ##
619 ## if self.cur_state == 'SHOW_DOC':
620 ## self.pager(self.doc)
621 ## if self.pager_state == 'DONE':
622 ## self.cur_state = 'SHOW_PROMPT'
623 ##
624 ## event.Skip()
913
625
914 #---------------------------- IPython pager ---------------------------------------
626 #---------------------------- IPython pager ---------------------------------------
915 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
627 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
@@ -961,7 +673,8 b' class WxIPythonViewPanel(wx.Panel):'
961 for line in self.pager_lines[self.pager_index:]:
673 for line in self.pager_lines[self.pager_index:]:
962 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
674 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
963 self.pager_nb_lines = 0
675 self.pager_nb_lines = 0
964 self.pager_state = 'DONE'
676 self.pager_state = 'DONE'
677 self.stateShowPrompt()
965
678
966 #---------------------------- Key Handler --------------------------------------------
679 #---------------------------- Key Handler --------------------------------------------
967 def keyPress(self, event):
680 def keyPress(self, event):
@@ -981,14 +694,17 b' class WxIPythonViewPanel(wx.Panel):'
981 if self.cur_state == 'IDLE':
694 if self.cur_state == 'IDLE':
982 #we change the state ot the state machine
695 #we change the state ot the state machine
983 self.cur_state = 'DO_EXECUTE_LINE'
696 self.cur_state = 'DO_EXECUTE_LINE'
697 self.stateDoExecuteLine()
984 return
698 return
985 if self.pager_state == 'WAITING':
699 if self.pager_state == 'WAITING':
986 self.pager_state = 'PROCESS_LINES'
700 self.pager_state = 'PROCESS_LINES'
701 self.pager(self.doc)
987 return
702 return
988
703
989 if event.GetKeyCode() in [ord('q'),ord('Q')]:
704 if event.GetKeyCode() in [ord('q'),ord('Q')]:
990 if self.pager_state == 'WAITING':
705 if self.pager_state == 'WAITING':
991 self.pager_state = 'DONE'
706 self.pager_state = 'DONE'
707 self.stateShowPrompt()
992 return
708 return
993
709
994 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
710 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
@@ -36,10 +36,10 b' class MyFrame(wx.Frame):'
36
36
37 #self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg,
37 #self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg,
38 # background_color = "WHITE")
38 # background_color = "WHITE")
39
39
40 self.ipython_panel.setHistoryTrackerHook(self.history_panel.write)
40 self.ipython_panel.setHistoryTrackerHook(self.history_panel.write)
41 self.ipython_panel.setStatusTrackerHook(self.updateStatus)
41 self.ipython_panel.setStatusTrackerHook(self.updateStatus)
42
42
43 self.statusbar = self.createStatus()
43 self.statusbar = self.createStatus()
44 self.createMenu()
44 self.createMenu()
45
45
@@ -48,13 +48,11 b' class MyFrame(wx.Frame):'
48 # main panels
48 # main panels
49 self._mgr.AddPane(self.ipython_panel , wx.CENTER, "IPython Shell")
49 self._mgr.AddPane(self.ipython_panel , wx.CENTER, "IPython Shell")
50 self._mgr.AddPane(self.history_panel , wx.RIGHT, "IPython history")
50 self._mgr.AddPane(self.history_panel , wx.RIGHT, "IPython history")
51
51
52 # now we specify some panel characteristics
52 # now we specify some panel characteristics
53 self._mgr.GetPane(self.ipython_panel).CaptionVisible(True);
53 self._mgr.GetPane(self.ipython_panel).CaptionVisible(True);
54 self._mgr.GetPane(self.history_panel).CaptionVisible(True);
54 self._mgr.GetPane(self.history_panel).CaptionVisible(True);
55 self._mgr.GetPane(self.history_panel).MinSize((200,400));
55 self._mgr.GetPane(self.history_panel).MinSize((200,400));
56
57
58
56
59 # tell the manager to "commit" all the changes just made
57 # tell the manager to "commit" all the changes just made
60 self._mgr.Update()
58 self._mgr.Update()
General Comments 0
You need to be logged in to leave comments. Login now