##// END OF EJS Templates
Better fonts on MacOSX....
gvaroquaux -
Show More
@@ -1,208 +1,212 b''
1 1 """
2 2 Frontend class that uses IPython0 to prefilter the inputs.
3 3
4 4 Using the IPython0 mechanism gives us access to the magics.
5 5
6 6 This is a transitory class, used here to do the transition between
7 7 ipython0 and ipython1. This class is meant to be short-lived as more
8 8 functionnality is abstracted out of ipython0 in reusable functions and
9 9 is added on the interpreter. This class can be a used to guide this
10 10 refactoring.
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 import sys
25 25
26 26 from linefrontendbase import LineFrontEndBase, common_prefix
27 27
28 28 from IPython.ipmaker import make_IPython
29 29 from IPython.ipapi import IPApi
30 30 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
31 31
32 32 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
33 33
34 34 from IPython.genutils import Term
35 35 import pydoc
36 36 import os
37 37
38 38
39 39 def mk_system_call(system_call_function, command):
40 40 """ given a os.system replacement, and a leading string command,
41 41 returns a function that will execute the command with the given
42 42 argument string.
43 43 """
44 44 def my_system_call(args):
45 45 system_call_function("%s %s" % (command, args))
46 46 return my_system_call
47 47
48 48 #-------------------------------------------------------------------------------
49 49 # Frontend class using ipython0 to do the prefiltering.
50 50 #-------------------------------------------------------------------------------
51 51 class PrefilterFrontEnd(LineFrontEndBase):
52 52 """ Class that uses ipython0 to do prefilter the input, do the
53 53 completion and the magics.
54 54
55 55 The core trick is to use an ipython0 instance to prefilter the
56 56 input, and share the namespace between the interpreter instance used
57 57 to execute the statements and the ipython0 used for code
58 58 completion...
59 59 """
60 60
61 61 def __init__(self, *args, **kwargs):
62 62 self.save_output_hooks()
63 63 # Instanciate an IPython0 interpreter to be able to use the
64 64 # prefiltering.
65 65 self.ipython0 = make_IPython()
66 66 # Set the pager:
67 67 self.ipython0.set_hook('show_in_pager',
68 68 lambda s, string: self.write("\n"+string))
69 69 self.ipython0.write = self.write
70 70 self._ip = _ip = IPApi(self.ipython0)
71 71 # Make sure the raw system call doesn't get called, as we don't
72 72 # have a stdin accessible.
73 73 self._ip.system = self.system_call
74 74 # XXX: Muck around with magics so that they work better
75 75 # in our environment
76 76 self.ipython0.magic_ls = mk_system_call(self.system_call,
77 77 'ls -CF')
78 78 # And now clean up the mess created by ipython0
79 79 self.release_output()
80 80 if not 'banner' in kwargs:
81 81 kwargs['banner'] = self.ipython0.BANNER + """
82 82 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
83 83
84 84 LineFrontEndBase.__init__(self, *args, **kwargs)
85 85 # XXX: Hack: mix the two namespaces
86 86 self.shell.user_ns = self.ipython0.user_ns
87 87 self.shell.user_global_ns = self.ipython0.user_global_ns
88 88
89 89 self.shell.output_trap = RedirectorOutputTrap(
90 90 out_callback=self.write,
91 91 err_callback=self.write,
92 92 )
93 93 self.shell.traceback_trap = SyncTracebackTrap(
94 94 formatters=self.shell.traceback_trap.formatters,
95 95 )
96 96
97 97 #--------------------------------------------------------------------------
98 98 # FrontEndBase interface
99 99 #--------------------------------------------------------------------------
100 100
101 101 def show_traceback(self):
102 102 """ Use ipython0 to capture the last traceback and display it.
103 103 """
104 104 self.capture_output()
105 105 self.ipython0.showtraceback()
106 106 self.release_output()
107 107
108 108
109 109 def execute(self, python_string, raw_string=None):
110 if self.debug:
111 print 'Executing Python code:', repr(python_string)
110 112 self.capture_output()
111 113 LineFrontEndBase.execute(self, python_string,
112 114 raw_string=raw_string)
113 115 self.release_output()
114 116
115 117
116 118 def save_output_hooks(self):
117 119 """ Store all the output hooks we can think of, to be able to
118 120 restore them.
119 121
120 122 We need to do this early, as starting the ipython0 instance will
121 123 screw ouput hooks.
122 124 """
123 125 self.__old_cout_write = Term.cout.write
124 126 self.__old_cerr_write = Term.cerr.write
125 127 self.__old_stdout = sys.stdout
126 128 self.__old_stderr= sys.stderr
127 129 self.__old_help_output = pydoc.help.output
128 130 self.__old_display_hook = sys.displayhook
129 131
130 132
131 133 def capture_output(self):
132 134 """ Capture all the output mechanisms we can think of.
133 135 """
134 136 self.save_output_hooks()
135 137 Term.cout.write = self.write
136 138 Term.cerr.write = self.write
137 139 sys.stdout = Term.cout
138 140 sys.stderr = Term.cerr
139 141 pydoc.help.output = self.shell.output_trap.out
140 142
141 143
142 144 def release_output(self):
143 145 """ Release all the different captures we have made.
144 146 """
145 147 Term.cout.write = self.__old_cout_write
146 148 Term.cerr.write = self.__old_cerr_write
147 149 sys.stdout = self.__old_stdout
148 150 sys.stderr = self.__old_stderr
149 151 pydoc.help.output = self.__old_help_output
150 152 sys.displayhook = self.__old_display_hook
151 153
152 154
153 155 def complete(self, line):
154 156 word = line.split('\n')[-1].split(' ')[-1]
155 157 completions = self.ipython0.complete(word)
156 158 # FIXME: The proper sort should be done in the complete method.
157 159 key = lambda x: x.replace('_', '')
158 160 completions.sort(key=key)
159 161 if completions:
160 162 prefix = common_prefix(completions)
161 163 line = line[:-len(word)] + prefix
162 164 return line, completions
163 165
164 166
165 167 #--------------------------------------------------------------------------
166 168 # LineFrontEndBase interface
167 169 #--------------------------------------------------------------------------
168 170
169 171 def prefilter_input(self, input_string):
170 172 """ Using IPython0 to prefilter the commands to turn them
171 173 in executable statements that are valid Python strings.
172 174 """
173 175 input_string = LineFrontEndBase.prefilter_input(self, input_string)
174 176 filtered_lines = []
175 177 # The IPython0 prefilters sometime produce output. We need to
176 178 # capture it.
177 179 self.capture_output()
178 180 self.last_result = dict(number=self.prompt_number)
179 181 try:
180 182 for line in input_string.split('\n'):
181 filtered_lines.append(self.ipython0.prefilter(line, False))
183 filtered_lines.append(
184 self.ipython0.prefilter(line, False).rstrip())
182 185 except:
183 186 # XXX: probably not the right thing to do.
184 187 self.ipython0.showsyntaxerror()
185 188 self.after_execute()
186 189 finally:
187 190 self.release_output()
188 191
192 # Clean up the trailing whitespace, to avoid indentation errors
189 193 filtered_string = '\n'.join(filtered_lines)
190 194 return filtered_string
191 195
192 196
193 197 #--------------------------------------------------------------------------
194 198 # PrefilterFrontEnd interface
195 199 #--------------------------------------------------------------------------
196 200
197 201 def system_call(self, command_string):
198 202 """ Allows for frontend to define their own system call, to be
199 203 able capture output and redirect input.
200 204 """
201 205 return os.system(command_string)
202 206
203 207
204 208 def do_exit(self):
205 209 """ Exit the shell, cleanup and save the history.
206 210 """
207 211 self.ipython0.atexit_operations()
208 212
@@ -1,415 +1,417 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 if sys.platform == 'darwin':
38 _DEFAULT_STYLE = 12
37 39
38 40 _DEFAULT_STYLE = {
39 41 'stdout' : 'fore:#0000FF',
40 42 'stderr' : 'fore:#007f00',
41 43 'trace' : 'fore:#FF0000',
42 44
43 45 'default' : 'size:%d' % _DEFAULT_SIZE,
44 46 'bracegood' : 'fore:#00AA00,back:#000000,bold',
45 47 'bracebad' : 'fore:#FF0000,back:#000000,bold',
46 48
47 49 # properties for the various Python lexer styles
48 50 'comment' : 'fore:#007F00',
49 51 'number' : 'fore:#007F7F',
50 52 'string' : 'fore:#7F007F,italic',
51 53 'char' : 'fore:#7F007F,italic',
52 54 'keyword' : 'fore:#00007F,bold',
53 55 'triple' : 'fore:#7F0000',
54 56 'tripledouble' : 'fore:#7F0000',
55 57 'class' : 'fore:#0000FF,bold,underline',
56 58 'def' : 'fore:#007F7F,bold',
57 59 'operator' : 'bold'
58 60 }
59 61
60 62 # new style numbers
61 63 _STDOUT_STYLE = 15
62 64 _STDERR_STYLE = 16
63 65 _TRACE_STYLE = 17
64 66
65 67
66 68 # system colors
67 69 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
68 70
69 71 #-------------------------------------------------------------------------------
70 72 # The console widget class
71 73 #-------------------------------------------------------------------------------
72 74 class ConsoleWidget(editwindow.EditWindow):
73 75 """ Specialized styled text control view for console-like workflow.
74 76
75 77 This widget is mainly interested in dealing with the prompt and
76 78 keeping the cursor inside the editing line.
77 79 """
78 80
79 81 # This is where the title captured from the ANSI escape sequences are
80 82 # stored.
81 83 title = 'Console'
82 84
83 85 # The buffer being edited.
84 86 def _set_input_buffer(self, string):
85 87 self.SetSelection(self.current_prompt_pos, self.GetLength())
86 88 self.ReplaceSelection(string)
87 89 self.GotoPos(self.GetLength())
88 90
89 91 def _get_input_buffer(self):
90 92 """ Returns the text in current edit buffer.
91 93 """
92 94 input_buffer = self.GetTextRange(self.current_prompt_pos,
93 95 self.GetLength())
94 96 input_buffer = input_buffer.replace(LINESEP, '\n')
95 97 return input_buffer
96 98
97 99 input_buffer = property(_get_input_buffer, _set_input_buffer)
98 100
99 101 style = _DEFAULT_STYLE.copy()
100 102
101 103 # Translation table from ANSI escape sequences to color. Override
102 104 # this to specify your colors.
103 105 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
104 106 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
105 107 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
106 108 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
107 109 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
108 110 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
109 111 '1;34': [12, 'LIGHT BLUE'], '1;35':
110 112 [13, 'MEDIUM VIOLET RED'],
111 113 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
112 114
113 115 # The color of the carret (call _apply_style() after setting)
114 116 carret_color = 'BLACK'
115 117
116 118 #--------------------------------------------------------------------------
117 119 # Public API
118 120 #--------------------------------------------------------------------------
119 121
120 122 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
121 123 size=wx.DefaultSize, style=0, ):
122 124 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
123 125 self._configure_scintilla()
124 126
125 127 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
126 128 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
127 129
128 130
129 131 def write(self, text, refresh=True):
130 132 """ Write given text to buffer, while translating the ansi escape
131 133 sequences.
132 134 """
133 135 # XXX: do not put print statements to sys.stdout/sys.stderr in
134 136 # this method, the print statements will call this method, as
135 137 # you will end up with an infinit loop
136 138 title = self.title_pat.split(text)
137 139 if len(title)>1:
138 140 self.title = title[-2]
139 141
140 142 text = self.title_pat.sub('', text)
141 143 segments = self.color_pat.split(text)
142 144 segment = segments.pop(0)
143 145 self.GotoPos(self.GetLength())
144 146 self.StartStyling(self.GetLength(), 0xFF)
145 147 try:
146 148 self.AppendText(segment)
147 149 except UnicodeDecodeError:
148 150 # XXX: Do I really want to skip the exception?
149 151 pass
150 152
151 153 if segments:
152 154 for ansi_tag, text in zip(segments[::2], segments[1::2]):
153 155 self.StartStyling(self.GetLength(), 0xFF)
154 156 try:
155 157 self.AppendText(text)
156 158 except UnicodeDecodeError:
157 159 # XXX: Do I really want to skip the exception?
158 160 pass
159 161
160 162 if ansi_tag not in self.ANSI_STYLES:
161 163 style = 0
162 164 else:
163 165 style = self.ANSI_STYLES[ansi_tag][0]
164 166
165 167 self.SetStyling(len(text), style)
166 168
167 169 self.GotoPos(self.GetLength())
168 170 if refresh:
169 171 wx.Yield()
170 172
171 173
172 174 def new_prompt(self, prompt):
173 175 """ Prints a prompt at start of line, and move the start of the
174 176 current block there.
175 177
176 178 The prompt can be given with ascii escape sequences.
177 179 """
178 180 self.write(prompt, refresh=False)
179 181 # now we update our cursor giving end of prompt
180 182 self.current_prompt_pos = self.GetLength()
181 183 self.current_prompt_line = self.GetCurrentLine()
182 184 wx.Yield()
183 185 self.EnsureCaretVisible()
184 186
185 187
186 188 def scroll_to_bottom(self):
187 189 maxrange = self.GetScrollRange(wx.VERTICAL)
188 190 self.ScrollLines(maxrange)
189 191
190 192
191 193 def pop_completion(self, possibilities, offset=0):
192 194 """ Pops up an autocompletion menu. Offset is the offset
193 195 in characters of the position at which the menu should
194 196 appear, relativ to the cursor.
195 197 """
196 198 self.AutoCompSetIgnoreCase(False)
197 199 self.AutoCompSetAutoHide(False)
198 200 self.AutoCompSetMaxHeight(len(possibilities))
199 201 self.AutoCompShow(offset, " ".join(possibilities))
200 202
201 203
202 204 def get_line_width(self):
203 205 """ Return the width of the line in characters.
204 206 """
205 207 return self.GetSize()[0]/self.GetCharWidth()
206 208
207 209
208 210 #--------------------------------------------------------------------------
209 211 # Private API
210 212 #--------------------------------------------------------------------------
211 213
212 214 def _apply_style(self):
213 215 """ Applies the colors for the different text elements and the
214 216 carret.
215 217 """
216 218 self.SetCaretForeground(self.carret_color)
217 219
218 220 #self.StyleClearAll()
219 221 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
220 222 "fore:#FF0000,back:#0000FF,bold")
221 223 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
222 224 "fore:#000000,back:#FF0000,bold")
223 225
224 226 for style in self.ANSI_STYLES.values():
225 227 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
226 228
227 229
228 230 def _configure_scintilla(self):
229 231 self.SetEOLMode(stc.STC_EOL_LF)
230 232
231 233 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
232 234 # the widget
233 235 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
234 236 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
235 237 # Also allow Ctrl Shift "=" for poor non US keyboard users.
236 238 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
237 239 stc.STC_CMD_ZOOMIN)
238 240
239 241 # Keys: we need to clear some of the keys the that don't play
240 242 # well with a console.
241 243 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
242 244 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
243 245 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
244 246 self.CmdKeyClear(ord('A'), stc.STC_SCMOD_CTRL)
245 247
246 248 self.SetEOLMode(stc.STC_EOL_CRLF)
247 249 self.SetWrapMode(stc.STC_WRAP_CHAR)
248 250 self.SetWrapMode(stc.STC_WRAP_WORD)
249 251 self.SetBufferedDraw(True)
250 252 self.SetUseAntiAliasing(True)
251 253 self.SetLayoutCache(stc.STC_CACHE_PAGE)
252 254 self.SetUndoCollection(False)
253 255 self.SetUseTabs(True)
254 256 self.SetIndent(4)
255 257 self.SetTabWidth(4)
256 258
257 259 # we don't want scintilla's autocompletion to choose
258 260 # automaticaly out of a single choice list, as we pop it up
259 261 # automaticaly
260 262 self.AutoCompSetChooseSingle(False)
261 263 self.AutoCompSetMaxHeight(10)
262 264 # XXX: this doesn't seem to have an effect.
263 265 self.AutoCompSetFillUps('\n')
264 266
265 267 self.SetMargins(3, 3) #text is moved away from border with 3px
266 268 # Suppressing Scintilla margins
267 269 self.SetMarginWidth(0, 0)
268 270 self.SetMarginWidth(1, 0)
269 271 self.SetMarginWidth(2, 0)
270 272
271 273 self._apply_style()
272 274
273 275 # Xterm escape sequences
274 276 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
275 277 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
276 278
277 279 #self.SetEdgeMode(stc.STC_EDGE_LINE)
278 280 #self.SetEdgeColumn(80)
279 281
280 282 # styles
281 283 p = self.style
282 284 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
283 285 self.StyleClearAll()
284 286 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
285 287 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
286 288 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
287 289
288 290 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
289 291 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
290 292 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
291 293 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
292 294 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
293 295 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
294 296 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
295 297 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
296 298 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
297 299 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
298 300 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
299 301 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
300 302 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
301 303 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
302 304
303 305 def _on_key_down(self, event, skip=True):
304 306 """ Key press callback used for correcting behavior for
305 307 console-like interfaces: the cursor is constraint to be after
306 308 the last prompt.
307 309
308 310 Return True if event as been catched.
309 311 """
310 312 catched = True
311 313 # Intercept some specific keys.
312 314 if event.KeyCode == ord('L') and event.ControlDown() :
313 315 self.scroll_to_bottom()
314 316 elif event.KeyCode == ord('K') and event.ControlDown() :
315 317 self.input_buffer = ''
316 318 elif event.KeyCode == ord('A') and event.ControlDown() :
317 319 self.GotoPos(self.GetLength())
318 320 self.SetSelectionStart(self.current_prompt_pos)
319 321 self.SetSelectionEnd(self.GetCurrentPos())
320 322 catched = True
321 323 elif event.KeyCode == ord('E') and event.ControlDown() :
322 324 self.GotoPos(self.GetLength())
323 325 catched = True
324 326 elif event.KeyCode == wx.WXK_PAGEUP:
325 327 self.ScrollPages(-1)
326 328 elif event.KeyCode == wx.WXK_PAGEDOWN:
327 329 self.ScrollPages(1)
328 330 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
329 331 self.ScrollLines(-1)
330 332 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
331 333 self.ScrollLines(1)
332 334 else:
333 335 catched = False
334 336
335 337 if self.AutoCompActive():
336 338 event.Skip()
337 339 else:
338 340 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
339 341 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
340 342 catched = True
341 343 self.CallTipCancel()
342 344 self.write('\n', refresh=False)
343 345 # Under windows scintilla seems to be doing funny stuff to the
344 346 # line returns here, but the getter for input_buffer filters
345 347 # this out.
346 348 if sys.platform == 'win32':
347 349 self.input_buffer = self.input_buffer
348 350 self._on_enter()
349 351
350 352 elif event.KeyCode == wx.WXK_HOME:
351 353 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
352 354 self.GotoPos(self.current_prompt_pos)
353 355 catched = True
354 356
355 357 elif event.Modifiers == wx.MOD_SHIFT:
356 358 # FIXME: This behavior is not ideal: if the selection
357 359 # is already started, it will jump.
358 360 self.SetSelectionStart(self.current_prompt_pos)
359 361 self.SetSelectionEnd(self.GetCurrentPos())
360 362 catched = True
361 363
362 364 elif event.KeyCode == wx.WXK_UP:
363 365 if self.GetCurrentLine() > self.current_prompt_line:
364 366 if self.GetCurrentLine() == self.current_prompt_line + 1 \
365 367 and self.GetColumn(self.GetCurrentPos()) < \
366 368 self.GetColumn(self.current_prompt_pos):
367 369 self.GotoPos(self.current_prompt_pos)
368 370 else:
369 371 event.Skip()
370 372 catched = True
371 373
372 374 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
373 375 if self.GetCurrentPos() > self.current_prompt_pos:
374 376 event.Skip()
375 377 catched = True
376 378
377 379 if skip and not catched:
378 380 # Put the cursor back in the edit region
379 381 if self.GetCurrentPos() < self.current_prompt_pos:
380 382 self.GotoPos(self.current_prompt_pos)
381 383 else:
382 384 event.Skip()
383 385
384 386 return catched
385 387
386 388
387 389 def _on_key_up(self, event, skip=True):
388 390 """ If cursor is outside the editing region, put it back.
389 391 """
390 392 event.Skip()
391 393 if self.GetCurrentPos() < self.current_prompt_pos:
392 394 self.GotoPos(self.current_prompt_pos)
393 395
394 396
395 397
396 398 if __name__ == '__main__':
397 399 # Some simple code to test the console widget.
398 400 class MainWindow(wx.Frame):
399 401 def __init__(self, parent, id, title):
400 402 wx.Frame.__init__(self, parent, id, title, size=(300,250))
401 403 self._sizer = wx.BoxSizer(wx.VERTICAL)
402 404 self.console_widget = ConsoleWidget(self)
403 405 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
404 406 self.SetSizer(self._sizer)
405 407 self.SetAutoLayout(1)
406 408 self.Show(True)
407 409
408 410 app = wx.PySimpleApp()
409 411 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
410 412 w.SetSize((780, 460))
411 413 w.Show()
412 414
413 415 app.MainLoop()
414 416
415 417
@@ -1,107 +1,110 b''
1 1 """
2 2 Entry point for a simple application giving a graphical frontend to
3 3 ipython.
4 4 """
5 5
6 6 try:
7 7 import wx
8 8 except ImportError, e:
9 9 e.message = """%s
10 10 ________________________________________________________________________________
11 11 You need wxPython to run this application.
12 12 """ % e.message
13 13 e.args = (e.message, ) + e.args[1:]
14 14 raise e
15 15
16 16 from wx_frontend import WxController
17 17 import __builtin__
18 18
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 # Scroll to top
31 31 maxrange = self.GetScrollRange(wx.VERTICAL)
32 32 self.ScrollLines(-maxrange)
33 33
34 34
35 35 def _on_key_down(self, event, skip=True):
36 36 # Intercept Ctrl-D to quit
37 37 if event.KeyCode == ord('D') and event.ControlDown() and \
38 38 self.input_buffer == '' and \
39 39 self._input_state == 'readline':
40 40 wx.CallAfter(self.ask_exit)
41 41 else:
42 42 WxController._on_key_down(self, event, skip=skip)
43 43
44 44
45 45 def ask_exit(self):
46 46 """ Ask the user whether to exit.
47 47 """
48 48 self._input_state = 'subprocess'
49 49 self.write('\n', refresh=False)
50 50 self.capture_output()
51 51 self.ipython0.shell.exit()
52 52 self.release_output()
53 53 if not self.ipython0.exit_now:
54 54 wx.CallAfter(self.new_prompt,
55 55 self.input_prompt_template.substitute(
56 56 number=self.last_result['number'] + 1))
57 else:
58 wx.CallAfter(wx.GetApp().Exit)
59 self.write('Exiting ...', refresh=False)
57 60
58 61
59 62 def do_exit(self):
60 63 """ Exits the interpreter, kills the windows.
61 64 """
62 65 WxController.do_exit(self)
63 66 self.release_output()
64 67 wx.CallAfter(wx.Exit)
65 68
66 69
67 70
68 71 class IPythonX(wx.Frame):
69 72 """ Main frame of the IPythonX app.
70 73 """
71 74
72 75 def __init__(self, parent, id, title, debug=False):
73 76 wx.Frame.__init__(self, parent, id, title, size=(300,250))
74 77 self._sizer = wx.BoxSizer(wx.VERTICAL)
75 78 self.shell = IPythonXController(self, debug=debug)
76 79 self._sizer.Add(self.shell, 1, wx.EXPAND)
77 80 self.SetSizer(self._sizer)
78 81 self.SetAutoLayout(1)
79 82 self.Show(True)
80 83
81 84
82 85 def main():
83 86 from optparse import OptionParser
84 87 usage = """usage: %prog [options]
85 88
86 89 Simple graphical frontend to IPython, using WxWidgets."""
87 90 parser = OptionParser(usage=usage)
88 91 parser.add_option("-d", "--debug",
89 92 action="store_true", dest="debug", default=False,
90 93 help="Enable debug message for the wx frontend.")
91 94
92 95 options, args = parser.parse_args()
93 96
94 97 # Clear the options, to avoid having the ipython0 instance complain
95 98 import sys
96 99 sys.argv = sys.argv[:1]
97 100
98 101 app = wx.PySimpleApp()
99 102 frame = IPythonX(None, wx.ID_ANY, 'IPythonX', debug=options.debug)
100 103 frame.shell.SetFocus()
101 104 frame.shell.app = app
102 105 frame.SetSize((680, 460))
103 106
104 107 app.MainLoop()
105 108
106 109 if __name__ == '__main__':
107 110 main()
General Comments 0
You need to be logged in to leave comments. Login now