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