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