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