##// END OF EJS Templates
some more little cleanup and a bug correction with prompt that comes with last commit
laurent.dufrechou@gmail.com -
Show More
@@ -1,533 +1,537 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 by our shell handler
174 #we replace the ipython default shell command caller
175 #by our shell handler
175 self._IP.set_hook('shell_hook', self._shell)
176 self._IP.set_hook('shell_hook', self._shell)
176
177
177 #we replace the ipython default input command caller by our method
178 #we replace the ipython default input command caller by our method
178 IPython.iplib.raw_input_original = self._raw_input
179 IPython.iplib.raw_input_original = self._raw_input
179 #we replace the ipython default exit command by our method
180 #we replace the ipython default exit command by our method
180 self._IP.exit = ask_exit_handler
181 self._IP.exit = ask_exit_handler
181 #we replace the help command
182 #we replace the help command
182 self._IP.user_ns['help'] = _Helper(self._pager_help)
183 self._IP.user_ns['help'] = _Helper(self._pager_help)
183
184
184 #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.
185 #import IPython.ipapi
186 #import IPython.ipapi
186 ip = IPython.ipapi.get()
187 ip = IPython.ipapi.get()
187 def bypass_magic(self, arg):
188 def bypass_magic(self, arg):
188 print '%this magic is currently disabled.'
189 print '%this magic is currently disabled.'
189 ip.expose_magic('cpaste', bypass_magic)
190 ip.expose_magic('cpaste', bypass_magic)
190
191
191 def reset_magic(self, arg):
192 def reset_magic(self, arg):
192 """Resets the namespace by removing all names defined by the user.
193 """Resets the namespace by removing all names defined by the user.
193
194
194 Input/Output history are left around in case you need them."""
195 Input/Output history are left around in case you need them."""
195
196
196 ans = True ##todo find away to ask the user...
197 ans = True ##todo find away to ask the user...
197 ##seems hard to do it cleanly...
198 ##seems hard to do it cleanly...
198 if not ans:
199 if not ans:
199 print 'Nothing done.'
200 print 'Nothing done.'
200 return
201 return
201 user_ns = self.shell.user_ns
202 user_ns = self.shell.user_ns
202 for i in self.magic_who_ls():
203 for i in self.magic_who_ls():
203 del(user_ns[i])
204 del(user_ns[i])
204
205
205 # Also flush the private list of module references kept for script
206 # Also flush the private list of module references kept for script
206 # execution protection
207 # execution protection
207 self.shell._user_main_modules[:] = []
208 self.shell._user_main_modules[:] = []
208
209
209 ip.expose_magic('reset', reset_magic)
210 ip.expose_magic('reset', reset_magic)
210
211
211 sys.excepthook = excepthook
212 sys.excepthook = excepthook
212
213
213 #----------------------- Thread management section ----------------------
214 #----------------------- Thread management section ----------------------
214 def do_execute(self, line):
215 def do_execute(self, line):
215 """
216 """
216 Tell the thread to process the 'line' command
217 Tell the thread to process the 'line' command
217 """
218 """
218
219
219 self._line_to_execute = line
220 self._line_to_execute = line
220
221
221 if self._threading:
222 if self._threading:
222 #we launch the ipython line execution in a thread to make it interruptible
223 #we launch the ipython line execution in a thread to make it
223 #with include it in self namespace to be able to call ce.raise_exc(KeyboardInterrupt)
224 #interruptible with include it in self namespace to be able
225 #to call ce.raise_exc(KeyboardInterrupt)
224 self.ce = _CodeExecutor(self)
226 self.ce = _CodeExecutor(self)
225 self.ce.start()
227 self.ce.start()
226 else:
228 else:
227 try:
229 try:
228 self._doc_text = None
230 self._doc_text = None
229 self._help_text = None
231 self._help_text = None
230 self._execute()
232 self._execute()
231 # used for uper class to generate event after execution
233 # used for uper class to generate event after execution
232 self._after_execute()
234 self._after_execute()
233
235
234 except KeyboardInterrupt:
236 except KeyboardInterrupt:
235 pass
237 pass
236
238
237 #----------------------- IPython management section ----------------------
239 #----------------------- IPython management section ----------------------
238 def get_threading(self):
240 def get_threading(self):
239 """
241 """
240 Returns threading status, is set to True, then each command sent to
242 Returns threading status, is set to True, then each command sent to
241 the interpreter will be executed in a separated thread allowing,
243 the interpreter will be executed in a separated thread allowing,
242 for example, breaking a long running commands.
244 for example, breaking a long running commands.
243 Disallowing it, permits better compatibilty with instance that is embedding
245 Disallowing it, permits better compatibilty with instance that is embedding
244 IPython instance.
246 IPython instance.
245
247
246 @return: Execution method
248 @return: Execution method
247 @rtype: bool
249 @rtype: bool
248 """
250 """
249 return self._threading
251 return self._threading
250
252
251 def set_threading(self, state):
253 def set_threading(self, state):
252 """
254 """
253 Sets threading state, if set to True, then each command sent to
255 Sets threading state, if set to True, then each command sent to
254 the interpreter will be executed in a separated thread allowing,
256 the interpreter will be executed in a separated thread allowing,
255 for example, breaking a long running commands.
257 for example, breaking a long running commands.
256 Disallowing it, permits better compatibilty with instance that is embedding
258 Disallowing it, permits better compatibilty with instance that is embedding
257 IPython instance.
259 IPython instance.
258
260
259 @param state: Sets threading state
261 @param state: Sets threading state
260 @type bool
262 @type bool
261 """
263 """
262 self._threading = state
264 self._threading = state
263
265
264 def get_doc_text(self):
266 def get_doc_text(self):
265 """
267 """
266 Returns the output of the processing that need to be paged (if any)
268 Returns the output of the processing that need to be paged (if any)
267
269
268 @return: The std output string.
270 @return: The std output string.
269 @rtype: string
271 @rtype: string
270 """
272 """
271 return self._doc_text
273 return self._doc_text
272
274
273 def get_help_text(self):
275 def get_help_text(self):
274 """
276 """
275 Returns the output of the processing that need to be paged via help pager(if any)
277 Returns the output of the processing that need to be paged via help pager(if any)
276
278
277 @return: The std output string.
279 @return: The std output string.
278 @rtype: string
280 @rtype: string
279 """
281 """
280 return self._help_text
282 return self._help_text
281
283
282 def get_banner(self):
284 def get_banner(self):
283 """
285 """
284 Returns the IPython banner for useful info on IPython instance
286 Returns the IPython banner for useful info on IPython instance
285
287
286 @return: The banner string.
288 @return: The banner string.
287 @rtype: string
289 @rtype: string
288 """
290 """
289 return self._IP.BANNER
291 return self._IP.BANNER
290
292
291 def get_prompt_count(self):
293 def get_prompt_count(self):
292 """
294 """
293 Returns the prompt number.
295 Returns the prompt number.
294 Each time a user execute a line in the IPython shell the prompt count is increased
296 Each time a user execute a line in the IPython shell the prompt count is increased
295
297
296 @return: The prompt number
298 @return: The prompt number
297 @rtype: int
299 @rtype: int
298 """
300 """
299 return self._IP.outputcache.prompt_count
301 return self._IP.outputcache.prompt_count
300
302
301 def get_prompt(self):
303 def get_prompt(self):
302 """
304 """
303 Returns current prompt inside IPython instance
305 Returns current prompt inside IPython instance
304 (Can be In [...]: ot ...:)
306 (Can be In [...]: ot ...:)
305
307
306 @return: The current prompt.
308 @return: The current prompt.
307 @rtype: string
309 @rtype: string
308 """
310 """
309 return self._prompt
311 return self._prompt
310
312
311 def get_indentation(self):
313 def get_indentation(self):
312 """
314 """
313 Returns the current indentation level
315 Returns the current indentation level
314 Usefull to put the caret at the good start position if we want to do autoindentation.
316 Usefull to put the caret at the good start position if we want to do autoindentation.
315
317
316 @return: The indentation level.
318 @return: The indentation level.
317 @rtype: int
319 @rtype: int
318 """
320 """
319 return self._IP.indent_current_nsp
321 return self._IP.indent_current_nsp
320
322
321 def update_namespace(self, ns_dict):
323 def update_namespace(self, ns_dict):
322 '''
324 '''
323 Add the current dictionary to the shell namespace.
325 Add the current dictionary to the shell namespace.
324
326
325 @param ns_dict: A dictionary of symbol-values.
327 @param ns_dict: A dictionary of symbol-values.
326 @type ns_dict: dictionary
328 @type ns_dict: dictionary
327 '''
329 '''
328 self._IP.user_ns.update(ns_dict)
330 self._IP.user_ns.update(ns_dict)
329
331
330 def complete(self, line):
332 def complete(self, line):
331 '''
333 '''
332 Returns an auto completed line and/or posibilities for completion.
334 Returns an auto completed line and/or posibilities for completion.
333
335
334 @param line: Given line so far.
336 @param line: Given line so far.
335 @type line: string
337 @type line: string
336
338
337 @return: Line completed as for as possible,
339 @return: Line completed as for as possible,
338 and possible further completions.
340 and possible further completions.
339 @rtype: tuple
341 @rtype: tuple
340 '''
342 '''
341 split_line = self._complete_sep.split(line)
343 split_line = self._complete_sep.split(line)
342 possibilities = self._IP.complete(split_line[-1])
344 possibilities = self._IP.complete(split_line[-1])
343 if possibilities:
345 if possibilities:
344
346
345 def _common_prefix(str1, str2):
347 def _common_prefix(str1, str2):
346 '''
348 '''
347 Reduction function. returns common prefix of two given strings.
349 Reduction function. returns common prefix of two given strings.
348
350
349 @param str1: First string.
351 @param str1: First string.
350 @type str1: string
352 @type str1: string
351 @param str2: Second string
353 @param str2: Second string
352 @type str2: string
354 @type str2: string
353
355
354 @return: Common prefix to both strings.
356 @return: Common prefix to both strings.
355 @rtype: string
357 @rtype: string
356 '''
358 '''
357 for i in range(len(str1)):
359 for i in range(len(str1)):
358 if not str2.startswith(str1[:i+1]):
360 if not str2.startswith(str1[:i+1]):
359 return str1[:i]
361 return str1[:i]
360 return str1
362 return str1
361 common_prefix = reduce(_common_prefix, possibilities)
363 common_prefix = reduce(_common_prefix, possibilities)
362 completed = line[:-len(split_line[-1])]+common_prefix
364 completed = line[:-len(split_line[-1])]+common_prefix
363 else:
365 else:
364 completed = line
366 completed = line
365 return completed, possibilities
367 return completed, possibilities
366
368
367 def history_back(self):
369 def history_back(self):
368 '''
370 '''
369 Provides one history command back.
371 Provides one history command back.
370
372
371 @return: The command string.
373 @return: The command string.
372 @rtype: string
374 @rtype: string
373 '''
375 '''
374 history = ''
376 history = ''
375 #the below while loop is used to suppress empty history lines
377 #the below while loop is used to suppress empty history lines
376 while((history == '' or history == '\n') and self._history_level >0):
378 while((history == '' or history == '\n') and self._history_level >0):
377 if self._history_level >= 1:
379 if self._history_level >= 1:
378 self._history_level -= 1
380 self._history_level -= 1
379 history = self._get_history()
381 history = self._get_history()
380 return history
382 return history
381
383
382 def history_forward(self):
384 def history_forward(self):
383 '''
385 '''
384 Provides one history command forward.
386 Provides one history command forward.
385
387
386 @return: The command string.
388 @return: The command string.
387 @rtype: string
389 @rtype: string
388 '''
390 '''
389 history = ''
391 history = ''
390 #the below while loop is used to suppress empty history lines
392 #the below while loop is used to suppress empty history lines
391 while((history == '' or history == '\n') \
393 while((history == '' or history == '\n') \
392 and self._history_level <= self._get_history_max_index()):
394 and self._history_level <= self._get_history_max_index()):
393 if self._history_level < self._get_history_max_index():
395 if self._history_level < self._get_history_max_index():
394 self._history_level += 1
396 self._history_level += 1
395 history = self._get_history()
397 history = self._get_history()
396 else:
398 else:
397 if self._history_level == self._get_history_max_index():
399 if self._history_level == self._get_history_max_index():
398 history = self._get_history()
400 history = self._get_history()
399 self._history_level += 1
401 self._history_level += 1
400 else:
402 else:
401 history = ''
403 history = ''
402 return history
404 return history
403
405
404 def init_history_index(self):
406 def init_history_index(self):
405 '''
407 '''
406 set history to last command entered
408 set history to last command entered
407 '''
409 '''
408 self._history_level = self._get_history_max_index()+1
410 self._history_level = self._get_history_max_index()+1
409
411
410 #----------------------- IPython PRIVATE management section --------------
412 #----------------------- IPython PRIVATE management section --------------
411 def _after_execute(self):
413 def _after_execute(self):
412 '''
414 '''
413 Can be redefined to generate post event after excution is done
415 Can be redefined to generate post event after excution is done
414 '''
416 '''
415 pass
417 pass
416
418
417 #def _ask_exit(self):
419 #def _ask_exit(self):
418 # '''
420 # '''
419 # Can be redefined to generate post event to exit the Ipython shell
421 # Can be redefined to generate post event to exit the Ipython shell
420 # '''
422 # '''
421 # pass
423 # pass
422
424
423 def _get_history_max_index(self):
425 def _get_history_max_index(self):
424 '''
426 '''
425 returns the max length of the history buffer
427 returns the max length of the history buffer
426
428
427 @return: history length
429 @return: history length
428 @rtype: int
430 @rtype: int
429 '''
431 '''
430 return len(self._IP.input_hist_raw)-1
432 return len(self._IP.input_hist_raw)-1
431
433
432 def _get_history(self):
434 def _get_history(self):
433 '''
435 '''
434 Get's the command string of the current history level.
436 Get's the command string of the current history level.
435
437
436 @return: Historic command stri
438 @return: Historic command stri
437 @rtype: string
439 @rtype: string
438 '''
440 '''
439 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
441 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
440 return rv
442 return rv
441
443
442 def _pager_help(self, text):
444 def _pager_help(self, text):
443 '''
445 '''
444 This function is used as a callback replacment to IPython help pager function
446 This function is used as a callback replacment to IPython help pager function
445
447
446 It puts the 'text' value inside the self._help_text string that can be retrived via
448 It puts the 'text' value inside the self._help_text string that can be retrived via
447 get_help_text function.
449 get_help_text function.
448 '''
450 '''
449 if self._help_text == None:
451 if self._help_text == None:
450 self._help_text = text
452 self._help_text = text
451 else:
453 else:
452 self._help_text += text
454 self._help_text += text
453
455
454 def _pager(self, IP, text):
456 def _pager(self, IP, text):
455 '''
457 '''
456 This function is used as a callback replacment to IPython pager function
458 This function is used as a callback replacment to IPython pager function
457
459
458 It puts the 'text' value inside the self._doc_text string that can be retrived via
460 It puts the 'text' value inside the self._doc_text string that can be retrived via
459 get_doc_text function.
461 get_doc_text function.
460 '''
462 '''
461 self._doc_text = text
463 self._doc_text = text
462
464
463 def _raw_input(self, prompt=''):
465 def _raw_input(self, prompt=''):
464 '''
466 '''
465 Custom raw_input() replacement. Get's current line from console buffer.
467 Custom raw_input() replacement. Get's current line from console buffer.
466
468
467 @param prompt: Prompt to print. Here for compatability as replacement.
469 @param prompt: Prompt to print. Here for compatability as replacement.
468 @type prompt: string
470 @type prompt: string
469
471
470 @return: The current command line text.
472 @return: The current command line text.
471 @rtype: string
473 @rtype: string
472 '''
474 '''
473 return self._line_to_execute
475 return self._line_to_execute
474
476
475 def _execute(self):
477 def _execute(self):
476 '''
478 '''
477 Executes the current line provided by the shell object.
479 Executes the current line provided by the shell object.
478 '''
480 '''
479
481
480 orig_stdout = sys.stdout
482 orig_stdout = sys.stdout
481 sys.stdout = IPython.Shell.Term.cout
483 sys.stdout = IPython.Shell.Term.cout
482 #self.sys_displayhook_ori = sys.displayhook
484 #self.sys_displayhook_ori = sys.displayhook
483 #sys.displayhook = self.displayhook
485 #sys.displayhook = self.displayhook
484
486
485 try:
487 try:
486 line = self._IP.raw_input(None, self._iter_more)
488 line = self._IP.raw_input(None, self._iter_more)
487 if self._IP.autoindent:
489 if self._IP.autoindent:
488 self._IP.readline_startup_hook(None)
490 self._IP.readline_startup_hook(None)
489
491
490 except KeyboardInterrupt:
492 except KeyboardInterrupt:
491 self._IP.write('\nKeyboardInterrupt\n')
493 self._IP.write('\nKeyboardInterrupt\n')
492 self._IP.resetbuffer()
494 self._IP.resetbuffer()
493 # keep cache in sync with the prompt counter:
495 # keep cache in sync with the prompt counter:
494 self._IP.outputcache.prompt_count -= 1
496 self._IP.outputcache.prompt_count -= 1
495
497
496 if self._IP.autoindent:
498 if self._IP.autoindent:
497 self._IP.indent_current_nsp = 0
499 self._IP.indent_current_nsp = 0
498 self._iter_more = 0
500 self._iter_more = 0
499 except:
501 except:
500 self._IP.showtraceback()
502 self._IP.showtraceback()
501 else:
503 else:
502 self._IP.write(str(self._IP.outputcache.prompt_out).strip())
504 self._IP.write(str(self._IP.outputcache.prompt_out).strip())
503 self._iter_more = self._IP.push(line)
505 self._iter_more = self._IP.push(line)
504 if (self._IP.SyntaxTB.last_syntax_error and self._IP.rc.autoedit_syntax):
506 if (self._IP.SyntaxTB.last_syntax_error and \
507 self._IP.rc.autoedit_syntax):
505 self._IP.edit_syntax_error()
508 self._IP.edit_syntax_error()
506 if self._iter_more:
509 if self._iter_more:
507 self._prompt = str(self._IP.outputcache.prompt2).strip()
510 self._prompt = str(self._IP.outputcache.prompt2).strip()
508 if self._IP.autoindent:
511 if self._IP.autoindent:
509 self._IP.readline_startup_hook(self._IP.pre_readline)
512 self._IP.readline_startup_hook(self._IP.pre_readline)
510 else:
513 else:
511 self._prompt = str(self._IP.outputcache.prompt1).strip()
514 self._prompt = str(self._IP.outputcache.prompt1).strip()
512 self._IP.indent_current_nsp = 0 #we set indentation to 0
515 self._IP.indent_current_nsp = 0 #we set indentation to 0
513
516
514 sys.stdout = orig_stdout
517 sys.stdout = orig_stdout
515 #sys.displayhook = self.sys_displayhook_ori
518 #sys.displayhook = self.sys_displayhook_ori
516
519
517 def _shell(self, ip, cmd):
520 def _shell(self, ip, cmd):
518 '''
521 '''
519 Replacement method to allow shell commands without them blocking.
522 Replacement method to allow shell commands without them blocking.
520
523
521 @param ip: Ipython instance, same as self._IP
524 @param ip: Ipython instance, same as self._IP
522 @type cmd: Ipython instance
525 @type cmd: Ipython instance
523 @param cmd: Shell command to execute.
526 @param cmd: Shell command to execute.
524 @type cmd: string
527 @type cmd: string
525 '''
528 '''
526 stdin, stdout = os.popen4(cmd)
529 stdin, stdout = os.popen4(cmd)
527 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
530 result = stdout.read().decode('cp437').\
531 encode(locale.getpreferredencoding())
528 #we use print command because the shell command is called
532 #we use print command because the shell command is called
529 #inside IPython instance and thus is redirected to thread cout
533 #inside IPython instance and thus is redirected to thread cout
530 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
534 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
531 print "\x01\x1b[1;36m\x02"+result
535 print "\x01\x1b[1;36m\x02"+result
532 stdout.close()
536 stdout.close()
533 stdin.close()
537 stdin.close()
@@ -1,475 +1,509 b''
1 #!/usr/bin/python
1 #!/usr/bin/python
2 # -*- coding: iso-8859-15 -*-
2 # -*- coding: iso-8859-15 -*-
3 import wx
3 import wx
4 import wx.stc as stc
4 import wx.stc as stc
5 import keyword
5 import keyword
6
6
7 #-----------------------------------------
7 #-----------------------------------------
8 # History widget for IPython
8 # History widget for IPython
9 __version__ = 0.5
9 __version__ = 0.5
10 __author__ = "Laurent Dufrechou"
10 __author__ = "Laurent Dufrechou"
11 __email__ = "laurent.dufrechou _at_ gmail.com"
11 __email__ = "laurent.dufrechou _at_ gmail.com"
12 __license__ = "BSD"
12 __license__ = "BSD"
13 #-----------------------------------------
13 #-----------------------------------------
14 class IPythonHistoryPanel(wx.Panel):
14 class IPythonHistoryPanel(wx.Panel):
15
15
16 def __init__(self, parent,flt_empty=True,
16 def __init__(self, parent,flt_empty=True,
17 flt_doc=True,flt_cmd=True,flt_magic=True):
17 flt_doc=True,flt_cmd=True,flt_magic=True):
18
18
19 wx.Panel.__init__(self,parent,-1)
19 wx.Panel.__init__(self,parent,-1)
20 #text_ctrl = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE)
20 #text_ctrl = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE)
21 text_ctrl = PythonSTC(self, -1)
21 text_ctrl = PythonSTC(self, -1)
22
22
23
23
24 st_filt = wx.StaticText(self, -1, " Filter:")
24 st_filt = wx.StaticText(self, -1, " Filter:")
25
25
26 self.filter_empty = wx.CheckBox(self, -1, "Empty commands")
26 self.filter_empty = wx.CheckBox(self, -1, "Empty commands")
27 self.filter_doc = wx.CheckBox(self, -1, "?: Doc commands")
27 self.filter_doc = wx.CheckBox(self, -1, "?: Doc commands")
28 self.filter_cmd = wx.CheckBox(self, -1, "!: Sys commands")
28 self.filter_cmd = wx.CheckBox(self, -1, "!: Sys commands")
29 self.filter_magic = wx.CheckBox(self, -1, "%: Magic keys")
29 self.filter_magic = wx.CheckBox(self, -1, "%: Magic keys")
30
30
31 self.options={'filter_empty':{'value':'True',
31 self.options={'filter_empty':{'value':'True',
32 'checkbox':self.filter_empty,'True':True,'False':False,
32 'checkbox':self.filter_empty, \
33 'setfunc':lambda x:None},
33 'True':True,'False':False,
34 'setfunc':lambda x:None},
34 'filter_doc':{'value':'True',
35 'filter_doc':{'value':'True',
35 'checkbox':self.filter_doc,'True':True,'False':False,
36 'checkbox':self.filter_doc, \
36 'setfunc':lambda x:None},
37 'True':True,'False':False,
38 'setfunc':lambda x:None},
37 'filter_cmd':{'value':'True',
39 'filter_cmd':{'value':'True',
38 'checkbox':self.filter_cmd,'True':True,'False':False,
40 'checkbox':self.filter_cmd, \
39 'setfunc':lambda x:None},
41 'True':True,'False':False,
42 'setfunc':lambda x:None},
40 'filter_magic':{'value':'True',
43 'filter_magic':{'value':'True',
41 'checkbox':self.filter_magic,'True':True,'False':False,
44 'checkbox':self.filter_magic, \
42 'setfunc':lambda x:None},
45 'True':True,'False':False,
46 'setfunc':lambda x:None},
43 }
47 }
44 self.reloadOptions(self.options)
48 self.reloadOptions(self.options)
45
49
46 self.filter_empty.Bind(wx.EVT_CHECKBOX, self.evtCheckEmptyFilter)
50 self.filter_empty.Bind(wx.EVT_CHECKBOX, self.evtCheckEmptyFilter)
47 self.filter_doc.Bind(wx.EVT_CHECKBOX, self.evtCheckDocFilter)
51 self.filter_doc.Bind(wx.EVT_CHECKBOX, self.evtCheckDocFilter)
48 self.filter_cmd.Bind(wx.EVT_CHECKBOX, self.evtCheckCmdFilter)
52 self.filter_cmd.Bind(wx.EVT_CHECKBOX, self.evtCheckCmdFilter)
49 self.filter_magic.Bind(wx.EVT_CHECKBOX, self.evtCheckMagicFilter)
53 self.filter_magic.Bind(wx.EVT_CHECKBOX, self.evtCheckMagicFilter)
50
54
51 #self.filter_empty.SetValue(flt_empty)
55 #self.filter_empty.SetValue(flt_empty)
52 #self.filter_doc.SetValue(flt_doc)
56 #self.filter_doc.SetValue(flt_doc)
53 #self.filter_cmd.SetValue(flt_cmd)
57 #self.filter_cmd.SetValue(flt_cmd)
54 #self.filter_magic.SetValue(flt_magic)
58 #self.filter_magic.SetValue(flt_magic)
55
59
56 sizer = wx.BoxSizer(wx.VERTICAL)
60 sizer = wx.BoxSizer(wx.VERTICAL)
57
61
58 sizer.Add(text_ctrl, 1, wx.EXPAND)
62 sizer.Add(text_ctrl, 1, wx.EXPAND)
59 sizer.AddMany( [(5,5),
63 sizer.AddMany( [(5,5),
60 st_filt,
64 st_filt,
61 (10,10),
65 (10,10),
62 self.filter_empty,
66 self.filter_empty,
63 self.filter_doc,
67 self.filter_doc,
64 self.filter_cmd,
68 self.filter_cmd,
65 self.filter_magic,
69 self.filter_magic,
66 (10,10),
70 (10,10),
67 ])
71 ])
68 self.SetAutoLayout(True)
72 self.SetAutoLayout(True)
69 sizer.Fit(self)
73 sizer.Fit(self)
70 sizer.SetSizeHints(self)
74 sizer.SetSizeHints(self)
71 self.SetSizer(sizer)
75 self.SetSizer(sizer)
72 self.text_ctrl=text_ctrl
76 self.text_ctrl=text_ctrl
73 #text_ctrl.SetText(demoText + open('Main.py').read())
77 #text_ctrl.SetText(demoText + open('Main.py').read())
74 text_ctrl.EmptyUndoBuffer()
78 text_ctrl.EmptyUndoBuffer()
75 text_ctrl.Colourise(0, -1)
79 text_ctrl.Colourise(0, -1)
76
80
77 # line numbers in the margin
81 # line numbers in the margin
78 text_ctrl.SetMarginType(1, stc.STC_MARGIN_NUMBER)
82 text_ctrl.SetMarginType(1, stc.STC_MARGIN_NUMBER)
79 text_ctrl.SetMarginWidth(1, 15)
83 text_ctrl.SetMarginWidth(1, 15)
80
84
81
85
82 def write(self,history_line):
86 def write(self,history_line):
83 add = True
87 add = True
84 if self.filter_empty.GetValue() == True and history_line == '':
88 if self.filter_empty.GetValue() == True and history_line == '':
85 add = False
89 add = False
86 if len(history_line)>0:
90 if len(history_line)>0:
87 if self.filter_doc.GetValue() == True and history_line[-1:] == '?':
91 if self.filter_doc.GetValue() == True and history_line[-1:] == '?':
88 add = False
92 add = False
89 if self.filter_cmd.GetValue() == True and history_line[0] == '!':
93 if self.filter_cmd.GetValue() == True and history_line[0] == '!':
90 add = False
94 add = False
91 if self.filter_magic.GetValue() == True and history_line[0] == '%':
95 if self.filter_magic.GetValue() == True and history_line[0] == '%':
92 add = False
96 add = False
93 if add:
97 if add:
94 self.text_ctrl.AppendText(history_line+'\n')
98 self.text_ctrl.AppendText(history_line+'\n')
95
99
96 #------------------------ Option Section -----------------------------------
100 #------------------------ Option Section -----------------------------------
97 def processOptionCheckedEvt(self, event, name):
101 def processOptionCheckedEvt(self, event, name):
98 if event.IsChecked():
102 if event.IsChecked():
99 self.options[name]['value']='True'
103 self.options[name]['value']='True'
100 else:
104 else:
101 self.options[name]['value']='False'
105 self.options[name]['value']='False'
102 self.updateOptionTracker(name,
106 self.updateOptionTracker(name,
103 self.options[name]['value'])
107 self.options[name]['value'])
104
108
105 def evtCheckEmptyFilter(self, event):
109 def evtCheckEmptyFilter(self, event):
106 self.processOptionCheckedEvt(event, 'filter_empty')
110 self.processOptionCheckedEvt(event, 'filter_empty')
107
111
108 def evtCheckDocFilter(self, event):
112 def evtCheckDocFilter(self, event):
109 self.processOptionCheckedEvt(event, 'filter_doc')
113 self.processOptionCheckedEvt(event, 'filter_doc')
110
114
111 def evtCheckCmdFilter(self, event):
115 def evtCheckCmdFilter(self, event):
112 self.processOptionCheckedEvt(event, 'filter_cmd')
116 self.processOptionCheckedEvt(event, 'filter_cmd')
113
117
114 def evtCheckMagicFilter(self, event):
118 def evtCheckMagicFilter(self, event):
115 self.processOptionCheckedEvt(event, 'filter_magic')
119 self.processOptionCheckedEvt(event, 'filter_magic')
116
120
117 def getOptions(self):
121 def getOptions(self):
118 return self.options
122 return self.options
119
123
120 def reloadOptions(self,options):
124 def reloadOptions(self,options):
121 self.options = options
125 self.options = options
122 for key in self.options.keys():
126 for key in self.options.keys():
123 value = self.options[key]['value']
127 value = self.options[key]['value']
124 self.options[key]['checkbox'].SetValue(self.options[key][value])
128 self.options[key]['checkbox'].SetValue(self.options[key][value])
125 self.options[key]['setfunc'](value)
129 self.options[key]['setfunc'](value)
126
130
127 #------------------------ Hook Section -----------------------------------
131 #------------------------ Hook Section -----------------------------------
128 def updateOptionTracker(self,name,value):
132 def updateOptionTracker(self,name,value):
129 '''
133 '''
130 Default history tracker (does nothing)
134 Default history tracker (does nothing)
131 '''
135 '''
132 pass
136 pass
133
137
134 def setOptionTrackerHook(self,func):
138 def setOptionTrackerHook(self,func):
135 '''
139 '''
136 Define a new history tracker
140 Define a new history tracker
137 '''
141 '''
138 self.updateOptionTracker = func
142 self.updateOptionTracker = func
139
143
140
144
141 #----------------------------------------------------------------------
145 #----------------------------------------------------------------------
142 # Font definition for Styled Text Control
146 # Font definition for Styled Text Control
143
147
144 if wx.Platform == '__WXMSW__':
148 if wx.Platform == '__WXMSW__':
145 faces = { 'times': 'Times New Roman',
149 faces = { 'times': 'Times New Roman',
146 'mono' : 'Courier New',
150 'mono' : 'Courier New',
147 'helv' : 'Arial',
151 'helv' : 'Arial',
148 'other': 'Comic Sans MS',
152 'other': 'Comic Sans MS',
149 'size' : 8,
153 'size' : 8,
150 'size2': 6,
154 'size2': 6,
151 }
155 }
152 elif wx.Platform == '__WXMAC__':
156 elif wx.Platform == '__WXMAC__':
153 faces = { 'times': 'Times New Roman',
157 faces = { 'times': 'Times New Roman',
154 'mono' : 'Monaco',
158 'mono' : 'Monaco',
155 'helv' : 'Arial',
159 'helv' : 'Arial',
156 'other': 'Comic Sans MS',
160 'other': 'Comic Sans MS',
157 'size' : 8,
161 'size' : 8,
158 'size2': 6,
162 'size2': 6,
159 }
163 }
160 else:
164 else:
161 faces = { 'times': 'Times',
165 faces = { 'times': 'Times',
162 'mono' : 'Courier',
166 'mono' : 'Courier',
163 'helv' : 'Helvetica',
167 'helv' : 'Helvetica',
164 'other': 'new century schoolbook',
168 'other': 'new century schoolbook',
165 'size' : 8,
169 'size' : 8,
166 'size2': 6,
170 'size2': 6,
167 }
171 }
168
172
169
173
170 #----------------------------------------------------------------------
174 #----------------------------------------------------------------------
171
175
172 class PythonSTC(stc.StyledTextCtrl):
176 class PythonSTC(stc.StyledTextCtrl):
173
177
174 fold_symbols = 3
178 fold_symbols = 3
175
179
176 def __init__(self, parent, ID,
180 def __init__(self, parent, ID,
177 pos=wx.DefaultPosition, size=wx.DefaultSize,
181 pos=wx.DefaultPosition, size=wx.DefaultSize,
178 style=0):
182 style=0):
179 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
183 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
180 #self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
184 #self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
181 #self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
185 #self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
182
186
183 self.SetLexer(stc.STC_LEX_PYTHON)
187 self.SetLexer(stc.STC_LEX_PYTHON)
184 self.SetKeyWords(0, " ".join(keyword.kwlist))
188 self.SetKeyWords(0, " ".join(keyword.kwlist))
185
189
186 #self.SetProperty("fold", "1")
190 #self.SetProperty("fold", "1")
187 #self.SetProperty("tab.timmy.whinge.level", "1")
191 #self.SetProperty("tab.timmy.whinge.level", "1")
188 #self.SetMargins(0,0)
192 #self.SetMargins(0,0)
189
193
190 #self.SetViewWhiteSpace(False)
194 #self.SetViewWhiteSpace(False)
191 #self.SetBufferedDraw(False)
195 #self.SetBufferedDraw(False)
192 #self.SetViewEOL(True)
196 #self.SetViewEOL(True)
193 self.SetEOLMode(stc.STC_EOL_CRLF)
197 self.SetEOLMode(stc.STC_EOL_CRLF)
194 #self.SetUseAntiAliasing(True)
198 #self.SetUseAntiAliasing(True)
195
199
196 self.SetEdgeMode(stc.STC_EDGE_LINE)
200 self.SetEdgeMode(stc.STC_EDGE_LINE)
197 self.SetEdgeColumn(80)
201 self.SetEdgeColumn(80)
198 self.SetEdgeColour(wx.LIGHT_GREY)
202 self.SetEdgeColour(wx.LIGHT_GREY)
199 self.SetLayoutCache(stc.STC_CACHE_PAGE)
203 self.SetLayoutCache(stc.STC_CACHE_PAGE)
200
204
201 # Setup a margin to hold fold markers
205 # Setup a margin to hold fold markers
202 #self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
206 #self.SetFoldFlags(16)
207 ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
203 self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
208 self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
204 self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
209 self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
205 self.SetMarginSensitive(2, True)
210 self.SetMarginSensitive(2, True)
206 self.SetMarginWidth(2, 12)
211 self.SetMarginWidth(2, 12)
207
212
208 if self.fold_symbols == 0:
213 if self.fold_symbols == 0:
209 # Arrow pointing right for contracted folders, arrow pointing down for expanded
214 # Arrow pointing right for contracted folders,
210 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_ARROWDOWN, "black", "black")
215 # arrow pointing down for expanded
211 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "black", "black")
216 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
212 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "black", "black")
217 stc.STC_MARK_ARROWDOWN, "black", "black")
213 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "black", "black")
218 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, \
214 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
219 stc.STC_MARK_ARROW, "black", "black")
215 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
220 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, \
216 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
221 stc.STC_MARK_EMPTY, "black", "black")
222 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, \
223 stc.STC_MARK_EMPTY, "black", "black")
224 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, \
225 stc.STC_MARK_EMPTY, "white", "black")
226 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, \
227 stc.STC_MARK_EMPTY, "white", "black")
228 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, \
229 stc.STC_MARK_EMPTY, "white", "black")
217
230
218 elif self.fold_symbols == 1:
231 elif self.fold_symbols == 1:
219 # Plus for contracted folders, minus for expanded
232 # Plus for contracted folders, minus for expanded
220 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black")
233 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
221 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black")
234 stc.STC_MARK_MINUS, "white", "black")
222 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black")
235 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, \
223 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black")
236 stc.STC_MARK_PLUS, "white", "black")
224 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
237 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, \
225 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
238 stc.STC_MARK_EMPTY, "white", "black")
226 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
239 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, \
240 stc.STC_MARK_EMPTY, "white", "black")
241 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, \
242 stc.STC_MARK_EMPTY, "white", "black")
243 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, \
244 stc.STC_MARK_EMPTY, "white", "black")
245 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, \
246 stc.STC_MARK_EMPTY, "white", "black")
227
247
228 elif self.fold_symbols == 2:
248 elif self.fold_symbols == 2:
229 # Like a flattened tree control using circular headers and curved joins
249 # Like a flattened tree control using circular headers and curved joins
230 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_CIRCLEMINUS, "white", "#404040")
250 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
231 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_CIRCLEPLUS, "white", "#404040")
251 stc.STC_MARK_CIRCLEMINUS, "white", "#404040")
232 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#404040")
252 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, \
233 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNERCURVE, "white", "#404040")
253 stc.STC_MARK_CIRCLEPLUS, "white", "#404040")
234 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040")
254 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, \
235 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
255 stc.STC_MARK_VLINE, "white", "#404040")
236 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, "white", "#404040")
256 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, \
257 stc.STC_MARK_LCORNERCURVE, "white", "#404040")
258 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, \
259 stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040")
260 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, \
261 stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
262 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, \
263 stc.STC_MARK_TCORNERCURVE, "white", "#404040")
237
264
238 elif self.fold_symbols == 3:
265 elif self.fold_symbols == 3:
239 # Like a flattened tree control using square headers
266 # Like a flattened tree control using square headers
240 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080")
267 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
241 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080")
268 stc.STC_MARK_BOXMINUS, "white", "#808080")
242 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080")
269 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, \
243 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080")
270 stc.STC_MARK_BOXPLUS, "white", "#808080")
244 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080")
271 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, \
245 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
272 stc.STC_MARK_VLINE, "white", "#808080")
246 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080")
273 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, \
274 stc.STC_MARK_LCORNER, "white", "#808080")
275 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, \
276 stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080")
277 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, \
278 stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
279 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, \
280 stc.STC_MARK_TCORNER, "white", "#808080")
247
281
248
282
249 self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
283 self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
250 self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
284 self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
251 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
285 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
252
286
253 # Make some styles, The lexer defines what each style is used for, we
287 # Make some styles, The lexer defines what each style is used for, we
254 # just have to define what each style looks like. This set is adapted from
288 # just have to define what each style looks like. This set is adapted from
255 # Scintilla sample property files.
289 # Scintilla sample property files.
256
290
257 # Global default styles for all languages
291 # Global default styles for all languages
258 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
292 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
259 self.StyleClearAll() # Reset all to be like the default
293 self.StyleClearAll() # Reset all to be like the default
260
294
261 # Global default styles for all languages
295 # Global default styles for all languages
262 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
296 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
263 self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces)
297 self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces)
264 self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces)
298 self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces)
265 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold")
299 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold")
266 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
300 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
267
301
268 # Python styles
302 # Python styles
269 # Default
303 # Default
270 self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
304 self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
271 # Comments
305 # Comments
272 self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
306 self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
273 # Number
307 # Number
274 self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces)
308 self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces)
275 # String
309 # String
276 self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
310 self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
277 # Single quoted string
311 # Single quoted string
278 self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
312 self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
279 # Keyword
313 # Keyword
280 self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
314 self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
281 # Triple quotes
315 # Triple quotes
282 self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces)
316 self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces)
283 # Triple double quotes
317 # Triple double quotes
284 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces)
318 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces)
285 # Class name definition
319 # Class name definition
286 self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % faces)
320 self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % faces)
287 # Function or method name definition
321 # Function or method name definition
288 self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces)
322 self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces)
289 # Operators
323 # Operators
290 self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces)
324 self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces)
291 # Identifiers
325 # Identifiers
292 self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
326 self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
293 # Comment-blocks
327 # Comment-blocks
294 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces)
328 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces)
295 # End of line where string is not closed
329 # End of line where string is not closed
296 self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces)
330 self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces)
297
331
298 self.SetCaretForeground("BLUE")
332 self.SetCaretForeground("BLUE")
299
333
300
334
301 # register some images for use in the AutoComplete box.
335 # register some images for use in the AutoComplete box.
302 #self.RegisterImage(1, images.getSmilesBitmap())
336 #self.RegisterImage(1, images.getSmilesBitmap())
303 #self.RegisterImage(2,
337 #self.RegisterImage(2,
304 # wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16)))
338 # wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16)))
305 #self.RegisterImage(3,
339 #self.RegisterImage(3,
306 # wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16)))
340 # wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16)))
307
341
308
342
309 def OnKeyPressed(self, event):
343 def OnKeyPressed(self, event):
310 if self.CallTipActive():
344 if self.CallTipActive():
311 self.CallTipCancel()
345 self.CallTipCancel()
312 key = event.GetKeyCode()
346 key = event.GetKeyCode()
313
347
314 if key == 32 and event.ControlDown():
348 if key == 32 and event.ControlDown():
315 pos = self.GetCurrentPos()
349 pos = self.GetCurrentPos()
316
350
317 # Tips
351 # Tips
318 if event.ShiftDown():
352 if event.ShiftDown():
319 self.CallTipSetBackground("yellow")
353 self.CallTipSetBackground("yellow")
320 self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n'
354 self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n'
321 'show some suff, maybe parameters..\n\n'
355 'show some suff, maybe parameters..\n\n'
322 'fubar(param1, param2)')
356 'fubar(param1, param2)')
323 # Code completion
357 # Code completion
324 else:
358 else:
325 #lst = []
359 #lst = []
326 #for x in range(50000):
360 #for x in range(50000):
327 # lst.append('%05d' % x)
361 # lst.append('%05d' % x)
328 #st = " ".join(lst)
362 #st = " ".join(lst)
329 #print len(st)
363 #print len(st)
330 #self.AutoCompShow(0, st)
364 #self.AutoCompShow(0, st)
331
365
332 kw = keyword.kwlist[:]
366 kw = keyword.kwlist[:]
333
367
334 kw.sort() # Python sorts are case sensitive
368 kw.sort() # Python sorts are case sensitive
335 self.AutoCompSetIgnoreCase(False) # so this needs to match
369 self.AutoCompSetIgnoreCase(False) # so this needs to match
336
370
337 # Images are specified with a appended "?type"
371 # Images are specified with a appended "?type"
338 for i in range(len(kw)):
372 for i in range(len(kw)):
339 if kw[i] in keyword.kwlist:
373 if kw[i] in keyword.kwlist:
340 kw[i] = kw[i]# + "?1"
374 kw[i] = kw[i]# + "?1"
341
375
342 self.AutoCompShow(0, " ".join(kw))
376 self.AutoCompShow(0, " ".join(kw))
343 else:
377 else:
344 event.Skip()
378 event.Skip()
345
379
346
380
347 def OnUpdateUI(self, evt):
381 def OnUpdateUI(self, evt):
348 # check for matching braces
382 # check for matching braces
349 braceAtCaret = -1
383 braceAtCaret = -1
350 braceOpposite = -1
384 braceOpposite = -1
351 charBefore = None
385 charBefore = None
352 caretPos = self.GetCurrentPos()
386 caretPos = self.GetCurrentPos()
353
387
354 if caretPos > 0:
388 if caretPos > 0:
355 charBefore = self.GetCharAt(caretPos - 1)
389 charBefore = self.GetCharAt(caretPos - 1)
356 styleBefore = self.GetStyleAt(caretPos - 1)
390 styleBefore = self.GetStyleAt(caretPos - 1)
357
391
358 # check before
392 # check before
359 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
393 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
360 braceAtCaret = caretPos - 1
394 braceAtCaret = caretPos - 1
361
395
362 # check after
396 # check after
363 if braceAtCaret < 0:
397 if braceAtCaret < 0:
364 charAfter = self.GetCharAt(caretPos)
398 charAfter = self.GetCharAt(caretPos)
365 styleAfter = self.GetStyleAt(caretPos)
399 styleAfter = self.GetStyleAt(caretPos)
366
400
367 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
401 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
368 braceAtCaret = caretPos
402 braceAtCaret = caretPos
369
403
370 if braceAtCaret >= 0:
404 if braceAtCaret >= 0:
371 braceOpposite = self.BraceMatch(braceAtCaret)
405 braceOpposite = self.BraceMatch(braceAtCaret)
372
406
373 if braceAtCaret != -1 and braceOpposite == -1:
407 if braceAtCaret != -1 and braceOpposite == -1:
374 self.BraceBadLight(braceAtCaret)
408 self.BraceBadLight(braceAtCaret)
375 else:
409 else:
376 self.BraceHighlight(braceAtCaret, braceOpposite)
410 self.BraceHighlight(braceAtCaret, braceOpposite)
377 #pt = self.PointFromPosition(braceOpposite)
411 #pt = self.PointFromPosition(braceOpposite)
378 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
412 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
379 #print pt
413 #print pt
380 #self.Refresh(False)
414 #self.Refresh(False)
381
415
382
416
383 def OnMarginClick(self, evt):
417 def OnMarginClick(self, evt):
384 # fold and unfold as needed
418 # fold and unfold as needed
385 if evt.GetMargin() == 2:
419 if evt.GetMargin() == 2:
386 if evt.GetShift() and evt.GetControl():
420 if evt.GetShift() and evt.GetControl():
387 self.FoldAll()
421 self.FoldAll()
388 else:
422 else:
389 lineClicked = self.LineFromPosition(evt.GetPosition())
423 lineClicked = self.LineFromPosition(evt.GetPosition())
390
424
391 if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
425 if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
392 if evt.GetShift():
426 if evt.GetShift():
393 self.SetFoldExpanded(lineClicked, True)
427 self.SetFoldExpanded(lineClicked, True)
394 self.Expand(lineClicked, True, True, 1)
428 self.Expand(lineClicked, True, True, 1)
395 elif evt.GetControl():
429 elif evt.GetControl():
396 if self.GetFoldExpanded(lineClicked):
430 if self.GetFoldExpanded(lineClicked):
397 self.SetFoldExpanded(lineClicked, False)
431 self.SetFoldExpanded(lineClicked, False)
398 self.Expand(lineClicked, False, True, 0)
432 self.Expand(lineClicked, False, True, 0)
399 else:
433 else:
400 self.SetFoldExpanded(lineClicked, True)
434 self.SetFoldExpanded(lineClicked, True)
401 self.Expand(lineClicked, True, True, 100)
435 self.Expand(lineClicked, True, True, 100)
402 else:
436 else:
403 self.ToggleFold(lineClicked)
437 self.ToggleFold(lineClicked)
404
438
405
439
406 def FoldAll(self):
440 def FoldAll(self):
407 lineCount = self.GetLineCount()
441 lineCount = self.GetLineCount()
408 expanding = True
442 expanding = True
409
443
410 # find out if we are folding or unfolding
444 # find out if we are folding or unfolding
411 for lineNum in range(lineCount):
445 for lineNum in range(lineCount):
412 if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
446 if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
413 expanding = not self.GetFoldExpanded(lineNum)
447 expanding = not self.GetFoldExpanded(lineNum)
414 break
448 break
415
449
416 lineNum = 0
450 lineNum = 0
417
451
418 while lineNum < lineCount:
452 while lineNum < lineCount:
419 level = self.GetFoldLevel(lineNum)
453 level = self.GetFoldLevel(lineNum)
420 if level & stc.STC_FOLDLEVELHEADERFLAG and \
454 if level & stc.STC_FOLDLEVELHEADERFLAG and \
421 (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
455 (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
422
456
423 if expanding:
457 if expanding:
424 self.SetFoldExpanded(lineNum, True)
458 self.SetFoldExpanded(lineNum, True)
425 lineNum = self.Expand(lineNum, True)
459 lineNum = self.Expand(lineNum, True)
426 lineNum = lineNum - 1
460 lineNum = lineNum - 1
427 else:
461 else:
428 lastChild = self.GetLastChild(lineNum, -1)
462 lastChild = self.GetLastChild(lineNum, -1)
429 self.SetFoldExpanded(lineNum, False)
463 self.SetFoldExpanded(lineNum, False)
430
464
431 if lastChild > lineNum:
465 if lastChild > lineNum:
432 self.HideLines(lineNum+1, lastChild)
466 self.HideLines(lineNum+1, lastChild)
433
467
434 lineNum = lineNum + 1
468 lineNum = lineNum + 1
435
469
436
470
437
471
438 def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
472 def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
439 lastChild = self.GetLastChild(line, level)
473 lastChild = self.GetLastChild(line, level)
440 line = line + 1
474 line = line + 1
441
475
442 while line <= lastChild:
476 while line <= lastChild:
443 if force:
477 if force:
444 if visLevels > 0:
478 if visLevels > 0:
445 self.ShowLines(line, line)
479 self.ShowLines(line, line)
446 else:
480 else:
447 self.HideLines(line, line)
481 self.HideLines(line, line)
448 else:
482 else:
449 if doExpand:
483 if doExpand:
450 self.ShowLines(line, line)
484 self.ShowLines(line, line)
451
485
452 if level == -1:
486 if level == -1:
453 level = self.GetFoldLevel(line)
487 level = self.GetFoldLevel(line)
454
488
455 if level & stc.STC_FOLDLEVELHEADERFLAG:
489 if level & stc.STC_FOLDLEVELHEADERFLAG:
456 if force:
490 if force:
457 if visLevels > 1:
491 if visLevels > 1:
458 self.SetFoldExpanded(line, True)
492 self.SetFoldExpanded(line, True)
459 else:
493 else:
460 self.SetFoldExpanded(line, False)
494 self.SetFoldExpanded(line, False)
461
495
462 line = self.Expand(line, doExpand, force, visLevels-1)
496 line = self.Expand(line, doExpand, force, visLevels-1)
463
497
464 else:
498 else:
465 if doExpand and self.GetFoldExpanded(line):
499 if doExpand and self.GetFoldExpanded(line):
466 line = self.Expand(line, True, force, visLevels-1)
500 line = self.Expand(line, True, force, visLevels-1)
467 else:
501 else:
468 line = self.Expand(line, False, force, visLevels-1)
502 line = self.Expand(line, False, force, visLevels-1)
469 else:
503 else:
470 line = line + 1
504 line = line + 1
471
505
472 return line
506 return line
473
507
474
508
475 #----------------------------------------------------------------------
509 #----------------------------------------------------------------------
@@ -1,915 +1,915 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 _ask_exit(self):
73 def _ask_exit(self):
74 wx.CallAfter(self.ask_exit_callback, ())
74 wx.CallAfter(self.ask_exit_callback, ())
75
75
76 def _after_execute(self):
76 def _after_execute(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 wx.MutexGuiEnter()
252 wx.MutexGuiEnter()
253
253
254 #be sure not to be interrutpted before the MutexGuiLeave!
254 #be sure not to be interrutpted before the MutexGuiLeave!
255 self.write(text)
255 self.write(text)
256
256
257 except KeyboardInterrupt:
257 except KeyboardInterrupt:
258 wx.MutexGuiLeave()
258 wx.MutexGuiLeave()
259 raise KeyboardInterrupt
259 raise KeyboardInterrupt
260 wx.MutexGuiLeave()
260 wx.MutexGuiLeave()
261
261
262
262
263 def write(self, text):
263 def write(self, text):
264 '''
264 '''
265 Write given text to buffer.
265 Write given text to buffer.
266
266
267 @param text: Text to append.
267 @param text: Text to append.
268 @type text: string
268 @type text: string
269 '''
269 '''
270 segments = self.color_pat.split(text)
270 segments = self.color_pat.split(text)
271 segment = segments.pop(0)
271 segment = segments.pop(0)
272 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
272 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
273 self.AppendText(segment)
273 self.AppendText(segment)
274
274
275 if segments:
275 if segments:
276 ansi_tags = self.color_pat.findall(text)
276 ansi_tags = self.color_pat.findall(text)
277
277
278 for tag in ansi_tags:
278 for tag in ansi_tags:
279 i = segments.index(tag)
279 i = segments.index(tag)
280 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
280 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
281 self.AppendText(segments[i+1])
281 self.AppendText(segments[i+1])
282
282
283 if tag != '0':
283 if tag != '0':
284 self.SetStyling(len(segments[i+1]), self.ANSI_STYLES[tag][0])
284 self.SetStyling(len(segments[i+1]), self.ANSI_STYLES[tag][0])
285
285
286 segments.pop(i)
286 segments.pop(i)
287
287
288 self.moveCursor(self.getCurrentLineEnd())
288 self.moveCursor(self.getCurrentLineEnd())
289
289
290 def getPromptLen(self):
290 def getPromptLen(self):
291 '''
291 '''
292 Return the length of current prompt
292 Return the length of current prompt
293 '''
293 '''
294 return len(str(self.prompt_count)) + 7
294 return len(str(self.prompt_count)) + 7
295
295
296 def setPrompt(self, prompt):
296 def setPrompt(self, prompt):
297 self.prompt = prompt
297 self.prompt = prompt
298
298
299 def setIndentation(self, indentation):
299 def setIndentation(self, indentation):
300 self.indent = indentation
300 self.indent = indentation
301
301
302 def setPromptCount(self, count):
302 def setPromptCount(self, count):
303 self.prompt_count = count
303 self.prompt_count = count
304
304
305 def showPrompt(self):
305 def showPrompt(self):
306 '''
306 '''
307 Prints prompt at start of line.
307 Prints prompt at start of line.
308
308
309 @param prompt: Prompt to print.
309 @param prompt: Prompt to print.
310 @type prompt: string
310 @type prompt: string
311 '''
311 '''
312 self.write(self.prompt)
312 self.write(self.prompt)
313 #now we update the position of end of prompt
313 #now we update the position of end of prompt
314 self.current_start = self.getCurrentLineEnd()
314 self.current_start = self.getCurrentLineEnd()
315
315
316 autoindent = self.indent*' '
316 autoindent = self.indent*' '
317 autoindent = autoindent.replace(' ','\t')
317 autoindent = autoindent.replace(' ','\t')
318 self.write(autoindent)
318 self.write(autoindent)
319
319
320 def changeLine(self, text):
320 def changeLine(self, text):
321 '''
321 '''
322 Replace currently entered command line with given text.
322 Replace currently entered command line with given text.
323
323
324 @param text: Text to use as replacement.
324 @param text: Text to use as replacement.
325 @type text: string
325 @type text: string
326 '''
326 '''
327 self.SetSelection(self.getCurrentPromptStart(), self.getCurrentLineEnd())
327 self.SetSelection(self.getCurrentPromptStart(), self.getCurrentLineEnd())
328 self.ReplaceSelection(text)
328 self.ReplaceSelection(text)
329 self.moveCursor(self.getCurrentLineEnd())
329 self.moveCursor(self.getCurrentLineEnd())
330
330
331 def getCurrentPromptStart(self):
331 def getCurrentPromptStart(self):
332 return self.current_start
332 return self.current_start
333
333
334 def getCurrentLineStart(self):
334 def getCurrentLineStart(self):
335 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
335 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
336
336
337 def getCurrentLineEnd(self):
337 def getCurrentLineEnd(self):
338 return self.GetLength()
338 return self.GetLength()
339
339
340 def getCurrentLine(self):
340 def getCurrentLine(self):
341 '''
341 '''
342 Get text in current command line.
342 Get text in current command line.
343
343
344 @return: Text of current command line.
344 @return: Text of current command line.
345 @rtype: string
345 @rtype: string
346 '''
346 '''
347 return self.GetTextRange(self.getCurrentPromptStart(),
347 return self.GetTextRange(self.getCurrentPromptStart(),
348 self.getCurrentLineEnd())
348 self.getCurrentLineEnd())
349
349
350 def moveCursorOnNewValidKey(self):
350 def moveCursorOnNewValidKey(self):
351 #If cursor is at wrong position put it at last line...
351 #If cursor is at wrong position put it at last line...
352 if self.GetCurrentPos() < self.getCurrentPromptStart():
352 if self.GetCurrentPos() < self.getCurrentPromptStart():
353 self.GotoPos(self.getCurrentPromptStart())
353 self.GotoPos(self.getCurrentPromptStart())
354
354
355 def removeFromTo(self, from_pos, to_pos):
355 def removeFromTo(self, from_pos, to_pos):
356 if from_pos < to_pos:
356 if from_pos < to_pos:
357 self.SetSelection(from_pos, to_pos)
357 self.SetSelection(from_pos, to_pos)
358 self.DeleteBack()
358 self.DeleteBack()
359
359
360 def removeCurrentLine(self):
360 def removeCurrentLine(self):
361 self.LineDelete()
361 self.LineDelete()
362
362
363 def moveCursor(self, position):
363 def moveCursor(self, position):
364 self.GotoPos(position)
364 self.GotoPos(position)
365
365
366 def getCursorPos(self):
366 def getCursorPos(self):
367 return self.GetCurrentPos()
367 return self.GetCurrentPos()
368
368
369 def selectFromTo(self, from_pos, to_pos):
369 def selectFromTo(self, from_pos, to_pos):
370 self.SetSelectionStart(from_pos)
370 self.SetSelectionStart(from_pos)
371 self.SetSelectionEnd(to_pos)
371 self.SetSelectionEnd(to_pos)
372
372
373 def writeHistory(self, history):
373 def writeHistory(self, history):
374 self.removeFromTo(self.getCurrentPromptStart(), self.getCurrentLineEnd())
374 self.removeFromTo(self.getCurrentPromptStart(), self.getCurrentLineEnd())
375 self.changeLine(history)
375 self.changeLine(history)
376
376
377 def setCompletionMethod(self, completion):
377 def setCompletionMethod(self, completion):
378 if completion in ['IPYTHON', 'STC']:
378 if completion in ['IPYTHON', 'STC']:
379 self.autocomplete_mode = completion
379 self.autocomplete_mode = completion
380 else:
380 else:
381 raise AttributeError
381 raise AttributeError
382
382
383 def getCompletionMethod(self, completion):
383 def getCompletionMethod(self, completion):
384 return self.autocomplete_mode
384 return self.autocomplete_mode
385
385
386 def writeCompletion(self, possibilities):
386 def writeCompletion(self, possibilities):
387 if self.autocomplete_mode == 'IPYTHON':
387 if self.autocomplete_mode == 'IPYTHON':
388 max_len = len(max(possibilities, key=len))
388 max_len = len(max(possibilities, key=len))
389 max_symbol = ' '*max_len
389 max_symbol = ' '*max_len
390
390
391 #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...
392 test_buffer = max_symbol + ' '*4
392 test_buffer = max_symbol + ' '*4
393
393
394 allowed_symbols = 80/len(test_buffer)
394 allowed_symbols = 80/len(test_buffer)
395 if allowed_symbols == 0:
395 if allowed_symbols == 0:
396 allowed_symbols = 1
396 allowed_symbols = 1
397
397
398 pos = 1
398 pos = 1
399 buf = ''
399 buf = ''
400 for symbol in possibilities:
400 for symbol in possibilities:
401 #buf += symbol+'\n'#*spaces)
401 #buf += symbol+'\n'#*spaces)
402 if pos < allowed_symbols:
402 if pos < allowed_symbols:
403 spaces = max_len - len(symbol) + 4
403 spaces = max_len - len(symbol) + 4
404 buf += symbol+' '*spaces
404 buf += symbol+' '*spaces
405 pos += 1
405 pos += 1
406 else:
406 else:
407 buf += symbol+'\n'
407 buf += symbol+'\n'
408 pos = 1
408 pos = 1
409 self.write(buf)
409 self.write(buf)
410 else:
410 else:
411 possibilities.sort() # Python sorts are case sensitive
411 possibilities.sort() # Python sorts are case sensitive
412 self.AutoCompSetIgnoreCase(False)
412 self.AutoCompSetIgnoreCase(False)
413 self.AutoCompSetAutoHide(False)
413 self.AutoCompSetAutoHide(False)
414 #let compute the length ot last word
414 #let compute the length ot last word
415 splitter = [' ', '(', '[', '{','=']
415 splitter = [' ', '(', '[', '{','=']
416 last_word = self.getCurrentLine()
416 last_word = self.getCurrentLine()
417 for breaker in splitter:
417 for breaker in splitter:
418 last_word = last_word.split(breaker)[-1]
418 last_word = last_word.split(breaker)[-1]
419 self.AutoCompShow(len(last_word), " ".join(possibilities))
419 self.AutoCompShow(len(last_word), " ".join(possibilities))
420
420
421 def _onKeypress(self, event, skip=True):
421 def _onKeypress(self, event, skip=True):
422 '''
422 '''
423 Key press callback used for correcting behavior for console-like
423 Key press callback used for correcting behavior for console-like
424 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
425 line.
425 line.
426
426
427 @param widget: Widget that key press accored in.
427 @param widget: Widget that key press accored in.
428 @type widget: gtk.Widget
428 @type widget: gtk.Widget
429 @param event: Event object
429 @param event: Event object
430 @type event: gtk.gdk.Event
430 @type event: gtk.gdk.Event
431
431
432 @return: Return True if event as been catched.
432 @return: Return True if event as been catched.
433 @rtype: boolean
433 @rtype: boolean
434 '''
434 '''
435
435
436 if not self.AutoCompActive():
436 if not self.AutoCompActive():
437 if event.GetKeyCode() == wx.WXK_HOME:
437 if event.GetKeyCode() == wx.WXK_HOME:
438 if event.Modifiers == wx.MOD_NONE:
438 if event.Modifiers == wx.MOD_NONE:
439 self.moveCursorOnNewValidKey()
439 self.moveCursorOnNewValidKey()
440 self.moveCursor(self.getCurrentPromptStart())
440 self.moveCursor(self.getCurrentPromptStart())
441 return True
441 return True
442 elif event.Modifiers == wx.MOD_SHIFT:
442 elif event.Modifiers == wx.MOD_SHIFT:
443 self.moveCursorOnNewValidKey()
443 self.moveCursorOnNewValidKey()
444 self.selectFromTo(self.getCurrentPromptStart(), self.getCursorPos())
444 self.selectFromTo(self.getCurrentPromptStart(), self.getCursorPos())
445 return True
445 return True
446 else:
446 else:
447 return False
447 return False
448
448
449 elif event.GetKeyCode() == wx.WXK_LEFT:
449 elif event.GetKeyCode() == wx.WXK_LEFT:
450 if event.Modifiers == wx.MOD_NONE:
450 if event.Modifiers == wx.MOD_NONE:
451 self.moveCursorOnNewValidKey()
451 self.moveCursorOnNewValidKey()
452
452
453 self.moveCursor(self.getCursorPos()-1)
453 self.moveCursor(self.getCursorPos()-1)
454 if self.getCursorPos() < self.getCurrentPromptStart():
454 if self.getCursorPos() < self.getCurrentPromptStart():
455 self.moveCursor(self.getCurrentPromptStart())
455 self.moveCursor(self.getCurrentPromptStart())
456 return True
456 return True
457
457
458 elif event.GetKeyCode() == wx.WXK_BACK:
458 elif event.GetKeyCode() == wx.WXK_BACK:
459 self.moveCursorOnNewValidKey()
459 self.moveCursorOnNewValidKey()
460 if self.getCursorPos() > self.getCurrentPromptStart():
460 if self.getCursorPos() > self.getCurrentPromptStart():
461 event.Skip()
461 event.Skip()
462 return True
462 return True
463
463
464 if skip:
464 if skip:
465 if event.GetKeyCode() not in [wx.WXK_PAGEUP, wx.WXK_PAGEDOWN]\
465 if event.GetKeyCode() not in [wx.WXK_PAGEUP, wx.WXK_PAGEDOWN]\
466 and event.Modifiers == wx.MOD_NONE:
466 and event.Modifiers == wx.MOD_NONE:
467 self.moveCursorOnNewValidKey()
467 self.moveCursorOnNewValidKey()
468
468
469 event.Skip()
469 event.Skip()
470 return True
470 return True
471 return False
471 return False
472 else:
472 else:
473 event.Skip()
473 event.Skip()
474
474
475 def OnUpdateUI(self, evt):
475 def OnUpdateUI(self, evt):
476 # check for matching braces
476 # check for matching braces
477 braceAtCaret = -1
477 braceAtCaret = -1
478 braceOpposite = -1
478 braceOpposite = -1
479 charBefore = None
479 charBefore = None
480 caretPos = self.GetCurrentPos()
480 caretPos = self.GetCurrentPos()
481
481
482 if caretPos > 0:
482 if caretPos > 0:
483 charBefore = self.GetCharAt(caretPos - 1)
483 charBefore = self.GetCharAt(caretPos - 1)
484 styleBefore = self.GetStyleAt(caretPos - 1)
484 styleBefore = self.GetStyleAt(caretPos - 1)
485
485
486 # check before
486 # check before
487 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:
488 braceAtCaret = caretPos - 1
488 braceAtCaret = caretPos - 1
489
489
490 # check after
490 # check after
491 if braceAtCaret < 0:
491 if braceAtCaret < 0:
492 charAfter = self.GetCharAt(caretPos)
492 charAfter = self.GetCharAt(caretPos)
493 styleAfter = self.GetStyleAt(caretPos)
493 styleAfter = self.GetStyleAt(caretPos)
494
494
495 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:
496 braceAtCaret = caretPos
496 braceAtCaret = caretPos
497
497
498 if braceAtCaret >= 0:
498 if braceAtCaret >= 0:
499 braceOpposite = self.BraceMatch(braceAtCaret)
499 braceOpposite = self.BraceMatch(braceAtCaret)
500
500
501 if braceAtCaret != -1 and braceOpposite == -1:
501 if braceAtCaret != -1 and braceOpposite == -1:
502 self.BraceBadLight(braceAtCaret)
502 self.BraceBadLight(braceAtCaret)
503 else:
503 else:
504 self.BraceHighlight(braceAtCaret, braceOpposite)
504 self.BraceHighlight(braceAtCaret, braceOpposite)
505 #pt = self.PointFromPosition(braceOpposite)
505 #pt = self.PointFromPosition(braceOpposite)
506 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
506 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
507 #print pt
507 #print pt
508 #self.Refresh(False)
508 #self.Refresh(False)
509
509
510 class IPShellWidget(wx.Panel):
510 class IPShellWidget(wx.Panel):
511 '''
511 '''
512 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
513 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
514 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
514 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
515 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
516 because it seems to be more useful
516 because it seems to be more useful
517 Any idea to make it more 'generic' welcomed.
517 Any idea to make it more 'generic' welcomed.
518 '''
518 '''
519
519
520 def __init__(self, parent, intro=None,
520 def __init__(self, parent, intro=None,
521 background_color="BLACK", add_button_handler=None,
521 background_color="BLACK", add_button_handler=None,
522 wx_ip_shell=None, user_ns={},user_global_ns=None,
522 wx_ip_shell=None, user_ns={},user_global_ns=None,
523 ):
523 ):
524 '''
524 '''
525 Initialize.
525 Initialize.
526 Instanciate an IPython thread.
526 Instanciate an IPython thread.
527 Instanciate a WxConsoleView.
527 Instanciate a WxConsoleView.
528 Redirect I/O to console.
528 Redirect I/O to console.
529 '''
529 '''
530 wx.Panel.__init__(self,parent,wx.ID_ANY)
530 wx.Panel.__init__(self,parent,wx.ID_ANY)
531
531
532 self.parent = parent
532 self.parent = parent
533 ### IPython non blocking shell instanciation ###
533 ### IPython non blocking shell instanciation ###
534 self.cout = StringIO()
534 self.cout = StringIO()
535 self.add_button_handler = add_button_handler
535 self.add_button_handler = add_button_handler
536
536
537 if wx_ip_shell is not None:
537 if wx_ip_shell is not None:
538 self.IP = wx_ip_shell
538 self.IP = wx_ip_shell
539 else:
539 else:
540 self.IP = WxNonBlockingIPShell(self,
540 self.IP = WxNonBlockingIPShell(self,
541 cout = self.cout, cerr = self.cout,
541 cout = self.cout, cerr = self.cout,
542 ask_exit_handler = self.askExitCallback)
542 ask_exit_handler = self.askExitCallback)
543
543
544 ### IPython wx console view instanciation ###
544 ### IPython wx console view instanciation ###
545 #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
546 #If you really wnat an empty intro just call wxIPythonViewPanel
546 #If you really wnat an empty intro just call wxIPythonViewPanel
547 #with intro=''
547 #with intro=''
548 if intro is None:
548 if intro is None:
549 welcome_text = "Welcome to WxIPython Shell.\n\n"
549 welcome_text = "Welcome to WxIPython Shell.\n\n"
550 welcome_text+= self.IP.get_banner()
550 welcome_text+= self.IP.get_banner()
551 welcome_text+= "!command -> Execute command in shell\n"
551 welcome_text+= "!command -> Execute command in shell\n"
552 welcome_text+= "TAB -> Autocompletion\n"
552 welcome_text+= "TAB -> Autocompletion\n"
553 else:
553 else:
554 welcome_text = intro
554 welcome_text = intro
555
555
556 self.text_ctrl = WxConsoleView(self,
556 self.text_ctrl = WxConsoleView(self,
557 self.IP.get_prompt(),
557 self.IP.get_prompt(),
558 intro=welcome_text,
558 intro=welcome_text,
559 background_color=background_color)
559 background_color=background_color)
560
560
561 option_text = wx.StaticText(self, -1, "Options:")
561 option_text = wx.StaticText(self, -1, "Options:")
562 self.completion_option = wx.CheckBox(self, -1, "Scintilla Completion")
562 self.completion_option = wx.CheckBox(self, -1, "Scintilla Completion")
563 self.completion_option.SetToolTip(wx.ToolTip(
563 self.completion_option.SetToolTip(wx.ToolTip(
564 "Selects the completion type:\nEither Ipython default style or Scintilla one"))
564 "Selects the completion type:\nEither Ipython default style or Scintilla one"))
565 #self.completion_option.SetValue(False)
565 #self.completion_option.SetValue(False)
566 self.background_option = wx.CheckBox(self, -1, "White Background")
566 self.background_option = wx.CheckBox(self, -1, "White Background")
567 self.background_option.SetToolTip(wx.ToolTip(
567 self.background_option.SetToolTip(wx.ToolTip(
568 "Selects the back ground color: BLACK or WHITE"))
568 "Selects the back ground color: BLACK or WHITE"))
569 #self.background_option.SetValue(False)
569 #self.background_option.SetValue(False)
570 self.threading_option = wx.CheckBox(self, -1, "Execute in thread")
570 self.threading_option = wx.CheckBox(self, -1, "Execute in thread")
571 self.threading_option.SetToolTip(wx.ToolTip(
571 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"))
572 "Use threading: infinite loop don't freeze the GUI and commands can be breaked\nNo threading: maximum compatibility"))
573 #self.threading_option.SetValue(False)
573 #self.threading_option.SetValue(False)
574
574
575 self.options={'completion':{'value':'IPYTHON',
575 self.options={'completion':{'value':'IPYTHON',
576 'checkbox':self.completion_option,'STC':True,'IPYTHON':False,
576 'checkbox':self.completion_option,'STC':True,'IPYTHON':False,
577 'setfunc':self.text_ctrl.setCompletionMethod},
577 'setfunc':self.text_ctrl.setCompletionMethod},
578 'background_color':{'value':'BLACK',
578 'background_color':{'value':'BLACK',
579 'checkbox':self.background_option,'WHITE':True,'BLACK':False,
579 'checkbox':self.background_option,'WHITE':True,'BLACK':False,
580 'setfunc':self.text_ctrl.setBackgroundColor},
580 'setfunc':self.text_ctrl.setBackgroundColor},
581 'threading':{'value':'True',
581 'threading':{'value':'True',
582 'checkbox':self.threading_option,'True':True,'False':False,
582 'checkbox':self.threading_option,'True':True,'False':False,
583 'setfunc':self.IP.set_threading},
583 'setfunc':self.IP.set_threading},
584 }
584 }
585
585
586 #self.cout.write dEfault option is asynchroneous because default sate is threading ON
586 #self.cout.write dEfault option is asynchroneous because default sate is threading ON
587 self.cout.write = self.text_ctrl.asyncWrite
587 self.cout.write = self.text_ctrl.asyncWrite
588 #we reloard options
588 #we reloard options
589 self.reloadOptions(self.options)
589 self.reloadOptions(self.options)
590
590
591 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress)
591 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress)
592 self.completion_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionCompletion)
592 self.completion_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionCompletion)
593 self.background_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionBackgroundColor)
593 self.background_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionBackgroundColor)
594 self.threading_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionThreading)
594 self.threading_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionThreading)
595
595
596 ### making the layout of the panel ###
596 ### making the layout of the panel ###
597 sizer = wx.BoxSizer(wx.VERTICAL)
597 sizer = wx.BoxSizer(wx.VERTICAL)
598 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
598 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
599 option_sizer = wx.BoxSizer(wx.HORIZONTAL)
599 option_sizer = wx.BoxSizer(wx.HORIZONTAL)
600 sizer.Add(option_sizer, 0)
600 sizer.Add(option_sizer, 0)
601 option_sizer.AddMany([(10, 20),
601 option_sizer.AddMany([(10, 20),
602 (option_text, 0, wx.ALIGN_CENTER_VERTICAL),
602 (option_text, 0, wx.ALIGN_CENTER_VERTICAL),
603 (5, 5),
603 (5, 5),
604 (self.completion_option, 0, wx.ALIGN_CENTER_VERTICAL),
604 (self.completion_option, 0, wx.ALIGN_CENTER_VERTICAL),
605 (8, 8),
605 (8, 8),
606 (self.background_option, 0, wx.ALIGN_CENTER_VERTICAL),
606 (self.background_option, 0, wx.ALIGN_CENTER_VERTICAL),
607 (8, 8),
607 (8, 8),
608 (self.threading_option, 0, wx.ALIGN_CENTER_VERTICAL)
608 (self.threading_option, 0, wx.ALIGN_CENTER_VERTICAL)
609 ])
609 ])
610 self.SetAutoLayout(True)
610 self.SetAutoLayout(True)
611 sizer.Fit(self)
611 sizer.Fit(self)
612 sizer.SetSizeHints(self)
612 sizer.SetSizeHints(self)
613 self.SetSizer(sizer)
613 self.SetSizer(sizer)
614 #and we focus on the widget :)
614 #and we focus on the widget :)
615 self.SetFocus()
615 self.SetFocus()
616
616
617 #widget state management (for key handling different cases)
617 #widget state management (for key handling different cases)
618 self.setCurrentState('IDLE')
618 self.setCurrentState('IDLE')
619 self.pager_state = 'DONE'
619 self.pager_state = 'DONE'
620 self.raw_input_current_line = 0
620 self.raw_input_current_line = 0
621
621
622 def askExitCallback(self, event):
622 def askExitCallback(self, event):
623 self.askExitHandler(event)
623 self.askExitHandler(event)
624
624
625 #---------------------- IPython Thread Management ------------------------
625 #---------------------- IPython Thread Management ------------------------
626 def stateDoExecuteLine(self):
626 def stateDoExecuteLine(self):
627 lines=self.text_ctrl.getCurrentLine()
627 lines=self.text_ctrl.getCurrentLine()
628 self.text_ctrl.write('\n')
628 self.text_ctrl.write('\n')
629 lines_to_execute = lines.replace('\t',' '*4)
629 lines_to_execute = lines.replace('\t',' '*4)
630 lines_to_execute = lines_to_execute.replace('\r','')
630 lines_to_execute = lines_to_execute.replace('\r','')
631 self.IP.do_execute(lines_to_execute.encode(ENCODING))
631 self.IP.do_execute(lines_to_execute.encode(ENCODING))
632 self.updateHistoryTracker(lines)
632 self.updateHistoryTracker(lines)
633 self.setCurrentState('WAIT_END_OF_EXECUTION')
633 self.setCurrentState('WAIT_END_OF_EXECUTION')
634
634
635 def evtStateExecuteDone(self,evt):
635 def evtStateExecuteDone(self,evt):
636 if(self.text_ctrl.getCursorPos()!=0):
637 self.text_ctrl.removeCurrentLine()
638 self.doc = self.IP.get_doc_text()
636 self.doc = self.IP.get_doc_text()
639 self.help = self.IP.get_help_text()
637 self.help = self.IP.get_help_text()
640 if self.doc:
638 if self.doc:
641 self.pager_lines = self.doc[7:].split('\n')
639 self.pager_lines = self.doc[7:].split('\n')
642 self.pager_state = 'INIT'
640 self.pager_state = 'INIT'
643 self.setCurrentState('SHOW_DOC')
641 self.setCurrentState('SHOW_DOC')
644 self.pager(self.doc)
642 self.pager(self.doc)
645 elif self.help:
643 elif self.help:
646 self.pager_lines = self.help.split('\n')
644 self.pager_lines = self.help.split('\n')
647 self.pager_state = 'INIT'
645 self.pager_state = 'INIT'
648 self.setCurrentState('SHOW_DOC')
646 self.setCurrentState('SHOW_DOC')
649 self.pager(self.help)
647 self.pager(self.help)
650 else:
648 else:
649 if(self.text_ctrl.getCursorPos()!=0):
650 self.text_ctrl.removeCurrentLine()
651 self.stateShowPrompt()
651 self.stateShowPrompt()
652
652
653 def stateShowPrompt(self):
653 def stateShowPrompt(self):
654 self.setCurrentState('SHOW_PROMPT')
654 self.setCurrentState('SHOW_PROMPT')
655 self.text_ctrl.setPrompt(self.IP.get_prompt())
655 self.text_ctrl.setPrompt(self.IP.get_prompt())
656 self.text_ctrl.setIndentation(self.IP.get_indentation())
656 self.text_ctrl.setIndentation(self.IP.get_indentation())
657 self.text_ctrl.setPromptCount(self.IP.get_prompt_count())
657 self.text_ctrl.setPromptCount(self.IP.get_prompt_count())
658 self.text_ctrl.showPrompt()
658 self.text_ctrl.showPrompt()
659 self.IP.init_history_index()
659 self.IP.init_history_index()
660 self.setCurrentState('IDLE')
660 self.setCurrentState('IDLE')
661
661
662 def setCurrentState(self, state):
662 def setCurrentState(self, state):
663 self.cur_state = state
663 self.cur_state = state
664 self.updateStatusTracker(self.cur_state)
664 self.updateStatusTracker(self.cur_state)
665
665
666 def pager(self,text):
666 def pager(self,text):
667
667
668 if self.pager_state == 'INIT':
668 if self.pager_state == 'INIT':
669 #print >>sys.__stdout__,"PAGER state:",self.pager_state
669 #print >>sys.__stdout__,"PAGER state:",self.pager_state
670 self.pager_nb_lines = len(self.pager_lines)
670 self.pager_nb_lines = len(self.pager_lines)
671 self.pager_index = 0
671 self.pager_index = 0
672 self.pager_do_remove = False
672 self.pager_do_remove = False
673 self.text_ctrl.write('\n')
673 self.text_ctrl.write('\n')
674 self.pager_state = 'PROCESS_LINES'
674 self.pager_state = 'PROCESS_LINES'
675
675
676 if self.pager_state == 'PROCESS_LINES':
676 if self.pager_state == 'PROCESS_LINES':
677 #print >>sys.__stdout__,"PAGER state:",self.pager_state
677 #print >>sys.__stdout__,"PAGER state:",self.pager_state
678 if self.pager_do_remove == True:
678 if self.pager_do_remove == True:
679 self.text_ctrl.removeCurrentLine()
679 self.text_ctrl.removeCurrentLine()
680 self.pager_do_remove = False
680 self.pager_do_remove = False
681
681
682 if self.pager_nb_lines > 10:
682 if self.pager_nb_lines > 10:
683 #print >>sys.__stdout__,"PAGER processing 10 lines"
683 #print >>sys.__stdout__,"PAGER processing 10 lines"
684 if self.pager_index > 0:
684 if self.pager_index > 0:
685 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
685 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
686 else:
686 else:
687 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
687 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
688
688
689 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
689 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')
690 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
691 self.pager_index += 10
691 self.pager_index += 10
692 self.pager_nb_lines -= 10
692 self.pager_nb_lines -= 10
693 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
693 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
694 self.pager_do_remove = True
694 self.pager_do_remove = True
695 self.pager_state = 'WAITING'
695 self.pager_state = 'WAITING'
696 return
696 return
697 else:
697 else:
698 #print >>sys.__stdout__,"PAGER processing last lines"
698 #print >>sys.__stdout__,"PAGER processing last lines"
699 if self.pager_nb_lines > 0:
699 if self.pager_nb_lines > 0:
700 if self.pager_index > 0:
700 if self.pager_index > 0:
701 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
701 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
702 else:
702 else:
703 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
703 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
704
704
705 self.pager_index += 1
705 self.pager_index += 1
706 self.pager_nb_lines -= 1
706 self.pager_nb_lines -= 1
707 if self.pager_nb_lines > 0:
707 if self.pager_nb_lines > 0:
708 for line in self.pager_lines[self.pager_index:]:
708 for line in self.pager_lines[self.pager_index:]:
709 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
709 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
710 self.pager_nb_lines = 0
710 self.pager_nb_lines = 0
711 self.pager_state = 'DONE'
711 self.pager_state = 'DONE'
712 self.stateShowPrompt()
712 self.stateShowPrompt()
713
713
714 #------------------------ Key Handler ------------------------------------
714 #------------------------ Key Handler ------------------------------------
715 def keyPress(self, event):
715 def keyPress(self, event):
716 '''
716 '''
717 Key press callback with plenty of shell goodness, like history,
717 Key press callback with plenty of shell goodness, like history,
718 autocompletions, etc.
718 autocompletions, etc.
719 '''
719 '''
720 if event.GetKeyCode() == ord('C'):
720 if event.GetKeyCode() == ord('C'):
721 if event.Modifiers == wx.MOD_CONTROL or event.Modifiers == wx.MOD_ALT:
721 if event.Modifiers == wx.MOD_CONTROL or event.Modifiers == wx.MOD_ALT:
722 if self.cur_state == 'WAIT_END_OF_EXECUTION':
722 if self.cur_state == 'WAIT_END_OF_EXECUTION':
723 #we raise an exception inside the IPython thread container
723 #we raise an exception inside the IPython thread container
724 self.IP.ce.raise_exc(KeyboardInterrupt)
724 self.IP.ce.raise_exc(KeyboardInterrupt)
725 return
725 return
726
726
727 #let this before 'wx.WXK_RETURN' because we have to put 'IDLE'
727 #let this before 'wx.WXK_RETURN' because we have to put 'IDLE'
728 #mode if AutoComp has been set as inactive
728 #mode if AutoComp has been set as inactive
729 if self.cur_state == 'COMPLETING':
729 if self.cur_state == 'COMPLETING':
730 if not self.text_ctrl.AutoCompActive():
730 if not self.text_ctrl.AutoCompActive():
731 self.cur_state = 'IDLE'
731 self.cur_state = 'IDLE'
732 else:
732 else:
733 event.Skip()
733 event.Skip()
734
734
735 if event.KeyCode == wx.WXK_RETURN:
735 if event.KeyCode == wx.WXK_RETURN:
736 if self.cur_state == 'IDLE':
736 if self.cur_state == 'IDLE':
737 #we change the state ot the state machine
737 #we change the state ot the state machine
738 self.setCurrentState('DO_EXECUTE_LINE')
738 self.setCurrentState('DO_EXECUTE_LINE')
739 self.stateDoExecuteLine()
739 self.stateDoExecuteLine()
740 return
740 return
741
741
742 if self.pager_state == 'WAITING':
742 if self.pager_state == 'WAITING':
743 self.pager_state = 'PROCESS_LINES'
743 self.pager_state = 'PROCESS_LINES'
744 self.pager(self.doc)
744 self.pager(self.doc)
745 return
745 return
746
746
747 if self.cur_state == 'WAITING_USER_INPUT':
747 if self.cur_state == 'WAITING_USER_INPUT':
748 line=self.text_ctrl.getCurrentLine()
748 line=self.text_ctrl.getCurrentLine()
749 self.text_ctrl.write('\n')
749 self.text_ctrl.write('\n')
750 self.setCurrentState('WAIT_END_OF_EXECUTION')
750 self.setCurrentState('WAIT_END_OF_EXECUTION')
751 return
751 return
752
752
753 if event.GetKeyCode() in [ord('q'),ord('Q')]:
753 if event.GetKeyCode() in [ord('q'),ord('Q')]:
754 if self.pager_state == 'WAITING':
754 if self.pager_state == 'WAITING':
755 self.pager_state = 'DONE'
755 self.pager_state = 'DONE'
756 self.text_ctrl.write('\n')
756 self.text_ctrl.write('\n')
757 self.stateShowPrompt()
757 self.stateShowPrompt()
758 return
758 return
759
759
760 if self.cur_state == 'WAITING_USER_INPUT':
760 if self.cur_state == 'WAITING_USER_INPUT':
761 event.Skip()
761 event.Skip()
762
762
763 if self.cur_state == 'IDLE':
763 if self.cur_state == 'IDLE':
764 if event.KeyCode == wx.WXK_UP:
764 if event.KeyCode == wx.WXK_UP:
765 history = self.IP.history_back()
765 history = self.IP.history_back()
766 self.text_ctrl.writeHistory(history)
766 self.text_ctrl.writeHistory(history)
767 return
767 return
768 if event.KeyCode == wx.WXK_DOWN:
768 if event.KeyCode == wx.WXK_DOWN:
769 history = self.IP.history_forward()
769 history = self.IP.history_forward()
770 self.text_ctrl.writeHistory(history)
770 self.text_ctrl.writeHistory(history)
771 return
771 return
772 if event.KeyCode == wx.WXK_TAB:
772 if event.KeyCode == wx.WXK_TAB:
773 #if line empty we disable tab completion
773 #if line empty we disable tab completion
774 if not self.text_ctrl.getCurrentLine().strip():
774 if not self.text_ctrl.getCurrentLine().strip():
775 self.text_ctrl.write('\t')
775 self.text_ctrl.write('\t')
776 return
776 return
777 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
777 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
778 if len(possibilities) > 1:
778 if len(possibilities) > 1:
779 if self.text_ctrl.autocomplete_mode == 'IPYTHON':
779 if self.text_ctrl.autocomplete_mode == 'IPYTHON':
780 cur_slice = self.text_ctrl.getCurrentLine()
780 cur_slice = self.text_ctrl.getCurrentLine()
781 self.text_ctrl.write('\n')
781 self.text_ctrl.write('\n')
782 self.text_ctrl.writeCompletion(possibilities)
782 self.text_ctrl.writeCompletion(possibilities)
783 self.text_ctrl.write('\n')
783 self.text_ctrl.write('\n')
784
784
785 self.text_ctrl.showPrompt()
785 self.text_ctrl.showPrompt()
786 self.text_ctrl.write(cur_slice)
786 self.text_ctrl.write(cur_slice)
787 self.text_ctrl.changeLine(completed or cur_slice)
787 self.text_ctrl.changeLine(completed or cur_slice)
788 else:
788 else:
789 self.cur_state = 'COMPLETING'
789 self.cur_state = 'COMPLETING'
790 self.text_ctrl.writeCompletion(possibilities)
790 self.text_ctrl.writeCompletion(possibilities)
791 else:
791 else:
792 self.text_ctrl.changeLine(completed or cur_slice)
792 self.text_ctrl.changeLine(completed or cur_slice)
793 return
793 return
794 event.Skip()
794 event.Skip()
795
795
796 #------------------------ Option Section ---------------------------------
796 #------------------------ Option Section ---------------------------------
797 def evtCheckOptionCompletion(self, event):
797 def evtCheckOptionCompletion(self, event):
798 if event.IsChecked():
798 if event.IsChecked():
799 self.options['completion']['value']='STC'
799 self.options['completion']['value']='STC'
800 else:
800 else:
801 self.options['completion']['value']='IPYTHON'
801 self.options['completion']['value']='IPYTHON'
802 self.text_ctrl.setCompletionMethod(self.options['completion']['value'])
802 self.text_ctrl.setCompletionMethod(self.options['completion']['value'])
803 self.updateOptionTracker('completion',
803 self.updateOptionTracker('completion',
804 self.options['completion']['value'])
804 self.options['completion']['value'])
805 self.text_ctrl.SetFocus()
805 self.text_ctrl.SetFocus()
806
806
807 def evtCheckOptionBackgroundColor(self, event):
807 def evtCheckOptionBackgroundColor(self, event):
808 if event.IsChecked():
808 if event.IsChecked():
809 self.options['background_color']['value']='WHITE'
809 self.options['background_color']['value']='WHITE'
810 else:
810 else:
811 self.options['background_color']['value']='BLACK'
811 self.options['background_color']['value']='BLACK'
812 self.text_ctrl.setBackgroundColor(self.options['background_color']['value'])
812 self.text_ctrl.setBackgroundColor(self.options['background_color']['value'])
813 self.updateOptionTracker('background_color',
813 self.updateOptionTracker('background_color',
814 self.options['background_color']['value'])
814 self.options['background_color']['value'])
815 self.text_ctrl.SetFocus()
815 self.text_ctrl.SetFocus()
816
816
817 def evtCheckOptionThreading(self, event):
817 def evtCheckOptionThreading(self, event):
818 if event.IsChecked():
818 if event.IsChecked():
819 self.options['threading']['value']='True'
819 self.options['threading']['value']='True'
820 self.IP.set_threading(True)
820 self.IP.set_threading(True)
821 self.cout.write = self.text_ctrl.asyncWrite
821 self.cout.write = self.text_ctrl.asyncWrite
822 else:
822 else:
823 self.options['threading']['value']='False'
823 self.options['threading']['value']='False'
824 self.IP.set_threading(False)
824 self.IP.set_threading(False)
825 self.cout.write = self.text_ctrl.write
825 self.cout.write = self.text_ctrl.write
826 self.updateOptionTracker('threading',
826 self.updateOptionTracker('threading',
827 self.options['threading']['value'])
827 self.options['threading']['value'])
828 self.text_ctrl.SetFocus()
828 self.text_ctrl.SetFocus()
829
829
830 def getOptions(self):
830 def getOptions(self):
831 return self.options
831 return self.options
832
832
833 def reloadOptions(self,options):
833 def reloadOptions(self,options):
834 self.options = options
834 self.options = options
835 for key in self.options.keys():
835 for key in self.options.keys():
836 value = self.options[key]['value']
836 value = self.options[key]['value']
837 self.options[key]['checkbox'].SetValue(self.options[key][value])
837 self.options[key]['checkbox'].SetValue(self.options[key][value])
838 self.options[key]['setfunc'](value)
838 self.options[key]['setfunc'](value)
839
839
840 if self.options['threading']['value']=='True':
840 if self.options['threading']['value']=='True':
841 self.IP.set_threading(True)
841 self.IP.set_threading(True)
842 self.cout.write = self.text_ctrl.asyncWrite
842 self.cout.write = self.text_ctrl.asyncWrite
843 else:
843 else:
844 self.IP.set_threading(False)
844 self.IP.set_threading(False)
845 self.cout.write = self.text_ctrl.write
845 self.cout.write = self.text_ctrl.write
846
846
847 #------------------------ Hook Section -----------------------------------
847 #------------------------ Hook Section -----------------------------------
848 def updateOptionTracker(self,name,value):
848 def updateOptionTracker(self,name,value):
849 '''
849 '''
850 Default history tracker (does nothing)
850 Default history tracker (does nothing)
851 '''
851 '''
852 pass
852 pass
853
853
854 def setOptionTrackerHook(self,func):
854 def setOptionTrackerHook(self,func):
855 '''
855 '''
856 Define a new history tracker
856 Define a new history tracker
857 '''
857 '''
858 self.updateOptionTracker = func
858 self.updateOptionTracker = func
859
859
860 def updateHistoryTracker(self,command_line):
860 def updateHistoryTracker(self,command_line):
861 '''
861 '''
862 Default history tracker (does nothing)
862 Default history tracker (does nothing)
863 '''
863 '''
864 pass
864 pass
865
865
866 def setHistoryTrackerHook(self,func):
866 def setHistoryTrackerHook(self,func):
867 '''
867 '''
868 Define a new history tracker
868 Define a new history tracker
869 '''
869 '''
870 self.updateHistoryTracker = func
870 self.updateHistoryTracker = func
871
871
872 def updateStatusTracker(self,status):
872 def updateStatusTracker(self,status):
873 '''
873 '''
874 Default status tracker (does nothing)
874 Default status tracker (does nothing)
875 '''
875 '''
876 pass
876 pass
877
877
878 def setStatusTrackerHook(self,func):
878 def setStatusTrackerHook(self,func):
879 '''
879 '''
880 Define a new status tracker
880 Define a new status tracker
881 '''
881 '''
882 self.updateStatusTracker = func
882 self.updateStatusTracker = func
883
883
884 def askExitHandler(self, event):
884 def askExitHandler(self, event):
885 '''
885 '''
886 Default exit handler
886 Default exit handler
887 '''
887 '''
888 self.text_ctrl.write('\nExit callback has not been set.')
888 self.text_ctrl.write('\nExit callback has not been set.')
889
889
890 def setAskExitHandler(self, func):
890 def setAskExitHandler(self, func):
891 '''
891 '''
892 Define an exit handler
892 Define an exit handler
893 '''
893 '''
894 self.askExitHandler = func
894 self.askExitHandler = func
895
895
896 if __name__ == '__main__':
896 if __name__ == '__main__':
897 # Some simple code to test the shell widget.
897 # Some simple code to test the shell widget.
898 class MainWindow(wx.Frame):
898 class MainWindow(wx.Frame):
899 def __init__(self, parent, id, title):
899 def __init__(self, parent, id, title):
900 wx.Frame.__init__(self, parent, id, title, size=(300,250))
900 wx.Frame.__init__(self, parent, id, title, size=(300,250))
901 self._sizer = wx.BoxSizer(wx.VERTICAL)
901 self._sizer = wx.BoxSizer(wx.VERTICAL)
902 self.shell = IPShellWidget(self)
902 self.shell = IPShellWidget(self)
903 self._sizer.Add(self.shell, 1, wx.EXPAND)
903 self._sizer.Add(self.shell, 1, wx.EXPAND)
904 self.SetSizer(self._sizer)
904 self.SetSizer(self._sizer)
905 self.SetAutoLayout(1)
905 self.SetAutoLayout(1)
906 self.Show(True)
906 self.Show(True)
907
907
908 app = wx.PySimpleApp()
908 app = wx.PySimpleApp()
909 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
909 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
910 frame.SetSize((780, 460))
910 frame.SetSize((780, 460))
911 shell = frame.shell
911 shell = frame.shell
912
912
913 app.MainLoop()
913 app.MainLoop()
914
914
915
915
General Comments 0
You need to be logged in to leave comments. Login now