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