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