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