##// END OF EJS Templates
Merged the cosmetic changes with the thread refactoring.
Gael Varoquaux -
r1097:f117697b merge
parent child Browse files
Show More
@@ -1,780 +1,780 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 os
33 import os
34 import locale
34 import locale
35 import time
35 import time
36 from StringIO import StringIO
36 from StringIO import StringIO
37 try:
37 try:
38 import IPython
38 import IPython
39 except Exception,e:
39 except Exception,e:
40 raise "Error importing IPython (%s)" % str(e)
40 raise "Error importing IPython (%s)" % str(e)
41
41
42
42
43 from non_blocking_ip_shell import *
43 from non_blocking_ip_shell import *
44
44
45 class WxNonBlockingIPShell(NonBlockingIPShell):
45 class WxNonBlockingIPShell(NonBlockingIPShell):
46 '''
46 '''
47 An NonBlockingIPShell Thread that is WX dependent.
47 An NonBlockingIPShell Thread that is WX dependent.
48 Thus it permits direct interaction with a WX GUI without OnIdle event state machine trick...
48 Thus it permits direct interaction with a WX GUI without OnIdle event state machine trick...
49 '''
49 '''
50 def __init__(self,wx_instance,
50 def __init__(self,wx_instance,
51 argv=[],user_ns={},user_global_ns=None,
51 argv=[],user_ns={},user_global_ns=None,
52 cin=None, cout=None, cerr=None,
52 cin=None, cout=None, cerr=None,
53 exit_handler=None,time_loop = 0.1):
53 ask_exit_handler=None):
54
54
55 user_ns['addGUIShortcut'] = self.addGUIShortcut
55 #user_ns['addGUIShortcut'] = self.addGUIShortcut
56 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
56 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
57 cin, cout, cerr,
57 cin, cout, cerr,
58 exit_handler,time_loop)
58 ask_exit_handler)
59
59
60 # This creates a new Event class and a EVT binder function
60 # This creates a new Event class and a EVT binder function
61 (self.IPythonAskExitEvent, EVT_IP_ASK_EXIT) = wx.lib.newevent.NewEvent()
61 (self.IPythonAskExitEvent, EVT_IP_ASK_EXIT) = wx.lib.newevent.NewEvent()
62 (self.IPythonAddButtonEvent, EVT_IP_ADD_BUTTON_EXIT) = wx.lib.newevent.NewEvent()
62 (self.IPythonAddButtonEvent, EVT_IP_ADD_BUTTON_EXIT) = \
63 (self.IPythonExecuteDoneEvent, EVT_IP_EXECUTE_DONE) = wx.lib.newevent.NewEvent()
63 wx.lib.newevent.NewEvent()
64 (self.IPythonExecuteDoneEvent, EVT_IP_EXECUTE_DONE) = \
65 wx.lib.newevent.NewEvent()
64
66
65 wx_instance.Bind(EVT_IP_ASK_EXIT, wx_instance.exit_handler)
67 wx_instance.Bind(EVT_IP_ASK_EXIT, wx_instance.ask_exit_handler)
66 wx_instance.Bind(EVT_IP_ADD_BUTTON_EXIT, wx_instance.add_button_handler)
68 wx_instance.Bind(EVT_IP_ADD_BUTTON_EXIT, wx_instance.add_button_handler)
67 wx_instance.Bind(EVT_IP_EXECUTE_DONE, wx_instance.evtStateExecuteDone)
69 wx_instance.Bind(EVT_IP_EXECUTE_DONE, wx_instance.evtStateExecuteDone)
68
70
69 self.wx_instance = wx_instance
71 self.wx_instance = wx_instance
70 self._IP.exit = self._AskExit
72 self._IP.ask_exit = self._askExit
71
73
72 def addGUIShortcut(self,text,func):
74 def addGUIShortcut(self,text,func):
73 evt = self.IPythonAddButtonEvent(button_info={'text':text,'func':self.wx_instance.doExecuteLine(func)})
75 evt = self.IPythonAddButtonEvent(
76 button_info={ 'text':text,
77 'func':self.wx_instance.doExecuteLine(func)})
74 wx.PostEvent(self.wx_instance, evt)
78 wx.PostEvent(self.wx_instance, evt)
75
79
76 def _AskExit(self):
80 def _askExit(self):
77 evt = self.IPythonAskExitEvent()
81 evt = self.IPythonAskExitEvent()
78 wx.PostEvent(self.wx_instance, evt)
82 wx.PostEvent(self.wx_instance, evt)
79
83
80 def _afterExecute(self):
84 def _afterExecute(self):
81 evt = self.IPythonExecuteDoneEvent()
85 evt = self.IPythonExecuteDoneEvent()
82 wx.PostEvent(self.wx_instance, evt)
86 wx.PostEvent(self.wx_instance, evt)
83
87
84
88
85 class WxConsoleView(stc.StyledTextCtrl):
89 class WxConsoleView(stc.StyledTextCtrl):
86 '''
90 '''
87 Specialized styled text control view for console-like workflow.
91 Specialized styled text control view for console-like workflow.
88 We use here a scintilla frontend thus it can be reused in any GUI taht supports
92 We use here a scintilla frontend thus it can be reused in any GUI taht supports
89 scintilla with less work.
93 scintilla with less work.
90
94
91 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.(with Black background)
95 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.(with Black background)
92 @type ANSI_COLORS_BLACK: dictionary
96 @type ANSI_COLORS_BLACK: dictionary
93
97
94 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.(with White background)
98 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.(with White background)
95 @type ANSI_COLORS_WHITE: dictionary
99 @type ANSI_COLORS_WHITE: dictionary
96
100
97 @ivar color_pat: Regex of terminal color pattern
101 @ivar color_pat: Regex of terminal color pattern
98 @type color_pat: _sre.SRE_Pattern
102 @type color_pat: _sre.SRE_Pattern
99 '''
103 '''
100 ANSI_STYLES_BLACK ={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
104 ANSI_STYLES_BLACK ={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
101 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
105 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
102 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
106 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
103 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
107 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
104 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
108 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
105 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
109 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
106 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
110 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
107 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
111 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
108
112
109 ANSI_STYLES_WHITE ={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
113 ANSI_STYLES_WHITE ={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
110 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
114 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
111 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
115 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
112 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
116 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
113 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
117 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
114 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
118 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
115 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
119 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
116 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
120 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
117
121
118 def __init__(self,parent,prompt,intro="",background_color="BLACK",pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
122 def __init__(self,parent,prompt,intro="",background_color="BLACK",pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
119 style=0):
123 style=0):
120 '''
124 '''
121 Initialize console view.
125 Initialize console view.
122
126
123 @param parent: Parent widget
127 @param parent: Parent widget
124 @param prompt: User specified prompt
128 @param prompt: User specified prompt
125 @type intro: string
129 @type intro: string
126 @param intro: User specified startup introduction string
130 @param intro: User specified startup introduction string
127 @type intro: string
131 @type intro: string
128 @param background_color: Can be BLACK or WHITE
132 @param background_color: Can be BLACK or WHITE
129 @type background_color: string
133 @type background_color: string
130 @param other: init param of styledTextControl (can be used as-is)
134 @param other: init param of styledTextControl (can be used as-is)
131 '''
135 '''
132 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
136 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
133
137
134 ####### Scintilla configuration ##################################################
138 ####### Scintilla configuration ##################################################
135
139
136 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside the widget
140 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside the widget
137 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
141 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
138 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
142 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
139
143
140 #we define platform specific fonts
144 #we define platform specific fonts
141 if wx.Platform == '__WXMSW__':
145 if wx.Platform == '__WXMSW__':
142 faces = { 'times': 'Times New Roman',
146 faces = { 'times': 'Times New Roman',
143 'mono' : 'Courier New',
147 'mono' : 'Courier New',
144 'helv' : 'Arial',
148 'helv' : 'Arial',
145 'other': 'Comic Sans MS',
149 'other': 'Comic Sans MS',
146 'size' : 10,
150 'size' : 10,
147 'size2': 8,
151 'size2': 8,
148 }
152 }
149 elif wx.Platform == '__WXMAC__':
153 elif wx.Platform == '__WXMAC__':
150 faces = { 'times': 'Times New Roman',
154 faces = { 'times': 'Times New Roman',
151 'mono' : 'Monaco',
155 'mono' : 'Monaco',
152 'helv' : 'Arial',
156 'helv' : 'Arial',
153 'other': 'Comic Sans MS',
157 'other': 'Comic Sans MS',
154 'size' : 10,
158 'size' : 10,
155 'size2': 8,
159 'size2': 8,
156 }
160 }
157 else:
161 else:
158 faces = { 'times': 'Times',
162 faces = { 'times': 'Times',
159 'mono' : 'Courier',
163 'mono' : 'Courier',
160 'helv' : 'Helvetica',
164 'helv' : 'Helvetica',
161 'other': 'new century schoolbook',
165 'other': 'new century schoolbook',
162 'size' : 10,
166 'size' : 10,
163 'size2': 8,
167 'size2': 8,
164 }
168 }
165
169
166 #We draw a line at position 80
170 #We draw a line at position 80
167 self.SetEdgeMode(stc.STC_EDGE_LINE)
171 self.SetEdgeMode(stc.STC_EDGE_LINE)
168 self.SetEdgeColumn(80)
172 self.SetEdgeColumn(80)
169 self.SetEdgeColour(wx.LIGHT_GREY)
173 self.SetEdgeColour(wx.LIGHT_GREY)
170
174
171 #self.SetViewWhiteSpace(True)
175 #self.SetViewWhiteSpace(True)
172 #self.SetViewEOL(True)
176 #self.SetViewEOL(True)
173 self.SetEOLMode(stc.STC_EOL_CRLF)
177 self.SetEOLMode(stc.STC_EOL_CRLF)
174 #self.SetWrapMode(stc.STC_WRAP_CHAR)
178 #self.SetWrapMode(stc.STC_WRAP_CHAR)
175 #self.SetWrapMode(stc.STC_WRAP_WORD)
179 #self.SetWrapMode(stc.STC_WRAP_WORD)
176 self.SetBufferedDraw(True)
180 self.SetBufferedDraw(True)
177 #self.SetUseAntiAliasing(True)
181 #self.SetUseAntiAliasing(True)
178 self.SetLayoutCache(stc.STC_CACHE_PAGE)
182 self.SetLayoutCache(stc.STC_CACHE_PAGE)
179
183
180 self.EnsureCaretVisible()
184 self.EnsureCaretVisible()
181
185
182 self.SetMargins(3,3) #text is moved away from border with 3px
186 self.SetMargins(3,3) #text is moved away from border with 3px
183 # Suppressing Scintilla margins
187 # Suppressing Scintilla margins
184 self.SetMarginWidth(0,0)
188 self.SetMarginWidth(0,0)
185 self.SetMarginWidth(1,0)
189 self.SetMarginWidth(1,0)
186 self.SetMarginWidth(2,0)
190 self.SetMarginWidth(2,0)
187
191
188 # make some styles
192 # make some styles
189 if background_color != "BLACK":
193 if background_color != "BLACK":
190 self.background_color = "WHITE"
194 self.background_color = "WHITE"
191 self.SetCaretForeground("BLACK")
195 self.SetCaretForeground("BLACK")
192 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
196 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
193 else:
197 else:
194 self.background_color = background_color
198 self.background_color = background_color
195 self.SetCaretForeground("WHITE")
199 self.SetCaretForeground("WHITE")
196 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
200 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
197
201
198 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s" % (self.ANSI_STYLES['0;30'][1],
202 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s" % (self.ANSI_STYLES['0;30'][1],
199 self.background_color,
203 self.background_color,
200 faces['size'], faces['mono']))
204 faces['size'], faces['mono']))
201 self.StyleClearAll()
205 self.StyleClearAll()
202 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FF0000,back:#0000FF,bold")
206 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FF0000,back:#0000FF,bold")
203 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
207 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
204
208
205 for style in self.ANSI_STYLES.values():
209 for style in self.ANSI_STYLES.values():
206 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
210 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
207
211
208 #######################################################################
212 #######################################################################
209
213
210 self.indent = 0
214 self.indent = 0
211 self.prompt_count = 0
215 self.prompt_count = 0
212 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
216 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
213
217
214 self.write(intro)
218 self.write(intro)
215 self.setPrompt(prompt)
219 self.setPrompt(prompt)
216 self.showPrompt()
220 self.showPrompt()
217
221
218 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
222 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
219 #self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
223 #self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
220
224
221 def write(self, text):
225 def write(self, text):
222 '''
226 '''
223 Write given text to buffer.
227 Write given text to buffer.
224
228
225 @param text: Text to append.
229 @param text: Text to append.
226 @type text: string
230 @type text: string
227 '''
231 '''
228 segments = self.color_pat.split(text)
232 segments = self.color_pat.split(text)
229 segment = segments.pop(0)
233 segment = segments.pop(0)
230 self.StartStyling(self.getCurrentLineEnd(),0xFF)
234 self.StartStyling(self.getCurrentLineEnd(),0xFF)
231 self.AppendText(segment)
235 self.AppendText(segment)
232
236
233 if segments:
237 if segments:
234 ansi_tags = self.color_pat.findall(text)
238 ansi_tags = self.color_pat.findall(text)
235
239
236 for tag in ansi_tags:
240 for tag in ansi_tags:
237 i = segments.index(tag)
241 i = segments.index(tag)
238 self.StartStyling(self.getCurrentLineEnd(),0xFF)
242 self.StartStyling(self.getCurrentLineEnd(),0xFF)
239 self.AppendText(segments[i+1])
243 self.AppendText(segments[i+1])
240
244
241 if tag != '0':
245 if tag != '0':
242 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
246 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
243
247
244 segments.pop(i)
248 segments.pop(i)
245
249
246 self.moveCursor(self.getCurrentLineEnd())
250 self.moveCursor(self.getCurrentLineEnd())
247
251
248 def getPromptLen(self):
252 def getPromptLen(self):
249 '''
253 '''
250 Return the length of current prompt
254 Return the length of current prompt
251 '''
255 '''
252 return len(str(self.prompt_count)) + 7
256 return len(str(self.prompt_count)) + 7
253
257
254 def setPrompt(self,prompt):
258 def setPrompt(self,prompt):
255 self.prompt = prompt
259 self.prompt = prompt
256
260
257 def setIndentation(self,indentation):
261 def setIndentation(self,indentation):
258 self.indent = indentation
262 self.indent = indentation
259
263
260 def setPromptCount(self,count):
264 def setPromptCount(self,count):
261 self.prompt_count = count
265 self.prompt_count = count
262
266
263 def showPrompt(self):
267 def showPrompt(self):
264 '''
268 '''
265 Prints prompt at start of line.
269 Prints prompt at start of line.
266
270
267 @param prompt: Prompt to print.
271 @param prompt: Prompt to print.
268 @type prompt: string
272 @type prompt: string
269 '''
273 '''
270 self.write(self.prompt)
274 self.write(self.prompt)
271 #now we update the position of end of prompt
275 #now we update the position of end of prompt
272 self.current_start = self.getCurrentLineEnd()
276 self.current_start = self.getCurrentLineEnd()
273
277
274 autoindent = self.indent*' '
278 autoindent = self.indent*' '
275 autoindent = autoindent.replace(' ','\t')
279 autoindent = autoindent.replace(' ','\t')
276 self.write(autoindent)
280 self.write(autoindent)
277
281
278 def changeLine(self, text):
282 def changeLine(self, text):
279 '''
283 '''
280 Replace currently entered command line with given text.
284 Replace currently entered command line with given text.
281
285
282 @param text: Text to use as replacement.
286 @param text: Text to use as replacement.
283 @type text: string
287 @type text: string
284 '''
288 '''
285 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
289 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
286 self.ReplaceSelection(text)
290 self.ReplaceSelection(text)
287 self.moveCursor(self.getCurrentLineEnd())
291 self.moveCursor(self.getCurrentLineEnd())
288
292
289 def getCurrentPromptStart(self):
293 def getCurrentPromptStart(self):
290 return self.current_start
294 return self.current_start
291
295
292 def getCurrentLineStart(self):
296 def getCurrentLineStart(self):
293 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
297 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
294
298
295 def getCurrentLineEnd(self):
299 def getCurrentLineEnd(self):
296 return self.GetLength()
300 return self.GetLength()
297
301
298 def getCurrentLine(self):
302 def getCurrentLine(self):
299 '''
303 '''
300 Get text in current command line.
304 Get text in current command line.
301
305
302 @return: Text of current command line.
306 @return: Text of current command line.
303 @rtype: string
307 @rtype: string
304 '''
308 '''
305 return self.GetTextRange(self.getCurrentPromptStart(),
309 return self.GetTextRange(self.getCurrentPromptStart(),
306 self.getCurrentLineEnd())
310 self.getCurrentLineEnd())
307
311
308 def showReturned(self, text):
312 def showReturned(self, text):
309 '''
313 '''
310 Show returned text from last command and print new prompt.
314 Show returned text from last command and print new prompt.
311
315
312 @param text: Text to show.
316 @param text: Text to show.
313 @type text: string
317 @type text: string
314 '''
318 '''
315 self.write('\n'+text)
319 self.write('\n'+text)
316 if text:
320 if text:
317 self.write('\n')
321 self.write('\n')
318 self.showPrompt()
322 self.showPrompt()
319
323
320 def moveCursorOnNewValidKey(self):
324 def moveCursorOnNewValidKey(self):
321 #If cursor is at wrong position put it at last line...
325 #If cursor is at wrong position put it at last line...
322 if self.GetCurrentPos() < self.getCurrentPromptStart():
326 if self.GetCurrentPos() < self.getCurrentPromptStart():
323 self.GotoPos(self.getCurrentPromptStart())
327 self.GotoPos(self.getCurrentPromptStart())
324
328
325 def removeFromTo(self,from_pos,to_pos):
329 def removeFromTo(self,from_pos,to_pos):
326 if from_pos < to_pos:
330 if from_pos < to_pos:
327 self.SetSelection(from_pos,to_pos)
331 self.SetSelection(from_pos,to_pos)
328 self.DeleteBack()
332 self.DeleteBack()
329
333
330 def removeCurrentLine(self):
334 def removeCurrentLine(self):
331 self.LineDelete()
335 self.LineDelete()
332
336
333 def moveCursor(self,position):
337 def moveCursor(self,position):
334 self.GotoPos(position)
338 self.GotoPos(position)
335
339
336 def getCursorPos(self):
340 def getCursorPos(self):
337 return self.GetCurrentPos()
341 return self.GetCurrentPos()
338
342
339 def selectFromTo(self,from_pos,to_pos):
343 def selectFromTo(self,from_pos,to_pos):
340 self.SetSelectionStart(from_pos)
344 self.SetSelectionStart(from_pos)
341 self.SetSelectionEnd(to_pos)
345 self.SetSelectionEnd(to_pos)
342
346
343 def writeHistory(self,history):
347 def writeHistory(self,history):
344 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
348 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
345 self.changeLine(history)
349 self.changeLine(history)
346
350
347 def writeCompletion(self, possibilities):
351 def writeCompletion(self, possibilities):
348 max_len = len(max(possibilities,key=len))
352 max_len = len(max(possibilities,key=len))
349 max_symbol =' '*max_len
353 max_symbol =' '*max_len
350
354
351 #now we check how much symbol we can put on a line...
355 #now we check how much symbol we can put on a line...
352 cursor_pos = self.getCursorPos()
356 cursor_pos = self.getCursorPos()
353 test_buffer = max_symbol + ' '*4
357 test_buffer = max_symbol + ' '*4
354 current_lines = self.GetLineCount()
358 current_lines = self.GetLineCount()
355
359
356 allowed_symbols = 80/len(test_buffer)
360 allowed_symbols = 80/len(test_buffer)
357 if allowed_symbols == 0:
361 if allowed_symbols == 0:
358 allowed_symbols = 1
362 allowed_symbols = 1
359
363
360 pos = 1
364 pos = 1
361 buf = ''
365 buf = ''
362 for symbol in possibilities:
366 for symbol in possibilities:
363 #buf += symbol+'\n'#*spaces)
367 #buf += symbol+'\n'#*spaces)
364 if pos<allowed_symbols:
368 if pos<allowed_symbols:
365 spaces = max_len - len(symbol) + 4
369 spaces = max_len - len(symbol) + 4
366 buf += symbol+' '*spaces
370 buf += symbol+' '*spaces
367 pos += 1
371 pos += 1
368 else:
372 else:
369 buf+=symbol+'\n'
373 buf+=symbol+'\n'
370 pos = 1
374 pos = 1
371 self.write(buf)
375 self.write(buf)
372
376
373 def _onKeypress(self, event, skip=True):
377 def _onKeypress(self, event, skip=True):
374 '''
378 '''
375 Key press callback used for correcting behavior for console-like
379 Key press callback used for correcting behavior for console-like
376 interfaces. For example 'home' should go to prompt, not to begining of
380 interfaces. For example 'home' should go to prompt, not to begining of
377 line.
381 line.
378
382
379 @param widget: Widget that key press accored in.
383 @param widget: Widget that key press accored in.
380 @type widget: gtk.Widget
384 @type widget: gtk.Widget
381 @param event: Event object
385 @param event: Event object
382 @type event: gtk.gdk.Event
386 @type event: gtk.gdk.Event
383
387
384 @return: Return True if event as been catched.
388 @return: Return True if event as been catched.
385 @rtype: boolean
389 @rtype: boolean
386 '''
390 '''
387
391
388 if event.GetKeyCode() == wx.WXK_HOME:
392 if event.GetKeyCode() == wx.WXK_HOME:
389 if event.Modifiers == wx.MOD_NONE:
393 if event.Modifiers == wx.MOD_NONE:
390 self.moveCursorOnNewValidKey()
394 self.moveCursorOnNewValidKey()
391 self.moveCursor(self.getCurrentPromptStart())
395 self.moveCursor(self.getCurrentPromptStart())
392 return True
396 return True
393 elif event.Modifiers == wx.MOD_SHIFT:
397 elif event.Modifiers == wx.MOD_SHIFT:
394 self.moveCursorOnNewValidKey()
398 self.moveCursorOnNewValidKey()
395 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
399 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
396 return True
400 return True
397 else:
401 else:
398 return False
402 return False
399
403
400 elif event.GetKeyCode() == wx.WXK_LEFT:
404 elif event.GetKeyCode() == wx.WXK_LEFT:
401 if event.Modifiers == wx.MOD_NONE:
405 if event.Modifiers == wx.MOD_NONE:
402 self.moveCursorOnNewValidKey()
406 self.moveCursorOnNewValidKey()
403
407
404 self.moveCursor(self.getCursorPos()-1)
408 self.moveCursor(self.getCursorPos()-1)
405 if self.getCursorPos() < self.getCurrentPromptStart():
409 if self.getCursorPos() < self.getCurrentPromptStart():
406 self.moveCursor(self.getCurrentPromptStart())
410 self.moveCursor(self.getCurrentPromptStart())
407 return True
411 return True
408
412
409 elif event.GetKeyCode() == wx.WXK_BACK:
413 elif event.GetKeyCode() == wx.WXK_BACK:
410 self.moveCursorOnNewValidKey()
414 self.moveCursorOnNewValidKey()
411 if self.getCursorPos() > self.getCurrentPromptStart():
415 if self.getCursorPos() > self.getCurrentPromptStart():
412 self.removeFromTo(self.getCursorPos()-1,self.getCursorPos())
416 self.removeFromTo(self.getCursorPos()-1,self.getCursorPos())
413 return True
417 return True
414
418
415 if skip:
419 if skip:
416 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
420 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
417 self.moveCursorOnNewValidKey()
421 self.moveCursorOnNewValidKey()
418
422
419 event.Skip()
423 event.Skip()
420 return True
424 return True
421 return False
425 return False
422
426
423 def OnUpdateUI(self, evt):
427 def OnUpdateUI(self, evt):
424 # check for matching braces
428 # check for matching braces
425 braceAtCaret = -1
429 braceAtCaret = -1
426 braceOpposite = -1
430 braceOpposite = -1
427 charBefore = None
431 charBefore = None
428 caretPos = self.GetCurrentPos()
432 caretPos = self.GetCurrentPos()
429
433
430 if caretPos > 0:
434 if caretPos > 0:
431 charBefore = self.GetCharAt(caretPos - 1)
435 charBefore = self.GetCharAt(caretPos - 1)
432 styleBefore = self.GetStyleAt(caretPos - 1)
436 styleBefore = self.GetStyleAt(caretPos - 1)
433
437
434 # check before
438 # check before
435 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
439 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
436 braceAtCaret = caretPos - 1
440 braceAtCaret = caretPos - 1
437
441
438 # check after
442 # check after
439 if braceAtCaret < 0:
443 if braceAtCaret < 0:
440 charAfter = self.GetCharAt(caretPos)
444 charAfter = self.GetCharAt(caretPos)
441 styleAfter = self.GetStyleAt(caretPos)
445 styleAfter = self.GetStyleAt(caretPos)
442
446
443 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
447 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
444 braceAtCaret = caretPos
448 braceAtCaret = caretPos
445
449
446 if braceAtCaret >= 0:
450 if braceAtCaret >= 0:
447 braceOpposite = self.BraceMatch(braceAtCaret)
451 braceOpposite = self.BraceMatch(braceAtCaret)
448
452
449 if braceAtCaret != -1 and braceOpposite == -1:
453 if braceAtCaret != -1 and braceOpposite == -1:
450 self.BraceBadLight(braceAtCaret)
454 self.BraceBadLight(braceAtCaret)
451 else:
455 else:
452 self.BraceHighlight(braceAtCaret, braceOpposite)
456 self.BraceHighlight(braceAtCaret, braceOpposite)
453 #pt = self.PointFromPosition(braceOpposite)
457 #pt = self.PointFromPosition(braceOpposite)
454 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
458 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
455 #print pt
459 #print pt
456 #self.Refresh(False)
460 #self.Refresh(False)
457
461
458 class WxIPythonViewPanel(wx.Panel):
462 class WxIPythonViewPanel(wx.Panel):
459 '''
463 '''
460 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
464 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
461 If you want to port this to any other GUI toolkit, just replace the WxConsoleView
465 If you want to port this to any other GUI toolkit, just replace the WxConsoleView
462 by YOURGUIConsoleView and make YOURGUIIPythonView derivate from whatever container you want.
466 by YOURGUIConsoleView and make YOURGUIIPythonView derivate from whatever container you want.
463 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
467 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
464 Any idea to make it more 'genric' welcomed.
468 Any idea to make it more 'genric' welcomed.
465 '''
469 '''
466 def __init__(self, parent, exit_handler=None, intro=None,
470
471 def __init__(self, parent, ask_exit_handler=None, intro=None,
467 background_color="BLACK", add_button_handler=None,
472 background_color="BLACK", add_button_handler=None,
468 wx_ip_shell=None,
473 wx_ip_shell=None,
469 ):
474 ):
470 '''
475 '''
471 Initialize.
476 Initialize.
472 Instanciate an IPython thread.
477 Instanciate an IPython thread.
473 Instanciate a WxConsoleView.
478 Instanciate a WxConsoleView.
474 Redirect I/O to console.
479 Redirect I/O to console.
475 '''
480 '''
476 wx.Panel.__init__(self,parent,-1)
481 wx.Panel.__init__(self,parent,-1)
477
482
478 ### IPython thread instanciation ###
483 ### IPython thread instanciation ###
479 self.cout = StringIO()
484 self.cout = StringIO()
480
485
481 self.add_button_handler = add_button_handler
486 self.add_button_handler = add_button_handler
482 self.exit_handler = exit_handler
487 self.ask_exit_handler = ask_exit_handler
483
488
484 if wx_ip_shell is not None:
489 if wx_ip_shell is not None:
485 self.IP = wx_ip_shell
490 self.IP = wx_ip_shell
486 else:
491 else:
487 self.IP = WxNonBlockingIPShell(self,
492 self.IP = WxNonBlockingIPShell(self,
488 cout=self.cout,cerr=self.cout,
493 cout=self.cout,cerr=self.cout,
489 exit_handler = exit_handler,
494 ask_exit_handler = ask_exit_handler)
490 time_loop = 0.1)
491 self.IP.start()
492
493 ### IPython wx console view instanciation ###
495 ### IPython wx console view instanciation ###
494 #If user didn't defined an intro text, we create one for him
496 #If user didn't defined an intro text, we create one for him
495 #If you really wnat an empty intrp just call wxIPythonViewPanel with intro=''
497 #If you really wnat an empty intrp just call wxIPythonViewPanel with intro=''
496 if intro == None:
498 if intro == None:
497 welcome_text = "Welcome to WxIPython Shell.\n\n"
499 welcome_text = "Welcome to WxIPython Shell.\n\n"
498 welcome_text+= self.IP.getBanner()
500 welcome_text+= self.IP.getBanner()
499 welcome_text+= "!command -> Execute command in shell\n"
501 welcome_text+= "!command -> Execute command in shell\n"
500 welcome_text+= "TAB -> Autocompletion\n"
502 welcome_text+= "TAB -> Autocompletion\n"
501
503
502 self.text_ctrl = WxConsoleView(self,
504 self.text_ctrl = WxConsoleView(self,
503 self.IP.getPrompt(),
505 self.IP.getPrompt(),
504 intro=welcome_text,
506 intro=welcome_text,
505 background_color=background_color)
507 background_color=background_color)
506
508
507 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
509 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
508
510
509 ### making the layout of the panel ###
511 ### making the layout of the panel ###
510 sizer = wx.BoxSizer(wx.VERTICAL)
512 sizer = wx.BoxSizer(wx.VERTICAL)
511 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
513 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
512 self.SetAutoLayout(True)
514 self.SetAutoLayout(True)
513 sizer.Fit(self)
515 sizer.Fit(self)
514 sizer.SetSizeHints(self)
516 sizer.SetSizeHints(self)
515 self.SetSizer(sizer)
517 self.SetSizer(sizer)
516 #and we focus on the widget :)
518 #and we focus on the widget :)
517 self.SetFocus()
519 self.SetFocus()
518
520
519 ### below are the thread communication variable ###
521 ### below are the thread communication variable ###
520 # the IPython thread is managed via unidirectional communication.
522 # the IPython thread is managed via unidirectional communication.
521 # It's a thread slave that can't interact by itself with the GUI.
523 # It's a thread slave that can't interact by itself with the GUI.
522 # When the GUI event loop is done runStateMachine() is called and the thread sate is then
524 # When the GUI event loop is done runStateMachine() is called and the thread sate is then
523 # managed.
525 # managed.
524
526
525 #Initialize the state machine #kept for information
527 #Initialize the state machine #kept for information
526 #self.states = ['IDLE',
528 #self.states = ['IDLE',
527 # 'DO_EXECUTE_LINE',
529 # 'DO_EXECUTE_LINE',
528 # 'WAIT_END_OF_EXECUTION',
530 # 'WAIT_END_OF_EXECUTION',
529 # 'SHOW_DOC',
531 # 'SHOW_DOC',
530 # 'SHOW_PROMPT']
532 # 'SHOW_PROMPT']
531
533
532 self.cur_state = 'IDLE'
534 self.cur_state = 'IDLE'
533 self.pager_state = 'DONE'
535 self.pager_state = 'DONE'
534 #wx.CallAfter(self.runStateMachine)
536 #wx.CallAfter(self.runStateMachine)
535
537
536 # This creates a new Event class and a EVT binder function
538 # This creates a new Event class and a EVT binder function
537 #(self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
539 #(self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
538 #(self.AddButtonEvent, EVT_ADDBUTTON_EXIT) = wx.lib.newevent.NewEvent()
540 #(self.AddButtonEvent, EVT_ADDBUTTON_EXIT) = wx.lib.newevent.NewEvent()
539
541
540
542
541 #self.Bind(wx.EVT_IDLE, self.runStateMachine)
543 #self.Bind(wx.EVT_IDLE, self.runStateMachine)
542
544
543 def __del__(self):
545 def __del__(self):
544 self.IP.shutdown()
545 self.IP.join()
546 WxConsoleView.__del__()
546 WxConsoleView.__del__()
547
547
548 #---------------------------- IPython Thread Management ---------------------------------------
548 #---------------------------- IPython Thread Management ---------------------------------------
549 def stateDoExecuteLine(self):
549 def stateDoExecuteLine(self):
550 #print >>sys.__stdout__,"command:",self.getCurrentLine()
550 #print >>sys.__stdout__,"command:",self.getCurrentLine()
551 self.doExecuteLine(self.text_ctrl.getCurrentLine())
551 self.doExecuteLine(self.text_ctrl.getCurrentLine())
552
552
553 def doExecuteLine(self,line):
553 def doExecuteLine(self,line):
554 #print >>sys.__stdout__,"command:",line
554 #print >>sys.__stdout__,"command:",line
555 self.IP.doExecute(line.replace('\t',' '*4))
555 self.IP.doExecute(line.replace('\t',' '*4))
556 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
556 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
557 self.cur_state = 'WAIT_END_OF_EXECUTION'
557 self.cur_state = 'WAIT_END_OF_EXECUTION'
558
558
559
559
560 def evtStateExecuteDone(self,evt):
560 def evtStateExecuteDone(self,evt):
561 self.doc = self.IP.getDocText()
561 self.doc = self.IP.getDocText()
562 self.help = self.IP.getHelpText()
562 self.help = self.IP.getHelpText()
563 if self.doc:
563 if self.doc:
564 self.pager_state = 'INIT'
564 self.pager_state = 'INIT'
565 self.cur_state = 'SHOW_DOC'
565 self.cur_state = 'SHOW_DOC'
566 self.pager(self.doc)
566 self.pager(self.doc)
567 #if self.pager_state == 'DONE':
567 #if self.pager_state == 'DONE':
568 if self.help:
568 if self.help:
569 self.pager_state = 'INIT_HELP'
569 self.pager_state = 'INIT_HELP'
570 self.cur_state = 'SHOW_DOC'
570 self.cur_state = 'SHOW_DOC'
571 self.pager(self.help)
571 self.pager(self.help)
572
572
573 else:
573 else:
574 self.stateShowPrompt()
574 self.stateShowPrompt()
575
575
576 def stateShowPrompt(self):
576 def stateShowPrompt(self):
577 self.cur_state = 'SHOW_PROMPT'
577 self.cur_state = 'SHOW_PROMPT'
578 self.text_ctrl.setPrompt(self.IP.getPrompt())
578 self.text_ctrl.setPrompt(self.IP.getPrompt())
579 self.text_ctrl.setIndentation(self.IP.getIndentation())
579 self.text_ctrl.setIndentation(self.IP.getIndentation())
580 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
580 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
581 rv = self.cout.getvalue()
581 rv = self.cout.getvalue()
582 if rv: rv = rv.strip('\n')
582 if rv: rv = rv.strip('\n')
583 self.text_ctrl.showReturned(rv)
583 self.text_ctrl.showReturned(rv)
584 self.cout.truncate(0)
584 self.cout.truncate(0)
585 self.IP.initHistoryIndex()
585 self.IP.initHistoryIndex()
586 self.cur_state = 'IDLE'
586 self.cur_state = 'IDLE'
587
587
588 ## def runStateMachine(self,event):
588 ## def runStateMachine(self,event):
589 ## #print >>sys.__stdout__,"state:",self.cur_state
589 ## #print >>sys.__stdout__,"state:",self.cur_state
590 ## self.updateStatusTracker(self.cur_state)
590 ## self.updateStatusTracker(self.cur_state)
591 ##
591 ##
592 ## #if self.cur_state == 'DO_EXECUTE_LINE':
592 ## #if self.cur_state == 'DO_EXECUTE_LINE':
593 ## # self.doExecuteLine()
593 ## # self.doExecuteLine()
594 ##
594 ##
595 ## if self.cur_state == 'WAIT_END_OF_EXECUTION':
595 ## if self.cur_state == 'WAIT_END_OF_EXECUTION':
596 ## if self.IP.isExecuteDone():
596 ## if self.IP.isExecuteDone():
597 ## #self.button = self.IP.getAddButton()
597 ## #self.button = self.IP.getAddButton()
598 ## #if self.IP.getAskExit():
598 ## #if self.IP.getAskExit():
599 ## # evt = self.AskExitEvent()
599 ## # evt = self.AskExitEvent()
600 ## # wx.PostEvent(self, evt)
600 ## # wx.PostEvent(self, evt)
601 ## # self.IP.clearAskExit()
601 ## # self.IP.clearAskExit()
602 ## self.doc = self.IP.getDocText()
602 ## self.doc = self.IP.getDocText()
603 ## if self.doc:
603 ## if self.doc:
604 ## self.pager_state = 'INIT'
604 ## self.pager_state = 'INIT'
605 ## self.cur_state = 'SHOW_DOC'
605 ## self.cur_state = 'SHOW_DOC'
606 ## #if self.button:
606 ## #if self.button:
607 ## #self.IP.doExecute('print "cool"')#self.button['func'])
607 ## #self.IP.doExecute('print "cool"')#self.button['func'])
608 ## #self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
608 ## #self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
609 ##
609 ##
610 ## # self.button['func']='print "cool!"'
610 ## # self.button['func']='print "cool!"'
611 ## # self.add_button_handler(self.button)
611 ## # self.add_button_handler(self.button)
612 ## # self.IP.shortcutProcessed()
612 ## # self.IP.shortcutProcessed()
613 ##
613 ##
614 ## else:
614 ## else:
615 ## self.cur_state = 'SHOW_PROMPT'
615 ## self.cur_state = 'SHOW_PROMPT'
616 ##
616 ##
617 ## if self.cur_state == 'SHOW_PROMPT':
617 ## if self.cur_state == 'SHOW_PROMPT':
618 ## self.text_ctrl.setPrompt(self.IP.getPrompt())
618 ## self.text_ctrl.setPrompt(self.IP.getPrompt())
619 ## self.text_ctrl.setIndentation(self.IP.getIndentation())
619 ## self.text_ctrl.setIndentation(self.IP.getIndentation())
620 ## self.text_ctrl.setPromptCount(self.IP.getPromptCount())
620 ## self.text_ctrl.setPromptCount(self.IP.getPromptCount())
621 ## rv = self.cout.getvalue()
621 ## rv = self.cout.getvalue()
622 ## if rv: rv = rv.strip('\n')
622 ## if rv: rv = rv.strip('\n')
623 ## self.text_ctrl.showReturned(rv)
623 ## self.text_ctrl.showReturned(rv)
624 ## self.cout.truncate(0)
624 ## self.cout.truncate(0)
625 ## self.IP.initHistoryIndex()
625 ## self.IP.initHistoryIndex()
626 ## self.cur_state = 'IDLE'
626 ## self.cur_state = 'IDLE'
627 ##
627 ##
628 ## if self.cur_state == 'SHOW_DOC':
628 ## if self.cur_state == 'SHOW_DOC':
629 ## self.pager(self.doc)
629 ## self.pager(self.doc)
630 ## if self.pager_state == 'DONE':
630 ## if self.pager_state == 'DONE':
631 ## self.cur_state = 'SHOW_PROMPT'
631 ## self.cur_state = 'SHOW_PROMPT'
632 ##
632 ##
633 ## event.Skip()
633 ## event.Skip()
634
634
635 #---------------------------- IPython pager ---------------------------------------
635 #---------------------------- IPython pager ---------------------------------------
636 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
636 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
637 if self.pager_state == 'WAITING':
637 if self.pager_state == 'WAITING':
638 #print >>sys.__stdout__,"PAGER waiting"
638 #print >>sys.__stdout__,"PAGER waiting"
639 return
639 return
640
640
641 if self.pager_state == 'INIT':
641 if self.pager_state == 'INIT':
642 #print >>sys.__stdout__,"PAGER state:",self.pager_state
642 #print >>sys.__stdout__,"PAGER state:",self.pager_state
643 self.pager_lines = text[7:].split('\n')
643 self.pager_lines = text[7:].split('\n')
644 self.pager_nb_lines = len(self.pager_lines)
644 self.pager_nb_lines = len(self.pager_lines)
645 self.pager_index = 0
645 self.pager_index = 0
646 self.pager_do_remove = False
646 self.pager_do_remove = False
647 self.text_ctrl.write('\n')
647 self.text_ctrl.write('\n')
648 self.pager_state = 'PROCESS_LINES'
648 self.pager_state = 'PROCESS_LINES'
649
649
650 if self.pager_state == 'INIT_HELP':
650 if self.pager_state == 'INIT_HELP':
651 #print >>sys.__stdout__,"HELP PAGER state:",self.pager_state
651 #print >>sys.__stdout__,"HELP PAGER state:",self.pager_state
652 self.pager_lines = text[:].split('\n')
652 self.pager_lines = text[:].split('\n')
653 self.pager_nb_lines = len(self.pager_lines)
653 self.pager_nb_lines = len(self.pager_lines)
654 self.pager_index = 0
654 self.pager_index = 0
655 self.pager_do_remove = False
655 self.pager_do_remove = False
656 self.text_ctrl.write('\n')
656 self.text_ctrl.write('\n')
657 self.pager_state = 'PROCESS_LINES'
657 self.pager_state = 'PROCESS_LINES'
658
658
659 if self.pager_state == 'PROCESS_LINES':
659 if self.pager_state == 'PROCESS_LINES':
660 #print >>sys.__stdout__,"PAGER state:",self.pager_state
660 #print >>sys.__stdout__,"PAGER state:",self.pager_state
661 if self.pager_do_remove == True:
661 if self.pager_do_remove == True:
662 self.text_ctrl.removeCurrentLine()
662 self.text_ctrl.removeCurrentLine()
663 self.pager_do_remove = False
663 self.pager_do_remove = False
664
664
665 if self.pager_nb_lines > 10:
665 if self.pager_nb_lines > 10:
666 #print >>sys.__stdout__,"PAGER processing 10 lines"
666 #print >>sys.__stdout__,"PAGER processing 10 lines"
667 if self.pager_index > 0:
667 if self.pager_index > 0:
668 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')
669 else:
669 else:
670 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
670 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
671
671
672 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
672 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
673 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
673 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
674 self.pager_index += 10
674 self.pager_index += 10
675 self.pager_nb_lines -= 10
675 self.pager_nb_lines -= 10
676 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
676 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
677 self.pager_do_remove = True
677 self.pager_do_remove = True
678 self.pager_state = 'WAITING'
678 self.pager_state = 'WAITING'
679 return
679 return
680 else:
680 else:
681 #print >>sys.__stdout__,"PAGER processing last lines"
681 #print >>sys.__stdout__,"PAGER processing last lines"
682 if self.pager_nb_lines > 0:
682 if self.pager_nb_lines > 0:
683 if self.pager_index > 0:
683 if self.pager_index > 0:
684 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')
685 else:
685 else:
686 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
686 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
687
687
688 self.pager_index += 1
688 self.pager_index += 1
689 self.pager_nb_lines -= 1
689 self.pager_nb_lines -= 1
690 if self.pager_nb_lines > 0:
690 if self.pager_nb_lines > 0:
691 for line in self.pager_lines[self.pager_index:]:
691 for line in self.pager_lines[self.pager_index:]:
692 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
692 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
693 self.pager_nb_lines = 0
693 self.pager_nb_lines = 0
694 self.pager_state = 'DONE'
694 self.pager_state = 'DONE'
695 self.stateShowPrompt()
695 self.stateShowPrompt()
696
696
697 #---------------------------- Key Handler --------------------------------------------
697 #---------------------------- Key Handler --------------------------------------------
698 def keyPress(self, event):
698 def keyPress(self, event):
699 '''
699 '''
700 Key press callback with plenty of shell goodness, like history,
700 Key press callback with plenty of shell goodness, like history,
701 autocompletions, etc.
701 autocompletions, etc.
702 '''
702 '''
703
703
704 if event.GetKeyCode() == ord('C'):
704 if event.GetKeyCode() == ord('C'):
705 if event.Modifiers == wx.MOD_CONTROL:
705 if event.Modifiers == wx.MOD_CONTROL:
706 if self.cur_state == 'WAIT_END_OF_EXECUTION':
706 if self.cur_state == 'WAIT_END_OF_EXECUTION':
707 #we raise an exception inside the IPython thread container
707 #we raise an exception inside the IPython thread container
708 self.IP.raise_exc(KeyboardInterrupt)
708 self.IP.ce.raise_exc(KeyboardInterrupt)
709 return
709 return
710
710
711 if event.KeyCode == wx.WXK_RETURN:
711 if event.KeyCode == wx.WXK_RETURN:
712 if self.cur_state == 'IDLE':
712 if self.cur_state == 'IDLE':
713 #we change the state ot the state machine
713 #we change the state ot the state machine
714 self.cur_state = 'DO_EXECUTE_LINE'
714 self.cur_state = 'DO_EXECUTE_LINE'
715 self.stateDoExecuteLine()
715 self.stateDoExecuteLine()
716 return
716 return
717 if self.pager_state == 'WAITING':
717 if self.pager_state == 'WAITING':
718 self.pager_state = 'PROCESS_LINES'
718 self.pager_state = 'PROCESS_LINES'
719 self.pager(self.doc)
719 self.pager(self.doc)
720 return
720 return
721
721
722 if event.GetKeyCode() in [ord('q'),ord('Q')]:
722 if event.GetKeyCode() in [ord('q'),ord('Q')]:
723 if self.pager_state == 'WAITING':
723 if self.pager_state == 'WAITING':
724 self.pager_state = 'DONE'
724 self.pager_state = 'DONE'
725 self.stateShowPrompt()
725 self.stateShowPrompt()
726 return
726 return
727
727
728 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
728 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
729 if self.cur_state == 'IDLE':
729 if self.cur_state == 'IDLE':
730 if event.KeyCode == wx.WXK_UP:
730 if event.KeyCode == wx.WXK_UP:
731 history = self.IP.historyBack()
731 history = self.IP.historyBack()
732 self.text_ctrl.writeHistory(history)
732 self.text_ctrl.writeHistory(history)
733 return
733 return
734 if event.KeyCode == wx.WXK_DOWN:
734 if event.KeyCode == wx.WXK_DOWN:
735 history = self.IP.historyForward()
735 history = self.IP.historyForward()
736 self.text_ctrl.writeHistory(history)
736 self.text_ctrl.writeHistory(history)
737 return
737 return
738 if event.KeyCode == wx.WXK_TAB:
738 if event.KeyCode == wx.WXK_TAB:
739 #if line empty we disable tab completion
739 #if line empty we disable tab completion
740 if not self.text_ctrl.getCurrentLine().strip():
740 if not self.text_ctrl.getCurrentLine().strip():
741 self.text_ctrl.write('\t')
741 self.text_ctrl.write('\t')
742 return
742 return
743 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
743 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
744 if len(possibilities) > 1:
744 if len(possibilities) > 1:
745 cur_slice = self.text_ctrl.getCurrentLine()
745 cur_slice = self.text_ctrl.getCurrentLine()
746 self.text_ctrl.write('\n')
746 self.text_ctrl.write('\n')
747 self.text_ctrl.writeCompletion(possibilities)
747 self.text_ctrl.writeCompletion(possibilities)
748 self.text_ctrl.write('\n')
748 self.text_ctrl.write('\n')
749
749
750 self.text_ctrl.showPrompt()
750 self.text_ctrl.showPrompt()
751 self.text_ctrl.write(cur_slice)
751 self.text_ctrl.write(cur_slice)
752 self.text_ctrl.changeLine(completed or cur_slice)
752 self.text_ctrl.changeLine(completed or cur_slice)
753
753
754 return
754 return
755 event.Skip()
755 event.Skip()
756
756
757 #---------------------------- Hook Section --------------------------------------------
757 #---------------------------- Hook Section --------------------------------------------
758 def updateHistoryTracker(self,command_line):
758 def updateHistoryTracker(self,command_line):
759 '''
759 '''
760 Default history tracker (does nothing)
760 Default history tracker (does nothing)
761 '''
761 '''
762 pass
762 pass
763
763
764 def setHistoryTrackerHook(self,func):
764 def setHistoryTrackerHook(self,func):
765 '''
765 '''
766 Define a new history tracker
766 Define a new history tracker
767 '''
767 '''
768 self.updateHistoryTracker = func
768 self.updateHistoryTracker = func
769 def updateStatusTracker(self,status):
769 def updateStatusTracker(self,status):
770 '''
770 '''
771 Default status tracker (does nothing)
771 Default status tracker (does nothing)
772 '''
772 '''
773 pass
773 pass
774
774
775 def setStatusTrackerHook(self,func):
775 def setStatusTrackerHook(self,func):
776 '''
776 '''
777 Define a new status tracker
777 Define a new status tracker
778 '''
778 '''
779 self.updateStatusTracker = func
779 self.updateStatusTracker = func
780
780
@@ -1,473 +1,473 b''
1 #!/usr/bin/python
1 #!/usr/bin/python
2 # -*- coding: iso-8859-15 -*-
2 # -*- coding: iso-8859-15 -*-
3 '''
3 '''
4 Provides IPython remote instance.
4 Provides IPython remote instance.
5
5
6 @author: Laurent Dufrechou
6 @author: Laurent Dufrechou
7 laurent.dufrechou _at_ gmail.com
7 laurent.dufrechou _at_ gmail.com
8 @license: BSD
8 @license: BSD
9
9
10 All rights reserved. This program and the accompanying materials are made
10 All rights reserved. This program and the accompanying materials are made
11 available under the terms of the BSD which accompanies this distribution, and
11 available under the terms of the BSD which accompanies this distribution, and
12 is available at U{http://www.opensource.org/licenses/bsd-license.php}
12 is available at U{http://www.opensource.org/licenses/bsd-license.php}
13 '''
13 '''
14
14
15 __version__ = 0.9
15 __version__ = 0.9
16 __author__ = "Laurent Dufrechou"
16 __author__ = "Laurent Dufrechou"
17 __email__ = "laurent.dufrechou _at_ gmail.com"
17 __email__ = "laurent.dufrechou _at_ gmail.com"
18 __license__ = "BSD"
18 __license__ = "BSD"
19
19
20 import re
20 import re
21 import sys
21 import sys
22 import os
22 import os
23 import locale
23 import locale
24 import time
24 import time
25 import pydoc,__builtin__,site
25 import pydoc,__builtin__,site
26 from thread_ex import ThreadEx
26 from thread_ex import ThreadEx
27 from StringIO import StringIO
27 from StringIO import StringIO
28
28
29 try:
29 try:
30 import IPython
30 import IPython
31 except Exception,e:
31 except Exception,e:
32 raise "Error importing IPython (%s)" % str(e)
32 raise "Error importing IPython (%s)" % str(e)
33
33
34 ##############################################################################
34 class _Helper(object):
35 class _Helper(object):
35 """Redefine the built-in 'help'.
36 """Redefine the built-in 'help'.
36 This is a wrapper around pydoc.help (with a twist).
37 This is a wrapper around pydoc.help (with a twist).
37 """
38 """
39
38 def __init__(self,pager):
40 def __init__(self,pager):
39 self._pager = pager
41 self._pager = pager
40
42
41 def __repr__(self):
43 def __repr__(self):
42 return "Type help() for interactive help, " \
44 return "Type help() for interactive help, " \
43 "or help(object) for help about object."
45 "or help(object) for help about object."
46
44 def __call__(self, *args, **kwds):
47 def __call__(self, *args, **kwds):
45 class DummyWriter(object):
48 class DummyWriter(object):
46 def __init__(self,pager):
49 def __init__(self,pager):
47 self._pager = pager
50 self._pager = pager
48
51
49 def write(self,data):
52 def write(self,data):
50 self._pager(data)
53 self._pager(data)
51
54
52 import pydoc
55 import pydoc
53 pydoc.help.output = DummyWriter(self._pager)
56 pydoc.help.output = DummyWriter(self._pager)
54 pydoc.help.interact = lambda :1
57 pydoc.help.interact = lambda :1
55
58
56 #helper.output.write = self.doc.append
59 #helper.output.write = self.doc.append
57 return pydoc.help(*args, **kwds)
60 return pydoc.help(*args, **kwds)
58
61
59
62
60 class NonBlockingIPShell(ThreadEx):
63 ##############################################################################
64 class _CodeExecutor(ThreadEx):
65
66 def __init__(self, instance, after):
67 ThreadEx.__init__(self)
68 self.instance = instance
69 self._afterExecute=after
70
71 def run(self):
72 try:
73 self.instance._doc_text = None
74 self.instance._help_text = None
75 self.instance._execute()
76 # used for uper class to generate event after execution
77 self._afterExecute()
78
79 except KeyboardInterrupt:
80 pass
81
82
83 ##############################################################################
84 class NonBlockingIPShell(object):
61 '''
85 '''
62 Create an IPython instance inside a dedicated thread.
86 Create an IPython instance, running the commands in a separate,
63 Does not start a blocking event loop, instead allow single iterations.
87 non-blocking thread.
64 This allows embedding in any GUI without blockage.
88 This allows embedding in any GUI without blockage.
65 The thread is a slave one, in that it doesn't interact directly with the GUI.
89
66 Note ThreadEx class supports asynchroneous function call
90 Note: The ThreadEx class supports asynchroneous function call
67 via raise_exc()
91 via raise_exc()
68 '''
92 '''
69
93
70 def __init__(self,argv
94 def __init__(self,argv
71 =[],user_ns={},user_global_ns=None,
95 =[],user_ns={},user_global_ns=None,
72 cin=None, cout=None, cerr=None,
96 cin=None, cout=None, cerr=None,
73 exit_handler=None,time_loop = 0.1):
97 ask_exit_handler=None):
74 '''
98 '''
75 @param argv: Command line options for IPython
99 @param argv: Command line options for IPython
76 @type argv: list
100 @type argv: list
77 @param user_ns: User namespace.
101 @param user_ns: User namespace.
78 @type user_ns: dictionary
102 @type user_ns: dictionary
79 @param user_global_ns: User global namespace.
103 @param user_global_ns: User global namespace.
80 @type user_global_ns: dictionary.
104 @type user_global_ns: dictionary.
81 @param cin: Console standard input.
105 @param cin: Console standard input.
82 @type cin: IO stream
106 @type cin: IO stream
83 @param cout: Console standard output.
107 @param cout: Console standard output.
84 @type cout: IO stream
108 @type cout: IO stream
85 @param cerr: Console standard error.
109 @param cerr: Console standard error.
86 @type cerr: IO stream
110 @type cerr: IO stream
87 @param exit_handler: Replacement for builtin exit() function
111 @param exit_handler: Replacement for builtin exit() function
88 @type exit_handler: function
112 @type exit_handler: function
89 @param time_loop: Define the sleep time between two thread's loop
113 @param time_loop: Define the sleep time between two thread's loop
90 @type int
114 @type int
91 '''
115 '''
92 ThreadEx.__init__(self)
93
94 #first we redefine in/out/error functions of IPython
116 #first we redefine in/out/error functions of IPython
95 if cin:
117 if cin:
96 IPython.Shell.Term.cin = cin
118 IPython.Shell.Term.cin = cin
97 if cout:
119 if cout:
98 IPython.Shell.Term.cout = cout
120 IPython.Shell.Term.cout = cout
99 if cerr:
121 if cerr:
100 IPython.Shell.Term.cerr = cerr
122 IPython.Shell.Term.cerr = cerr
101
123
102 # This is to get rid of the blockage that accurs during
124 # This is to get rid of the blockage that accurs during
103 # IPython.Shell.InteractiveShell.user_setup()
125 # IPython.Shell.InteractiveShell.user_setup()
104 IPython.iplib.raw_input = lambda x: None
126 IPython.iplib.raw_input = lambda x: None
105
127
106 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
128 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
107
129
108 excepthook = sys.excepthook
130 excepthook = sys.excepthook
109
131
110 self._IP = IPython.Shell.make_IPython(
132 self._IP = IPython.Shell.make_IPython(
111 argv,user_ns=user_ns,
133 argv,user_ns=user_ns,
112 user_global_ns=user_global_ns,
134 user_global_ns=user_global_ns,
113 embedded=True,
135 embedded=True,
114 shell_class=IPython.Shell.InteractiveShell)
136 shell_class=IPython.Shell.InteractiveShell)
115
137
116 #we replace IPython default encoding by wx locale encoding
138 #we replace IPython default encoding by wx locale encoding
117 loc = locale.getpreferredencoding()
139 loc = locale.getpreferredencoding()
118 if loc:
140 if loc:
119 self._IP.stdin_encoding = loc
141 self._IP.stdin_encoding = loc
120 #we replace the ipython default pager by our pager
142 #we replace the ipython default pager by our pager
121 self._IP.set_hook('show_in_pager',self._pager)
143 self._IP.set_hook('show_in_pager',self._pager)
122
144
123 #we replace the ipython default shell command caller by our shell handler
145 #we replace the ipython default shell command caller by our shell handler
124 self._IP.set_hook('shell_hook',self._shell)
146 self._IP.set_hook('shell_hook',self._shell)
125
147
126 #we replace the ipython default input command caller by our method
148 #we replace the ipython default input command caller by our method
127 IPython.iplib.raw_input_original = self._raw_input
149 IPython.iplib.raw_input_original = self._raw_input
128 #we replace the ipython default exit command by our method
150 #we replace the ipython default exit command by our method
129 self._IP.exit = self._setAskExit
151 self._IP.exit = self._setAskExit
152 #we modify Exit and Quit Magic
153 ip = IPython.ipapi.get()
154 ip.expose_magic('Exit', self._setDoExit)
155 ip.expose_magic('Quit', self._setDoExit)
130
156
131 sys.excepthook = excepthook
157 sys.excepthook = excepthook
132
158
133 self._iter_more = 0
159 self._iter_more = 0
134 self._history_level = 0
160 self._history_level = 0
135 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
161 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
136 self._prompt = str(self._IP.outputcache.prompt1).strip()
162 self._prompt = str(self._IP.outputcache.prompt1).strip()
137
163
138 #thread working vars
164 #thread working vars
139 self._terminate = False
140 self._time_loop = time_loop
141 self._do_execute = False
142 self._line_to_execute = ''
165 self._line_to_execute = ''
143
166
144 #vars that will be checked by GUI loop to handle thread states...
167 #vars that will be checked by GUI loop to handle thread states...
145 #will be replaced later by PostEvent GUI funtions...
168 #will be replaced later by PostEvent GUI funtions...
146 self._doc_text = None
169 self._doc_text = None
147 self._help_text = None
170 self._help_text = None
148 self._ask_exit = False
171 self._ask_exit = False
149 self._add_button = None
172 self._add_button = None
150
173
151 #we replace the help command
174 #we replace the help command
152 self._IP.user_ns['help'] = _Helper(self._pager_help)
175 self._IP.user_ns['help'] = _Helper(self._pager_help)
153
176
154 #----------------------- Thread management section ----------------------
177 #----------------------- Thread management section ----------------------
155 def run (self):
156 """
157 Thread main loop
158 The thread will run until self._terminate will be set to True via shutdown() function
159 Command processing can be interrupted with Instance.raise_exc(KeyboardInterrupt) call in the
160 GUI thread.
161 """
162 while(not self._terminate):
163 try:
164 if self._do_execute:
165 self._doc_text = None
166 self._help_text = None
167 self._execute()
168 self._do_execute = False
169 self._afterExecute() #used for uper class to generate event after execution
170
171 except KeyboardInterrupt:
172 pass
173
174 time.sleep(self._time_loop)
175
176 def shutdown(self):
177 """
178 Shutdown the tread
179 """
180 self._terminate = True
181
182 def doExecute(self,line):
178 def doExecute(self,line):
183 """
179 """
184 Tell the thread to process the 'line' command
180 Tell the thread to process the 'line' command
185 """
181 """
186 self._do_execute = True
182
187 self._line_to_execute = line
183 self._line_to_execute = line
188
184
189 def isExecuteDone(self):
185 self.ce = _CodeExecutor(self,self._afterExecute)
190 """
186 self.ce.start()
191 Returns the processing state
192 """
193 return not self._do_execute
194
187
195 #----------------------- IPython management section ----------------------
188 #----------------------- IPython management section ----------------------
196 def getAskExit(self):
189 def getAskExit(self):
197 '''
190 '''
198 returns the _ask_exit variable that can be checked by GUI to see if
191 returns the _ask_exit variable that can be checked by GUI to see if
199 IPython request an exit handling
192 IPython request an exit handling
200 '''
193 '''
201 return self._ask_exit
194 return self._ask_exit
202
195
203 def clearAskExit(self):
196 def clearAskExit(self):
204 '''
197 '''
205 clear the _ask_exit var when GUI as handled the request.
198 clear the _ask_exit var when GUI as handled the request.
206 '''
199 '''
207 self._ask_exit = False
200 self._ask_exit = False
208
201
209 def getDocText(self):
202 def getDocText(self):
210 """
203 """
211 Returns the output of the processing that need to be paged (if any)
204 Returns the output of the processing that need to be paged (if any)
212
205
213 @return: The std output string.
206 @return: The std output string.
214 @rtype: string
207 @rtype: string
215 """
208 """
216 return self._doc_text
209 return self._doc_text
217
210
218 def getHelpText(self):
211 def getHelpText(self):
219 """
212 """
220 Returns the output of the processing that need to be paged via help pager(if any)
213 Returns the output of the processing that need to be paged via help pager(if any)
221
214
222 @return: The std output string.
215 @return: The std output string.
223 @rtype: string
216 @rtype: string
224 """
217 """
225 return self._help_text
218 return self._help_text
226
219
227 def getBanner(self):
220 def getBanner(self):
228 """
221 """
229 Returns the IPython banner for useful info on IPython instance
222 Returns the IPython banner for useful info on IPython instance
230
223
231 @return: The banner string.
224 @return: The banner string.
232 @rtype: string
225 @rtype: string
233 """
226 """
234 return self._IP.BANNER
227 return self._IP.BANNER
235
228
236 def getPromptCount(self):
229 def getPromptCount(self):
237 """
230 """
238 Returns the prompt number.
231 Returns the prompt number.
239 Each time a user execute a line in the IPython shell the prompt count is increased
232 Each time a user execute a line in the IPython shell the prompt count is increased
240
233
241 @return: The prompt number
234 @return: The prompt number
242 @rtype: int
235 @rtype: int
243 """
236 """
244 return self._IP.outputcache.prompt_count
237 return self._IP.outputcache.prompt_count
245
238
246 def getPrompt(self):
239 def getPrompt(self):
247 """
240 """
248 Returns current prompt inside IPython instance
241 Returns current prompt inside IPython instance
249 (Can be In [...]: ot ...:)
242 (Can be In [...]: ot ...:)
250
243
251 @return: The current prompt.
244 @return: The current prompt.
252 @rtype: string
245 @rtype: string
253 """
246 """
254 return self._prompt
247 return self._prompt
255
248
256 def getIndentation(self):
249 def getIndentation(self):
257 """
250 """
258 Returns the current indentation level
251 Returns the current indentation level
259 Usefull to put the caret at the good start position if we want to do autoindentation.
252 Usefull to put the caret at the good start position if we want to do autoindentation.
260
253
261 @return: The indentation level.
254 @return: The indentation level.
262 @rtype: int
255 @rtype: int
263 """
256 """
264 return self._IP.indent_current_nsp
257 return self._IP.indent_current_nsp
265
258
266 def updateNamespace(self, ns_dict):
259 def updateNamespace(self, ns_dict):
267 '''
260 '''
268 Add the current dictionary to the shell namespace.
261 Add the current dictionary to the shell namespace.
269
262
270 @param ns_dict: A dictionary of symbol-values.
263 @param ns_dict: A dictionary of symbol-values.
271 @type ns_dict: dictionary
264 @type ns_dict: dictionary
272 '''
265 '''
273 self._IP.user_ns.update(ns_dict)
266 self._IP.user_ns.update(ns_dict)
274
267
275 def complete(self, line):
268 def complete(self, line):
276 '''
269 '''
277 Returns an auto completed line and/or posibilities for completion.
270 Returns an auto completed line and/or posibilities for completion.
278
271
279 @param line: Given line so far.
272 @param line: Given line so far.
280 @type line: string
273 @type line: string
281
274
282 @return: Line completed as for as possible,
275 @return: Line completed as for as possible,
283 and possible further completions.
276 and possible further completions.
284 @rtype: tuple
277 @rtype: tuple
285 '''
278 '''
286 split_line = self._complete_sep.split(line)
279 split_line = self._complete_sep.split(line)
287 possibilities = self._IP.complete(split_line[-1])
280 possibilities = self._IP.complete(split_line[-1])
288 if possibilities:
281 if possibilities:
289
282
290 def _commonPrefix(str1, str2):
283 def _commonPrefix(str1, str2):
291 '''
284 '''
292 Reduction function. returns common prefix of two given strings.
285 Reduction function. returns common prefix of two given strings.
293
286
294 @param str1: First string.
287 @param str1: First string.
295 @type str1: string
288 @type str1: string
296 @param str2: Second string
289 @param str2: Second string
297 @type str2: string
290 @type str2: string
298
291
299 @return: Common prefix to both strings.
292 @return: Common prefix to both strings.
300 @rtype: string
293 @rtype: string
301 '''
294 '''
302 for i in range(len(str1)):
295 for i in range(len(str1)):
303 if not str2.startswith(str1[:i+1]):
296 if not str2.startswith(str1[:i+1]):
304 return str1[:i]
297 return str1[:i]
305 return str1
298 return str1
306 common_prefix = reduce(_commonPrefix, possibilities)
299 common_prefix = reduce(_commonPrefix, possibilities)
307 completed = line[:-len(split_line[-1])]+common_prefix
300 completed = line[:-len(split_line[-1])]+common_prefix
308 else:
301 else:
309 completed = line
302 completed = line
310 return completed, possibilities
303 return completed, possibilities
311
304
312 def historyBack(self):
305 def historyBack(self):
313 '''
306 '''
314 Provides one history command back.
307 Provides one history command back.
315
308
316 @return: The command string.
309 @return: The command string.
317 @rtype: string
310 @rtype: string
318 '''
311 '''
319 history = ''
312 history = ''
320 #the below while loop is used to suppress empty history lines
313 #the below while loop is used to suppress empty history lines
321 while((history == '' or history == '\n') and self._history_level >0):
314 while((history == '' or history == '\n') and self._history_level >0):
322 if self._history_level>=1:
315 if self._history_level>=1:
323 self._history_level -= 1
316 self._history_level -= 1
324 history = self._getHistory()
317 history = self._getHistory()
325 return history
318 return history
326
319
327 def historyForward(self):
320 def historyForward(self):
328 '''
321 '''
329 Provides one history command forward.
322 Provides one history command forward.
330
323
331 @return: The command string.
324 @return: The command string.
332 @rtype: string
325 @rtype: string
333 '''
326 '''
334 history = ''
327 history = ''
335 #the below while loop is used to suppress empty history lines
328 #the below while loop is used to suppress empty history lines
336 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
329 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
337 if self._history_level < self._getHistoryMaxIndex():
330 if self._history_level < self._getHistoryMaxIndex():
338 self._history_level += 1
331 self._history_level += 1
339 history = self._getHistory()
332 history = self._getHistory()
340 else:
333 else:
341 if self._history_level == self._getHistoryMaxIndex():
334 if self._history_level == self._getHistoryMaxIndex():
342 history = self._getHistory()
335 history = self._getHistory()
343 self._history_level += 1
336 self._history_level += 1
344 else:
337 else:
345 history = ''
338 history = ''
346 return history
339 return history
347
340
348 def initHistoryIndex(self):
341 def initHistoryIndex(self):
349 '''
342 '''
350 set history to last command entered
343 set history to last command entered
351 '''
344 '''
352 self._history_level = self._getHistoryMaxIndex()+1
345 self._history_level = self._getHistoryMaxIndex()+1
353
346
354 #----------------------- IPython PRIVATE management section ----------------------
347 #----------------------- IPython PRIVATE management section --------------
355 def _afterExecute(self):
348 def _afterExecute(self):
356 '''
349 '''
357 Can be redefined to generate post event after excution is done
350 Can be redefined to generate post event after excution is done
358 '''
351 '''
359 pass
352 pass
360
353
361 def _setAskExit(self):
354 def _setAskExit(self):
362 '''
355 '''
363 set the _ask_exit variable that can be cjhecked by GUI to see if
356 set the _ask_exit variable that can be checked by GUI to see if
364 IPython request an exit handling
357 IPython request an exit handling
365 '''
358 '''
366 self._ask_exit = True
359 self._ask_exit = True
367
360
361 def _setDoExit(self, toto, arg):
362 '''
363 set the _do_exit variable that can be checked by GUI to see if
364 IPython do a direct exit of the app
365 '''
366 self._do_exit = True
367
368 def _getHistoryMaxIndex(self):
368 def _getHistoryMaxIndex(self):
369 '''
369 '''
370 returns the max length of the history buffer
370 returns the max length of the history buffer
371
371
372 @return: history length
372 @return: history length
373 @rtype: int
373 @rtype: int
374 '''
374 '''
375 return len(self._IP.input_hist_raw)-1
375 return len(self._IP.input_hist_raw)-1
376
376
377 def _getHistory(self):
377 def _getHistory(self):
378 '''
378 '''
379 Get's the command string of the current history level.
379 Get's the command string of the current history level.
380
380
381 @return: Historic command string.
381 @return: Historic command stri
382 @rtype: string
382 @rtype: string
383 '''
383 '''
384 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
384 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
385 return rv
385 return rv
386
386
387 def _pager_help(self,text):
387 def _pager_help(self,text):
388 '''
388 '''
389 This function is used as a callback replacment to IPython help pager function
389 This function is used as a callback replacment to IPython help pager function
390
390
391 It puts the 'text' value inside the self._help_text string that can be retrived via getHelpText
391 It puts the 'text' value inside the self._help_text string that can be retrived via getHelpText
392 function.
392 function.
393 '''
393 '''
394 if self._help_text == None:
394 if self._help_text == None:
395 self._help_text = text
395 self._help_text = text
396 else:
396 else:
397 self._help_text += text
397 self._help_text += text
398
398
399 def _pager(self,IP,text):
399 def _pager(self,IP,text):
400 '''
400 '''
401 This function is used as a callback replacment to IPython pager function
401 This function is used as a callback replacment to IPython pager function
402
402
403 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
403 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
404 function.
404 function.
405 '''
405 '''
406 self._doc_text = text
406 self._doc_text = text
407
407
408 def _raw_input(self, prompt=''):
408 def _raw_input(self, prompt=''):
409 '''
409 '''
410 Custom raw_input() replacement. Get's current line from console buffer.
410 Custom raw_input() replacement. Get's current line from console buffer.
411
411
412 @param prompt: Prompt to print. Here for compatability as replacement.
412 @param prompt: Prompt to print. Here for compatability as replacement.
413 @type prompt: string
413 @type prompt: string
414
414
415 @return: The current command line text.
415 @return: The current command line text.
416 @rtype: string
416 @rtype: string
417 '''
417 '''
418 return self._line_to_execute
418 return self._line_to_execute
419
419
420 def _execute(self):
420 def _execute(self):
421 '''
421 '''
422 Executes the current line provided by the shell object.
422 Executes the current line provided by the shell object.
423 '''
423 '''
424 orig_stdout = sys.stdout
424 orig_stdout = sys.stdout
425 sys.stdout = IPython.Shell.Term.cout
425 sys.stdout = IPython.Shell.Term.cout
426
426
427 try:
427 try:
428 line = self._IP.raw_input(None, self._iter_more)
428 line = self._IP.raw_input(None, self._iter_more)
429 if self._IP.autoindent:
429 if self._IP.autoindent:
430 self._IP.readline_startup_hook(None)
430 self._IP.readline_startup_hook(None)
431
431
432 except KeyboardInterrupt:
432 except KeyboardInterrupt:
433 self._IP.write('\nKeyboardInterrupt\n')
433 self._IP.write('\nKeyboardInterrupt\n')
434 self._IP.resetbuffer()
434 self._IP.resetbuffer()
435 # keep cache in sync with the prompt counter:
435 # keep cache in sync with the prompt counter:
436 self._IP.outputcache.prompt_count -= 1
436 self._IP.outputcache.prompt_count -= 1
437
437
438 if self._IP.autoindent:
438 if self._IP.autoindent:
439 self._IP.indent_current_nsp = 0
439 self._IP.indent_current_nsp = 0
440 self._iter_more = 0
440 self._iter_more = 0
441 except:
441 except:
442 self._IP.showtraceback()
442 self._IP.showtraceback()
443 else:
443 else:
444 self._iter_more = self._IP.push(line)
444 self._iter_more = self._IP.push(line)
445 if (self._IP.SyntaxTB.last_syntax_error and
445 if (self._IP.SyntaxTB.last_syntax_error and
446 self._IP.rc.autoedit_syntax):
446 self._IP.rc.autoedit_syntax):
447 self._IP.edit_syntax_error()
447 self._IP.edit_syntax_error()
448 if self._iter_more:
448 if self._iter_more:
449 self._prompt = str(self._IP.outputcache.prompt2).strip()
449 self._prompt = str(self._IP.outputcache.prompt2).strip()
450 if self._IP.autoindent:
450 if self._IP.autoindent:
451 self._IP.readline_startup_hook(self._IP.pre_readline)
451 self._IP.readline_startup_hook(self._IP.pre_readline)
452 else:
452 else:
453 self._prompt = str(self._IP.outputcache.prompt1).strip()
453 self._prompt = str(self._IP.outputcache.prompt1).strip()
454 self._IP.indent_current_nsp = 0 #we set indentation to 0
454 self._IP.indent_current_nsp = 0 #we set indentation to 0
455 sys.stdout = orig_stdout
455 sys.stdout = orig_stdout
456
456
457 def _shell(self, ip, cmd):
457 def _shell(self, ip, cmd):
458 '''
458 '''
459 Replacement method to allow shell commands without them blocking.
459 Replacement method to allow shell commands without them blocking.
460
460
461 @param ip: Ipython instance, same as self._IP
461 @param ip: Ipython instance, same as self._IP
462 @type cmd: Ipython instance
462 @type cmd: Ipython instance
463 @param cmd: Shell command to execute.
463 @param cmd: Shell command to execute.
464 @type cmd: string
464 @type cmd: string
465 '''
465 '''
466 stdin, stdout = os.popen4(cmd)
466 stdin, stdout = os.popen4(cmd)
467 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
467 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
468 #we use print command because the shell command is called inside IPython instance and thus is
468 #we use print command because the shell command is called inside IPython instance and thus is
469 #redirected to thread cout
469 #redirected to thread cout
470 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
470 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
471 print "\x01\x1b[1;36m\x02"+result
471 print "\x01\x1b[1;36m\x02"+result
472 stdout.close()
472 stdout.close()
473 stdin.close()
473 stdin.close()
General Comments 0
You need to be logged in to leave comments. Login now