##// END OF EJS Templates
clean up...
ldufrechou -
Show More
@@ -1,735 +1,737 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 import wx.lib.newevent
29 import wx.lib.newevent
30
30
31 import re
31 import re
32 import sys
32 import sys
33 import locale
33 import locale
34 from StringIO import StringIO
34 from StringIO import StringIO
35 try:
35 try:
36 import IPython
36 import IPython
37 except Exception,e:
37 except Exception,e:
38 raise "Error importing IPython (%s)" % str(e)
38 raise "Error importing IPython (%s)" % str(e)
39
39
40 from ipshell_nonblocking import NonBlockingIPShell
40 from ipshell_nonblocking import NonBlockingIPShell
41
41
42
42
43 class WxNonBlockingIPShell(NonBlockingIPShell):
43 class WxNonBlockingIPShell(NonBlockingIPShell):
44 '''
44 '''
45 An NonBlockingIPShell Thread that is WX dependent.
45 An NonBlockingIPShell Thread that is WX dependent.
46 '''
46 '''
47 def __init__(self, parent,
47 def __init__(self, parent,
48 argv=[],user_ns={},user_global_ns=None,
48 argv=[],user_ns={},user_global_ns=None,
49 cin=None, cout=None, cerr=None,
49 cin=None, cout=None, cerr=None,
50 ask_exit_handler=None, rawinput=None):
50 ask_exit_handler=None, rawinput=None):
51
51
52 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
52 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
53 cin, cout, cerr,
53 cin, cout, cerr,
54 ask_exit_handler,
54 ask_exit_handler,
55 rawinput)
55 rawinput)
56
56
57 self.parent = parent
57 self.parent = parent
58
58
59 self.ask_exit_callback = ask_exit_handler
59 self.ask_exit_callback = ask_exit_handler
60 self._IP.exit = self._askExit
60 self._IP.exit = self._askExit
61
61
62 def addGUIShortcut(self,text,func):
62 def addGUIShortcut(self,text,func):
63 wx.CallAfter(self.parent.add_button_handler,
63 wx.CallAfter(self.parent.add_button_handler,
64 button_info={ 'text':text,
64 button_info={ 'text':text,
65 'func':self.parent.doExecuteLine(func)})
65 'func':self.parent.doExecuteLine(func)})
66
66
67 def _askExit(self):
67 def _askExit(self):
68 wx.CallAfter(self.ask_exit_callback, ())
68 wx.CallAfter(self.ask_exit_callback, ())
69
69
70 def _afterExecute(self):
70 def _afterExecute(self):
71 wx.CallAfter(self.parent.evtStateExecuteDone, ())
71 wx.CallAfter(self.parent.evtStateExecuteDone, ())
72
72
73
73
74 class WxConsoleView(stc.StyledTextCtrl):
74 class WxConsoleView(stc.StyledTextCtrl):
75 '''
75 '''
76 Specialized styled text control view for console-like workflow.
76 Specialized styled text control view for console-like workflow.
77 We use here a scintilla frontend thus it can be reused in any GUI that
77 We use here a scintilla frontend thus it can be reused in any GUI that
78 supports scintilla with less work.
78 supports scintilla with less work.
79
79
80 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.
80 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.
81 (with Black background)
81 (with Black background)
82 @type ANSI_COLORS_BLACK: dictionary
82 @type ANSI_COLORS_BLACK: dictionary
83
83
84 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.
84 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.
85 (with White background)
85 (with White background)
86 @type ANSI_COLORS_WHITE: dictionary
86 @type ANSI_COLORS_WHITE: dictionary
87
87
88 @ivar color_pat: Regex of terminal color pattern
88 @ivar color_pat: Regex of terminal color pattern
89 @type color_pat: _sre.SRE_Pattern
89 @type color_pat: _sre.SRE_Pattern
90 '''
90 '''
91 ANSI_STYLES_BLACK={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
91 ANSI_STYLES_BLACK={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
92 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
92 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
93 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
93 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
94 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
94 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
95 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
95 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
96 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
96 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
97 '1;34': [12,'LIGHT BLUE'], '1;35':
97 '1;34': [12,'LIGHT BLUE'], '1;35':
98 [13,'MEDIUM VIOLET RED'],
98 [13,'MEDIUM VIOLET RED'],
99 '1;36': [14,'LIGHT STEEL BLUE'],'1;37': [15,'YELLOW']}
99 '1;36': [14,'LIGHT STEEL BLUE'],'1;37': [15,'YELLOW']}
100
100
101 ANSI_STYLES_WHITE={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
101 ANSI_STYLES_WHITE={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
102 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
102 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
103 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
103 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
104 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
104 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
105 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
105 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
106 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
106 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
107 '1;34': [12,'LIGHT BLUE'], '1;35':
107 '1;34': [12,'LIGHT BLUE'], '1;35':
108 [13,'MEDIUM VIOLET RED'],
108 [13,'MEDIUM VIOLET RED'],
109 '1;36': [14,'LIGHT STEEL BLUE'],'1;37': [15,'YELLOW']}
109 '1;36': [14,'LIGHT STEEL BLUE'],'1;37': [15,'YELLOW']}
110
110
111 def __init__(self,parent,prompt,intro="",background_color="BLACK",
111 def __init__(self,parent,prompt,intro="",background_color="BLACK",
112 pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
112 pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
113 style=0):
113 style=0):
114 '''
114 '''
115 Initialize console view.
115 Initialize console view.
116
116
117 @param parent: Parent widget
117 @param parent: Parent widget
118 @param prompt: User specified prompt
118 @param prompt: User specified prompt
119 @type intro: string
119 @type intro: string
120 @param intro: User specified startup introduction string
120 @param intro: User specified startup introduction string
121 @type intro: string
121 @type intro: string
122 @param background_color: Can be BLACK or WHITE
122 @param background_color: Can be BLACK or WHITE
123 @type background_color: string
123 @type background_color: string
124 @param other: init param of styledTextControl (can be used as-is)
124 @param other: init param of styledTextControl (can be used as-is)
125 '''
125 '''
126 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
126 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
127
127
128 ####### Scintilla configuration ###################################
128 ####### Scintilla configuration ###################################
129
129
130 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside
130 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside
131 # the widget
131 # the widget
132 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
132 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
133 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
133 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
134
134
135 #we define platform specific fonts
135 #we define platform specific fonts
136 if wx.Platform == '__WXMSW__':
136 if wx.Platform == '__WXMSW__':
137 faces = { 'times': 'Times New Roman',
137 faces = { 'times': 'Times New Roman',
138 'mono' : 'Courier New',
138 'mono' : 'Courier New',
139 'helv' : 'Arial',
139 'helv' : 'Arial',
140 'other': 'Comic Sans MS',
140 'other': 'Comic Sans MS',
141 'size' : 10,
141 'size' : 10,
142 'size2': 8,
142 'size2': 8,
143 }
143 }
144 elif wx.Platform == '__WXMAC__':
144 elif wx.Platform == '__WXMAC__':
145 faces = { 'times': 'Times New Roman',
145 faces = { 'times': 'Times New Roman',
146 'mono' : 'Monaco',
146 'mono' : 'Monaco',
147 'helv' : 'Arial',
147 'helv' : 'Arial',
148 'other': 'Comic Sans MS',
148 'other': 'Comic Sans MS',
149 'size' : 10,
149 'size' : 10,
150 'size2': 8,
150 'size2': 8,
151 }
151 }
152 else:
152 else:
153 faces = { 'times': 'Times',
153 faces = { 'times': 'Times',
154 'mono' : 'Courier',
154 'mono' : 'Courier',
155 'helv' : 'Helvetica',
155 'helv' : 'Helvetica',
156 'other': 'new century schoolbook',
156 'other': 'new century schoolbook',
157 'size' : 10,
157 'size' : 10,
158 'size2': 8,
158 'size2': 8,
159 }
159 }
160
160
161 #We draw a line at position 80
161 #We draw a line at position 80
162 self.SetEdgeMode(stc.STC_EDGE_LINE)
162 self.SetEdgeMode(stc.STC_EDGE_LINE)
163 self.SetEdgeColumn(80)
163 self.SetEdgeColumn(80)
164 self.SetEdgeColour(wx.LIGHT_GREY)
164 self.SetEdgeColour(wx.LIGHT_GREY)
165
165
166 #self.SetViewWhiteSpace(True)
166 #self.SetViewWhiteSpace(True)
167 #self.SetViewEOL(True)
167 #self.SetViewEOL(True)
168 self.SetEOLMode(stc.STC_EOL_CRLF)
168 self.SetEOLMode(stc.STC_EOL_CRLF)
169 #self.SetWrapMode(stc.STC_WRAP_CHAR)
169 #self.SetWrapMode(stc.STC_WRAP_CHAR)
170 #self.SetWrapMode(stc.STC_WRAP_WORD)
170 #self.SetWrapMode(stc.STC_WRAP_WORD)
171 self.SetBufferedDraw(True)
171 self.SetBufferedDraw(True)
172 #self.SetUseAntiAliasing(True)
172 #self.SetUseAntiAliasing(True)
173 self.SetLayoutCache(stc.STC_CACHE_PAGE)
173 self.SetLayoutCache(stc.STC_CACHE_PAGE)
174
174
175 self.EnsureCaretVisible()
175 self.EnsureCaretVisible()
176
176
177 self.SetMargins(3,3) #text is moved away from border with 3px
177 self.SetMargins(3,3) #text is moved away from border with 3px
178 # Suppressing Scintilla margins
178 # Suppressing Scintilla margins
179 self.SetMarginWidth(0,0)
179 self.SetMarginWidth(0,0)
180 self.SetMarginWidth(1,0)
180 self.SetMarginWidth(1,0)
181 self.SetMarginWidth(2,0)
181 self.SetMarginWidth(2,0)
182
182
183 # make some styles
183 # make some styles
184 if background_color != "BLACK":
184 if background_color != "BLACK":
185 self.background_color = "WHITE"
185 self.background_color = "WHITE"
186 self.SetCaretForeground("BLACK")
186 self.SetCaretForeground("BLACK")
187 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
187 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
188 else:
188 else:
189 self.background_color = background_color
189 self.background_color = background_color
190 self.SetCaretForeground("WHITE")
190 self.SetCaretForeground("WHITE")
191 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
191 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
192
192
193 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
193 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
194 "fore:%s,back:%s,size:%d,face:%s"
194 "fore:%s,back:%s,size:%d,face:%s"
195 % (self.ANSI_STYLES['0;30'][1],
195 % (self.ANSI_STYLES['0;30'][1],
196 self.background_color,
196 self.background_color,
197 faces['size'], faces['mono']))
197 faces['size'], faces['mono']))
198 self.StyleClearAll()
198 self.StyleClearAll()
199 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
199 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
200 "fore:#FF0000,back:#0000FF,bold")
200 "fore:#FF0000,back:#0000FF,bold")
201 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
201 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
202 "fore:#000000,back:#FF0000,bold")
202 "fore:#000000,back:#FF0000,bold")
203
203
204 for style in self.ANSI_STYLES.values():
204 for style in self.ANSI_STYLES.values():
205 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
205 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
206
206
207 #######################################################################
207 #######################################################################
208
208
209 self.indent = 0
209 self.indent = 0
210 self.prompt_count = 0
210 self.prompt_count = 0
211 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
211 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
212
212
213 self.write(intro)
213 self.write(intro)
214 self.setPrompt(prompt)
214 self.setPrompt(prompt)
215 self.showPrompt()
215 self.showPrompt()
216
216
217 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
217 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
218
218
219 def write(self, text):
219 def write(self, text):
220 '''
220 '''
221 Write given text to buffer.
221 Write given text to buffer.
222
222
223 @param text: Text to append.
223 @param text: Text to append.
224 @type text: string
224 @type text: string
225 '''
225 '''
226 segments = self.color_pat.split(text)
226 segments = self.color_pat.split(text)
227 segment = segments.pop(0)
227 segment = segments.pop(0)
228 self.StartStyling(self.getCurrentLineEnd(),0xFF)
228 self.StartStyling(self.getCurrentLineEnd(),0xFF)
229 self.AppendText(segment)
229 self.AppendText(segment)
230
230
231 if segments:
231 if segments:
232 ansi_tags = self.color_pat.findall(text)
232 ansi_tags = self.color_pat.findall(text)
233
233
234 for tag in ansi_tags:
234 for tag in ansi_tags:
235 i = segments.index(tag)
235 i = segments.index(tag)
236 self.StartStyling(self.getCurrentLineEnd(),0xFF)
236 self.StartStyling(self.getCurrentLineEnd(),0xFF)
237 self.AppendText(segments[i+1])
237 self.AppendText(segments[i+1])
238
238
239 if tag != '0':
239 if tag != '0':
240 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
240 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
241
241
242 segments.pop(i)
242 segments.pop(i)
243
243
244 self.moveCursor(self.getCurrentLineEnd())
244 self.moveCursor(self.getCurrentLineEnd())
245
245
246 def getPromptLen(self):
246 def getPromptLen(self):
247 '''
247 '''
248 Return the length of current prompt
248 Return the length of current prompt
249 '''
249 '''
250 return len(str(self.prompt_count)) + 7
250 return len(str(self.prompt_count)) + 7
251
251
252 def setPrompt(self,prompt):
252 def setPrompt(self,prompt):
253 self.prompt = prompt
253 self.prompt = prompt
254
254
255 def setIndentation(self,indentation):
255 def setIndentation(self,indentation):
256 self.indent = indentation
256 self.indent = indentation
257
257
258 def setPromptCount(self,count):
258 def setPromptCount(self,count):
259 self.prompt_count = count
259 self.prompt_count = count
260
260
261 def showPrompt(self):
261 def showPrompt(self):
262 '''
262 '''
263 Prints prompt at start of line.
263 Prints prompt at start of line.
264
264
265 @param prompt: Prompt to print.
265 @param prompt: Prompt to print.
266 @type prompt: string
266 @type prompt: string
267 '''
267 '''
268 self.write(self.prompt)
268 self.write(self.prompt)
269 #now we update the position of end of prompt
269 #now we update the position of end of prompt
270 self.current_start = self.getCurrentLineEnd()
270 self.current_start = self.getCurrentLineEnd()
271
271
272 autoindent = self.indent*' '
272 autoindent = self.indent*' '
273 autoindent = autoindent.replace(' ','\t')
273 autoindent = autoindent.replace(' ','\t')
274 self.write(autoindent)
274 self.write(autoindent)
275
275
276 def changeLine(self, text):
276 def changeLine(self, text):
277 '''
277 '''
278 Replace currently entered command line with given text.
278 Replace currently entered command line with given text.
279
279
280 @param text: Text to use as replacement.
280 @param text: Text to use as replacement.
281 @type text: string
281 @type text: string
282 '''
282 '''
283 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
283 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
284 self.ReplaceSelection(text)
284 self.ReplaceSelection(text)
285 self.moveCursor(self.getCurrentLineEnd())
285 self.moveCursor(self.getCurrentLineEnd())
286
286
287 def getCurrentPromptStart(self):
287 def getCurrentPromptStart(self):
288 return self.current_start
288 return self.current_start
289
289
290 def getCurrentLineStart(self):
290 def getCurrentLineStart(self):
291 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
291 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
292
292
293 def getCurrentLineEnd(self):
293 def getCurrentLineEnd(self):
294 return self.GetLength()
294 return self.GetLength()
295
295
296 def getCurrentLine(self):
296 def getCurrentLine(self):
297 '''
297 '''
298 Get text in current command line.
298 Get text in current command line.
299
299
300 @return: Text of current command line.
300 @return: Text of current command line.
301 @rtype: string
301 @rtype: string
302 '''
302 '''
303 return self.GetTextRange(self.getCurrentPromptStart(),
303 return self.GetTextRange(self.getCurrentPromptStart(),
304 self.getCurrentLineEnd())
304 self.getCurrentLineEnd())
305
305
306 def moveCursorOnNewValidKey(self):
306 def moveCursorOnNewValidKey(self):
307 #If cursor is at wrong position put it at last line...
307 #If cursor is at wrong position put it at last line...
308 if self.GetCurrentPos() < self.getCurrentPromptStart():
308 if self.GetCurrentPos() < self.getCurrentPromptStart():
309 self.GotoPos(self.getCurrentPromptStart())
309 self.GotoPos(self.getCurrentPromptStart())
310
310
311 def removeFromTo(self,from_pos,to_pos):
311 def removeFromTo(self,from_pos,to_pos):
312 if from_pos < to_pos:
312 if from_pos < to_pos:
313 self.SetSelection(from_pos,to_pos)
313 self.SetSelection(from_pos,to_pos)
314 self.DeleteBack()
314 self.DeleteBack()
315
315
316 def removeCurrentLine(self):
316 def removeCurrentLine(self):
317 self.LineDelete()
317 self.LineDelete()
318
318
319 def moveCursor(self,position):
319 def moveCursor(self,position):
320 self.GotoPos(position)
320 self.GotoPos(position)
321
321
322 def getCursorPos(self):
322 def getCursorPos(self):
323 return self.GetCurrentPos()
323 return self.GetCurrentPos()
324
324
325 def selectFromTo(self,from_pos,to_pos):
325 def selectFromTo(self,from_pos,to_pos):
326 self.SetSelectionStart(from_pos)
326 self.SetSelectionStart(from_pos)
327 self.SetSelectionEnd(to_pos)
327 self.SetSelectionEnd(to_pos)
328
328
329 def writeHistory(self,history):
329 def writeHistory(self,history):
330 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
330 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
331 self.changeLine(history)
331 self.changeLine(history)
332
332
333 def writeCompletion(self, possibilities):
333 def writeCompletion(self, possibilities):
334 max_len = len(max(possibilities,key=len))
334 max_len = len(max(possibilities,key=len))
335 max_symbol =' '*max_len
335 max_symbol =' '*max_len
336
336
337 #now we check how much symbol we can put on a line...
337 #now we check how much symbol we can put on a line...
338 cursor_pos = self.getCursorPos()
338 cursor_pos = self.getCursorPos()
339 test_buffer = max_symbol + ' '*4
339 test_buffer = max_symbol + ' '*4
340 current_lines = self.GetLineCount()
340 current_lines = self.GetLineCount()
341
341
342 allowed_symbols = 80/len(test_buffer)
342 allowed_symbols = 80/len(test_buffer)
343 if allowed_symbols == 0:
343 if allowed_symbols == 0:
344 allowed_symbols = 1
344 allowed_symbols = 1
345
345
346 pos = 1
346 pos = 1
347 buf = ''
347 buf = ''
348 for symbol in possibilities:
348 for symbol in possibilities:
349 #buf += symbol+'\n'#*spaces)
349 #buf += symbol+'\n'#*spaces)
350 if pos<allowed_symbols:
350 if pos<allowed_symbols:
351 spaces = max_len - len(symbol) + 4
351 spaces = max_len - len(symbol) + 4
352 buf += symbol+' '*spaces
352 buf += symbol+' '*spaces
353 pos += 1
353 pos += 1
354 else:
354 else:
355 buf+=symbol+'\n'
355 buf+=symbol+'\n'
356 pos = 1
356 pos = 1
357 self.write(buf)
357 self.write(buf)
358
358
359 def _onKeypress(self, event, skip=True):
359 def _onKeypress(self, event, skip=True):
360 '''
360 '''
361 Key press callback used for correcting behavior for console-like
361 Key press callback used for correcting behavior for console-like
362 interfaces. For example 'home' should go to prompt, not to begining of
362 interfaces. For example 'home' should go to prompt, not to begining of
363 line.
363 line.
364
364
365 @param widget: Widget that key press accored in.
365 @param widget: Widget that key press accored in.
366 @type widget: gtk.Widget
366 @type widget: gtk.Widget
367 @param event: Event object
367 @param event: Event object
368 @type event: gtk.gdk.Event
368 @type event: gtk.gdk.Event
369
369
370 @return: Return True if event as been catched.
370 @return: Return True if event as been catched.
371 @rtype: boolean
371 @rtype: boolean
372 '''
372 '''
373
373
374 if event.GetKeyCode() == wx.WXK_HOME:
374 if event.GetKeyCode() == wx.WXK_HOME:
375 if event.Modifiers == wx.MOD_NONE:
375 if event.Modifiers == wx.MOD_NONE:
376 self.moveCursorOnNewValidKey()
376 self.moveCursorOnNewValidKey()
377 self.moveCursor(self.getCurrentPromptStart())
377 self.moveCursor(self.getCurrentPromptStart())
378 return True
378 return True
379 elif event.Modifiers == wx.MOD_SHIFT:
379 elif event.Modifiers == wx.MOD_SHIFT:
380 self.moveCursorOnNewValidKey()
380 self.moveCursorOnNewValidKey()
381 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
381 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
382 return True
382 return True
383 else:
383 else:
384 return False
384 return False
385
385
386 elif event.GetKeyCode() == wx.WXK_LEFT:
386 elif event.GetKeyCode() == wx.WXK_LEFT:
387 if event.Modifiers == wx.MOD_NONE:
387 if event.Modifiers == wx.MOD_NONE:
388 self.moveCursorOnNewValidKey()
388 self.moveCursorOnNewValidKey()
389
389
390 self.moveCursor(self.getCursorPos()-1)
390 self.moveCursor(self.getCursorPos()-1)
391 if self.getCursorPos() < self.getCurrentPromptStart():
391 if self.getCursorPos() < self.getCurrentPromptStart():
392 self.moveCursor(self.getCurrentPromptStart())
392 self.moveCursor(self.getCurrentPromptStart())
393 return True
393 return True
394
394
395 elif event.GetKeyCode() == wx.WXK_BACK:
395 elif event.GetKeyCode() == wx.WXK_BACK:
396 self.moveCursorOnNewValidKey()
396 self.moveCursorOnNewValidKey()
397 if self.getCursorPos() > self.getCurrentPromptStart():
397 if self.getCursorPos() > self.getCurrentPromptStart():
398 event.Skip()
398 event.Skip()
399 return True
399 return True
400
400
401 if skip:
401 if skip:
402 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
402 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
403 self.moveCursorOnNewValidKey()
403 self.moveCursorOnNewValidKey()
404
404
405 event.Skip()
405 event.Skip()
406 return True
406 return True
407 return False
407 return False
408
408
409 def OnUpdateUI(self, evt):
409 def OnUpdateUI(self, evt):
410 # check for matching braces
410 # check for matching braces
411 braceAtCaret = -1
411 braceAtCaret = -1
412 braceOpposite = -1
412 braceOpposite = -1
413 charBefore = None
413 charBefore = None
414 caretPos = self.GetCurrentPos()
414 caretPos = self.GetCurrentPos()
415
415
416 if caretPos > 0:
416 if caretPos > 0:
417 charBefore = self.GetCharAt(caretPos - 1)
417 charBefore = self.GetCharAt(caretPos - 1)
418 styleBefore = self.GetStyleAt(caretPos - 1)
418 styleBefore = self.GetStyleAt(caretPos - 1)
419
419
420 # check before
420 # check before
421 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
421 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
422 braceAtCaret = caretPos - 1
422 braceAtCaret = caretPos - 1
423
423
424 # check after
424 # check after
425 if braceAtCaret < 0:
425 if braceAtCaret < 0:
426 charAfter = self.GetCharAt(caretPos)
426 charAfter = self.GetCharAt(caretPos)
427 styleAfter = self.GetStyleAt(caretPos)
427 styleAfter = self.GetStyleAt(caretPos)
428
428
429 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
429 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
430 braceAtCaret = caretPos
430 braceAtCaret = caretPos
431
431
432 if braceAtCaret >= 0:
432 if braceAtCaret >= 0:
433 braceOpposite = self.BraceMatch(braceAtCaret)
433 braceOpposite = self.BraceMatch(braceAtCaret)
434
434
435 if braceAtCaret != -1 and braceOpposite == -1:
435 if braceAtCaret != -1 and braceOpposite == -1:
436 self.BraceBadLight(braceAtCaret)
436 self.BraceBadLight(braceAtCaret)
437 else:
437 else:
438 self.BraceHighlight(braceAtCaret, braceOpposite)
438 self.BraceHighlight(braceAtCaret, braceOpposite)
439 #pt = self.PointFromPosition(braceOpposite)
439 #pt = self.PointFromPosition(braceOpposite)
440 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
440 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
441 #print pt
441 #print pt
442 #self.Refresh(False)
442 #self.Refresh(False)
443
443
444 class IPShellWidget(wx.Panel):
444 class IPShellWidget(wx.Panel):
445 '''
445 '''
446 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
446 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
447 If you want to port this to any other GUI toolkit, just replace the
447 If you want to port this to any other GUI toolkit, just replace the
448 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
448 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
449 from whatever container you want. I've choosed to derivate from a wx.Panel
449 from whatever container you want. I've choosed to derivate from a wx.Panel
450 because it seems to be more useful
450 because it seems to be more useful
451 Any idea to make it more 'generic' welcomed.
451 Any idea to make it more 'generic' welcomed.
452 '''
452 '''
453
453
454 def __init__(self, parent, intro=None,
454 def __init__(self, parent, intro=None,
455 background_color="BLACK", add_button_handler=None,
455 background_color="BLACK", add_button_handler=None,
456 wx_ip_shell=None,
456 wx_ip_shell=None,
457 ):
457 ):
458 '''
458 '''
459 Initialize.
459 Initialize.
460 Instanciate an IPython thread.
460 Instanciate an IPython thread.
461 Instanciate a WxConsoleView.
461 Instanciate a WxConsoleView.
462 Redirect I/O to console.
462 Redirect I/O to console.
463 '''
463 '''
464 wx.Panel.__init__(self,parent,-1)
464 wx.Panel.__init__(self,parent,-1)
465
465
466 ### IPython non blocking shell instanciation ###
466 ### IPython non blocking shell instanciation ###
467 self.cout = StringIO()
467 self.cout = StringIO()
468 self.add_button_handler = add_button_handler
468 self.add_button_handler = add_button_handler
469
469
470 if wx_ip_shell is not None:
470 if wx_ip_shell is not None:
471 self.IP = wx_ip_shell
471 self.IP = wx_ip_shell
472 else:
472 else:
473 self.IP = WxNonBlockingIPShell(self,
473 self.IP = WxNonBlockingIPShell(self,
474 cout = self.cout, cerr = self.cout,
474 cout = self.cout, cerr = self.cout,
475 ask_exit_handler = self.askExitCallback,
475 ask_exit_handler = self.askExitCallback,
476 rawinput = self.rawInput)
476 rawinput = self.rawInput)
477
477
478 ### IPython wx console view instanciation ###
478 ### IPython wx console view instanciation ###
479 #If user didn't defined an intro text, we create one for him
479 #If user didn't defined an intro text, we create one for him
480 #If you really wnat an empty intrp just call wxIPythonViewPanel
480 #If you really wnat an empty intrp just call wxIPythonViewPanel
481 #with intro=''
481 #with intro=''
482 if intro is None:
482 if intro is None:
483 welcome_text = "Welcome to WxIPython Shell.\n\n"
483 welcome_text = "Welcome to WxIPython Shell.\n\n"
484 welcome_text+= self.IP.getBanner()
484 welcome_text+= self.IP.getBanner()
485 welcome_text+= "!command -> Execute command in shell\n"
485 welcome_text+= "!command -> Execute command in shell\n"
486 welcome_text+= "TAB -> Autocompletion\n"
486 welcome_text+= "TAB -> Autocompletion\n"
487 else:
487 else:
488 welcome_text = intro
488 welcome_text = intro
489
489
490 self.text_ctrl = WxConsoleView(self,
490 self.text_ctrl = WxConsoleView(self,
491 self.IP.getPrompt(),
491 self.IP.getPrompt(),
492 intro=welcome_text,
492 intro=welcome_text,
493 background_color=background_color)
493 background_color=background_color)
494
494
495 self.cout.write = self.text_ctrl.write
495 self.cout.write = self.text_ctrl.write
496
496
497 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
497 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
498
498
499 ### making the layout of the panel ###
499 ### making the layout of the panel ###
500 sizer = wx.BoxSizer(wx.VERTICAL)
500 sizer = wx.BoxSizer(wx.VERTICAL)
501 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
501 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
502 self.SetAutoLayout(True)
502 self.SetAutoLayout(True)
503 sizer.Fit(self)
503 sizer.Fit(self)
504 sizer.SetSizeHints(self)
504 sizer.SetSizeHints(self)
505 self.SetSizer(sizer)
505 self.SetSizer(sizer)
506 #and we focus on the widget :)
506 #and we focus on the widget :)
507 self.SetFocus()
507 self.SetFocus()
508
508
509 #widget state management (for key handling different cases)
509 #widget state management (for key handling different cases)
510 self.setCurrentState('IDLE')
510 self.setCurrentState('IDLE')
511 self.pager_state = 'DONE'
511 self.pager_state = 'DONE'
512
512
513 def askExitCallback(self, event):
513 def askExitCallback(self, event):
514 self.askExitHandler(event)
514 self.askExitHandler(event)
515
515
516 #---------------------- IPython Thread Management ------------------------
516 #---------------------- IPython Thread Management ------------------------
517 def stateDoExecuteLine(self):
517 def stateDoExecuteLine(self):
518 #print >>sys.__stdout__,"command:",self.getCurrentLine()
518 #print >>sys.__stdout__,"command:",self.getCurrentLine()
519 line=self.text_ctrl.getCurrentLine()
519 lines=self.text_ctrl.getCurrentLine()
520 self.text_ctrl.write('\n')
520 self.text_ctrl.write('\n')
521 self.IP.doExecute((line.replace('\t',' '*4)).encode('cp1252'))
521 for line in lines.split('\n'):
522 self.IP.doExecute((line.replace('\t',' '*4)).encode('cp1252'))
522 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
523 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
523 self.setCurrentState('WAIT_END_OF_EXECUTION')
524 self.setCurrentState('WAIT_END_OF_EXECUTION')
524
525
525 def evtStateExecuteDone(self,evt):
526 def evtStateExecuteDone(self,evt):
526 self.doc = self.IP.getDocText()
527 self.doc = self.IP.getDocText()
527 self.help = self.IP.getHelpText()
528 self.help = self.IP.getHelpText()
528 if self.doc:
529 if self.doc:
529 self.pager_lines = self.doc[7:].split('\n')
530 self.pager_lines = self.doc[7:].split('\n')
530 self.pager_state = 'INIT'
531 self.pager_state = 'INIT'
531 self.setCurrentState('SHOW_DOC')
532 self.setCurrentState('SHOW_DOC')
532 self.pager(self.doc)
533 self.pager(self.doc)
533 elif self.help:
534 elif self.help:
534 self.pager_lines = self.help.split('\n')
535 self.pager_lines = self.help.split('\n')
535 self.pager_state = 'INIT'
536 self.pager_state = 'INIT'
536 self.setCurrentState('SHOW_DOC')
537 self.setCurrentState('SHOW_DOC')
537 self.pager(self.help)
538 self.pager(self.help)
538 else:
539 else:
539 self.stateShowPrompt()
540 self.stateShowPrompt()
540
541
541 def stateShowPrompt(self):
542 def stateShowPrompt(self):
542 self.setCurrentState('SHOW_PROMPT')
543 self.setCurrentState('SHOW_PROMPT')
543 self.text_ctrl.setPrompt(self.IP.getPrompt())
544 self.text_ctrl.setPrompt(self.IP.getPrompt())
544 self.text_ctrl.setIndentation(self.IP.getIndentation())
545 self.text_ctrl.setIndentation(self.IP.getIndentation())
545 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
546 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
546 self.text_ctrl.showPrompt()
547 self.text_ctrl.showPrompt()
547 self.IP.initHistoryIndex()
548 self.IP.initHistoryIndex()
548 self.setCurrentState('IDLE')
549 self.setCurrentState('IDLE')
549
550
550 def setCurrentState(self, state):
551 def setCurrentState(self, state):
551 self.cur_state = state
552 self.cur_state = state
552 self.updateStatusTracker(self.cur_state)
553 self.updateStatusTracker(self.cur_state)
553 #---------------------------- Ipython raw_input -----------------------------------
554 #---------------------------- Ipython raw_input -----------------------------------
554 def rawInput(self, prompt=''):
555 def rawInput(self, prompt=''):
555 self.setCurrentState('WAITING_USER_INPUT')
556 self.setCurrentState('WAITING_USER_INPUT')
556 while self.cur_state != 'WAIT_END_OF_EXECUTION':
557 while self.cur_state != 'WAIT_END_OF_EXECUTION':
557 pass
558 pass
558 line = self.text_ctrl.getCurrentLine()
559 line = self.text_ctrl.getCurrentLine()
559 line = line.split('\n')
560 line = line.split('\n')
560 return line[-2]
561 return line[-2]
561
562
562 #---------------------------- IPython pager ---------------------------------------
563 #---------------------------- IPython pager ---------------------------------------
563 def pager(self,text):
564 def pager(self,text):
564
565
565 if self.pager_state == 'INIT':
566 if self.pager_state == 'INIT':
566 #print >>sys.__stdout__,"PAGER state:",self.pager_state
567 #print >>sys.__stdout__,"PAGER state:",self.pager_state
567 self.pager_nb_lines = len(self.pager_lines)
568 self.pager_nb_lines = len(self.pager_lines)
568 self.pager_index = 0
569 self.pager_index = 0
569 self.pager_do_remove = False
570 self.pager_do_remove = False
570 self.text_ctrl.write('\n')
571 self.text_ctrl.write('\n')
571 self.pager_state = 'PROCESS_LINES'
572 self.pager_state = 'PROCESS_LINES'
572
573
573 if self.pager_state == 'PROCESS_LINES':
574 if self.pager_state == 'PROCESS_LINES':
574 #print >>sys.__stdout__,"PAGER state:",self.pager_state
575 #print >>sys.__stdout__,"PAGER state:",self.pager_state
575 if self.pager_do_remove == True:
576 if self.pager_do_remove == True:
576 self.text_ctrl.removeCurrentLine()
577 self.text_ctrl.removeCurrentLine()
577 self.pager_do_remove = False
578 self.pager_do_remove = False
578
579
579 if self.pager_nb_lines > 10:
580 if self.pager_nb_lines > 10:
580 #print >>sys.__stdout__,"PAGER processing 10 lines"
581 #print >>sys.__stdout__,"PAGER processing 10 lines"
581 if self.pager_index > 0:
582 if self.pager_index > 0:
582 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
583 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
583 else:
584 else:
584 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
585 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
585
586
586 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
587 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
587 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
588 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
588 self.pager_index += 10
589 self.pager_index += 10
589 self.pager_nb_lines -= 10
590 self.pager_nb_lines -= 10
590 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
591 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
591 self.pager_do_remove = True
592 self.pager_do_remove = True
592 self.pager_state = 'WAITING'
593 self.pager_state = 'WAITING'
593 return
594 return
594 else:
595 else:
595 #print >>sys.__stdout__,"PAGER processing last lines"
596 #print >>sys.__stdout__,"PAGER processing last lines"
596 if self.pager_nb_lines > 0:
597 if self.pager_nb_lines > 0:
597 if self.pager_index > 0:
598 if self.pager_index > 0:
598 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
599 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
599 else:
600 else:
600 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
601 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
601
602
602 self.pager_index += 1
603 self.pager_index += 1
603 self.pager_nb_lines -= 1
604 self.pager_nb_lines -= 1
604 if self.pager_nb_lines > 0:
605 if self.pager_nb_lines > 0:
605 for line in self.pager_lines[self.pager_index:]:
606 for line in self.pager_lines[self.pager_index:]:
606 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
607 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
607 self.pager_nb_lines = 0
608 self.pager_nb_lines = 0
608 self.pager_state = 'DONE'
609 self.pager_state = 'DONE'
609 self.stateShowPrompt()
610 self.stateShowPrompt()
610
611
611 #------------------------ Key Handler ------------------------------------
612 #------------------------ Key Handler ------------------------------------
612 def keyPress(self, event):
613 def keyPress(self, event):
613 '''
614 '''
614 Key press callback with plenty of shell goodness, like history,
615 Key press callback with plenty of shell goodness, like history,
615 autocompletions, etc.
616 autocompletions, etc.
616 '''
617 '''
617
618
618 if event.GetKeyCode() == ord('C'):
619 if event.GetKeyCode() == ord('C'):
619 if event.Modifiers == wx.MOD_CONTROL:
620 if event.Modifiers == wx.MOD_CONTROL:
620 if self.cur_state == 'WAIT_END_OF_EXECUTION':
621 if self.cur_state == 'WAIT_END_OF_EXECUTION':
621 #we raise an exception inside the IPython thread container
622 #we raise an exception inside the IPython thread container
622 self.IP.ce.raise_exc(KeyboardInterrupt)
623 self.IP.ce.raise_exc(KeyboardInterrupt)
623 return
624 return
624
625
625 if event.KeyCode == wx.WXK_RETURN:
626 if event.KeyCode == wx.WXK_RETURN:
626 if self.cur_state == 'IDLE':
627 if self.cur_state == 'IDLE':
627 #we change the state ot the state machine
628 #we change the state ot the state machine
628 self.setCurrentState('DO_EXECUTE_LINE')
629 self.setCurrentState('DO_EXECUTE_LINE')
629 self.stateDoExecuteLine()
630 self.stateDoExecuteLine()
630 return
631 return
631 if self.pager_state == 'WAITING':
632 if self.pager_state == 'WAITING':
632 self.pager_state = 'PROCESS_LINES'
633 self.pager_state = 'PROCESS_LINES'
633 self.pager(self.doc)
634 self.pager(self.doc)
634 return
635 return
635
636
636 if self.cur_state == 'WAITING_USER_INPUT':
637 if self.cur_state == 'WAITING_USER_INPUT':
637 line=self.text_ctrl.getCurrentLine()
638 line=self.text_ctrl.getCurrentLine()
638 self.text_ctrl.write('\n')
639 self.text_ctrl.write('\n')
639 self.setCurrentState('WAIT_END_OF_EXECUTION')
640 self.setCurrentState('WAIT_END_OF_EXECUTION')
640 return
641 return
641
642
642 if event.GetKeyCode() in [ord('q'),ord('Q')]:
643 if event.GetKeyCode() in [ord('q'),ord('Q')]:
643 if self.pager_state == 'WAITING':
644 if self.pager_state == 'WAITING':
644 self.pager_state = 'DONE'
645 self.pager_state = 'DONE'
646 self.text_ctrl.write('\n')
645 self.stateShowPrompt()
647 self.stateShowPrompt()
646 return
648 return
647
649
648 if self.cur_state == 'WAITING_USER_INPUT':
650 if self.cur_state == 'WAITING_USER_INPUT':
649 event.Skip()
651 event.Skip()
650
652
651 if self.cur_state == 'IDLE':
653 if self.cur_state == 'IDLE':
652 if event.KeyCode == wx.WXK_UP:
654 if event.KeyCode == wx.WXK_UP:
653 history = self.IP.historyBack()
655 history = self.IP.historyBack()
654 self.text_ctrl.writeHistory(history)
656 self.text_ctrl.writeHistory(history)
655 return
657 return
656 if event.KeyCode == wx.WXK_DOWN:
658 if event.KeyCode == wx.WXK_DOWN:
657 history = self.IP.historyForward()
659 history = self.IP.historyForward()
658 self.text_ctrl.writeHistory(history)
660 self.text_ctrl.writeHistory(history)
659 return
661 return
660 if event.KeyCode == wx.WXK_TAB:
662 if event.KeyCode == wx.WXK_TAB:
661 #if line empty we disable tab completion
663 #if line empty we disable tab completion
662 if not self.text_ctrl.getCurrentLine().strip():
664 if not self.text_ctrl.getCurrentLine().strip():
663 self.text_ctrl.write('\t')
665 self.text_ctrl.write('\t')
664 return
666 return
665 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
667 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
666 if len(possibilities) > 1:
668 if len(possibilities) > 1:
667 cur_slice = self.text_ctrl.getCurrentLine()
669 cur_slice = self.text_ctrl.getCurrentLine()
668 self.text_ctrl.write('\n')
670 self.text_ctrl.write('\n')
669 self.text_ctrl.writeCompletion(possibilities)
671 self.text_ctrl.writeCompletion(possibilities)
670 self.text_ctrl.write('\n')
672 self.text_ctrl.write('\n')
671
673
672 self.text_ctrl.showPrompt()
674 self.text_ctrl.showPrompt()
673 self.text_ctrl.write(cur_slice)
675 self.text_ctrl.write(cur_slice)
674 self.text_ctrl.changeLine(completed or cur_slice)
676 self.text_ctrl.changeLine(completed or cur_slice)
675
677
676 return
678 return
677 event.Skip()
679 event.Skip()
678
680
679 #------------------------ Hook Section -----------------------------------
681 #------------------------ Hook Section -----------------------------------
680 def updateHistoryTracker(self,command_line):
682 def updateHistoryTracker(self,command_line):
681 '''
683 '''
682 Default history tracker (does nothing)
684 Default history tracker (does nothing)
683 '''
685 '''
684 pass
686 pass
685
687
686 def setHistoryTrackerHook(self,func):
688 def setHistoryTrackerHook(self,func):
687 '''
689 '''
688 Define a new history tracker
690 Define a new history tracker
689 '''
691 '''
690 self.updateHistoryTracker = func
692 self.updateHistoryTracker = func
691
693
692 def updateStatusTracker(self,status):
694 def updateStatusTracker(self,status):
693 '''
695 '''
694 Default status tracker (does nothing)
696 Default status tracker (does nothing)
695 '''
697 '''
696 pass
698 pass
697
699
698 def setStatusTrackerHook(self,func):
700 def setStatusTrackerHook(self,func):
699 '''
701 '''
700 Define a new status tracker
702 Define a new status tracker
701 '''
703 '''
702 self.updateStatusTracker = func
704 self.updateStatusTracker = func
703
705
704 def askExitHandler(self, event):
706 def askExitHandler(self, event):
705 '''
707 '''
706 Default exit handler
708 Default exit handler
707 '''
709 '''
708 self.text_ctrl.write('\nExit callback has not been set.')
710 self.text_ctrl.write('\nExit callback has not been set.')
709
711
710 def setAskExitHandler(self, func):
712 def setAskExitHandler(self, func):
711 '''
713 '''
712 Define an exit handler
714 Define an exit handler
713 '''
715 '''
714 self.askExitHandler = func
716 self.askExitHandler = func
715
717
716 if __name__ == '__main__':
718 if __name__ == '__main__':
717 # Some simple code to test the shell widget.
719 # Some simple code to test the shell widget.
718 class MainWindow(wx.Frame):
720 class MainWindow(wx.Frame):
719 def __init__(self, parent, id, title):
721 def __init__(self, parent, id, title):
720 wx.Frame.__init__(self, parent, id, title, size=(300,250))
722 wx.Frame.__init__(self, parent, id, title, size=(300,250))
721 self._sizer = wx.BoxSizer(wx.VERTICAL)
723 self._sizer = wx.BoxSizer(wx.VERTICAL)
722 self.shell = IPShellWidget(self)
724 self.shell = IPShellWidget(self)
723 self._sizer.Add(self.shell, 1, wx.EXPAND)
725 self._sizer.Add(self.shell, 1, wx.EXPAND)
724 self.SetSizer(self._sizer)
726 self.SetSizer(self._sizer)
725 self.SetAutoLayout(1)
727 self.SetAutoLayout(1)
726 self.Show(True)
728 self.Show(True)
727
729
728 app = wx.PySimpleApp()
730 app = wx.PySimpleApp()
729 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
731 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
730 frame.SetSize((780, 460))
732 frame.SetSize((780, 460))
731 shell = frame.shell
733 shell = frame.shell
732
734
733 app.MainLoop()
735 app.MainLoop()
734
736
735
737
@@ -1,4 +1,4 b''
1 import os
1 import os
2 def oscmd(c):
2 def oscmd(c):
3 os.system(c)
3 os.system(c)
4 oscmd('sphinx-build -d build/doctrees source build/html') No newline at end of file
4 oscmd('sphinx-build -d build/doctrees source build/html')
General Comments 0
You need to be logged in to leave comments. Login now