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