##// END OF EJS Templates
Misspelling in a docstring.
gvaroquaux -
Show More
@@ -1,307 +1,307 b''
1 1 """
2 2 Base front end class for all line-oriented frontends, rather than
3 3 block-oriented.
4 4
5 5 Currently this focuses on synchronous frontends.
6 6 """
7 7 __docformat__ = "restructuredtext en"
8 8
9 9 #-------------------------------------------------------------------------------
10 10 # Copyright (C) 2008 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-------------------------------------------------------------------------------
15 15
16 16 #-------------------------------------------------------------------------------
17 17 # Imports
18 18 #-------------------------------------------------------------------------------
19 19 import re
20 20
21 21 import IPython
22 22 import sys
23 23
24 24 from frontendbase import FrontEndBase
25 25 from IPython.kernel.core.interpreter import Interpreter
26 26
27 27 def common_prefix(strings):
28 28 """ Given a list of strings, return the common prefix between all
29 29 these strings.
30 30 """
31 31 ref = strings[0]
32 32 prefix = ''
33 33 for size in range(len(ref)):
34 34 test_prefix = ref[:size+1]
35 35 for string in strings[1:]:
36 36 if not string.startswith(test_prefix):
37 37 return prefix
38 38 prefix = test_prefix
39 39
40 40 return prefix
41 41
42 42 #-------------------------------------------------------------------------------
43 43 # Base class for the line-oriented front ends
44 44 #-------------------------------------------------------------------------------
45 45 class LineFrontEndBase(FrontEndBase):
46 46 """ Concrete implementation of the FrontEndBase class. This is meant
47 47 to be the base class behind all the frontend that are line-oriented,
48 48 rather than block-oriented.
49 49 """
50 50
51 51 # We need to keep the prompt number, to be able to increment
52 52 # it when there is an exception.
53 53 prompt_number = 1
54 54
55 55 # We keep a reference to the last result: it helps testing and
56 56 # programatic control of the frontend.
57 57 last_result = dict(number=0)
58 58
59 59 # The input buffer being edited
60 60 input_buffer = ''
61 61
62 62 # Set to true for debug output
63 63 debug = False
64 64
65 65 # A banner to print at startup
66 66 banner = None
67 67
68 68 #--------------------------------------------------------------------------
69 69 # FrontEndBase interface
70 70 #--------------------------------------------------------------------------
71 71
72 72 def __init__(self, shell=None, history=None, banner=None, *args, **kwargs):
73 73 if shell is None:
74 74 shell = Interpreter()
75 75 FrontEndBase.__init__(self, shell=shell, history=history)
76 76
77 77 if banner is not None:
78 78 self.banner = banner
79 79
80 80 def start(self):
81 81 """ Put the frontend in a state where it is ready for user
82 82 interaction.
83 83 """
84 84 if self.banner is not None:
85 85 self.write(self.banner, refresh=False)
86 86
87 87 self.new_prompt(self.input_prompt_template.substitute(number=1))
88 88
89 89
90 90 def complete(self, line):
91 91 """Complete line in engine's user_ns
92 92
93 93 Parameters
94 94 ----------
95 95 line : string
96 96
97 97 Result
98 98 ------
99 99 The replacement for the line and the list of possible completions.
100 100 """
101 101 completions = self.shell.complete(line)
102 102 complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
103 103 if completions:
104 104 prefix = common_prefix(completions)
105 105 residual = complete_sep.split(line)[:-1]
106 106 line = line[:-len(residual)] + prefix
107 107 return line, completions
108 108
109 109
110 110 def render_result(self, result):
111 111 """ Frontend-specific rendering of the result of a calculation
112 112 that has been sent to an engine.
113 113 """
114 114 if 'stdout' in result and result['stdout']:
115 115 self.write('\n' + result['stdout'])
116 116 if 'display' in result and result['display']:
117 117 self.write("%s%s\n" % (
118 118 self.output_prompt_template.substitute(
119 119 number=result['number']),
120 120 result['display']['pprint']
121 121 ) )
122 122
123 123
124 124 def render_error(self, failure):
125 125 """ Frontend-specific rendering of error.
126 126 """
127 127 self.write('\n\n'+str(failure)+'\n\n')
128 128 return failure
129 129
130 130
131 131 def is_complete(self, string):
132 132 """ Check if a string forms a complete, executable set of
133 133 commands.
134 134
135 135 For the line-oriented frontend, multi-line code is not executed
136 136 as soon as it is complete: the users has to enter two line
137 137 returns.
138 138 """
139 139 if string in ('', '\n'):
140 140 # Prefiltering, eg through ipython0, may return an empty
141 141 # string although some operations have been accomplished. We
142 142 # thus want to consider an empty string as a complete
143 143 # statement.
144 144 return True
145 145 elif ( len(self.input_buffer.split('\n'))>2
146 146 and not re.findall(r"\n[\t ]*\n[\t ]*$", string)):
147 147 return False
148 148 else:
149 149 # Add line returns here, to make sure that the statement is
150 150 # complete.
151 151 return FrontEndBase.is_complete(self, string.rstrip() + '\n\n')
152 152
153 153
154 154 def write(self, string, refresh=True):
155 155 """ Write some characters to the display.
156 156
157 157 Subclass should overide this method.
158 158
159 159 The refresh keyword argument is used in frontends with an
160 160 event loop, to choose whether the write should trigget an UI
161 161 refresh, and thus be syncrhonous, or not.
162 162 """
163 163 print >>sys.__stderr__, string
164 164
165 165
166 166 def execute(self, python_string, raw_string=None):
167 167 """ Stores the raw_string in the history, and sends the
168 168 python string to the interpreter.
169 169 """
170 170 if raw_string is None:
171 171 raw_string = python_string
172 172 # Create a false result, in case there is an exception
173 173 self.last_result = dict(number=self.prompt_number)
174 174 try:
175 175 self.history.input_cache[-1] = raw_string.rstrip()
176 176 result = self.shell.execute(python_string)
177 177 self.last_result = result
178 178 self.render_result(result)
179 179 except:
180 180 self.show_traceback()
181 181 finally:
182 182 self.after_execute()
183 183
184 184 #--------------------------------------------------------------------------
185 185 # LineFrontEndBase interface
186 186 #--------------------------------------------------------------------------
187 187
188 188 def prefilter_input(self, string):
189 """ Priflter the input to turn it in valid python.
189 """ Prefilter the input to turn it in valid python.
190 190 """
191 191 string = string.replace('\r\n', '\n')
192 192 string = string.replace('\t', 4*' ')
193 193 # Clean the trailing whitespace
194 194 string = '\n'.join(l.rstrip() for l in string.split('\n'))
195 195 return string
196 196
197 197
198 198 def after_execute(self):
199 199 """ All the operations required after an execution to put the
200 200 terminal back in a shape where it is usable.
201 201 """
202 202 self.prompt_number += 1
203 203 self.new_prompt(self.input_prompt_template.substitute(
204 204 number=(self.last_result['number'] + 1)))
205 205 # Start a new empty history entry
206 206 self._add_history(None, '')
207 207 self.history_cursor = len(self.history.input_cache) - 1
208 208
209 209
210 210 def complete_current_input(self):
211 211 """ Do code completion on current line.
212 212 """
213 213 if self.debug:
214 214 print >>sys.__stdout__, "complete_current_input",
215 215 line = self.input_buffer
216 216 new_line, completions = self.complete(line)
217 217 if len(completions)>1:
218 218 self.write_completion(completions, new_line=new_line)
219 219 elif not line == new_line:
220 220 self.input_buffer = new_line
221 221 if self.debug:
222 222 print >>sys.__stdout__, 'line', line
223 223 print >>sys.__stdout__, 'new_line', new_line
224 224 print >>sys.__stdout__, completions
225 225
226 226
227 227 def get_line_width(self):
228 228 """ Return the width of the line in characters.
229 229 """
230 230 return 80
231 231
232 232
233 233 def write_completion(self, possibilities, new_line=None):
234 234 """ Write the list of possible completions.
235 235
236 236 new_line is the completed input line that should be displayed
237 237 after the completion are writen. If None, the input_buffer
238 238 before the completion is used.
239 239 """
240 240 if new_line is None:
241 241 new_line = self.input_buffer
242 242
243 243 self.write('\n')
244 244 max_len = len(max(possibilities, key=len)) + 1
245 245
246 246 # Now we check how much symbol we can put on a line...
247 247 chars_per_line = self.get_line_width()
248 248 symbols_per_line = max(1, chars_per_line/max_len)
249 249
250 250 pos = 1
251 251 buf = []
252 252 for symbol in possibilities:
253 253 if pos < symbols_per_line:
254 254 buf.append(symbol.ljust(max_len))
255 255 pos += 1
256 256 else:
257 257 buf.append(symbol.rstrip() + '\n')
258 258 pos = 1
259 259 self.write(''.join(buf))
260 260 self.new_prompt(self.input_prompt_template.substitute(
261 261 number=self.last_result['number'] + 1))
262 262 self.input_buffer = new_line
263 263
264 264
265 265 def new_prompt(self, prompt):
266 266 """ Prints a prompt and starts a new editing buffer.
267 267
268 268 Subclasses should use this method to make sure that the
269 269 terminal is put in a state favorable for a new line
270 270 input.
271 271 """
272 272 self.input_buffer = ''
273 273 self.write(prompt)
274 274
275 275
276 276 #--------------------------------------------------------------------------
277 277 # Private API
278 278 #--------------------------------------------------------------------------
279 279
280 280 def _on_enter(self):
281 281 """ Called when the return key is pressed in a line editing
282 282 buffer.
283 283 """
284 284 current_buffer = self.input_buffer
285 285 cleaned_buffer = self.prefilter_input(current_buffer)
286 286 if self.is_complete(cleaned_buffer):
287 287 self.execute(cleaned_buffer, raw_string=current_buffer)
288 288 else:
289 289 self.input_buffer += self._get_indent_string(
290 290 current_buffer[:-1])
291 291 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
292 292 self.input_buffer += '\t'
293 293
294 294
295 295 def _get_indent_string(self, string):
296 296 """ Return the string of whitespace that prefixes a line. Used to
297 297 add the right amount of indendation when creating a new line.
298 298 """
299 299 string = string.replace('\t', ' '*4)
300 300 string = string.split('\n')[-1]
301 301 indent_chars = len(string) - len(string.lstrip())
302 302 indent_string = '\t'*(indent_chars // 4) + \
303 303 ' '*(indent_chars % 4)
304 304
305 305 return indent_string
306 306
307 307
@@ -1,435 +1,435 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 LINESEP = '\n'
29 29 if sys.platform == 'win32':
30 30 LINESEP = '\n\r'
31 31
32 32 import re
33 33
34 34 # FIXME: Need to provide an API for non user-generated display on the
35 35 # screen: this should not be editable by the user.
36 36
37 37 _DEFAULT_SIZE = 10
38 38 if sys.platform == 'darwin':
39 39 _DEFAULT_STYLE = 12
40 40
41 41 _DEFAULT_STYLE = {
42 42 'stdout' : 'fore:#0000FF',
43 43 'stderr' : 'fore:#007f00',
44 44 'trace' : 'fore:#FF0000',
45 45
46 46 'default' : 'size:%d' % _DEFAULT_SIZE,
47 47 'bracegood' : 'fore:#00AA00,back:#000000,bold',
48 48 'bracebad' : 'fore:#FF0000,back:#000000,bold',
49 49
50 50 # properties for the various Python lexer styles
51 51 'comment' : 'fore:#007F00',
52 52 'number' : 'fore:#007F7F',
53 53 'string' : 'fore:#7F007F,italic',
54 54 'char' : 'fore:#7F007F,italic',
55 55 'keyword' : 'fore:#00007F,bold',
56 56 'triple' : 'fore:#7F0000',
57 57 'tripledouble' : 'fore:#7F0000',
58 58 'class' : 'fore:#0000FF,bold,underline',
59 59 'def' : 'fore:#007F7F,bold',
60 60 'operator' : 'bold'
61 61 }
62 62
63 63 # new style numbers
64 64 _STDOUT_STYLE = 15
65 65 _STDERR_STYLE = 16
66 66 _TRACE_STYLE = 17
67 67
68 68
69 69 # system colors
70 70 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
71 71
72 72 #-------------------------------------------------------------------------------
73 73 # The console widget class
74 74 #-------------------------------------------------------------------------------
75 75 class ConsoleWidget(editwindow.EditWindow):
76 76 """ Specialized styled text control view for console-like workflow.
77 77
78 78 This widget is mainly interested in dealing with the prompt and
79 79 keeping the cursor inside the editing line.
80 80 """
81 81
82 82 # This is where the title captured from the ANSI escape sequences are
83 83 # stored.
84 84 title = 'Console'
85 85
86 86 # The buffer being edited.
87 87 def _set_input_buffer(self, string):
88 88 self.SetSelection(self.current_prompt_pos, self.GetLength())
89 89 self.ReplaceSelection(string)
90 90 self.GotoPos(self.GetLength())
91 91
92 92 def _get_input_buffer(self):
93 93 """ Returns the text in current edit buffer.
94 94 """
95 95 input_buffer = self.GetTextRange(self.current_prompt_pos,
96 96 self.GetLength())
97 97 input_buffer = input_buffer.replace(LINESEP, '\n')
98 98 return input_buffer
99 99
100 100 input_buffer = property(_get_input_buffer, _set_input_buffer)
101 101
102 102 style = _DEFAULT_STYLE.copy()
103 103
104 104 # Translation table from ANSI escape sequences to color. Override
105 105 # this to specify your colors.
106 106 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
107 107 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
108 108 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
109 109 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
110 110 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
111 111 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
112 112 '1;34': [12, 'LIGHT BLUE'], '1;35':
113 113 [13, 'MEDIUM VIOLET RED'],
114 114 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
115 115
116 116 # The color of the carret (call _apply_style() after setting)
117 117 carret_color = 'BLACK'
118 118
119 119 # Store the last time a refresh was done
120 120 _last_refresh_time = 0
121 121
122 122 #--------------------------------------------------------------------------
123 123 # Public API
124 124 #--------------------------------------------------------------------------
125 125
126 126 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
127 size=wx.DefaultSize, style=0, ):
127 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
128 128 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
129 129 self._configure_scintilla()
130 130
131 131 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
132 132 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
133 133
134 134
135 135 def write(self, text, refresh=True):
136 136 """ Write given text to buffer, while translating the ansi escape
137 137 sequences.
138 138 """
139 139 # XXX: do not put print statements to sys.stdout/sys.stderr in
140 140 # this method, the print statements will call this method, as
141 141 # you will end up with an infinit loop
142 142 title = self.title_pat.split(text)
143 143 if len(title)>1:
144 144 self.title = title[-2]
145 145
146 146 text = self.title_pat.sub('', text)
147 147 segments = self.color_pat.split(text)
148 148 segment = segments.pop(0)
149 149 self.GotoPos(self.GetLength())
150 150 self.StartStyling(self.GetLength(), 0xFF)
151 151 try:
152 152 self.AppendText(segment)
153 153 except UnicodeDecodeError:
154 154 # XXX: Do I really want to skip the exception?
155 155 pass
156 156
157 157 if segments:
158 158 for ansi_tag, text in zip(segments[::2], segments[1::2]):
159 159 self.StartStyling(self.GetLength(), 0xFF)
160 160 try:
161 161 self.AppendText(text)
162 162 except UnicodeDecodeError:
163 163 # XXX: Do I really want to skip the exception?
164 164 pass
165 165
166 166 if ansi_tag not in self.ANSI_STYLES:
167 167 style = 0
168 168 else:
169 169 style = self.ANSI_STYLES[ansi_tag][0]
170 170
171 171 self.SetStyling(len(text), style)
172 172
173 173 self.GotoPos(self.GetLength())
174 174 if refresh:
175 175 # Maybe this is faster than wx.Yield()
176 176 self.ProcessEvent(wx.PaintEvent())
177 177 current_time = time.time()
178 178 if current_time - self._last_refresh_time > 0.03:
179 179 wx.Yield()
180 180 self._last_refresh_time = current_time
181 181
182 182
183 183 def new_prompt(self, prompt):
184 184 """ Prints a prompt at start of line, and move the start of the
185 185 current block there.
186 186
187 187 The prompt can be given with ascii escape sequences.
188 188 """
189 189 self.write(prompt, refresh=False)
190 190 # now we update our cursor giving end of prompt
191 191 self.current_prompt_pos = self.GetLength()
192 192 self.current_prompt_line = self.GetCurrentLine()
193 193 wx.Yield()
194 194 self.EnsureCaretVisible()
195 195
196 196
197 197 def scroll_to_bottom(self):
198 198 maxrange = self.GetScrollRange(wx.VERTICAL)
199 199 self.ScrollLines(maxrange)
200 200
201 201
202 202 def pop_completion(self, possibilities, offset=0):
203 203 """ Pops up an autocompletion menu. Offset is the offset
204 204 in characters of the position at which the menu should
205 205 appear, relativ to the cursor.
206 206 """
207 207 self.AutoCompSetIgnoreCase(False)
208 208 self.AutoCompSetAutoHide(False)
209 209 self.AutoCompSetMaxHeight(len(possibilities))
210 210 self.AutoCompShow(offset, " ".join(possibilities))
211 211
212 212
213 213 def get_line_width(self):
214 214 """ Return the width of the line in characters.
215 215 """
216 216 return self.GetSize()[0]/self.GetCharWidth()
217 217
218 218 #--------------------------------------------------------------------------
219 219 # EditWindow API
220 220 #--------------------------------------------------------------------------
221 221
222 222 def OnUpdateUI(self, event):
223 223 """ Override the OnUpdateUI of the EditWindow class, to prevent
224 224 syntax highlighting both for faster redraw, and for more
225 225 consistent look and feel.
226 226 """
227 227
228 228 #--------------------------------------------------------------------------
229 229 # Private API
230 230 #--------------------------------------------------------------------------
231 231
232 232 def _apply_style(self):
233 233 """ Applies the colors for the different text elements and the
234 234 carret.
235 235 """
236 236 self.SetCaretForeground(self.carret_color)
237 237
238 238 #self.StyleClearAll()
239 239 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
240 240 "fore:#FF0000,back:#0000FF,bold")
241 241 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
242 242 "fore:#000000,back:#FF0000,bold")
243 243
244 244 for style in self.ANSI_STYLES.values():
245 245 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
246 246
247 247
248 248 def _configure_scintilla(self):
249 249 self.SetEOLMode(stc.STC_EOL_LF)
250 250
251 251 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
252 252 # the widget
253 253 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
254 254 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
255 255 # Also allow Ctrl Shift "=" for poor non US keyboard users.
256 256 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
257 257 stc.STC_CMD_ZOOMIN)
258 258
259 259 # Keys: we need to clear some of the keys the that don't play
260 260 # well with a console.
261 261 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
262 262 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
263 263 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
264 264 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
265 265
266 266 self.SetEOLMode(stc.STC_EOL_CRLF)
267 267 self.SetWrapMode(stc.STC_WRAP_CHAR)
268 268 self.SetWrapMode(stc.STC_WRAP_WORD)
269 269 self.SetBufferedDraw(True)
270 270 self.SetUseAntiAliasing(True)
271 271 self.SetLayoutCache(stc.STC_CACHE_PAGE)
272 272 self.SetUndoCollection(False)
273 273 self.SetUseTabs(True)
274 274 self.SetIndent(4)
275 275 self.SetTabWidth(4)
276 276
277 277 # we don't want scintilla's autocompletion to choose
278 278 # automaticaly out of a single choice list, as we pop it up
279 279 # automaticaly
280 280 self.AutoCompSetChooseSingle(False)
281 281 self.AutoCompSetMaxHeight(10)
282 282 # XXX: this doesn't seem to have an effect.
283 283 self.AutoCompSetFillUps('\n')
284 284
285 285 self.SetMargins(3, 3) #text is moved away from border with 3px
286 286 # Suppressing Scintilla margins
287 287 self.SetMarginWidth(0, 0)
288 288 self.SetMarginWidth(1, 0)
289 289 self.SetMarginWidth(2, 0)
290 290
291 291 self._apply_style()
292 292
293 293 # Xterm escape sequences
294 294 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
295 295 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
296 296
297 297 #self.SetEdgeMode(stc.STC_EDGE_LINE)
298 298 #self.SetEdgeColumn(80)
299 299
300 300 # styles
301 301 p = self.style
302 302 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
303 303 self.StyleClearAll()
304 304 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
305 305 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
306 306 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
307 307
308 308 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
309 309 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
310 310 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
311 311 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
312 312 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
313 313 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
314 314 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
315 315 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
316 316 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
317 317 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
318 318 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
319 319 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
320 320 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
321 321 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
322 322
323 323 def _on_key_down(self, event, skip=True):
324 324 """ Key press callback used for correcting behavior for
325 325 console-like interfaces: the cursor is constraint to be after
326 326 the last prompt.
327 327
328 328 Return True if event as been catched.
329 329 """
330 330 catched = True
331 331 # Intercept some specific keys.
332 332 if event.KeyCode == ord('L') and event.ControlDown() :
333 333 self.scroll_to_bottom()
334 334 elif event.KeyCode == ord('K') and event.ControlDown() :
335 335 self.input_buffer = ''
336 336 elif event.KeyCode == ord('A') and event.ControlDown() :
337 337 self.GotoPos(self.GetLength())
338 338 self.SetSelectionStart(self.current_prompt_pos)
339 339 self.SetSelectionEnd(self.GetCurrentPos())
340 340 catched = True
341 341 elif event.KeyCode == ord('E') and event.ControlDown() :
342 342 self.GotoPos(self.GetLength())
343 343 catched = True
344 344 elif event.KeyCode == wx.WXK_PAGEUP:
345 345 self.ScrollPages(-1)
346 346 elif event.KeyCode == wx.WXK_PAGEDOWN:
347 347 self.ScrollPages(1)
348 348 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
349 349 self.ScrollLines(-1)
350 350 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
351 351 self.ScrollLines(1)
352 352 else:
353 353 catched = False
354 354
355 355 if self.AutoCompActive():
356 356 event.Skip()
357 357 else:
358 358 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
359 359 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
360 360 catched = True
361 361 self.CallTipCancel()
362 362 self.write('\n', refresh=False)
363 363 # Under windows scintilla seems to be doing funny stuff to the
364 364 # line returns here, but the getter for input_buffer filters
365 365 # this out.
366 366 if sys.platform == 'win32':
367 367 self.input_buffer = self.input_buffer
368 368 self._on_enter()
369 369
370 370 elif event.KeyCode == wx.WXK_HOME:
371 371 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
372 372 self.GotoPos(self.current_prompt_pos)
373 373 catched = True
374 374
375 375 elif event.Modifiers == wx.MOD_SHIFT:
376 376 # FIXME: This behavior is not ideal: if the selection
377 377 # is already started, it will jump.
378 378 self.SetSelectionStart(self.current_prompt_pos)
379 379 self.SetSelectionEnd(self.GetCurrentPos())
380 380 catched = True
381 381
382 382 elif event.KeyCode == wx.WXK_UP:
383 383 if self.GetCurrentLine() > self.current_prompt_line:
384 384 if self.GetCurrentLine() == self.current_prompt_line + 1 \
385 385 and self.GetColumn(self.GetCurrentPos()) < \
386 386 self.GetColumn(self.current_prompt_pos):
387 387 self.GotoPos(self.current_prompt_pos)
388 388 else:
389 389 event.Skip()
390 390 catched = True
391 391
392 392 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
393 393 if self.GetCurrentPos() > self.current_prompt_pos:
394 394 event.Skip()
395 395 catched = True
396 396
397 397 if skip and not catched:
398 398 # Put the cursor back in the edit region
399 399 if self.GetCurrentPos() < self.current_prompt_pos:
400 400 self.GotoPos(self.current_prompt_pos)
401 401 else:
402 402 event.Skip()
403 403
404 404 return catched
405 405
406 406
407 407 def _on_key_up(self, event, skip=True):
408 408 """ If cursor is outside the editing region, put it back.
409 409 """
410 410 event.Skip()
411 411 if self.GetCurrentPos() < self.current_prompt_pos:
412 412 self.GotoPos(self.current_prompt_pos)
413 413
414 414
415 415
416 416 if __name__ == '__main__':
417 417 # Some simple code to test the console widget.
418 418 class MainWindow(wx.Frame):
419 419 def __init__(self, parent, id, title):
420 420 wx.Frame.__init__(self, parent, id, title, size=(300,250))
421 421 self._sizer = wx.BoxSizer(wx.VERTICAL)
422 422 self.console_widget = ConsoleWidget(self)
423 423 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
424 424 self.SetSizer(self._sizer)
425 425 self.SetAutoLayout(1)
426 426 self.Show(True)
427 427
428 428 app = wx.PySimpleApp()
429 429 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
430 430 w.SetSize((780, 460))
431 431 w.Show()
432 432
433 433 app.MainLoop()
434 434
435 435
@@ -1,511 +1,512 b''
1 1 # encoding: utf-8 -*- test-case-name:
2 2 # FIXME: Need to add tests.
3 3 # ipython1.frontend.wx.tests.test_wx_frontend -*-
4 4
5 5 """Classes to provide a Wx frontend to the
6 6 IPython.kernel.core.interpreter.
7 7
8 8 This class inherits from ConsoleWidget, that provides a console-like
9 9 widget to provide a text-rendering widget suitable for a terminal.
10 10 """
11 11
12 12 __docformat__ = "restructuredtext en"
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Copyright (C) 2008 The IPython Development Team
16 16 #
17 17 # Distributed under the terms of the BSD License. The full license is in
18 18 # the file COPYING, distributed as part of this software.
19 19 #-------------------------------------------------------------------------------
20 20
21 21 #-------------------------------------------------------------------------------
22 22 # Imports
23 23 #-------------------------------------------------------------------------------
24 24
25 25 # Major library imports
26 26 import re
27 27 import __builtin__
28 28 from time import sleep
29 29 import sys
30 30 from threading import Lock
31 31 import string
32 32
33 33 import wx
34 34 from wx import stc
35 35
36 36 # Ipython-specific imports.
37 37 from IPython.frontend._process import PipedProcess
38 38 from console_widget import ConsoleWidget
39 39 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
40 40
41 41 #-------------------------------------------------------------------------------
42 42 # Constants
43 43 #-------------------------------------------------------------------------------
44 44
45 45 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
46 46 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
47 47 _ERROR_BG = '#FFF1F1' # Nice red
48 48
49 49 _COMPLETE_BUFFER_MARKER = 31
50 50 _ERROR_MARKER = 30
51 51 _INPUT_MARKER = 29
52 52
53 53 prompt_in1 = \
54 54 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
55 55
56 56 prompt_out = \
57 57 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
58 58
59 59 #-------------------------------------------------------------------------------
60 60 # Classes to implement the Wx frontend
61 61 #-------------------------------------------------------------------------------
62 62 class WxController(ConsoleWidget, PrefilterFrontEnd):
63 63 """Classes to provide a Wx frontend to the
64 64 IPython.kernel.core.interpreter.
65 65
66 66 This class inherits from ConsoleWidget, that provides a console-like
67 67 widget to provide a text-rendering widget suitable for a terminal.
68 68 """
69 69
70 70 output_prompt_template = string.Template(prompt_out)
71 71
72 72 input_prompt_template = string.Template(prompt_in1)
73 73
74 74 # Print debug info on what is happening to the console.
75 75 debug = False
76 76
77 77 # The title of the terminal, as captured through the ANSI escape
78 78 # sequences.
79 79 def _set_title(self, title):
80 80 return self.Parent.SetTitle(title)
81 81
82 82 def _get_title(self):
83 83 return self.Parent.GetTitle()
84 84
85 85 title = property(_get_title, _set_title)
86 86
87 87
88 88 # The buffer being edited.
89 89 # We are duplicating the definition here because of multiple
90 90 # inheritence
91 91 def _set_input_buffer(self, string):
92 92 ConsoleWidget._set_input_buffer(self, string)
93 93 self._colorize_input_buffer()
94 94
95 95 def _get_input_buffer(self):
96 96 """ Returns the text in current edit buffer.
97 97 """
98 98 return ConsoleWidget._get_input_buffer(self)
99 99
100 100 input_buffer = property(_get_input_buffer, _set_input_buffer)
101 101
102 102
103 103 #--------------------------------------------------------------------------
104 104 # Private Attributes
105 105 #--------------------------------------------------------------------------
106 106
107 107 # A flag governing the behavior of the input. Can be:
108 108 #
109 109 # 'readline' for readline-like behavior with a prompt
110 110 # and an edit buffer.
111 111 # 'raw_input' similar to readline, but triggered by a raw-input
112 112 # call. Can be used by subclasses to act differently.
113 113 # 'subprocess' for sending the raw input directly to a
114 114 # subprocess.
115 115 # 'buffering' for buffering of the input, that will be used
116 116 # when the input state switches back to another state.
117 117 _input_state = 'readline'
118 118
119 119 # Attribute to store reference to the pipes of a subprocess, if we
120 120 # are running any.
121 121 _running_process = False
122 122
123 123 # A queue for writing fast streams to the screen without flooding the
124 124 # event loop
125 125 _out_buffer = []
126 126
127 127 # A lock to lock the _out_buffer to make sure we don't empty it
128 128 # while it is being swapped
129 129 _out_buffer_lock = Lock()
130 130
131 131 # The different line markers used to higlight the prompts.
132 132 _markers = dict()
133 133
134 134 #--------------------------------------------------------------------------
135 135 # Public API
136 136 #--------------------------------------------------------------------------
137 137
138 138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
139 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
139 size=wx.DefaultSize,
140 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
140 141 *args, **kwds):
141 142 """ Create Shell instance.
142 143 """
143 144 ConsoleWidget.__init__(self, parent, id, pos, size, style)
144 145 PrefilterFrontEnd.__init__(self, **kwds)
145 146
146 147 # Marker for complete buffer.
147 148 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
148 149 background=_COMPLETE_BUFFER_BG)
149 150 # Marker for current input buffer.
150 151 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
151 152 background=_INPUT_BUFFER_BG)
152 153 # Marker for tracebacks.
153 154 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
154 155 background=_ERROR_BG)
155 156
156 157 # A time for flushing the write buffer
157 158 BUFFER_FLUSH_TIMER_ID = 100
158 159 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
159 160 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
160 161
161 162 if 'debug' in kwds:
162 163 self.debug = kwds['debug']
163 164 kwds.pop('debug')
164 165
165 166 # Inject self in namespace, for debug
166 167 if self.debug:
167 168 self.shell.user_ns['self'] = self
168 169
169 170
170 171 def raw_input(self, prompt):
171 172 """ A replacement from python's raw_input.
172 173 """
173 174 self.new_prompt(prompt)
174 175 self._input_state = 'raw_input'
175 176 if hasattr(self, '_cursor'):
176 177 del self._cursor
177 178 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
178 179 self.waiting = True
179 180 self.__old_on_enter = self._on_enter
180 181 def my_on_enter():
181 182 self.waiting = False
182 183 self._on_enter = my_on_enter
183 184 # XXX: Busy waiting, ugly.
184 185 while self.waiting:
185 186 wx.Yield()
186 187 sleep(0.1)
187 188 self._on_enter = self.__old_on_enter
188 189 self._input_state = 'buffering'
189 190 self._cursor = wx.BusyCursor()
190 191 return self.input_buffer.rstrip('\n')
191 192
192 193
193 194 def system_call(self, command_string):
194 195 self._input_state = 'subprocess'
195 196 self._running_process = PipedProcess(command_string,
196 197 out_callback=self.buffered_write,
197 198 end_callback = self._end_system_call)
198 199 self._running_process.start()
199 200 # XXX: another one of these polling loops to have a blocking
200 201 # call
201 202 wx.Yield()
202 203 while self._running_process:
203 204 wx.Yield()
204 205 sleep(0.1)
205 206 # Be sure to flush the buffer.
206 207 self._buffer_flush(event=None)
207 208
208 209
209 210 def do_calltip(self):
210 211 """ Analyse current and displays useful calltip for it.
211 212 """
212 213 if self.debug:
213 214 print >>sys.__stdout__, "do_calltip"
214 215 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
215 216 symbol = self.input_buffer
216 217 symbol_string = separators.split(symbol)[-1]
217 218 base_symbol_string = symbol_string.split('.')[0]
218 219 if base_symbol_string in self.shell.user_ns:
219 220 symbol = self.shell.user_ns[base_symbol_string]
220 221 elif base_symbol_string in self.shell.user_global_ns:
221 222 symbol = self.shell.user_global_ns[base_symbol_string]
222 223 elif base_symbol_string in __builtin__.__dict__:
223 224 symbol = __builtin__.__dict__[base_symbol_string]
224 225 else:
225 226 return False
226 227 try:
227 228 for name in symbol_string.split('.')[1:] + ['__doc__']:
228 229 symbol = getattr(symbol, name)
229 230 self.AutoCompCancel()
230 231 wx.Yield()
231 232 self.CallTipShow(self.GetCurrentPos(), symbol)
232 233 except:
233 234 # The retrieve symbol couldn't be converted to a string
234 235 pass
235 236
236 237
237 238 def _popup_completion(self, create=False):
238 239 """ Updates the popup completion menu if it exists. If create is
239 240 true, open the menu.
240 241 """
241 242 if self.debug:
242 243 print >>sys.__stdout__, "_popup_completion"
243 244 line = self.input_buffer
244 245 if (self.AutoCompActive() and line and not line[-1] == '.') \
245 246 or create==True:
246 247 suggestion, completions = self.complete(line)
247 248 offset=0
248 249 if completions:
249 250 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
250 251 residual = complete_sep.split(line)[-1]
251 252 offset = len(residual)
252 253 self.pop_completion(completions, offset=offset)
253 254 if self.debug:
254 255 print >>sys.__stdout__, completions
255 256
256 257
257 258 def buffered_write(self, text):
258 259 """ A write method for streams, that caches the stream in order
259 260 to avoid flooding the event loop.
260 261
261 262 This can be called outside of the main loop, in separate
262 263 threads.
263 264 """
264 265 self._out_buffer_lock.acquire()
265 266 self._out_buffer.append(text)
266 267 self._out_buffer_lock.release()
267 268 if not self._buffer_flush_timer.IsRunning():
268 269 wx.CallAfter(self._buffer_flush_timer.Start,
269 270 milliseconds=100, oneShot=True)
270 271
271 272
272 273 #--------------------------------------------------------------------------
273 274 # LineFrontEnd interface
274 275 #--------------------------------------------------------------------------
275 276
276 277 def execute(self, python_string, raw_string=None):
277 278 self._input_state = 'buffering'
278 279 self.CallTipCancel()
279 280 self._cursor = wx.BusyCursor()
280 281 if raw_string is None:
281 282 raw_string = python_string
282 283 end_line = self.current_prompt_line \
283 284 + max(1, len(raw_string.split('\n'))-1)
284 285 for i in range(self.current_prompt_line, end_line):
285 286 if i in self._markers:
286 287 self.MarkerDeleteHandle(self._markers[i])
287 288 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
288 289 # Update the display:
289 290 wx.Yield()
290 291 self.GotoPos(self.GetLength())
291 292 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
292 293
293 294 def save_output_hooks(self):
294 295 self.__old_raw_input = __builtin__.raw_input
295 296 PrefilterFrontEnd.save_output_hooks(self)
296 297
297 298 def capture_output(self):
298 299 __builtin__.raw_input = self.raw_input
299 300 self.SetLexer(stc.STC_LEX_NULL)
300 301 PrefilterFrontEnd.capture_output(self)
301 302
302 303
303 304 def release_output(self):
304 305 __builtin__.raw_input = self.__old_raw_input
305 306 PrefilterFrontEnd.release_output(self)
306 307 self.SetLexer(stc.STC_LEX_PYTHON)
307 308
308 309
309 310 def after_execute(self):
310 311 PrefilterFrontEnd.after_execute(self)
311 312 # Clear the wait cursor
312 313 if hasattr(self, '_cursor'):
313 314 del self._cursor
314 315 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
315 316
316 317
317 318 def show_traceback(self):
318 319 start_line = self.GetCurrentLine()
319 320 PrefilterFrontEnd.show_traceback(self)
320 321 wx.Yield()
321 322 for i in range(start_line, self.GetCurrentLine()):
322 323 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
323 324
324 325
325 326 #--------------------------------------------------------------------------
326 327 # ConsoleWidget interface
327 328 #--------------------------------------------------------------------------
328 329
329 330 def new_prompt(self, prompt):
330 331 """ Display a new prompt, and start a new input buffer.
331 332 """
332 333 self._input_state = 'readline'
333 334 ConsoleWidget.new_prompt(self, prompt)
334 335 i = self.current_prompt_line
335 336 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
336 337
337 338
338 339 def write(self, *args, **kwargs):
339 340 # Avoid multiple inheritence, be explicit about which
340 341 # parent method class gets called
341 342 ConsoleWidget.write(self, *args, **kwargs)
342 343
343 344
344 345 def _on_key_down(self, event, skip=True):
345 346 """ Capture the character events, let the parent
346 347 widget handle them, and put our logic afterward.
347 348 """
348 349 # FIXME: This method needs to be broken down in smaller ones.
349 350 current_line_number = self.GetCurrentLine()
350 351 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
351 352 # Capture Control-C
352 353 if self._input_state == 'subprocess':
353 354 if self.debug:
354 355 print >>sys.__stderr__, 'Killing running process'
355 356 self._running_process.process.kill()
356 357 elif self._input_state == 'buffering':
357 358 if self.debug:
358 359 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
359 360 raise KeyboardInterrupt
360 361 # XXX: We need to make really sure we
361 362 # get back to a prompt.
362 363 elif self._input_state == 'subprocess' and (
363 364 ( event.KeyCode<256 and
364 365 not event.ControlDown() )
365 366 or
366 367 ( event.KeyCode in (ord('d'), ord('D')) and
367 368 event.ControlDown())):
368 369 # We are running a process, we redirect keys.
369 370 ConsoleWidget._on_key_down(self, event, skip=skip)
370 371 char = chr(event.KeyCode)
371 372 # Deal with some inconsistency in wx keycodes:
372 373 if char == '\r':
373 374 char = '\n'
374 375 elif not event.ShiftDown():
375 376 char = char.lower()
376 377 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
377 378 char = '\04'
378 379 self._running_process.process.stdin.write(char)
379 380 self._running_process.process.stdin.flush()
380 381 elif event.KeyCode in (ord('('), 57):
381 382 # Calltips
382 383 event.Skip()
383 384 self.do_calltip()
384 385 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
385 386 event.Skip()
386 387 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
387 388 wx.CallAfter(self._popup_completion, create=True)
388 389 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
389 390 wx.WXK_RIGHT, wx.WXK_ESCAPE):
390 391 wx.CallAfter(self._popup_completion)
391 392 else:
392 393 # Up history
393 394 if event.KeyCode == wx.WXK_UP and (
394 395 ( current_line_number == self.current_prompt_line and
395 396 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
396 397 or event.ControlDown() ):
397 398 new_buffer = self.get_history_previous(
398 399 self.input_buffer)
399 400 if new_buffer is not None:
400 401 self.input_buffer = new_buffer
401 402 if self.GetCurrentLine() > self.current_prompt_line:
402 403 # Go to first line, for seemless history up.
403 404 self.GotoPos(self.current_prompt_pos)
404 405 # Down history
405 406 elif event.KeyCode == wx.WXK_DOWN and (
406 407 ( current_line_number == self.LineCount -1 and
407 408 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
408 409 or event.ControlDown() ):
409 410 new_buffer = self.get_history_next()
410 411 if new_buffer is not None:
411 412 self.input_buffer = new_buffer
412 413 # Tab-completion
413 414 elif event.KeyCode == ord('\t'):
414 415 last_line = self.input_buffer.split('\n')[-1]
415 416 if not re.match(r'^\s*$', last_line):
416 417 self.complete_current_input()
417 418 if self.AutoCompActive():
418 419 wx.CallAfter(self._popup_completion, create=True)
419 420 else:
420 421 event.Skip()
421 422 else:
422 423 ConsoleWidget._on_key_down(self, event, skip=skip)
423 424
424 425
425 426 def _on_key_up(self, event, skip=True):
426 427 """ Called when any key is released.
427 428 """
428 429 if event.KeyCode in (59, ord('.')):
429 430 # Intercepting '.'
430 431 event.Skip()
431 432 wx.CallAfter(self._popup_completion, create=True)
432 433 else:
433 434 ConsoleWidget._on_key_up(self, event, skip=skip)
434 435
435 436
436 437 def _on_enter(self):
437 438 """ Called on return key down, in readline input_state.
438 439 """
439 440 if self.debug:
440 441 print >>sys.__stdout__, repr(self.input_buffer)
441 442 PrefilterFrontEnd._on_enter(self)
442 443
443 444
444 445 #--------------------------------------------------------------------------
445 446 # EditWindow API
446 447 #--------------------------------------------------------------------------
447 448
448 449 def OnUpdateUI(self, event):
449 450 """ Override the OnUpdateUI of the EditWindow class, to prevent
450 451 syntax highlighting both for faster redraw, and for more
451 452 consistent look and feel.
452 453 """
453 454 if not self._input_state == 'readline':
454 455 ConsoleWidget.OnUpdateUI(self, event)
455 456
456 457 #--------------------------------------------------------------------------
457 458 # Private API
458 459 #--------------------------------------------------------------------------
459 460
460 461 def _end_system_call(self):
461 462 """ Called at the end of a system call.
462 463 """
463 464 self._input_state = 'buffering'
464 465 self._running_process = False
465 466
466 467
467 468 def _buffer_flush(self, event):
468 469 """ Called by the timer to flush the write buffer.
469 470
470 471 This is always called in the mainloop, by the wx timer.
471 472 """
472 473 self._out_buffer_lock.acquire()
473 474 _out_buffer = self._out_buffer
474 475 self._out_buffer = []
475 476 self._out_buffer_lock.release()
476 477 self.write(''.join(_out_buffer), refresh=False)
477 478
478 479
479 480 def _colorize_input_buffer(self):
480 481 """ Keep the input buffer lines at a bright color.
481 482 """
482 483 if not self._input_state in ('readline', 'raw_input'):
483 484 return
484 485 end_line = self.GetCurrentLine()
485 486 if not sys.platform == 'win32':
486 487 end_line += 1
487 488 for i in range(self.current_prompt_line, end_line):
488 489 if i in self._markers:
489 490 self.MarkerDeleteHandle(self._markers[i])
490 491 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
491 492
492 493
493 494 if __name__ == '__main__':
494 495 class MainWindow(wx.Frame):
495 496 def __init__(self, parent, id, title):
496 497 wx.Frame.__init__(self, parent, id, title, size=(300,250))
497 498 self._sizer = wx.BoxSizer(wx.VERTICAL)
498 499 self.shell = WxController(self)
499 500 self._sizer.Add(self.shell, 1, wx.EXPAND)
500 501 self.SetSizer(self._sizer)
501 502 self.SetAutoLayout(1)
502 503 self.Show(True)
503 504
504 505 app = wx.PySimpleApp()
505 506 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
506 507 frame.shell.SetFocus()
507 508 frame.SetSize((680, 460))
508 509 self = frame.shell
509 510
510 511 app.MainLoop()
511 512
General Comments 0
You need to be logged in to leave comments. Login now