##// 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
2 import inspect
3 import ctypes
4
5
6 def _async_raise(tid, exctype):
7 """raises the exception, performs cleanup if needed"""
8 if not inspect.isclass(exctype):
9 raise TypeError("Only types can be raised (not instances)")
10 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
11 if res == 0:
12 raise ValueError("invalid thread id")
13 elif res != 1:
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"""
16 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
17 raise SystemError("PyThreadState_SetAsyncExc failed")
18
19
20 class Thread(threading.Thread):
21 def _get_my_tid(self):
22 """determines this (self's) thread id"""
23 if not self.isAlive():
24 raise threading.ThreadError("the thread is not active")
25
26 # do we have it cached?
27 if hasattr(self, "_thread_id"):
28 return self._thread_id
29
30 # no, look for it in the _active dict
31 for tid, tobj in threading._active.items():
32 if tobj is self:
33 self._thread_id = tid
34 return tid
35
36 raise AssertionError("could not determine the thread's id")
37
38 def raise_exc(self, exctype):
39 """raises the given exception type in the context of this thread"""
40 _async_raise(self._get_my_tid(), exctype)
41
42 def kill(self):
43 """raises SystemExit in the context of the given thread, which should
44 cause the thread to exit silently (unless caught)"""
45 self.raise_exc(SystemExit)
1 import threading
2 import inspect
3 import ctypes
4
5
6 def _async_raise(tid, exctype):
7 """raises the exception, performs cleanup if needed"""
8 if not inspect.isclass(exctype):
9 raise TypeError("Only types can be raised (not instances)")
10 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
11 if res == 0:
12 raise ValueError("invalid thread id")
13 elif res != 1:
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"""
16 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
17 raise SystemError("PyThreadState_SetAsyncExc failed")
18
19
20 class Thread(threading.Thread):
21 def _get_my_tid(self):
22 """determines this (self's) thread id"""
23 if not self.isAlive():
24 raise threading.ThreadError("the thread is not active")
25
26 # do we have it cached?
27 if hasattr(self, "_thread_id"):
28 return self._thread_id
29
30 # no, look for it in the _active dict
31 for tid, tobj in threading._active.items():
32 if tobj is self:
33 self._thread_id = tid
34 return tid
35
36 raise AssertionError("could not determine the thread's id")
37
38 def raise_exc(self, exctype):
39 """raises the given exception type in the context of this thread"""
40 _async_raise(self._get_my_tid(), exctype)
41
42 def kill(self):
43 """raises SystemExit in the context of the given thread, which should
44 cause the thread to exit silently (unless caught)"""
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 33 import os
34 34 import locale
35 35 import time
36 from ThreadEx import Thread
36 #from ThreadEx import Thread
37 37 from StringIO import StringIO
38
39 38 try:
40 39 import IPython
41 40 except Exception,e:
42 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.
47 Does not start a blocking event loop, instead allow single iterations.
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()
48 An IterableIPShell Thread that is WX dependent.
49 Thus it permits direct interaction with a WX GUI without OnIdle event state machine trick...
52 50 '''
53
54 def __init__(self,argv=[],user_ns=None,user_global_ns=None,
51 def __init__(self,wx_instance,
52 argv=[],user_ns={},user_global_ns=None,
55 53 cin=None, cout=None, cerr=None,
56 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):
192 """
193 Returns the IPython banner for useful info on IPython instance
194
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
56 user_ns['addGUIShortcut'] = self.addGUIShortcut
57 IterableIPShell.__init__(self,argv,user_ns,user_global_ns,
58 cin, cout, cerr,
59 exit_handler,time_loop)
311 60
312 def initHistoryIndex(self):
313 '''
314 set history to last command entered
315 '''
316 self._history_level = self._getHistoryMaxIndex()+1
61 # This creates a new Event class and a EVT binder function
62 (self.IPythonAskExitEvent, EVT_IP_ASK_EXIT) = wx.lib.newevent.NewEvent()
63 (self.IPythonAddButtonEvent, EVT_IP_ADD_BUTTON_EXIT) = wx.lib.newevent.NewEvent()
64 (self.IPythonExecuteDoneEvent, EVT_IP_EXECUTE_DONE) = wx.lib.newevent.NewEvent()
317 65
318 #----------------------- IPython PRIVATE management section ----------------------
319 def _setAskExit(self):
320 '''
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
66 wx_instance.Bind(EVT_IP_ASK_EXIT, wx_instance.exit_handler)
67 wx_instance.Bind(EVT_IP_ADD_BUTTON_EXIT, wx_instance.add_button_handler)
68 wx_instance.Bind(EVT_IP_EXECUTE_DONE, wx_instance.evtStateExecuteDone)
329 69
330 @return: history length
331 @rtype: int
332 '''
333 return len(self._IP.input_hist_raw)-1
70 self.wx_instance = wx_instance
71 self._IP.exit = self._AskExit
334 72
335 def _getHistory(self):
336 '''
337 Get's the command string of the current history level.
338
339 @return: Historic command string.
340 @rtype: string
341 '''
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
73 def addGUIShortcut(self,text,func):
74 evt = self.IPythonAddButtonEvent(button_info={'text':text,'func':self.wx_instance.doExecuteLine(func)})
75 wx.PostEvent(self.wx_instance, evt)
76
77 def _AskExit(self):
78 evt = self.IPythonAskExitEvent()
79 wx.PostEvent(self.wx_instance, evt)
360 80
361 @return: The current command line text.
362 @rtype: string
363 '''
364 return self._line_to_execute
81 def _afterExecute(self):
82 evt = self.IPythonExecuteDoneEvent()
83 wx.PostEvent(self.wx_instance, evt)
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 86 class WxConsoleView(stc.StyledTextCtrl):
422 87 '''
423 88 Specialized styled text control view for console-like workflow.
@@ -799,7 +464,8 b' class WxIPythonViewPanel(wx.Panel):'
799 464 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
800 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 470 Initialize.
805 471 Instanciate an IPython thread.
@@ -810,9 +476,14 b' class WxIPythonViewPanel(wx.Panel):'
810 476
811 477 ### IPython thread instanciation ###
812 478 self.cout = StringIO()
813 self.IP = IterableIPShell(cout=self.cout,cerr=self.cout,
814 exit_handler = exit_handler,
815 time_loop = 0.1)
479
480 self.add_button_handler = add_button_handler
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 487 self.IP.start()
817 488
818 489 ### IPython wx console view instanciation ###
@@ -859,57 +530,98 b' class WxIPythonViewPanel(wx.Panel):'
859 530 #wx.CallAfter(self.runStateMachine)
860 531
861 532 # This creates a new Event class and a EVT binder function
862 (self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
863
864 self.Bind(wx.EVT_IDLE, self.runStateMachine)
865 self.Bind(EVT_ASK_EXIT, exit_handler)
533 #(self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
534 #(self.AddButtonEvent, EVT_ADDBUTTON_EXIT) = wx.lib.newevent.NewEvent()
866 535
536
537 #self.Bind(wx.EVT_IDLE, self.runStateMachine)
538
867 539 def __del__(self):
868 540 self.IP.shutdown()
869 541 self.IP.join()
870 542 WxConsoleView.__del__()
871 543
872 544 #---------------------------- IPython Thread Management ---------------------------------------
873 def runStateMachine(self,event):
874 #print >>self.sys_stdout,"state:",self.cur_state
875 self.updateStatusTracker(self.cur_state)
876
877 if self.cur_state == 'DO_EXECUTE_LINE':
878 #print >>self.sys_stdout,"command:",self.getCurrentLine()
879 self.IP.doExecute(self.text_ctrl.getCurrentLine().replace('\t',' '*4))
880 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
881 self.cur_state = 'WAIT_END_OF_EXECUTION'
882
883 if self.cur_state == 'WAIT_END_OF_EXECUTION':
884 if self.IP.isExecuteDone():
885 self.doc = self.IP.getDocText()
886 if self.IP.getAskExit():
887 evt = self.AskExitEvent()
888 wx.PostEvent(self, evt)
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':
545 def stateDoExecuteLine(self):
546 #print >>self.sys_stdout,"command:",self.getCurrentLine()
547 self.doExecuteLine(self.text_ctrl.getCurrentLine())
548
549 def doExecuteLine(self,line):
550 print >>sys.__stdout__,"command:",line
551 self.IP.doExecute(line.replace('\t',' '*4))
552 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
553 self.cur_state = 'WAIT_END_OF_EXECUTION'
554
555
556 def evtStateExecuteDone(self,evt):
557 self.doc = self.IP.getDocText()
558 if self.doc:
559 self.pager_state = 'INIT'
560 self.cur_state = 'SHOW_DOC'
908 561 self.pager(self.doc)
909 if self.pager_state == 'DONE':
910 self.cur_state = 'SHOW_PROMPT'
562 #if self.pager_state == 'DONE':
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 626 #---------------------------- IPython pager ---------------------------------------
915 627 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
@@ -961,7 +673,8 b' class WxIPythonViewPanel(wx.Panel):'
961 673 for line in self.pager_lines[self.pager_index:]:
962 674 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
963 675 self.pager_nb_lines = 0
964 self.pager_state = 'DONE'
676 self.pager_state = 'DONE'
677 self.stateShowPrompt()
965 678
966 679 #---------------------------- Key Handler --------------------------------------------
967 680 def keyPress(self, event):
@@ -981,14 +694,17 b' class WxIPythonViewPanel(wx.Panel):'
981 694 if self.cur_state == 'IDLE':
982 695 #we change the state ot the state machine
983 696 self.cur_state = 'DO_EXECUTE_LINE'
697 self.stateDoExecuteLine()
984 698 return
985 699 if self.pager_state == 'WAITING':
986 700 self.pager_state = 'PROCESS_LINES'
701 self.pager(self.doc)
987 702 return
988 703
989 704 if event.GetKeyCode() in [ord('q'),ord('Q')]:
990 705 if self.pager_state == 'WAITING':
991 706 self.pager_state = 'DONE'
707 self.stateShowPrompt()
992 708 return
993 709
994 710 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
@@ -36,10 +36,10 b' class MyFrame(wx.Frame):'
36 36
37 37 #self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg,
38 38 # background_color = "WHITE")
39
39
40 40 self.ipython_panel.setHistoryTrackerHook(self.history_panel.write)
41 41 self.ipython_panel.setStatusTrackerHook(self.updateStatus)
42
42
43 43 self.statusbar = self.createStatus()
44 44 self.createMenu()
45 45
@@ -48,13 +48,11 b' class MyFrame(wx.Frame):'
48 48 # main panels
49 49 self._mgr.AddPane(self.ipython_panel , wx.CENTER, "IPython Shell")
50 50 self._mgr.AddPane(self.history_panel , wx.RIGHT, "IPython history")
51
51
52 52 # now we specify some panel characteristics
53 53 self._mgr.GetPane(self.ipython_panel).CaptionVisible(True);
54 54 self._mgr.GetPane(self.history_panel).CaptionVisible(True);
55 55 self._mgr.GetPane(self.history_panel).MinSize((200,400));
56
57
58 56
59 57 # tell the manager to "commit" all the changes just made
60 58 self._mgr.Update()
General Comments 0
You need to be logged in to leave comments. Login now