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