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