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