##// END OF EJS Templates
Better encoding handling....
Cody Precord -
Show More
@@ -1,870 +1,885 b''
1 #!/usr/bin/python
1 #!/usr/bin/python
2 # -*- coding: iso-8859-15 -*-
2 # -*- coding: iso-8859-15 -*-
3 '''
3 '''
4 Provides IPython WX console widgets.
4 Provides IPython WX console widgets.
5
5
6 @author: Laurent Dufrechou
6 @author: Laurent Dufrechou
7 laurent.dufrechou _at_ gmail.com
7 laurent.dufrechou _at_ gmail.com
8 This WX widget is based on the original work of Eitan Isaacson
8 This WX widget is based on the original work of Eitan Isaacson
9 that provided the console for the GTK toolkit.
9 that provided the console for the GTK toolkit.
10
10
11 Original work from:
11 Original work from:
12 @author: Eitan Isaacson
12 @author: Eitan Isaacson
13 @organization: IBM Corporation
13 @organization: IBM Corporation
14 @copyright: Copyright (c) 2007 IBM Corporation
14 @copyright: Copyright (c) 2007 IBM Corporation
15 @license: BSD
15 @license: BSD
16
16
17 All rights reserved. This program and the accompanying materials are made
17 All rights reserved. This program and the accompanying materials are made
18 available under the terms of the BSD which accompanies this distribution, and
18 available under the terms of the BSD which accompanies this distribution, and
19 is available at U{http://www.opensource.org/licenses/bsd-license.php}
19 is available at U{http://www.opensource.org/licenses/bsd-license.php}
20 '''
20 '''
21
21
22 __version__ = 0.8
22 __version__ = 0.8
23 __author__ = "Laurent Dufrechou"
23 __author__ = "Laurent Dufrechou"
24 __email__ = "laurent.dufrechou _at_ gmail.com"
24 __email__ = "laurent.dufrechou _at_ gmail.com"
25 __license__ = "BSD"
25 __license__ = "BSD"
26
26
27 import wx
27 import wx
28 import wx.stc as stc
28 import wx.stc as stc
29
29
30 import re
30 import re
31 from StringIO import StringIO
31 from StringIO import StringIO
32
32
33 import sys
34 import codecs
35 import locale
36 for enc in (locale.getpreferredencoding(),
37 sys.getfilesystemencoding(),
38 sys.getdefaultencoding()):
39 try:
40 codecs.lookup(enc)
41 ENCODING = enc
42 break
43 except LookupError:
44 pass
45 else:
46 ENCODING = 'utf-8'
47
33 from ipshell_nonblocking import NonBlockingIPShell
48 from ipshell_nonblocking import NonBlockingIPShell
34
49
35 class WxNonBlockingIPShell(NonBlockingIPShell):
50 class WxNonBlockingIPShell(NonBlockingIPShell):
36 '''
51 '''
37 An NonBlockingIPShell Thread that is WX dependent.
52 An NonBlockingIPShell Thread that is WX dependent.
38 '''
53 '''
39 def __init__(self, parent,
54 def __init__(self, parent,
40 argv=[],user_ns={},user_global_ns=None,
55 argv=[],user_ns={},user_global_ns=None,
41 cin=None, cout=None, cerr=None,
56 cin=None, cout=None, cerr=None,
42 ask_exit_handler=None):
57 ask_exit_handler=None):
43
58
44 NonBlockingIPShell.__init__(self, argv, user_ns, user_global_ns,
59 NonBlockingIPShell.__init__(self, argv, user_ns, user_global_ns,
45 cin, cout, cerr,
60 cin, cout, cerr,
46 ask_exit_handler)
61 ask_exit_handler)
47
62
48 self.parent = parent
63 self.parent = parent
49
64
50 self.ask_exit_callback = ask_exit_handler
65 self.ask_exit_callback = ask_exit_handler
51 self._IP.exit = self._askExit
66 self._IP.exit = self._askExit
52
67
53 def addGUIShortcut(self, text, func):
68 def addGUIShortcut(self, text, func):
54 wx.CallAfter(self.parent.add_button_handler,
69 wx.CallAfter(self.parent.add_button_handler,
55 button_info={ 'text':text,
70 button_info={ 'text':text,
56 'func':self.parent.doExecuteLine(func)})
71 'func':self.parent.doExecuteLine(func)})
57
72
58 def _askExit(self):
73 def _askExit(self):
59 wx.CallAfter(self.ask_exit_callback, ())
74 wx.CallAfter(self.ask_exit_callback, ())
60
75
61 def _afterExecute(self):
76 def _afterExecute(self):
62 wx.CallAfter(self.parent.evtStateExecuteDone, ())
77 wx.CallAfter(self.parent.evtStateExecuteDone, ())
63
78
64
79
65 class WxConsoleView(stc.StyledTextCtrl):
80 class WxConsoleView(stc.StyledTextCtrl):
66 '''
81 '''
67 Specialized styled text control view for console-like workflow.
82 Specialized styled text control view for console-like workflow.
68 We use here a scintilla frontend thus it can be reused in any GUI that
83 We use here a scintilla frontend thus it can be reused in any GUI that
69 supports scintilla with less work.
84 supports scintilla with less work.
70
85
71 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.
86 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.
72 (with Black background)
87 (with Black background)
73 @type ANSI_COLORS_BLACK: dictionary
88 @type ANSI_COLORS_BLACK: dictionary
74
89
75 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.
90 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.
76 (with White background)
91 (with White background)
77 @type ANSI_COLORS_WHITE: dictionary
92 @type ANSI_COLORS_WHITE: dictionary
78
93
79 @ivar color_pat: Regex of terminal color pattern
94 @ivar color_pat: Regex of terminal color pattern
80 @type color_pat: _sre.SRE_Pattern
95 @type color_pat: _sre.SRE_Pattern
81 '''
96 '''
82 ANSI_STYLES_BLACK = {'0;30': [0, 'WHITE'], '0;31': [1, 'RED'],
97 ANSI_STYLES_BLACK = {'0;30': [0, 'WHITE'], '0;31': [1, 'RED'],
83 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
98 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
84 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
99 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
85 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
100 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
86 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
101 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
87 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
102 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
88 '1;34': [12, 'LIGHT BLUE'], '1;35':
103 '1;34': [12, 'LIGHT BLUE'], '1;35':
89 [13, 'MEDIUM VIOLET RED'],
104 [13, 'MEDIUM VIOLET RED'],
90 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
105 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
91
106
92 ANSI_STYLES_WHITE = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
107 ANSI_STYLES_WHITE = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
93 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
108 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
94 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
109 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
95 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
110 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
96 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
111 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
97 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
112 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
98 '1;34': [12, 'LIGHT BLUE'], '1;35':
113 '1;34': [12, 'LIGHT BLUE'], '1;35':
99 [13, 'MEDIUM VIOLET RED'],
114 [13, 'MEDIUM VIOLET RED'],
100 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
115 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
101
116
102 def __init__(self, parent, prompt, intro="", background_color="BLACK",
117 def __init__(self, parent, prompt, intro="", background_color="BLACK",
103 pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
118 pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
104 style=0, autocomplete_mode = 'IPYTHON'):
119 style=0, autocomplete_mode = 'IPYTHON'):
105 '''
120 '''
106 Initialize console view.
121 Initialize console view.
107
122
108 @param parent: Parent widget
123 @param parent: Parent widget
109 @param prompt: User specified prompt
124 @param prompt: User specified prompt
110 @type intro: string
125 @type intro: string
111 @param intro: User specified startup introduction string
126 @param intro: User specified startup introduction string
112 @type intro: string
127 @type intro: string
113 @param background_color: Can be BLACK or WHITE
128 @param background_color: Can be BLACK or WHITE
114 @type background_color: string
129 @type background_color: string
115 @param other: init param of styledTextControl (can be used as-is)
130 @param other: init param of styledTextControl (can be used as-is)
116 @param autocomplete_mode: Can be 'IPYTHON' or 'STC'
131 @param autocomplete_mode: Can be 'IPYTHON' or 'STC'
117 'IPYTHON' show autocompletion the ipython way
132 'IPYTHON' show autocompletion the ipython way
118 'STC" show it scintilla text control way
133 'STC" show it scintilla text control way
119 '''
134 '''
120 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
135 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
121
136
122 ####### Scintilla configuration ###################################
137 ####### Scintilla configuration ###################################
123
138
124 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside
139 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside
125 # the widget
140 # the widget
126 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
141 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
127 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
142 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
128
143
129 #We draw a line at position 80
144 #We draw a line at position 80
130 self.SetEdgeMode(stc.STC_EDGE_LINE)
145 self.SetEdgeMode(stc.STC_EDGE_LINE)
131 self.SetEdgeColumn(80)
146 self.SetEdgeColumn(80)
132 self.SetEdgeColour(wx.LIGHT_GREY)
147 self.SetEdgeColour(wx.LIGHT_GREY)
133
148
134 #self.SetViewWhiteSpace(True)
149 #self.SetViewWhiteSpace(True)
135 #self.SetViewEOL(True)
150 #self.SetViewEOL(True)
136 self.SetEOLMode(stc.STC_EOL_CRLF)
151 self.SetEOLMode(stc.STC_EOL_CRLF)
137 #self.SetWrapMode(stc.STC_WRAP_CHAR)
152 #self.SetWrapMode(stc.STC_WRAP_CHAR)
138 #self.SetWrapMode(stc.STC_WRAP_WORD)
153 #self.SetWrapMode(stc.STC_WRAP_WORD)
139 self.SetBufferedDraw(True)
154 self.SetBufferedDraw(True)
140 #self.SetUseAntiAliasing(True)
155 #self.SetUseAntiAliasing(True)
141 self.SetLayoutCache(stc.STC_CACHE_PAGE)
156 self.SetLayoutCache(stc.STC_CACHE_PAGE)
142 self.SetUndoCollection(False)
157 self.SetUndoCollection(False)
143 self.SetUseTabs(True)
158 self.SetUseTabs(True)
144 self.SetIndent(4)
159 self.SetIndent(4)
145 self.SetTabWidth(4)
160 self.SetTabWidth(4)
146
161
147 self.EnsureCaretVisible()
162 self.EnsureCaretVisible()
148
163
149 self.SetMargins(3, 3) #text is moved away from border with 3px
164 self.SetMargins(3, 3) #text is moved away from border with 3px
150 # Suppressing Scintilla margins
165 # Suppressing Scintilla margins
151 self.SetMarginWidth(0, 0)
166 self.SetMarginWidth(0, 0)
152 self.SetMarginWidth(1, 0)
167 self.SetMarginWidth(1, 0)
153 self.SetMarginWidth(2, 0)
168 self.SetMarginWidth(2, 0)
154
169
155 self.background_color = background_color
170 self.background_color = background_color
156 self.buildStyles()
171 self.buildStyles()
157
172
158 self.indent = 0
173 self.indent = 0
159 self.prompt_count = 0
174 self.prompt_count = 0
160 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
175 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
161
176
162 self.write(intro)
177 self.write(intro)
163 self.setPrompt(prompt)
178 self.setPrompt(prompt)
164 self.showPrompt()
179 self.showPrompt()
165
180
166 self.autocomplete_mode = autocomplete_mode
181 self.autocomplete_mode = autocomplete_mode
167
182
168 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress)
183 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress)
169
184
170 def buildStyles(self):
185 def buildStyles(self):
171 #we define platform specific fonts
186 #we define platform specific fonts
172 if wx.Platform == '__WXMSW__':
187 if wx.Platform == '__WXMSW__':
173 faces = { 'times': 'Times New Roman',
188 faces = { 'times': 'Times New Roman',
174 'mono' : 'Courier New',
189 'mono' : 'Courier New',
175 'helv' : 'Arial',
190 'helv' : 'Arial',
176 'other': 'Comic Sans MS',
191 'other': 'Comic Sans MS',
177 'size' : 10,
192 'size' : 10,
178 'size2': 8,
193 'size2': 8,
179 }
194 }
180 elif wx.Platform == '__WXMAC__':
195 elif wx.Platform == '__WXMAC__':
181 faces = { 'times': 'Times New Roman',
196 faces = { 'times': 'Times New Roman',
182 'mono' : 'Monaco',
197 'mono' : 'Monaco',
183 'helv' : 'Arial',
198 'helv' : 'Arial',
184 'other': 'Comic Sans MS',
199 'other': 'Comic Sans MS',
185 'size' : 10,
200 'size' : 10,
186 'size2': 8,
201 'size2': 8,
187 }
202 }
188 else:
203 else:
189 faces = { 'times': 'Times',
204 faces = { 'times': 'Times',
190 'mono' : 'Courier',
205 'mono' : 'Courier',
191 'helv' : 'Helvetica',
206 'helv' : 'Helvetica',
192 'other': 'new century schoolbook',
207 'other': 'new century schoolbook',
193 'size' : 10,
208 'size' : 10,
194 'size2': 8,
209 'size2': 8,
195 }
210 }
196
211
197 # make some styles
212 # make some styles
198 if self.background_color != "BLACK":
213 if self.background_color != "BLACK":
199 self.background_color = "WHITE"
214 self.background_color = "WHITE"
200 self.SetCaretForeground("BLACK")
215 self.SetCaretForeground("BLACK")
201 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
216 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
202 else:
217 else:
203 self.SetCaretForeground("WHITE")
218 self.SetCaretForeground("WHITE")
204 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
219 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
205
220
206 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
221 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
207 "fore:%s,back:%s,size:%d,face:%s"
222 "fore:%s,back:%s,size:%d,face:%s"
208 % (self.ANSI_STYLES['0;30'][1],
223 % (self.ANSI_STYLES['0;30'][1],
209 self.background_color,
224 self.background_color,
210 faces['size'], faces['mono']))
225 faces['size'], faces['mono']))
211 self.StyleClearAll()
226 self.StyleClearAll()
212 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
227 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
213 "fore:#FF0000,back:#0000FF,bold")
228 "fore:#FF0000,back:#0000FF,bold")
214 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
229 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
215 "fore:#000000,back:#FF0000,bold")
230 "fore:#000000,back:#FF0000,bold")
216
231
217 for style in self.ANSI_STYLES.values():
232 for style in self.ANSI_STYLES.values():
218 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
233 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
219
234
220 #######################################################################
235 #######################################################################
221
236
222 def setBackgroundColor(self, color):
237 def setBackgroundColor(self, color):
223 self.background_color = color
238 self.background_color = color
224 self.buildStyles()
239 self.buildStyles()
225
240
226 def getBackgroundColor(self, color):
241 def getBackgroundColor(self, color):
227 return self.background_color
242 return self.background_color
228
243
229 def asyncWrite(self, text):
244 def asyncWrite(self, text):
230 '''
245 '''
231 Write given text to buffer in an asynchroneous way.
246 Write given text to buffer in an asynchroneous way.
232 It is used from another thread to be able to acces the GUI.
247 It is used from another thread to be able to acces the GUI.
233 @param text: Text to append
248 @param text: Text to append
234 @type text: string
249 @type text: string
235 '''
250 '''
236 try:
251 try:
237 #print >>sys.__stdout__,'entering'
252 #print >>sys.__stdout__,'entering'
238 wx.MutexGuiEnter()
253 wx.MutexGuiEnter()
239 #print >>sys.__stdout__,'locking the GUI'
254 #print >>sys.__stdout__,'locking the GUI'
240
255
241 #be sure not to be interrutpted before the MutexGuiLeave!
256 #be sure not to be interrutpted before the MutexGuiLeave!
242 self.write(text)
257 self.write(text)
243
258
244 #print >>sys.__stdout__,'done'
259 #print >>sys.__stdout__,'done'
245
260
246 except KeyboardInterrupt:
261 except KeyboardInterrupt:
247 #print >>sys.__stdout__,'got keyboard interrupt'
262 #print >>sys.__stdout__,'got keyboard interrupt'
248 wx.MutexGuiLeave()
263 wx.MutexGuiLeave()
249 #print >>sys.__stdout__,'interrupt unlock the GUI'
264 #print >>sys.__stdout__,'interrupt unlock the GUI'
250 raise KeyboardInterrupt
265 raise KeyboardInterrupt
251 wx.MutexGuiLeave()
266 wx.MutexGuiLeave()
252 #print >>sys.__stdout__,'normal unlock the GUI'
267 #print >>sys.__stdout__,'normal unlock the GUI'
253
268
254
269
255 def write(self, text):
270 def write(self, text):
256 '''
271 '''
257 Write given text to buffer.
272 Write given text to buffer.
258
273
259 @param text: Text to append.
274 @param text: Text to append.
260 @type text: string
275 @type text: string
261 '''
276 '''
262 segments = self.color_pat.split(text)
277 segments = self.color_pat.split(text)
263 segment = segments.pop(0)
278 segment = segments.pop(0)
264 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
279 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
265 self.AppendText(segment)
280 self.AppendText(segment)
266
281
267 if segments:
282 if segments:
268 ansi_tags = self.color_pat.findall(text)
283 ansi_tags = self.color_pat.findall(text)
269
284
270 for tag in ansi_tags:
285 for tag in ansi_tags:
271 i = segments.index(tag)
286 i = segments.index(tag)
272 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
287 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
273 self.AppendText(segments[i+1])
288 self.AppendText(segments[i+1])
274
289
275 if tag != '0':
290 if tag != '0':
276 self.SetStyling(len(segments[i+1]), self.ANSI_STYLES[tag][0])
291 self.SetStyling(len(segments[i+1]), self.ANSI_STYLES[tag][0])
277
292
278 segments.pop(i)
293 segments.pop(i)
279
294
280 self.moveCursor(self.getCurrentLineEnd())
295 self.moveCursor(self.getCurrentLineEnd())
281
296
282 def getPromptLen(self):
297 def getPromptLen(self):
283 '''
298 '''
284 Return the length of current prompt
299 Return the length of current prompt
285 '''
300 '''
286 return len(str(self.prompt_count)) + 7
301 return len(str(self.prompt_count)) + 7
287
302
288 def setPrompt(self, prompt):
303 def setPrompt(self, prompt):
289 self.prompt = prompt
304 self.prompt = prompt
290
305
291 def setIndentation(self, indentation):
306 def setIndentation(self, indentation):
292 self.indent = indentation
307 self.indent = indentation
293
308
294 def setPromptCount(self, count):
309 def setPromptCount(self, count):
295 self.prompt_count = count
310 self.prompt_count = count
296
311
297 def showPrompt(self):
312 def showPrompt(self):
298 '''
313 '''
299 Prints prompt at start of line.
314 Prints prompt at start of line.
300
315
301 @param prompt: Prompt to print.
316 @param prompt: Prompt to print.
302 @type prompt: string
317 @type prompt: string
303 '''
318 '''
304 self.write(self.prompt)
319 self.write(self.prompt)
305 #now we update the position of end of prompt
320 #now we update the position of end of prompt
306 self.current_start = self.getCurrentLineEnd()
321 self.current_start = self.getCurrentLineEnd()
307
322
308 autoindent = self.indent*' '
323 autoindent = self.indent*' '
309 autoindent = autoindent.replace(' ','\t')
324 autoindent = autoindent.replace(' ','\t')
310 self.write(autoindent)
325 self.write(autoindent)
311
326
312 def changeLine(self, text):
327 def changeLine(self, text):
313 '''
328 '''
314 Replace currently entered command line with given text.
329 Replace currently entered command line with given text.
315
330
316 @param text: Text to use as replacement.
331 @param text: Text to use as replacement.
317 @type text: string
332 @type text: string
318 '''
333 '''
319 self.SetSelection(self.getCurrentPromptStart(), self.getCurrentLineEnd())
334 self.SetSelection(self.getCurrentPromptStart(), self.getCurrentLineEnd())
320 self.ReplaceSelection(text)
335 self.ReplaceSelection(text)
321 self.moveCursor(self.getCurrentLineEnd())
336 self.moveCursor(self.getCurrentLineEnd())
322
337
323 def getCurrentPromptStart(self):
338 def getCurrentPromptStart(self):
324 return self.current_start
339 return self.current_start
325
340
326 def getCurrentLineStart(self):
341 def getCurrentLineStart(self):
327 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
342 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
328
343
329 def getCurrentLineEnd(self):
344 def getCurrentLineEnd(self):
330 return self.GetLength()
345 return self.GetLength()
331
346
332 def getCurrentLine(self):
347 def getCurrentLine(self):
333 '''
348 '''
334 Get text in current command line.
349 Get text in current command line.
335
350
336 @return: Text of current command line.
351 @return: Text of current command line.
337 @rtype: string
352 @rtype: string
338 '''
353 '''
339 return self.GetTextRange(self.getCurrentPromptStart(),
354 return self.GetTextRange(self.getCurrentPromptStart(),
340 self.getCurrentLineEnd())
355 self.getCurrentLineEnd())
341
356
342 def moveCursorOnNewValidKey(self):
357 def moveCursorOnNewValidKey(self):
343 #If cursor is at wrong position put it at last line...
358 #If cursor is at wrong position put it at last line...
344 if self.GetCurrentPos() < self.getCurrentPromptStart():
359 if self.GetCurrentPos() < self.getCurrentPromptStart():
345 self.GotoPos(self.getCurrentPromptStart())
360 self.GotoPos(self.getCurrentPromptStart())
346
361
347 def removeFromTo(self, from_pos, to_pos):
362 def removeFromTo(self, from_pos, to_pos):
348 if from_pos < to_pos:
363 if from_pos < to_pos:
349 self.SetSelection(from_pos, to_pos)
364 self.SetSelection(from_pos, to_pos)
350 self.DeleteBack()
365 self.DeleteBack()
351
366
352 def removeCurrentLine(self):
367 def removeCurrentLine(self):
353 self.LineDelete()
368 self.LineDelete()
354
369
355 def moveCursor(self, position):
370 def moveCursor(self, position):
356 self.GotoPos(position)
371 self.GotoPos(position)
357
372
358 def getCursorPos(self):
373 def getCursorPos(self):
359 return self.GetCurrentPos()
374 return self.GetCurrentPos()
360
375
361 def selectFromTo(self, from_pos, to_pos):
376 def selectFromTo(self, from_pos, to_pos):
362 self.SetSelectionStart(from_pos)
377 self.SetSelectionStart(from_pos)
363 self.SetSelectionEnd(to_pos)
378 self.SetSelectionEnd(to_pos)
364
379
365 def writeHistory(self, history):
380 def writeHistory(self, history):
366 self.removeFromTo(self.getCurrentPromptStart(), self.getCurrentLineEnd())
381 self.removeFromTo(self.getCurrentPromptStart(), self.getCurrentLineEnd())
367 self.changeLine(history)
382 self.changeLine(history)
368
383
369 def setCompletionMethod(self, completion):
384 def setCompletionMethod(self, completion):
370 if completion in ['IPYTHON', 'STC']:
385 if completion in ['IPYTHON', 'STC']:
371 self.autocomplete_mode = completion
386 self.autocomplete_mode = completion
372 else:
387 else:
373 raise AttributeError
388 raise AttributeError
374
389
375 def getCompletionMethod(self, completion):
390 def getCompletionMethod(self, completion):
376 return self.autocomplete_mode
391 return self.autocomplete_mode
377
392
378 def writeCompletion(self, possibilities):
393 def writeCompletion(self, possibilities):
379 if self.autocomplete_mode == 'IPYTHON':
394 if self.autocomplete_mode == 'IPYTHON':
380 max_len = len(max(possibilities, key=len))
395 max_len = len(max(possibilities, key=len))
381 max_symbol = ' '*max_len
396 max_symbol = ' '*max_len
382
397
383 #now we check how much symbol we can put on a line...
398 #now we check how much symbol we can put on a line...
384 test_buffer = max_symbol + ' '*4
399 test_buffer = max_symbol + ' '*4
385
400
386 allowed_symbols = 80/len(test_buffer)
401 allowed_symbols = 80/len(test_buffer)
387 if allowed_symbols == 0:
402 if allowed_symbols == 0:
388 allowed_symbols = 1
403 allowed_symbols = 1
389
404
390 pos = 1
405 pos = 1
391 buf = ''
406 buf = ''
392 for symbol in possibilities:
407 for symbol in possibilities:
393 #buf += symbol+'\n'#*spaces)
408 #buf += symbol+'\n'#*spaces)
394 if pos < allowed_symbols:
409 if pos < allowed_symbols:
395 spaces = max_len - len(symbol) + 4
410 spaces = max_len - len(symbol) + 4
396 buf += symbol+' '*spaces
411 buf += symbol+' '*spaces
397 pos += 1
412 pos += 1
398 else:
413 else:
399 buf += symbol+'\n'
414 buf += symbol+'\n'
400 pos = 1
415 pos = 1
401 self.write(buf)
416 self.write(buf)
402 else:
417 else:
403 possibilities.sort() # Python sorts are case sensitive
418 possibilities.sort() # Python sorts are case sensitive
404 self.AutoCompSetIgnoreCase(False)
419 self.AutoCompSetIgnoreCase(False)
405 self.AutoCompSetAutoHide(False)
420 self.AutoCompSetAutoHide(False)
406 #let compute the length ot last word
421 #let compute the length ot last word
407 splitter = [' ', '(', '[', '{']
422 splitter = [' ', '(', '[', '{']
408 last_word = self.getCurrentLine()
423 last_word = self.getCurrentLine()
409 for breaker in splitter:
424 for breaker in splitter:
410 last_word = last_word.split(breaker)[-1]
425 last_word = last_word.split(breaker)[-1]
411 self.AutoCompShow(len(last_word), " ".join(possibilities))
426 self.AutoCompShow(len(last_word), " ".join(possibilities))
412
427
413 def _onKeypress(self, event, skip=True):
428 def _onKeypress(self, event, skip=True):
414 '''
429 '''
415 Key press callback used for correcting behavior for console-like
430 Key press callback used for correcting behavior for console-like
416 interfaces. For example 'home' should go to prompt, not to begining of
431 interfaces. For example 'home' should go to prompt, not to begining of
417 line.
432 line.
418
433
419 @param widget: Widget that key press accored in.
434 @param widget: Widget that key press accored in.
420 @type widget: gtk.Widget
435 @type widget: gtk.Widget
421 @param event: Event object
436 @param event: Event object
422 @type event: gtk.gdk.Event
437 @type event: gtk.gdk.Event
423
438
424 @return: Return True if event as been catched.
439 @return: Return True if event as been catched.
425 @rtype: boolean
440 @rtype: boolean
426 '''
441 '''
427
442
428 if not self.AutoCompActive():
443 if not self.AutoCompActive():
429 if event.GetKeyCode() == wx.WXK_HOME:
444 if event.GetKeyCode() == wx.WXK_HOME:
430 if event.Modifiers == wx.MOD_NONE:
445 if event.Modifiers == wx.MOD_NONE:
431 self.moveCursorOnNewValidKey()
446 self.moveCursorOnNewValidKey()
432 self.moveCursor(self.getCurrentPromptStart())
447 self.moveCursor(self.getCurrentPromptStart())
433 return True
448 return True
434 elif event.Modifiers == wx.MOD_SHIFT:
449 elif event.Modifiers == wx.MOD_SHIFT:
435 self.moveCursorOnNewValidKey()
450 self.moveCursorOnNewValidKey()
436 self.selectFromTo(self.getCurrentPromptStart(), self.getCursorPos())
451 self.selectFromTo(self.getCurrentPromptStart(), self.getCursorPos())
437 return True
452 return True
438 else:
453 else:
439 return False
454 return False
440
455
441 elif event.GetKeyCode() == wx.WXK_LEFT:
456 elif event.GetKeyCode() == wx.WXK_LEFT:
442 if event.Modifiers == wx.MOD_NONE:
457 if event.Modifiers == wx.MOD_NONE:
443 self.moveCursorOnNewValidKey()
458 self.moveCursorOnNewValidKey()
444
459
445 self.moveCursor(self.getCursorPos()-1)
460 self.moveCursor(self.getCursorPos()-1)
446 if self.getCursorPos() < self.getCurrentPromptStart():
461 if self.getCursorPos() < self.getCurrentPromptStart():
447 self.moveCursor(self.getCurrentPromptStart())
462 self.moveCursor(self.getCurrentPromptStart())
448 return True
463 return True
449
464
450 elif event.GetKeyCode() == wx.WXK_BACK:
465 elif event.GetKeyCode() == wx.WXK_BACK:
451 self.moveCursorOnNewValidKey()
466 self.moveCursorOnNewValidKey()
452 if self.getCursorPos() > self.getCurrentPromptStart():
467 if self.getCursorPos() > self.getCurrentPromptStart():
453 event.Skip()
468 event.Skip()
454 return True
469 return True
455
470
456 if skip:
471 if skip:
457 if event.GetKeyCode() not in [wx.WXK_PAGEUP, wx.WXK_PAGEDOWN]\
472 if event.GetKeyCode() not in [wx.WXK_PAGEUP, wx.WXK_PAGEDOWN]\
458 and event.Modifiers == wx.MOD_NONE:
473 and event.Modifiers == wx.MOD_NONE:
459 self.moveCursorOnNewValidKey()
474 self.moveCursorOnNewValidKey()
460
475
461 event.Skip()
476 event.Skip()
462 return True
477 return True
463 return False
478 return False
464 else:
479 else:
465 event.Skip()
480 event.Skip()
466
481
467 def OnUpdateUI(self, evt):
482 def OnUpdateUI(self, evt):
468 # check for matching braces
483 # check for matching braces
469 braceAtCaret = -1
484 braceAtCaret = -1
470 braceOpposite = -1
485 braceOpposite = -1
471 charBefore = None
486 charBefore = None
472 caretPos = self.GetCurrentPos()
487 caretPos = self.GetCurrentPos()
473
488
474 if caretPos > 0:
489 if caretPos > 0:
475 charBefore = self.GetCharAt(caretPos - 1)
490 charBefore = self.GetCharAt(caretPos - 1)
476 styleBefore = self.GetStyleAt(caretPos - 1)
491 styleBefore = self.GetStyleAt(caretPos - 1)
477
492
478 # check before
493 # check before
479 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
494 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
480 braceAtCaret = caretPos - 1
495 braceAtCaret = caretPos - 1
481
496
482 # check after
497 # check after
483 if braceAtCaret < 0:
498 if braceAtCaret < 0:
484 charAfter = self.GetCharAt(caretPos)
499 charAfter = self.GetCharAt(caretPos)
485 styleAfter = self.GetStyleAt(caretPos)
500 styleAfter = self.GetStyleAt(caretPos)
486
501
487 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
502 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
488 braceAtCaret = caretPos
503 braceAtCaret = caretPos
489
504
490 if braceAtCaret >= 0:
505 if braceAtCaret >= 0:
491 braceOpposite = self.BraceMatch(braceAtCaret)
506 braceOpposite = self.BraceMatch(braceAtCaret)
492
507
493 if braceAtCaret != -1 and braceOpposite == -1:
508 if braceAtCaret != -1 and braceOpposite == -1:
494 self.BraceBadLight(braceAtCaret)
509 self.BraceBadLight(braceAtCaret)
495 else:
510 else:
496 self.BraceHighlight(braceAtCaret, braceOpposite)
511 self.BraceHighlight(braceAtCaret, braceOpposite)
497 #pt = self.PointFromPosition(braceOpposite)
512 #pt = self.PointFromPosition(braceOpposite)
498 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
513 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
499 #print pt
514 #print pt
500 #self.Refresh(False)
515 #self.Refresh(False)
501
516
502 class IPShellWidget(wx.Panel):
517 class IPShellWidget(wx.Panel):
503 '''
518 '''
504 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
519 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
505 If you want to port this to any other GUI toolkit, just replace the
520 If you want to port this to any other GUI toolkit, just replace the
506 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
521 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
507 from whatever container you want. I've choosed to derivate from a wx.Panel
522 from whatever container you want. I've choosed to derivate from a wx.Panel
508 because it seems to be more useful
523 because it seems to be more useful
509 Any idea to make it more 'generic' welcomed.
524 Any idea to make it more 'generic' welcomed.
510 '''
525 '''
511
526
512 def __init__(self, parent, intro=None,
527 def __init__(self, parent, intro=None,
513 background_color="BLACK", add_button_handler=None,
528 background_color="BLACK", add_button_handler=None,
514 wx_ip_shell=None, user_ns={},user_global_ns=None,
529 wx_ip_shell=None, user_ns={},user_global_ns=None,
515 ):
530 ):
516 '''
531 '''
517 Initialize.
532 Initialize.
518 Instanciate an IPython thread.
533 Instanciate an IPython thread.
519 Instanciate a WxConsoleView.
534 Instanciate a WxConsoleView.
520 Redirect I/O to console.
535 Redirect I/O to console.
521 '''
536 '''
522 wx.Panel.__init__(self,parent,wx.ID_ANY)
537 wx.Panel.__init__(self,parent,wx.ID_ANY)
523
538
524 self.parent = parent
539 self.parent = parent
525 ### IPython non blocking shell instanciation ###
540 ### IPython non blocking shell instanciation ###
526 self.cout = StringIO()
541 self.cout = StringIO()
527 self.add_button_handler = add_button_handler
542 self.add_button_handler = add_button_handler
528
543
529 if wx_ip_shell is not None:
544 if wx_ip_shell is not None:
530 self.IP = wx_ip_shell
545 self.IP = wx_ip_shell
531 else:
546 else:
532 self.IP = WxNonBlockingIPShell(self,
547 self.IP = WxNonBlockingIPShell(self,
533 cout = self.cout, cerr = self.cout,
548 cout = self.cout, cerr = self.cout,
534 ask_exit_handler = self.askExitCallback)
549 ask_exit_handler = self.askExitCallback)
535
550
536 ### IPython wx console view instanciation ###
551 ### IPython wx console view instanciation ###
537 #If user didn't defined an intro text, we create one for him
552 #If user didn't defined an intro text, we create one for him
538 #If you really wnat an empty intro just call wxIPythonViewPanel
553 #If you really wnat an empty intro just call wxIPythonViewPanel
539 #with intro=''
554 #with intro=''
540 if intro is None:
555 if intro is None:
541 welcome_text = "Welcome to WxIPython Shell.\n\n"
556 welcome_text = "Welcome to WxIPython Shell.\n\n"
542 welcome_text+= self.IP.getBanner()
557 welcome_text+= self.IP.getBanner()
543 welcome_text+= "!command -> Execute command in shell\n"
558 welcome_text+= "!command -> Execute command in shell\n"
544 welcome_text+= "TAB -> Autocompletion\n"
559 welcome_text+= "TAB -> Autocompletion\n"
545 else:
560 else:
546 welcome_text = intro
561 welcome_text = intro
547
562
548 self.text_ctrl = WxConsoleView(self,
563 self.text_ctrl = WxConsoleView(self,
549 self.IP.getPrompt(),
564 self.IP.getPrompt(),
550 intro=welcome_text,
565 intro=welcome_text,
551 background_color=background_color)
566 background_color=background_color)
552
567
553 self.cout.write = self.text_ctrl.asyncWrite
568 self.cout.write = self.text_ctrl.asyncWrite
554
569
555 option_text = wx.StaticText(self, -1, "Options:")
570 option_text = wx.StaticText(self, -1, "Options:")
556 self.completion_option = wx.CheckBox(self, -1, "Scintilla Completion")
571 self.completion_option = wx.CheckBox(self, -1, "Scintilla Completion")
557 #self.completion_option.SetValue(False)
572 #self.completion_option.SetValue(False)
558 self.background_option = wx.CheckBox(self, -1, "White Background")
573 self.background_option = wx.CheckBox(self, -1, "White Background")
559 #self.background_option.SetValue(False)
574 #self.background_option.SetValue(False)
560
575
561 self.options={'completion':{'value':'IPYTHON',
576 self.options={'completion':{'value':'IPYTHON',
562 'checkbox':self.completion_option,'STC':True,'IPYTHON':False,
577 'checkbox':self.completion_option,'STC':True,'IPYTHON':False,
563 'setfunc':self.text_ctrl.setCompletionMethod},
578 'setfunc':self.text_ctrl.setCompletionMethod},
564 'background_color':{'value':'BLACK',
579 'background_color':{'value':'BLACK',
565 'checkbox':self.background_option,'WHITE':True,'BLACK':False,
580 'checkbox':self.background_option,'WHITE':True,'BLACK':False,
566 'setfunc':self.text_ctrl.setBackgroundColor},
581 'setfunc':self.text_ctrl.setBackgroundColor},
567 }
582 }
568 self.reloadOptions(self.options)
583 self.reloadOptions(self.options)
569
584
570 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress)
585 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress)
571 self.completion_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionCompletion)
586 self.completion_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionCompletion)
572 self.background_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionBackgroundColor)
587 self.background_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionBackgroundColor)
573
588
574 ### making the layout of the panel ###
589 ### making the layout of the panel ###
575 sizer = wx.BoxSizer(wx.VERTICAL)
590 sizer = wx.BoxSizer(wx.VERTICAL)
576 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
591 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
577 option_sizer = wx.BoxSizer(wx.HORIZONTAL)
592 option_sizer = wx.BoxSizer(wx.HORIZONTAL)
578 sizer.Add(option_sizer, 0)
593 sizer.Add(option_sizer, 0)
579 option_sizer.AddMany([(10, 20),
594 option_sizer.AddMany([(10, 20),
580 (option_text, 0, wx.ALIGN_CENTER_VERTICAL),
595 (option_text, 0, wx.ALIGN_CENTER_VERTICAL),
581 (5, 5),
596 (5, 5),
582 (self.completion_option, 0, wx.ALIGN_CENTER_VERTICAL),
597 (self.completion_option, 0, wx.ALIGN_CENTER_VERTICAL),
583 (8, 8),
598 (8, 8),
584 (self.background_option, 0, wx.ALIGN_CENTER_VERTICAL)
599 (self.background_option, 0, wx.ALIGN_CENTER_VERTICAL)
585 ])
600 ])
586 self.SetAutoLayout(True)
601 self.SetAutoLayout(True)
587 sizer.Fit(self)
602 sizer.Fit(self)
588 sizer.SetSizeHints(self)
603 sizer.SetSizeHints(self)
589 self.SetSizer(sizer)
604 self.SetSizer(sizer)
590 #and we focus on the widget :)
605 #and we focus on the widget :)
591 self.SetFocus()
606 self.SetFocus()
592
607
593 #widget state management (for key handling different cases)
608 #widget state management (for key handling different cases)
594 self.setCurrentState('IDLE')
609 self.setCurrentState('IDLE')
595 self.pager_state = 'DONE'
610 self.pager_state = 'DONE'
596 self.raw_input_current_line = 0
611 self.raw_input_current_line = 0
597
612
598 def askExitCallback(self, event):
613 def askExitCallback(self, event):
599 self.askExitHandler(event)
614 self.askExitHandler(event)
600
615
601 #---------------------- IPython Thread Management ------------------------
616 #---------------------- IPython Thread Management ------------------------
602 def stateDoExecuteLine(self):
617 def stateDoExecuteLine(self):
603 lines=self.text_ctrl.getCurrentLine()
618 lines=self.text_ctrl.getCurrentLine()
604 self.text_ctrl.write('\n')
619 self.text_ctrl.write('\n')
605 lines_to_execute = lines.replace('\t',' '*4)
620 lines_to_execute = lines.replace('\t',' '*4)
606 lines_to_execute = lines_to_execute.replace('\r','')
621 lines_to_execute = lines_to_execute.replace('\r','')
607 self.IP.doExecute(lines_to_execute.encode('cp1252'))
622 self.IP.doExecute(lines_to_execute.encode(ENCODING))
608 self.updateHistoryTracker(lines)
623 self.updateHistoryTracker(lines)
609 self.setCurrentState('WAIT_END_OF_EXECUTION')
624 self.setCurrentState('WAIT_END_OF_EXECUTION')
610
625
611 def evtStateExecuteDone(self,evt):
626 def evtStateExecuteDone(self,evt):
612 self.doc = self.IP.getDocText()
627 self.doc = self.IP.getDocText()
613 self.help = self.IP.getHelpText()
628 self.help = self.IP.getHelpText()
614 if self.doc:
629 if self.doc:
615 self.pager_lines = self.doc[7:].split('\n')
630 self.pager_lines = self.doc[7:].split('\n')
616 self.pager_state = 'INIT'
631 self.pager_state = 'INIT'
617 self.setCurrentState('SHOW_DOC')
632 self.setCurrentState('SHOW_DOC')
618 self.pager(self.doc)
633 self.pager(self.doc)
619 elif self.help:
634 elif self.help:
620 self.pager_lines = self.help.split('\n')
635 self.pager_lines = self.help.split('\n')
621 self.pager_state = 'INIT'
636 self.pager_state = 'INIT'
622 self.setCurrentState('SHOW_DOC')
637 self.setCurrentState('SHOW_DOC')
623 self.pager(self.help)
638 self.pager(self.help)
624 else:
639 else:
625 self.stateShowPrompt()
640 self.stateShowPrompt()
626
641
627 def stateShowPrompt(self):
642 def stateShowPrompt(self):
628 self.setCurrentState('SHOW_PROMPT')
643 self.setCurrentState('SHOW_PROMPT')
629 self.text_ctrl.setPrompt(self.IP.getPrompt())
644 self.text_ctrl.setPrompt(self.IP.getPrompt())
630 self.text_ctrl.setIndentation(self.IP.getIndentation())
645 self.text_ctrl.setIndentation(self.IP.getIndentation())
631 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
646 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
632 self.text_ctrl.showPrompt()
647 self.text_ctrl.showPrompt()
633 self.IP.initHistoryIndex()
648 self.IP.initHistoryIndex()
634 self.setCurrentState('IDLE')
649 self.setCurrentState('IDLE')
635
650
636 def setCurrentState(self, state):
651 def setCurrentState(self, state):
637 self.cur_state = state
652 self.cur_state = state
638 self.updateStatusTracker(self.cur_state)
653 self.updateStatusTracker(self.cur_state)
639
654
640 def pager(self,text):
655 def pager(self,text):
641
656
642 if self.pager_state == 'INIT':
657 if self.pager_state == 'INIT':
643 #print >>sys.__stdout__,"PAGER state:",self.pager_state
658 #print >>sys.__stdout__,"PAGER state:",self.pager_state
644 self.pager_nb_lines = len(self.pager_lines)
659 self.pager_nb_lines = len(self.pager_lines)
645 self.pager_index = 0
660 self.pager_index = 0
646 self.pager_do_remove = False
661 self.pager_do_remove = False
647 self.text_ctrl.write('\n')
662 self.text_ctrl.write('\n')
648 self.pager_state = 'PROCESS_LINES'
663 self.pager_state = 'PROCESS_LINES'
649
664
650 if self.pager_state == 'PROCESS_LINES':
665 if self.pager_state == 'PROCESS_LINES':
651 #print >>sys.__stdout__,"PAGER state:",self.pager_state
666 #print >>sys.__stdout__,"PAGER state:",self.pager_state
652 if self.pager_do_remove == True:
667 if self.pager_do_remove == True:
653 self.text_ctrl.removeCurrentLine()
668 self.text_ctrl.removeCurrentLine()
654 self.pager_do_remove = False
669 self.pager_do_remove = False
655
670
656 if self.pager_nb_lines > 10:
671 if self.pager_nb_lines > 10:
657 #print >>sys.__stdout__,"PAGER processing 10 lines"
672 #print >>sys.__stdout__,"PAGER processing 10 lines"
658 if self.pager_index > 0:
673 if self.pager_index > 0:
659 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
674 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
660 else:
675 else:
661 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
676 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
662
677
663 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
678 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
664 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
679 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
665 self.pager_index += 10
680 self.pager_index += 10
666 self.pager_nb_lines -= 10
681 self.pager_nb_lines -= 10
667 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
682 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
668 self.pager_do_remove = True
683 self.pager_do_remove = True
669 self.pager_state = 'WAITING'
684 self.pager_state = 'WAITING'
670 return
685 return
671 else:
686 else:
672 #print >>sys.__stdout__,"PAGER processing last lines"
687 #print >>sys.__stdout__,"PAGER processing last lines"
673 if self.pager_nb_lines > 0:
688 if self.pager_nb_lines > 0:
674 if self.pager_index > 0:
689 if self.pager_index > 0:
675 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
690 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
676 else:
691 else:
677 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
692 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
678
693
679 self.pager_index += 1
694 self.pager_index += 1
680 self.pager_nb_lines -= 1
695 self.pager_nb_lines -= 1
681 if self.pager_nb_lines > 0:
696 if self.pager_nb_lines > 0:
682 for line in self.pager_lines[self.pager_index:]:
697 for line in self.pager_lines[self.pager_index:]:
683 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
698 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
684 self.pager_nb_lines = 0
699 self.pager_nb_lines = 0
685 self.pager_state = 'DONE'
700 self.pager_state = 'DONE'
686 self.stateShowPrompt()
701 self.stateShowPrompt()
687
702
688 #------------------------ Key Handler ------------------------------------
703 #------------------------ Key Handler ------------------------------------
689 def keyPress(self, event):
704 def keyPress(self, event):
690 '''
705 '''
691 Key press callback with plenty of shell goodness, like history,
706 Key press callback with plenty of shell goodness, like history,
692 autocompletions, etc.
707 autocompletions, etc.
693 '''
708 '''
694 if event.GetKeyCode() == ord('C'):
709 if event.GetKeyCode() == ord('C'):
695 if event.Modifiers == wx.MOD_CONTROL or event.Modifiers == wx.MOD_ALT:
710 if event.Modifiers == wx.MOD_CONTROL or event.Modifiers == wx.MOD_ALT:
696 if self.cur_state == 'WAIT_END_OF_EXECUTION':
711 if self.cur_state == 'WAIT_END_OF_EXECUTION':
697 #we raise an exception inside the IPython thread container
712 #we raise an exception inside the IPython thread container
698 self.IP.ce.raise_exc(KeyboardInterrupt)
713 self.IP.ce.raise_exc(KeyboardInterrupt)
699 return
714 return
700
715
701 #let this before 'wx.WXK_RETURN' because we have to put 'IDLE'
716 #let this before 'wx.WXK_RETURN' because we have to put 'IDLE'
702 #mode if AutoComp has been set as inactive
717 #mode if AutoComp has been set as inactive
703 if self.cur_state == 'COMPLETING':
718 if self.cur_state == 'COMPLETING':
704 if not self.text_ctrl.AutoCompActive():
719 if not self.text_ctrl.AutoCompActive():
705 self.cur_state = 'IDLE'
720 self.cur_state = 'IDLE'
706 else:
721 else:
707 event.Skip()
722 event.Skip()
708
723
709 if event.KeyCode == wx.WXK_RETURN:
724 if event.KeyCode == wx.WXK_RETURN:
710 if self.cur_state == 'IDLE':
725 if self.cur_state == 'IDLE':
711 #we change the state ot the state machine
726 #we change the state ot the state machine
712 self.setCurrentState('DO_EXECUTE_LINE')
727 self.setCurrentState('DO_EXECUTE_LINE')
713 self.stateDoExecuteLine()
728 self.stateDoExecuteLine()
714 return
729 return
715
730
716 if self.pager_state == 'WAITING':
731 if self.pager_state == 'WAITING':
717 self.pager_state = 'PROCESS_LINES'
732 self.pager_state = 'PROCESS_LINES'
718 self.pager(self.doc)
733 self.pager(self.doc)
719 return
734 return
720
735
721 if self.cur_state == 'WAITING_USER_INPUT':
736 if self.cur_state == 'WAITING_USER_INPUT':
722 line=self.text_ctrl.getCurrentLine()
737 line=self.text_ctrl.getCurrentLine()
723 self.text_ctrl.write('\n')
738 self.text_ctrl.write('\n')
724 self.setCurrentState('WAIT_END_OF_EXECUTION')
739 self.setCurrentState('WAIT_END_OF_EXECUTION')
725 return
740 return
726
741
727 if event.GetKeyCode() in [ord('q'),ord('Q')]:
742 if event.GetKeyCode() in [ord('q'),ord('Q')]:
728 if self.pager_state == 'WAITING':
743 if self.pager_state == 'WAITING':
729 self.pager_state = 'DONE'
744 self.pager_state = 'DONE'
730 self.text_ctrl.write('\n')
745 self.text_ctrl.write('\n')
731 self.stateShowPrompt()
746 self.stateShowPrompt()
732 return
747 return
733
748
734 if self.cur_state == 'WAITING_USER_INPUT':
749 if self.cur_state == 'WAITING_USER_INPUT':
735 event.Skip()
750 event.Skip()
736
751
737 if self.cur_state == 'IDLE':
752 if self.cur_state == 'IDLE':
738 if event.KeyCode == wx.WXK_UP:
753 if event.KeyCode == wx.WXK_UP:
739 history = self.IP.historyBack()
754 history = self.IP.historyBack()
740 self.text_ctrl.writeHistory(history)
755 self.text_ctrl.writeHistory(history)
741 return
756 return
742 if event.KeyCode == wx.WXK_DOWN:
757 if event.KeyCode == wx.WXK_DOWN:
743 history = self.IP.historyForward()
758 history = self.IP.historyForward()
744 self.text_ctrl.writeHistory(history)
759 self.text_ctrl.writeHistory(history)
745 return
760 return
746 if event.KeyCode == wx.WXK_TAB:
761 if event.KeyCode == wx.WXK_TAB:
747 #if line empty we disable tab completion
762 #if line empty we disable tab completion
748 if not self.text_ctrl.getCurrentLine().strip():
763 if not self.text_ctrl.getCurrentLine().strip():
749 self.text_ctrl.write('\t')
764 self.text_ctrl.write('\t')
750 return
765 return
751 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
766 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
752 if len(possibilities) > 1:
767 if len(possibilities) > 1:
753 if self.text_ctrl.autocomplete_mode == 'IPYTHON':
768 if self.text_ctrl.autocomplete_mode == 'IPYTHON':
754 cur_slice = self.text_ctrl.getCurrentLine()
769 cur_slice = self.text_ctrl.getCurrentLine()
755 self.text_ctrl.write('\n')
770 self.text_ctrl.write('\n')
756 self.text_ctrl.writeCompletion(possibilities)
771 self.text_ctrl.writeCompletion(possibilities)
757 self.text_ctrl.write('\n')
772 self.text_ctrl.write('\n')
758
773
759 self.text_ctrl.showPrompt()
774 self.text_ctrl.showPrompt()
760 self.text_ctrl.write(cur_slice)
775 self.text_ctrl.write(cur_slice)
761 self.text_ctrl.changeLine(completed or cur_slice)
776 self.text_ctrl.changeLine(completed or cur_slice)
762 else:
777 else:
763 self.cur_state = 'COMPLETING'
778 self.cur_state = 'COMPLETING'
764 self.text_ctrl.writeCompletion(possibilities)
779 self.text_ctrl.writeCompletion(possibilities)
765 else:
780 else:
766 self.text_ctrl.changeLine(completed or cur_slice)
781 self.text_ctrl.changeLine(completed or cur_slice)
767 return
782 return
768 event.Skip()
783 event.Skip()
769
784
770 #------------------------ Option Section ---------------------------------
785 #------------------------ Option Section ---------------------------------
771 def evtCheckOptionCompletion(self, event):
786 def evtCheckOptionCompletion(self, event):
772 if event.IsChecked():
787 if event.IsChecked():
773 self.options['completion']['value']='STC'
788 self.options['completion']['value']='STC'
774 else:
789 else:
775 self.options['completion']['value']='IPYTHON'
790 self.options['completion']['value']='IPYTHON'
776 self.text_ctrl.setCompletionMethod(self.options['completion']['value'])
791 self.text_ctrl.setCompletionMethod(self.options['completion']['value'])
777 self.updateOptionTracker('completion',
792 self.updateOptionTracker('completion',
778 self.options['completion']['value'])
793 self.options['completion']['value'])
779 self.text_ctrl.SetFocus()
794 self.text_ctrl.SetFocus()
780
795
781 def evtCheckOptionBackgroundColor(self, event):
796 def evtCheckOptionBackgroundColor(self, event):
782 if event.IsChecked():
797 if event.IsChecked():
783 self.options['background_color']['value']='WHITE'
798 self.options['background_color']['value']='WHITE'
784 else:
799 else:
785 self.options['background_color']['value']='BLACK'
800 self.options['background_color']['value']='BLACK'
786 self.text_ctrl.setBackgroundColor(self.options['background_color']['value'])
801 self.text_ctrl.setBackgroundColor(self.options['background_color']['value'])
787 self.updateOptionTracker('background_color',
802 self.updateOptionTracker('background_color',
788 self.options['background_color']['value'])
803 self.options['background_color']['value'])
789 self.text_ctrl.SetFocus()
804 self.text_ctrl.SetFocus()
790
805
791 def getOptions(self):
806 def getOptions(self):
792 return self.options
807 return self.options
793
808
794 def reloadOptions(self,options):
809 def reloadOptions(self,options):
795 self.options = options
810 self.options = options
796 for key in self.options.keys():
811 for key in self.options.keys():
797 value = self.options[key]['value']
812 value = self.options[key]['value']
798 self.options[key]['checkbox'].SetValue(self.options[key][value])
813 self.options[key]['checkbox'].SetValue(self.options[key][value])
799 self.options[key]['setfunc'](value)
814 self.options[key]['setfunc'](value)
800
815
801
816
802 #------------------------ Hook Section -----------------------------------
817 #------------------------ Hook Section -----------------------------------
803 def updateOptionTracker(self,name,value):
818 def updateOptionTracker(self,name,value):
804 '''
819 '''
805 Default history tracker (does nothing)
820 Default history tracker (does nothing)
806 '''
821 '''
807 pass
822 pass
808
823
809 def setOptionTrackerHook(self,func):
824 def setOptionTrackerHook(self,func):
810 '''
825 '''
811 Define a new history tracker
826 Define a new history tracker
812 '''
827 '''
813 self.updateOptionTracker = func
828 self.updateOptionTracker = func
814
829
815 def updateHistoryTracker(self,command_line):
830 def updateHistoryTracker(self,command_line):
816 '''
831 '''
817 Default history tracker (does nothing)
832 Default history tracker (does nothing)
818 '''
833 '''
819 pass
834 pass
820
835
821 def setHistoryTrackerHook(self,func):
836 def setHistoryTrackerHook(self,func):
822 '''
837 '''
823 Define a new history tracker
838 Define a new history tracker
824 '''
839 '''
825 self.updateHistoryTracker = func
840 self.updateHistoryTracker = func
826
841
827 def updateStatusTracker(self,status):
842 def updateStatusTracker(self,status):
828 '''
843 '''
829 Default status tracker (does nothing)
844 Default status tracker (does nothing)
830 '''
845 '''
831 pass
846 pass
832
847
833 def setStatusTrackerHook(self,func):
848 def setStatusTrackerHook(self,func):
834 '''
849 '''
835 Define a new status tracker
850 Define a new status tracker
836 '''
851 '''
837 self.updateStatusTracker = func
852 self.updateStatusTracker = func
838
853
839 def askExitHandler(self, event):
854 def askExitHandler(self, event):
840 '''
855 '''
841 Default exit handler
856 Default exit handler
842 '''
857 '''
843 self.text_ctrl.write('\nExit callback has not been set.')
858 self.text_ctrl.write('\nExit callback has not been set.')
844
859
845 def setAskExitHandler(self, func):
860 def setAskExitHandler(self, func):
846 '''
861 '''
847 Define an exit handler
862 Define an exit handler
848 '''
863 '''
849 self.askExitHandler = func
864 self.askExitHandler = func
850
865
851 if __name__ == '__main__':
866 if __name__ == '__main__':
852 # Some simple code to test the shell widget.
867 # Some simple code to test the shell widget.
853 class MainWindow(wx.Frame):
868 class MainWindow(wx.Frame):
854 def __init__(self, parent, id, title):
869 def __init__(self, parent, id, title):
855 wx.Frame.__init__(self, parent, id, title, size=(300,250))
870 wx.Frame.__init__(self, parent, id, title, size=(300,250))
856 self._sizer = wx.BoxSizer(wx.VERTICAL)
871 self._sizer = wx.BoxSizer(wx.VERTICAL)
857 self.shell = IPShellWidget(self)
872 self.shell = IPShellWidget(self)
858 self._sizer.Add(self.shell, 1, wx.EXPAND)
873 self._sizer.Add(self.shell, 1, wx.EXPAND)
859 self.SetSizer(self._sizer)
874 self.SetSizer(self._sizer)
860 self.SetAutoLayout(1)
875 self.SetAutoLayout(1)
861 self.Show(True)
876 self.Show(True)
862
877
863 app = wx.PySimpleApp()
878 app = wx.PySimpleApp()
864 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
879 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
865 frame.SetSize((780, 460))
880 frame.SetSize((780, 460))
866 shell = frame.shell
881 shell = frame.shell
867
882
868 app.MainLoop()
883 app.MainLoop()
869
884
870
885
General Comments 0
You need to be logged in to leave comments. Login now