##// END OF EJS Templates
Cleanup of config, renamed _xxxx_BG keus into stdout,stdin,stderr keys
Laurent Dufrechou -
Show More
@@ -1,552 +1,548 b''
1 1 # encoding: utf-8
2 2 """
3 3 A Wx widget to act as a console and input commands.
4 4
5 5 This widget deals with prompts and provides an edit buffer
6 6 restricted to after the last prompt.
7 7 """
8 8
9 9 __docformat__ = "restructuredtext en"
10 10
11 11 #-------------------------------------------------------------------------------
12 12 # Copyright (C) 2008 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is
15 15 # in the file COPYING, distributed as part of this software.
16 16 #-------------------------------------------------------------------------------
17 17
18 18 #-------------------------------------------------------------------------------
19 19 # Imports
20 20 #-------------------------------------------------------------------------------
21 21
22 22 import wx
23 23 import wx.stc as stc
24 24
25 25 from wx.py import editwindow
26 26 import time
27 27 import sys
28 28 import string
29 29
30 30 LINESEP = '\n'
31 31 if sys.platform == 'win32':
32 32 LINESEP = '\n\r'
33 33
34 34 import re
35 35
36 36 # FIXME: Need to provide an API for non user-generated display on the
37 37 # screen: this should not be editable by the user.
38 38 #-------------------------------------------------------------------------------
39 39 # Constants
40 40 #-------------------------------------------------------------------------------
41 41 _COMPLETE_BUFFER_MARKER = 31
42 42 _ERROR_MARKER = 30
43 43 _INPUT_MARKER = 29
44 44
45 45 _DEFAULT_SIZE = 10
46 46 if sys.platform == 'darwin':
47 47 _DEFAULT_SIZE = 12
48 48
49 49 _DEFAULT_STYLE = {
50 50 #background definition
51 'stdout' : 'fore:#0000FF',
52 'stderr' : 'fore:#007f00',
53 'trace' : 'fore:#FF0000',
54
55 51 'default' : 'size:%d' % _DEFAULT_SIZE,
56 52 'bracegood' : 'fore:#00AA00,back:#000000,bold',
57 53 'bracebad' : 'fore:#FF0000,back:#000000,bold',
58 54
59 55 # properties for the various Python lexer styles
60 56 'comment' : 'fore:#007F00',
61 57 'number' : 'fore:#007F7F',
62 58 'string' : 'fore:#7F007F,italic',
63 59 'char' : 'fore:#7F007F,italic',
64 60 'keyword' : 'fore:#00007F,bold',
65 61 'triple' : 'fore:#7F0000',
66 62 'tripledouble' : 'fore:#7F0000',
67 63 'class' : 'fore:#0000FF,bold,underline',
68 64 'def' : 'fore:#007F7F,bold',
69 65 'operator' : 'bold'
70 66 }
71 67
72 68 # new style numbers
73 69 _STDOUT_STYLE = 15
74 70 _STDERR_STYLE = 16
75 71 _TRACE_STYLE = 17
76 72
77 73
78 74 # system colors
79 75 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
80 76
81 77 #-------------------------------------------------------------------------------
82 78 # The console widget class
83 79 #-------------------------------------------------------------------------------
84 80 class ConsoleWidget(editwindow.EditWindow):
85 81 """ Specialized styled text control view for console-like workflow.
86 82
87 83 This widget is mainly interested in dealing with the prompt and
88 84 keeping the cursor inside the editing line.
89 85 """
90 86
91 87 # This is where the title captured from the ANSI escape sequences are
92 88 # stored.
93 89 title = 'Console'
94 90
95 91 # The buffer being edited.
96 92 def _set_input_buffer(self, string):
97 93 self.SetSelection(self.current_prompt_pos, self.GetLength())
98 94 self.ReplaceSelection(string)
99 95 self.GotoPos(self.GetLength())
100 96
101 97 def _get_input_buffer(self):
102 98 """ Returns the text in current edit buffer.
103 99 """
104 100 input_buffer = self.GetTextRange(self.current_prompt_pos,
105 101 self.GetLength())
106 102 input_buffer = input_buffer.replace(LINESEP, '\n')
107 103 return input_buffer
108 104
109 105 input_buffer = property(_get_input_buffer, _set_input_buffer)
110 106
111 107 style = _DEFAULT_STYLE.copy()
112 108
113 109 # Translation table from ANSI escape sequences to color. Override
114 110 # this to specify your colors.
115 111 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
116 112 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
117 113 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
118 114 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
119 115 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
120 116 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
121 117 '1;34': [12, 'LIGHT BLUE'], '1;35': [13, 'MEDIUM VIOLET RED'],
122 118 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
123 119
124 120 #we define platform specific fonts
125 121 if wx.Platform == '__WXMSW__':
126 122 faces = { 'times': 'Times New Roman',
127 123 'mono' : 'Courier New',
128 124 'helv' : 'Arial',
129 125 'other': 'Comic Sans MS',
130 126 'size' : 10,
131 127 'size2': 8,
132 128 }
133 129 elif wx.Platform == '__WXMAC__':
134 130 faces = { 'times': 'Times New Roman',
135 131 'mono' : 'Monaco',
136 132 'helv' : 'Arial',
137 133 'other': 'Comic Sans MS',
138 134 'size' : 10,
139 135 'size2': 8,
140 136 }
141 137 else:
142 138 faces = { 'times': 'Times',
143 139 'mono' : 'Courier',
144 140 'helv' : 'Helvetica',
145 141 'other': 'new century schoolbook',
146 142 'size' : 10,
147 143 'size2': 8,
148 144 }
149 145
150 146 # Store the last time a refresh was done
151 147 _last_refresh_time = 0
152 148
153 149 #--------------------------------------------------------------------------
154 150 # Public API
155 151 #--------------------------------------------------------------------------
156 152
157 153 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
158 154 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
159 155 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
160 156 self.configure_scintilla()
161 157 self.enter_catched = False #this var track if 'enter' key as ever been processed
162 158 #thus it will only be reallowed until key goes up
163 159 self.current_prompt_pos = 0
164 160
165 161 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
166 162 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
167 163
168 164
169 165 def write(self, text, refresh=True):
170 166 """ Write given text to buffer, while translating the ansi escape
171 167 sequences.
172 168 """
173 169 # XXX: do not put print statements to sys.stdout/sys.stderr in
174 170 # this method, the print statements will call this method, as
175 171 # you will end up with an infinit loop
176 172 title = self.title_pat.split(text)
177 173 if len(title)>1:
178 174 self.title = title[-2]
179 175
180 176 text = self.title_pat.sub('', text)
181 177 segments = self.color_pat.split(text)
182 178 segment = segments.pop(0)
183 179 self.GotoPos(self.GetLength())
184 180 self.StartStyling(self.GetLength(), 0xFF)
185 181 try:
186 182 self.AppendText(segment)
187 183 except UnicodeDecodeError:
188 184 # XXX: Do I really want to skip the exception?
189 185 pass
190 186
191 187 if segments:
192 188 for ansi_tag, text in zip(segments[::2], segments[1::2]):
193 189 self.StartStyling(self.GetLength(), 0xFF)
194 190 try:
195 191 self.AppendText(text)
196 192 except UnicodeDecodeError:
197 193 # XXX: Do I really want to skip the exception?
198 194 pass
199 195
200 196 if ansi_tag not in self.ANSI_STYLES:
201 197 style = 0
202 198 else:
203 199 style = self.ANSI_STYLES[ansi_tag][0]
204 200
205 201 self.SetStyling(len(text), style)
206 202
207 203 self.GotoPos(self.GetLength())
208 204 if refresh:
209 205 current_time = time.time()
210 206 if current_time - self._last_refresh_time > 0.03:
211 207 if sys.platform == 'win32':
212 208 wx.SafeYield()
213 209 else:
214 210 wx.Yield()
215 211 # self.ProcessEvent(wx.PaintEvent())
216 212 self._last_refresh_time = current_time
217 213
218 214
219 215 def new_prompt(self, prompt):
220 216 """ Prints a prompt at start of line, and move the start of the
221 217 current block there.
222 218
223 219 The prompt can be given with ascii escape sequences.
224 220 """
225 221 self.write(prompt, refresh=False)
226 222 # now we update our cursor giving end of prompt
227 223 self.current_prompt_pos = self.GetLength()
228 224 self.current_prompt_line = self.GetCurrentLine()
229 225 self.EnsureCaretVisible()
230 226
231 227
232 228 def scroll_to_bottom(self):
233 229 maxrange = self.GetScrollRange(wx.VERTICAL)
234 230 self.ScrollLines(maxrange)
235 231
236 232
237 233 def pop_completion(self, possibilities, offset=0):
238 234 """ Pops up an autocompletion menu. Offset is the offset
239 235 in characters of the position at which the menu should
240 236 appear, relativ to the cursor.
241 237 """
242 238 self.AutoCompSetIgnoreCase(False)
243 239 self.AutoCompSetAutoHide(False)
244 240 self.AutoCompSetMaxHeight(len(possibilities))
245 241 self.AutoCompShow(offset, " ".join(possibilities))
246 242
247 243
248 244 def get_line_width(self):
249 245 """ Return the width of the line in characters.
250 246 """
251 247 return self.GetSize()[0]/self.GetCharWidth()
252 248
253 249 #--------------------------------------------------------------------------
254 250 # EditWindow API
255 251 #--------------------------------------------------------------------------
256 252
257 253 def OnUpdateUI(self, event):
258 254 """ Override the OnUpdateUI of the EditWindow class, to prevent
259 255 syntax highlighting both for faster redraw, and for more
260 256 consistent look and feel.
261 257 """
262 258
263 259 #--------------------------------------------------------------------------
264 260 # Styling API
265 261 #--------------------------------------------------------------------------
266 262
267 263 def configure_scintilla(self):
268 264
269 265 p = self.style
270
266
271 267 #First we define the special background colors
272 if '_COMPLETE_BUFFER_BG' in p:
273 _COMPLETE_BUFFER_BG = p['_COMPLETE_BUFFER_BG']
268 if 'trace' in p:
269 _COMPLETE_BUFFER_BG = p['trace']
274 270 else:
275 271 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
276 272
277 if '_INPUT_BUFFER_BG' in p:
278 _INPUT_BUFFER_BG = p['_INPUT_BUFFER_BG']
273 if 'stdout' in p:
274 _INPUT_BUFFER_BG = p['stdout']
279 275 else:
280 276 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
281 277
282 if '_ERROR_BG' in p:
283 _ERROR_BG = p['_ERROR_BG']
278 if 'stderr' in p:
279 _ERROR_BG = p['stderr']
284 280 else:
285 281 _ERROR_BG = '#FFF1F1' # Nice red
286 282
287 283 # Marker for complete buffer.
288 284 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
289 285 background = _COMPLETE_BUFFER_BG)
290 286
291 287 # Marker for current input buffer.
292 288 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
293 289 background = _INPUT_BUFFER_BG)
294 290 # Marker for tracebacks.
295 291 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
296 292 background = _ERROR_BG)
297 293
298 294 self.SetEOLMode(stc.STC_EOL_LF)
299 295
300 296 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
301 297 # the widget
302 298 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
303 299 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
304 300 # Also allow Ctrl Shift "=" for poor non US keyboard users.
305 301 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
306 302 stc.STC_CMD_ZOOMIN)
307 303
308 304 # Keys: we need to clear some of the keys the that don't play
309 305 # well with a console.
310 306 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
311 307 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
312 308 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
313 309 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
314 310
315 311 self.SetEOLMode(stc.STC_EOL_CRLF)
316 312 self.SetWrapMode(stc.STC_WRAP_CHAR)
317 313 self.SetWrapMode(stc.STC_WRAP_WORD)
318 314 self.SetBufferedDraw(True)
319 315
320 316 if 'antialiasing' in p:
321 317 self.SetUseAntiAliasing(p['antialiasing'])
322 318 else:
323 319 self.SetUseAntiAliasing(True)
324 320
325 321 self.SetLayoutCache(stc.STC_CACHE_PAGE)
326 322 self.SetUndoCollection(False)
327 323 self.SetUseTabs(True)
328 324 self.SetIndent(4)
329 325 self.SetTabWidth(4)
330 326
331 327 # we don't want scintilla's autocompletion to choose
332 328 # automaticaly out of a single choice list, as we pop it up
333 329 # automaticaly
334 330 self.AutoCompSetChooseSingle(False)
335 331 self.AutoCompSetMaxHeight(10)
336 332 # XXX: this doesn't seem to have an effect.
337 333 self.AutoCompSetFillUps('\n')
338 334
339 335 self.SetMargins(3, 3) #text is moved away from border with 3px
340 336 # Suppressing Scintilla margins
341 337 self.SetMarginWidth(0, 0)
342 338 self.SetMarginWidth(1, 0)
343 339 self.SetMarginWidth(2, 0)
344 340
345 341 # Xterm escape sequences
346 342 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
347 343 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
348 344
349 345 # styles
350 346
351 347 if 'carret_color' in p:
352 348 self.SetCaretForeground(p['carret_color'])
353 349 else:
354 350 self.SetCaretForeground('BLACK')
355 351
356 352 if 'background_color' in p:
357 353 background_color = p['background_color']
358 354 else:
359 355 background_color = 'WHITE'
360 356
361 357 if 'default' in p:
362 358 if 'back' not in p['default']:
363 359 p['default']+=',back:%s' % background_color
364 360 if 'size' not in p['default']:
365 361 p['default']+=',size:%s' % self.faces['size']
366 362 if 'face' not in p['default']:
367 363 p['default']+=',face:%s' % self.faces['mono']
368 364
369 365 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
370 366 else:
371 367 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s"
372 368 % (self.ANSI_STYLES['0;30'][1], background_color,
373 369 self.faces['size'], self.faces['mono']))
374 370
375 371 #all styles = default one
376 372 self.StyleClearAll()
377 373
378 374 # XXX: two lines below are usefull if not using the lexer
379 375 #for style in self.ANSI_STYLES.values():
380 376 # self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
381 377
382 378 #prompt definition
383 379 if 'prompt_in1' in p:
384 380 self.prompt_in1 = p['prompt_in1']
385 381 else:
386 382 self.prompt_in1 = \
387 383 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
388 384
389 385 if 'prompt_out' in p:
390 386 self.prompt_out = p['prompt_out']
391 387 else:
392 388 self.prompt_out = \
393 389 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
394 390
395 391 self.output_prompt_template = string.Template(self.prompt_out)
396 392 self.input_prompt_template = string.Template(self.prompt_in1)
397 393
398 394 if 'stdout' in p:
399 395 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
400 396 if 'stderr' in p:
401 397 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
402 398 if 'trace' in p:
403 399 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
404 400 if 'bracegood' in p:
405 401 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
406 402 if 'bracebad' in p:
407 403 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
408 404 if 'comment' in p:
409 405 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
410 406 if 'number' in p:
411 407 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
412 408 if 'string' in p:
413 409 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
414 410 if 'char' in p:
415 411 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
416 412 if 'keyword' in p:
417 413 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
418 414 if 'keyword' in p:
419 415 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
420 416 if 'triple' in p:
421 417 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
422 418 if 'tripledouble' in p:
423 419 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
424 420 if 'class' in p:
425 421 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
426 422 if 'def' in p:
427 423 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
428 424 if 'operator' in p:
429 425 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
430 426 if 'comment' in p:
431 427 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
432 428
433 429 #--------------------------------------------------------------------------
434 430 # Private API
435 431 #--------------------------------------------------------------------------
436 432
437 433 def _on_key_down(self, event, skip=True):
438 434 """ Key press callback used for correcting behavior for
439 435 console-like interfaces: the cursor is constraint to be after
440 436 the last prompt.
441 437
442 438 Return True if event as been catched.
443 439 """
444 440 catched = True
445 441 # Intercept some specific keys.
446 442 if event.KeyCode == ord('L') and event.ControlDown() :
447 443 self.scroll_to_bottom()
448 444 elif event.KeyCode == ord('K') and event.ControlDown() :
449 445 self.input_buffer = ''
450 446 elif event.KeyCode == ord('A') and event.ControlDown() :
451 447 self.GotoPos(self.GetLength())
452 448 self.SetSelectionStart(self.current_prompt_pos)
453 449 self.SetSelectionEnd(self.GetCurrentPos())
454 450 catched = True
455 451 elif event.KeyCode == ord('E') and event.ControlDown() :
456 452 self.GotoPos(self.GetLength())
457 453 catched = True
458 454 elif event.KeyCode == wx.WXK_PAGEUP:
459 455 self.ScrollPages(-1)
460 456 elif event.KeyCode == wx.WXK_PAGEDOWN:
461 457 self.ScrollPages(1)
462 458 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
463 459 self.ScrollLines(-1)
464 460 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
465 461 self.ScrollLines(1)
466 462 else:
467 463 catched = False
468 464
469 465 if self.AutoCompActive():
470 466 event.Skip()
471 467 else:
472 468 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
473 469 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
474 470 catched = True
475 471 if not self.enter_catched:
476 472 self.CallTipCancel()
477 473 self.write('\n', refresh=False)
478 474 # Under windows scintilla seems to be doing funny stuff to the
479 475 # line returns here, but the getter for input_buffer filters
480 476 # this out.
481 477 if sys.platform == 'win32':
482 478 self.input_buffer = self.input_buffer
483 479 self._on_enter()
484 480 self.enter_catched = True
485 481
486 482 elif event.KeyCode == wx.WXK_HOME:
487 483 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
488 484 self.GotoPos(self.current_prompt_pos)
489 485 catched = True
490 486
491 487 elif event.Modifiers == wx.MOD_SHIFT:
492 488 # FIXME: This behavior is not ideal: if the selection
493 489 # is already started, it will jump.
494 490 self.SetSelectionStart(self.current_prompt_pos)
495 491 self.SetSelectionEnd(self.GetCurrentPos())
496 492 catched = True
497 493
498 494 elif event.KeyCode == wx.WXK_UP:
499 495 if self.GetCurrentLine() > self.current_prompt_line:
500 496 if self.GetCurrentLine() == self.current_prompt_line + 1 \
501 497 and self.GetColumn(self.GetCurrentPos()) < \
502 498 self.GetColumn(self.current_prompt_pos):
503 499 self.GotoPos(self.current_prompt_pos)
504 500 else:
505 501 event.Skip()
506 502 catched = True
507 503
508 504 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
509 505 if self.GetCurrentPos() > self.current_prompt_pos:
510 506 event.Skip()
511 507 catched = True
512 508
513 509 if skip and not catched:
514 510 # Put the cursor back in the edit region
515 511 if self.GetCurrentPos() < self.current_prompt_pos:
516 512 self.GotoPos(self.current_prompt_pos)
517 513 else:
518 514 event.Skip()
519 515
520 516 return catched
521 517
522 518
523 519 def _on_key_up(self, event, skip=True):
524 520 """ If cursor is outside the editing region, put it back.
525 521 """
526 522 event.Skip()
527 523 if self.GetCurrentPos() < self.current_prompt_pos:
528 524 self.GotoPos(self.current_prompt_pos)
529 525
530 526 self.enter_catched = False #we re-allow enter event processing
531 527
532 528
533 529 if __name__ == '__main__':
534 530 # Some simple code to test the console widget.
535 531 class MainWindow(wx.Frame):
536 532 def __init__(self, parent, id, title):
537 533 wx.Frame.__init__(self, parent, id, title, size=(300,250))
538 534 self._sizer = wx.BoxSizer(wx.VERTICAL)
539 535 self.console_widget = ConsoleWidget(self)
540 536 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
541 537 self.SetSizer(self._sizer)
542 538 self.SetAutoLayout(1)
543 539 self.Show(True)
544 540
545 541 app = wx.PySimpleApp()
546 542 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
547 543 w.SetSize((780, 460))
548 544 w.Show()
549 545
550 546 app.MainLoop()
551 547
552 548
@@ -1,183 +1,179 b''
1 1 """
2 2 Entry point for a simple application giving a graphical frontend to
3 3 ipython.
4 4 """
5 5
6 6 try:
7 7 import wx
8 8 except ImportError, e:
9 9 e.message = """%s
10 10 ________________________________________________________________________________
11 11 You need wxPython to run this application.
12 12 """ % e.message
13 13 e.args = (e.message, ) + e.args[1:]
14 14 raise e
15 15
16 16 import wx.stc as stc
17 17
18 18 from wx_frontend import WxController
19 19 import __builtin__
20 20
21 21
22 22 class IPythonXController(WxController):
23 23 """ Sub class of WxController that adds some application-specific
24 24 bindings.
25 25 """
26 26
27 27 debug = False
28 28
29 29 def __init__(self, *args, **kwargs):
30 30
31 31 WxController.__init__(self, *args, **kwargs)
32 32 self.ipython0.ask_exit = self.do_exit
33 33
34 34 if kwargs['styledef'] is not None:
35 35 self.style = kwargs['styledef']
36 36
37 37 #we add a vertical line to console widget
38 38 self.SetEdgeMode(stc.STC_EDGE_LINE)
39 39 self.SetEdgeColumn(88)
40 40
41 41 self.configure_scintilla()
42 42
43 43 # Scroll to top
44 44 maxrange = self.GetScrollRange(wx.VERTICAL)
45 45 self.ScrollLines(-maxrange)
46 46
47 47
48 48 def _on_key_down(self, event, skip=True):
49 49 # Intercept Ctrl-D to quit
50 50 if event.KeyCode == ord('D') and event.ControlDown() and \
51 51 self.input_buffer == '' and \
52 52 self._input_state == 'readline':
53 53 wx.CallAfter(self.ask_exit)
54 54 else:
55 55 WxController._on_key_down(self, event, skip=skip)
56 56
57 57
58 58 def ask_exit(self):
59 59 """ Ask the user whether to exit.
60 60 """
61 61 self._input_state = 'subprocess'
62 62 self.write('\n', refresh=False)
63 63 self.capture_output()
64 64 self.ipython0.shell.exit()
65 65 self.release_output()
66 66 if not self.ipython0.exit_now:
67 67 wx.CallAfter(self.new_prompt,
68 68 self.input_prompt_template.substitute(
69 69 number=self.last_result['number'] + 1))
70 70 else:
71 71 wx.CallAfter(wx.GetApp().Exit)
72 72 self.write('Exiting ...', refresh=False)
73 73
74 74
75 75 def do_exit(self):
76 76 """ Exits the interpreter, kills the windows.
77 77 """
78 78 WxController.do_exit(self)
79 79 self.release_output()
80 80 wx.CallAfter(wx.Exit)
81 81
82 82
83 83
84 84 class IPythonX(wx.Frame):
85 85 """ Main frame of the IPythonX app.
86 86 """
87 87
88 88 def __init__(self, parent, id, title, debug=False, style=None):
89 89 wx.Frame.__init__(self, parent, id, title, size=(300,250))
90 90 self._sizer = wx.BoxSizer(wx.VERTICAL)
91 91 self.shell = IPythonXController(self, debug=debug, styledef=style)
92 92 self._sizer.Add(self.shell, 1, wx.EXPAND)
93 93 self.SetSizer(self._sizer)
94 94 self.SetAutoLayout(1)
95 95 self.Show(True)
96 96 wx.EVT_CLOSE(self, self.on_close)
97 97
98 98
99 99 def on_close(self, event):
100 100 """ Called on closing the windows.
101 101
102 102 Stops the event loop, to close all the child windows.
103 103 """
104 104 wx.CallAfter(wx.Exit)
105 105
106 106
107 107 def main():
108 108 from optparse import OptionParser
109 109 usage = """usage: %prog [options]
110 110
111 111 Simple graphical frontend to IPython, using WxWidgets."""
112 112 parser = OptionParser(usage=usage)
113 113 parser.add_option("-d", "--debug",
114 114 action="store_true", dest="debug", default=False,
115 115 help="Enable debug message for the wx frontend.")
116 116
117 117 parser.add_option("-s", "--style",
118 118 dest="colorset", default="white",
119 119 help="style: white, black")
120 120
121 121 options, args = parser.parse_args()
122 122
123 123 # Clear the options, to avoid having the ipython0 instance complain
124 124 import sys
125 125 sys.argv = sys.argv[:1]
126 126
127 127 style = None
128 128
129 129 if options.colorset == 'black':
130 130 style = {
131 131 #advanced options
132 132 'antialiasing' : True,
133 133
134 134 #background + carret color
135 135 'carret_color' : 'WHITE',
136 136 'background_color' : 'BLACK',
137 137
138 138 #prompt
139 139 'prompt_in1' : \
140 140 '\n\x01\x1b[0;30m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;30m\x02]: \x01\x1b[0m\x02',
141 141
142 142 'prompt_out' : \
143 143 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02',
144 144
145 145 #we define the background of old inputs
146 '_COMPLETE_BUFFER_BG' : '#000000', # RRGGBB: Black
146 'trace' : '#000000', # RRGGBB: Black
147 147 #we define the background of current input
148 '_INPUT_BUFFER_BG' : '#444444', # RRGGBB: Light black
148 'stdout' : '#444444', # RRGGBB: Light black
149 149 #we define the background when an error is reported
150 '_ERROR_BG' : '#800000', # RRGGBB: Light Red
150 'stderr' : '#800000', # RRGGBB: Light Red
151 151
152 #'stdout' : '',#fore:#0000FF',
153 #'stderr' : '',#fore:#007f00',
154 #'trace' : '',#fore:#FF0000',
155
156 152 #'bracegood' : 'fore:#0000FF,back:#0000FF,bold',
157 153 #'bracebad' : 'fore:#FF0000,back:#0000FF,bold',
158 154 'default' : "fore:%s,bold" % ("#EEEEEE"),
159 155
160 156 # properties for the various Python lexer styles
161 157 'comment' : 'fore:#BBBBBB,italic',
162 158 'number' : 'fore:#FF9692',
163 159 'string' : 'fore:#ed9d13,italic',
164 160 'char' : 'fore:#FFFFFF,italic',
165 161 'keyword' : 'fore:#6AB825,bold',
166 162 'triple' : 'fore:#FF7BDD',
167 163 'tripledouble' : 'fore:#FF7BDD',
168 164 'class' : 'fore:#FF00FF,bold,underline',
169 165 'def' : 'fore:#FFFF00,bold',
170 166 'operator' : 'bold'
171 167 }
172 168
173 169
174 170 app = wx.PySimpleApp()
175 171 frame = IPythonX(None, wx.ID_ANY, 'IPythonX', debug=options.debug, style=style)
176 172 frame.shell.SetFocus()
177 173 frame.shell.app = app
178 174 frame.SetSize((680, 460))
179 175
180 176 app.MainLoop()
181 177
182 178 if __name__ == '__main__':
183 179 main()
General Comments 0
You need to be logged in to leave comments. Login now