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