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