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