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