##// END OF EJS Templates
Tracebacks in red.
Gael Varoquaux -
Show More
@@ -1,218 +1,231 b''
1 1 # encoding: utf-8 -*- test-case-name:
2 2 # FIXME: Need to add tests.
3 3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
4 4
5 5 """Classes to provide a Wx frontend to the
6 6 IPython.kernel.core.interpreter.
7 7
8 8 """
9 9
10 10 __docformat__ = "restructuredtext en"
11 11
12 12 #-------------------------------------------------------------------------------
13 13 # Copyright (C) 2008 The IPython Development Team
14 14 #
15 15 # Distributed under the terms of the BSD License. The full license is in
16 16 # the file COPYING, distributed as part of this software.
17 17 #-------------------------------------------------------------------------------
18 18
19 19 #-------------------------------------------------------------------------------
20 20 # Imports
21 21 #-------------------------------------------------------------------------------
22 22
23 23
24 24 import wx
25 25 import re
26 26 from wx import stc
27 27 from console_widget import ConsoleWidget
28 28 import __builtin__
29 29
30 30 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
31 31
32 32 #_COMMAND_BG = '#FAFAF1' # Nice green
33 33 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
34 _ERROR_BG = '#FFF1F1' # Nice red
34 35
35 36 _RUNNING_BUFFER_MARKER = 31
36
37 _ERROR_MARKER = 30
37 38
38 39 #-------------------------------------------------------------------------------
39 40 # Classes to implement the Wx frontend
40 41 #-------------------------------------------------------------------------------
41 42 class IPythonWxController(PrefilterFrontEnd, ConsoleWidget):
42 43
43 44 output_prompt = \
44 45 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
45 46
46 47 #--------------------------------------------------------------------------
47 48 # Public API
48 49 #--------------------------------------------------------------------------
49 50
50 51 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
51 52 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
52 53 *args, **kwds):
53 54 """ Create Shell instance.
54 55 """
55 56 ConsoleWidget.__init__(self, parent, id, pos, size, style)
56 57 PrefilterFrontEnd.__init__(self)
57 58
58 59 # Capture Character keys
59 60 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
60 61
61 62 # Marker for running buffer.
62 63 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
63 64 background=_RUNNING_BUFFER_BG)
65 # Marker for tracebacks.
66 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
67 background=_ERROR_BG)
64 68
65 69
66 70
67 71 def do_completion(self):
68 72 """ Do code completion.
69 73 """
70 74 line = self.get_current_edit_buffer()
71 75 new_line, completions = self.complete(line)
72 76 if len(completions)>1:
73 77 self.write_completion(completions)
74 78 self.replace_current_edit_buffer(new_line)
75 79
76 80
77 81 def do_calltip(self):
78 82 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
79 83 symbol = self.get_current_edit_buffer()
80 84 symbol_string = separators.split(symbol)[-1]
81 85 base_symbol_string = symbol_string.split('.')[0]
82 86 if base_symbol_string in self.shell.user_ns:
83 87 symbol = self.shell.user_ns[base_symbol_string]
84 88 elif base_symbol_string in self.shell.user_global_ns:
85 89 symbol = self.shell.user_global_ns[base_symbol_string]
86 90 elif base_symbol_string in __builtin__.__dict__:
87 91 symbol = __builtin__.__dict__[base_symbol_string]
88 92 else:
89 93 return False
90 94 for name in symbol_string.split('.')[1:] + ['__doc__']:
91 95 symbol = getattr(symbol, name)
92 96 try:
93 97 self.AutoCompCancel()
94 98 wx.Yield()
95 99 self.CallTipShow(self.GetCurrentPos(), symbol)
96 100 except:
97 101 # The retrieve symbol couldn't be converted to a string
98 102 pass
99 103
100 104
101 105 def popup_completion(self, create=False):
102 106 """ Updates the popup completion menu if it exists. If create is
103 107 true, open the menu.
104 108 """
105 109 line = self.get_current_edit_buffer()
106 110 if (self.AutoCompActive() and not line[-1] == '.') \
107 111 or create==True:
108 112 suggestion, completions = self.complete(line)
109 113 offset=0
110 114 if completions:
111 115 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
112 116 residual = complete_sep.split(line)[-1]
113 117 offset = len(residual)
114 118 self.pop_completion(completions, offset=offset)
115 119
116 120
117 121 def execute(self, python_string, raw_string=None):
118 122 self.CallTipCancel()
119 123 self._cursor = wx.BusyCursor()
120 124 if raw_string is None:
121 125 raw_string = python_string
122 126 end_line = self.current_prompt_line \
123 127 + max(1, len(raw_string.split('\n'))-1)
124 128 for i in range(self.current_prompt_line, end_line):
125 self.MarkerAdd(i, 31)
129 self.MarkerAdd(i, _RUNNING_BUFFER_MARKER)
126 130 # Update the display:
127 131 wx.Yield()
128 132 ## Remove the trailing "\n" for cleaner display
129 133 #self.SetSelection(self.GetLength()-1, self.GetLength())
130 134 #self.ReplaceSelection('')
131 135 self.GotoPos(self.GetLength())
132 136 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
133 137
134 138
135 139 def after_execute(self):
136 140 PrefilterFrontEnd.after_execute(self)
137 141 if hasattr(self, '_cursor'):
138 142 del self._cursor
139 143
144
145 def show_traceback(self):
146 start_line = self.GetCurrentLine()
147 PrefilterFrontEnd.show_traceback(self)
148 wx.Yield()
149 for i in range(start_line, self.GetCurrentLine()):
150 self.MarkerAdd(i, _ERROR_MARKER)
151
152
140 153 #--------------------------------------------------------------------------
141 154 # Private API
142 155 #--------------------------------------------------------------------------
143 156
144 157
145 158 def _on_key_down(self, event, skip=True):
146 159 """ Capture the character events, let the parent
147 160 widget handle them, and put our logic afterward.
148 161 """
149 162 current_line_number = self.GetCurrentLine()
150 163 if event.KeyCode == ord('('):
151 164 event.Skip()
152 165 self.do_calltip()
153 166 elif self.AutoCompActive():
154 167 event.Skip()
155 168 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
156 169 wx.CallAfter(self.popup_completion, create=True)
157 170 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
158 171 wx.WXK_RIGHT):
159 172 wx.CallAfter(self.popup_completion)
160 173 else:
161 174 # Up history
162 175 if event.KeyCode == wx.WXK_UP and (
163 176 ( current_line_number == self.current_prompt_line and
164 177 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
165 178 or event.ControlDown() ):
166 179 new_buffer = self.get_history_previous(
167 180 self.get_current_edit_buffer())
168 181 if new_buffer is not None:
169 182 self.replace_current_edit_buffer(new_buffer)
170 183 if self.GetCurrentLine() > self.current_prompt_line:
171 184 # Go to first line, for seemless history up.
172 185 self.GotoPos(self.current_prompt_pos)
173 186 # Down history
174 187 elif event.KeyCode == wx.WXK_DOWN and (
175 188 ( current_line_number == self.LineCount -1 and
176 189 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
177 190 or event.ControlDown() ):
178 191 new_buffer = self.get_history_next()
179 192 if new_buffer is not None:
180 193 self.replace_current_edit_buffer(new_buffer)
181 194 elif event.KeyCode == ord('\t'):
182 195 last_line = self.get_current_edit_buffer().split('\n')[-1]
183 196 if not re.match(r'^\s*$', last_line):
184 197 self.do_completion()
185 198 else:
186 199 event.Skip()
187 200 else:
188 201 ConsoleWidget._on_key_down(self, event, skip=skip)
189 202
190 203
191 204 def _on_key_up(self, event, skip=True):
192 205 if event.KeyCode == 59:
193 206 # Intercepting '.'
194 207 event.Skip()
195 208 self.popup_completion(create=True)
196 209 else:
197 210 ConsoleWidget._on_key_up(self, event, skip=skip)
198 211
199 212
200 213 if __name__ == '__main__':
201 214 class MainWindow(wx.Frame):
202 215 def __init__(self, parent, id, title):
203 216 wx.Frame.__init__(self, parent, id, title, size=(300,250))
204 217 self._sizer = wx.BoxSizer(wx.VERTICAL)
205 218 self.shell = IPythonWxController(self)
206 219 self._sizer.Add(self.shell, 1, wx.EXPAND)
207 220 self.SetSizer(self._sizer)
208 221 self.SetAutoLayout(1)
209 222 self.Show(True)
210 223
211 224 app = wx.PySimpleApp()
212 225 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
213 226 frame.shell.SetFocus()
214 227 frame.SetSize((680, 460))
215 228 self = frame.shell
216 229
217 230 app.MainLoop()
218 231
General Comments 0
You need to be logged in to leave comments. Login now