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 |
|
|
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, |
|
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 |
|
545 | def stateDoExecuteLine(self): | |
874 |
#print >>self.sys_stdout," |
|
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 |
|
|
550 | print >>sys.__stdout__,"command:",line | |
879 |
|
|
551 | self.IP.doExecute(line.replace('\t',' '*4)) | |
880 |
|
|
552 | self.updateHistoryTracker(self.text_ctrl.getCurrentLine()) | |
881 |
|
|
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 |
|
|
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 |
e |
|
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