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