##// END OF EJS Templates
Changed tabs to whitespace in the wx frontend...
Gael Varoquaux -
Show More
@@ -1,411 +1,411 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3 import wx
4 4 import wx.stc as stc
5 5 import keyword
6 6
7 7 #-----------------------------------------
8 8 # History widget for IPython
9 9 __version__ = 0.5
10 10 __author__ = "Laurent Dufrechou"
11 11 __email__ = "laurent.dufrechou _at_ gmail.com"
12 12 __license__ = "BSD"
13 13 #-----------------------------------------
14 14 class IPythonHistoryPanel(wx.Panel):
15 15
16 16 def __init__(self, parent,flt_empty=True,
17 17 flt_doc=True,flt_cmd=True,flt_magic=True):
18 18
19 19 wx.Panel.__init__(self,parent,-1)
20 20 #text_ctrl = wx.TextCtrl(self, -1, style=wx.TE_MULTILINE)
21 21 text_ctrl = PythonSTC(self, -1)
22 22
23 23
24 24 st_filt = wx.StaticText(self, -1, " Filter:")
25 25
26 26 self.filter_empty = wx.CheckBox(self, -1, "Empty commands")
27 27 self.filter_doc = wx.CheckBox(self, -1, "?: Doc commands")
28 28 self.filter_cmd = wx.CheckBox(self, -1, "!: Sys commands")
29 29 self.filter_magic = wx.CheckBox(self, -1, "%: Magic keys")
30 30
31 31 self.filter_empty.SetValue(flt_empty)
32 32 self.filter_doc.SetValue(flt_doc)
33 33 self.filter_cmd.SetValue(flt_cmd)
34 34 self.filter_magic.SetValue(flt_magic)
35 35
36 36 sizer = wx.BoxSizer(wx.VERTICAL)
37 37
38 38 sizer.Add(text_ctrl, 1, wx.EXPAND)
39 39 sizer.AddMany( [(5,5),
40 40 st_filt,
41 41 (10,10),
42 42 self.filter_empty,
43 43 self.filter_doc,
44 44 self.filter_cmd,
45 45 self.filter_magic,
46 46 (10,10),
47 47 ])
48 48 self.SetAutoLayout(True)
49 49 sizer.Fit(self)
50 50 sizer.SetSizeHints(self)
51 51 self.SetSizer(sizer)
52 52 self.text_ctrl=text_ctrl
53 53 #text_ctrl.SetText(demoText + open('Main.py').read())
54 54 text_ctrl.EmptyUndoBuffer()
55 55 text_ctrl.Colourise(0, -1)
56 56
57 57 # line numbers in the margin
58 58 text_ctrl.SetMarginType(1, stc.STC_MARGIN_NUMBER)
59 59 text_ctrl.SetMarginWidth(1, 15)
60 60
61 61
62 62 def write(self,history_line):
63 63 add = True
64 64 if self.filter_empty.GetValue() == True and history_line == '':
65 65 add = False
66 66 if len(history_line)>0:
67 67 if self.filter_doc.GetValue() == True and history_line[-1:] == '?':
68 68 add = False
69 69 if self.filter_cmd.GetValue() == True and history_line[0] == '!':
70 70 add = False
71 71 if self.filter_magic.GetValue() == True and history_line[0] == '%':
72 72 add = False
73 if add:
74 self.text_ctrl.AppendText(history_line+'\n')
73 if add:
74 self.text_ctrl.AppendText(history_line+'\n')
75 75
76 76
77 77 #----------------------------------------------------------------------
78 78 # Font definition for Styled Text Control
79 79
80 80 if wx.Platform == '__WXMSW__':
81 81 faces = { 'times': 'Times New Roman',
82 82 'mono' : 'Courier New',
83 83 'helv' : 'Arial',
84 84 'other': 'Comic Sans MS',
85 85 'size' : 8,
86 86 'size2': 6,
87 87 }
88 88 elif wx.Platform == '__WXMAC__':
89 89 faces = { 'times': 'Times New Roman',
90 90 'mono' : 'Monaco',
91 91 'helv' : 'Arial',
92 92 'other': 'Comic Sans MS',
93 93 'size' : 8,
94 94 'size2': 6,
95 95 }
96 96 else:
97 97 faces = { 'times': 'Times',
98 98 'mono' : 'Courier',
99 99 'helv' : 'Helvetica',
100 100 'other': 'new century schoolbook',
101 101 'size' : 8,
102 102 'size2': 6,
103 103 }
104 104
105 105
106 106 #----------------------------------------------------------------------
107 107
108 108 class PythonSTC(stc.StyledTextCtrl):
109 109
110 110 fold_symbols = 3
111 111
112 112 def __init__(self, parent, ID,
113 113 pos=wx.DefaultPosition, size=wx.DefaultSize,
114 114 style=0):
115 115 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
116 116 #self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
117 117 #self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
118 118
119 119 self.SetLexer(stc.STC_LEX_PYTHON)
120 120 self.SetKeyWords(0, " ".join(keyword.kwlist))
121 121
122 122 #self.SetProperty("fold", "1")
123 123 #self.SetProperty("tab.timmy.whinge.level", "1")
124 124 #self.SetMargins(0,0)
125 125
126 126 #self.SetViewWhiteSpace(False)
127 127 #self.SetBufferedDraw(False)
128 128 #self.SetViewEOL(True)
129 129 self.SetEOLMode(stc.STC_EOL_CRLF)
130 130 #self.SetUseAntiAliasing(True)
131 131
132 132 self.SetEdgeMode(stc.STC_EDGE_LINE)
133 133 self.SetEdgeColumn(80)
134 134 self.SetEdgeColour(wx.LIGHT_GREY)
135 135 self.SetLayoutCache(stc.STC_CACHE_PAGE)
136 136
137 137 # Setup a margin to hold fold markers
138 138 #self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
139 139 self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
140 140 self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
141 141 self.SetMarginSensitive(2, True)
142 142 self.SetMarginWidth(2, 12)
143 143
144 144 if self.fold_symbols == 0:
145 145 # Arrow pointing right for contracted folders, arrow pointing down for expanded
146 146 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_ARROWDOWN, "black", "black")
147 147 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "black", "black")
148 148 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "black", "black")
149 149 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "black", "black")
150 150 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
151 151 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
152 152 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
153 153
154 154 elif self.fold_symbols == 1:
155 155 # Plus for contracted folders, minus for expanded
156 156 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black")
157 157 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black")
158 158 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black")
159 159 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black")
160 160 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
161 161 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
162 162 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
163 163
164 164 elif self.fold_symbols == 2:
165 165 # Like a flattened tree control using circular headers and curved joins
166 166 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_CIRCLEMINUS, "white", "#404040")
167 167 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_CIRCLEPLUS, "white", "#404040")
168 168 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#404040")
169 169 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNERCURVE, "white", "#404040")
170 170 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040")
171 171 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
172 172 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, "white", "#404040")
173 173
174 174 elif self.fold_symbols == 3:
175 175 # Like a flattened tree control using square headers
176 176 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080")
177 177 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080")
178 178 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080")
179 179 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080")
180 180 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080")
181 181 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
182 182 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080")
183 183
184 184
185 185 self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
186 186 self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
187 187 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
188 188
189 189 # Make some styles, The lexer defines what each style is used for, we
190 190 # just have to define what each style looks like. This set is adapted from
191 191 # Scintilla sample property files.
192 192
193 193 # Global default styles for all languages
194 194 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
195 195 self.StyleClearAll() # Reset all to be like the default
196 196
197 197 # Global default styles for all languages
198 198 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
199 199 self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces)
200 200 self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces)
201 201 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold")
202 202 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
203 203
204 204 # Python styles
205 205 # Default
206 206 self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
207 207 # Comments
208 208 self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
209 209 # Number
210 210 self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces)
211 211 # String
212 212 self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
213 213 # Single quoted string
214 214 self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
215 215 # Keyword
216 216 self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
217 217 # Triple quotes
218 218 self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces)
219 219 # Triple double quotes
220 220 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces)
221 221 # Class name definition
222 222 self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % faces)
223 223 # Function or method name definition
224 224 self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces)
225 225 # Operators
226 226 self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces)
227 227 # Identifiers
228 228 self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
229 229 # Comment-blocks
230 230 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces)
231 231 # End of line where string is not closed
232 232 self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces)
233 233
234 234 self.SetCaretForeground("BLUE")
235 235
236 236
237 237 # register some images for use in the AutoComplete box.
238 238 #self.RegisterImage(1, images.getSmilesBitmap())
239 239 #self.RegisterImage(2,
240 240 # wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16)))
241 241 #self.RegisterImage(3,
242 242 # wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16)))
243 243
244 244
245 245 def OnKeyPressed(self, event):
246 246 if self.CallTipActive():
247 247 self.CallTipCancel()
248 248 key = event.GetKeyCode()
249 249
250 250 if key == 32 and event.ControlDown():
251 251 pos = self.GetCurrentPos()
252 252
253 253 # Tips
254 254 if event.ShiftDown():
255 255 self.CallTipSetBackground("yellow")
256 256 self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n'
257 257 'show some suff, maybe parameters..\n\n'
258 258 'fubar(param1, param2)')
259 259 # Code completion
260 260 else:
261 261 #lst = []
262 262 #for x in range(50000):
263 263 # lst.append('%05d' % x)
264 264 #st = " ".join(lst)
265 265 #print len(st)
266 266 #self.AutoCompShow(0, st)
267 267
268 268 kw = keyword.kwlist[:]
269 269
270 270 kw.sort() # Python sorts are case sensitive
271 271 self.AutoCompSetIgnoreCase(False) # so this needs to match
272 272
273 273 # Images are specified with a appended "?type"
274 274 for i in range(len(kw)):
275 275 if kw[i] in keyword.kwlist:
276 276 kw[i] = kw[i]# + "?1"
277 277
278 278 self.AutoCompShow(0, " ".join(kw))
279 279 else:
280 280 event.Skip()
281 281
282 282
283 283 def OnUpdateUI(self, evt):
284 284 # check for matching braces
285 285 braceAtCaret = -1
286 286 braceOpposite = -1
287 287 charBefore = None
288 288 caretPos = self.GetCurrentPos()
289 289
290 290 if caretPos > 0:
291 291 charBefore = self.GetCharAt(caretPos - 1)
292 292 styleBefore = self.GetStyleAt(caretPos - 1)
293 293
294 294 # check before
295 295 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
296 296 braceAtCaret = caretPos - 1
297 297
298 298 # check after
299 299 if braceAtCaret < 0:
300 300 charAfter = self.GetCharAt(caretPos)
301 301 styleAfter = self.GetStyleAt(caretPos)
302 302
303 303 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
304 304 braceAtCaret = caretPos
305 305
306 306 if braceAtCaret >= 0:
307 307 braceOpposite = self.BraceMatch(braceAtCaret)
308 308
309 309 if braceAtCaret != -1 and braceOpposite == -1:
310 310 self.BraceBadLight(braceAtCaret)
311 311 else:
312 312 self.BraceHighlight(braceAtCaret, braceOpposite)
313 313 #pt = self.PointFromPosition(braceOpposite)
314 314 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
315 315 #print pt
316 316 #self.Refresh(False)
317 317
318 318
319 319 def OnMarginClick(self, evt):
320 320 # fold and unfold as needed
321 321 if evt.GetMargin() == 2:
322 322 if evt.GetShift() and evt.GetControl():
323 323 self.FoldAll()
324 324 else:
325 325 lineClicked = self.LineFromPosition(evt.GetPosition())
326 326
327 327 if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
328 328 if evt.GetShift():
329 329 self.SetFoldExpanded(lineClicked, True)
330 330 self.Expand(lineClicked, True, True, 1)
331 331 elif evt.GetControl():
332 332 if self.GetFoldExpanded(lineClicked):
333 333 self.SetFoldExpanded(lineClicked, False)
334 334 self.Expand(lineClicked, False, True, 0)
335 335 else:
336 336 self.SetFoldExpanded(lineClicked, True)
337 337 self.Expand(lineClicked, True, True, 100)
338 338 else:
339 339 self.ToggleFold(lineClicked)
340 340
341 341
342 342 def FoldAll(self):
343 343 lineCount = self.GetLineCount()
344 344 expanding = True
345 345
346 346 # find out if we are folding or unfolding
347 347 for lineNum in range(lineCount):
348 348 if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
349 349 expanding = not self.GetFoldExpanded(lineNum)
350 350 break
351 351
352 352 lineNum = 0
353 353
354 354 while lineNum < lineCount:
355 355 level = self.GetFoldLevel(lineNum)
356 356 if level & stc.STC_FOLDLEVELHEADERFLAG and \
357 357 (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
358 358
359 359 if expanding:
360 360 self.SetFoldExpanded(lineNum, True)
361 361 lineNum = self.Expand(lineNum, True)
362 362 lineNum = lineNum - 1
363 363 else:
364 364 lastChild = self.GetLastChild(lineNum, -1)
365 365 self.SetFoldExpanded(lineNum, False)
366 366
367 367 if lastChild > lineNum:
368 368 self.HideLines(lineNum+1, lastChild)
369 369
370 370 lineNum = lineNum + 1
371 371
372 372
373 373
374 374 def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
375 375 lastChild = self.GetLastChild(line, level)
376 376 line = line + 1
377 377
378 378 while line <= lastChild:
379 379 if force:
380 380 if visLevels > 0:
381 381 self.ShowLines(line, line)
382 382 else:
383 383 self.HideLines(line, line)
384 384 else:
385 385 if doExpand:
386 386 self.ShowLines(line, line)
387 387
388 388 if level == -1:
389 389 level = self.GetFoldLevel(line)
390 390
391 391 if level & stc.STC_FOLDLEVELHEADERFLAG:
392 392 if force:
393 393 if visLevels > 1:
394 394 self.SetFoldExpanded(line, True)
395 395 else:
396 396 self.SetFoldExpanded(line, False)
397 397
398 398 line = self.Expand(line, doExpand, force, visLevels-1)
399 399
400 400 else:
401 401 if doExpand and self.GetFoldExpanded(line):
402 402 line = self.Expand(line, True, force, visLevels-1)
403 403 else:
404 404 line = self.Expand(line, False, force, visLevels-1)
405 405 else:
406 406 line = line + 1
407 407
408 408 return line
409 409
410 410
411 411 #----------------------------------------------------------------------
@@ -1,767 +1,767 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3 '''
4 4 Provides IPython WX console widgets.
5 5
6 6 @author: Laurent Dufrechou
7 7 laurent.dufrechou _at_ gmail.com
8 8 This WX widget is based on the original work of Eitan Isaacson
9 9 that provided the console for the GTK toolkit.
10 10
11 11 Original work from:
12 12 @author: Eitan Isaacson
13 13 @organization: IBM Corporation
14 14 @copyright: Copyright (c) 2007 IBM Corporation
15 15 @license: BSD
16 16
17 17 All rights reserved. This program and the accompanying materials are made
18 18 available under the terms of the BSD which accompanies this distribution, and
19 19 is available at U{http://www.opensource.org/licenses/bsd-license.php}
20 20 '''
21 21
22 22 __version__ = 0.8
23 23 __author__ = "Laurent Dufrechou"
24 24 __email__ = "laurent.dufrechou _at_ gmail.com"
25 25 __license__ = "BSD"
26 26
27 27 import wx
28 28 import wx.stc as stc
29 29 import wx.lib.newevent
30 30
31 31 import re
32 32 import sys
33 33 import os
34 34 import locale
35 35 import time
36 36 from StringIO import StringIO
37 37 try:
38 38 import IPython
39 39 except Exception,e:
40 40 raise "Error importing IPython (%s)" % str(e)
41 41
42 42
43 43 from non_blocking_ip_shell import NonBlockingIPShell
44 44
45 45 class WxNonBlockingIPShell(NonBlockingIPShell):
46 46 '''
47 47 An NonBlockingIPShell Thread that is WX dependent.
48 48 '''
49 49 def __init__(self, parent,
50 50 argv=[],user_ns={},user_global_ns=None,
51 51 cin=None, cout=None, cerr=None,
52 52 ask_exit_handler=None):
53 53
54 54 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
55 55 cin, cout, cerr,
56 56 ask_exit_handler)
57 57
58 58 self.parent = parent
59 59
60 60 self.ask_exit_callback = ask_exit_handler
61 61 self._IP.ask_exit = self._askExit
62 62
63 63
64 64 def addGUIShortcut(self,text,func):
65 65 wx.CallAfter(self.parent.add_button_handler,
66 66 button_info={ 'text':text,
67 67 'func':self.parent.doExecuteLine(func)})
68 68
69 69 def _askExit(self):
70 70 wx.CallAfter(self.ask_exit_callback, ())
71 71
72 72 def _afterExecute(self):
73 73 wx.CallAfter(self.parent.evtStateExecuteDone, ())
74 74
75 75
76 76 class WxConsoleView(stc.StyledTextCtrl):
77 77 '''
78 78 Specialized styled text control view for console-like workflow.
79 79 We use here a scintilla frontend thus it can be reused in any GUI taht supports
80 80 scintilla with less work.
81 81
82 82 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.(with Black background)
83 83 @type ANSI_COLORS_BLACK: dictionary
84 84
85 85 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.(with White background)
86 86 @type ANSI_COLORS_WHITE: dictionary
87 87
88 88 @ivar color_pat: Regex of terminal color pattern
89 89 @type color_pat: _sre.SRE_Pattern
90 90 '''
91 91 ANSI_STYLES_BLACK ={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
92 92 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
93 93 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
94 94 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
95 95 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
96 96 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
97 97 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
98 98 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
99 99
100 100 ANSI_STYLES_WHITE ={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
101 101 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
102 102 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
103 103 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
104 104 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
105 105 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
106 106 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
107 107 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
108 108
109 109 def __init__(self,parent,prompt,intro="",background_color="BLACK",pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
110 110 style=0):
111 111 '''
112 112 Initialize console view.
113 113
114 114 @param parent: Parent widget
115 115 @param prompt: User specified prompt
116 116 @type intro: string
117 117 @param intro: User specified startup introduction string
118 118 @type intro: string
119 119 @param background_color: Can be BLACK or WHITE
120 120 @type background_color: string
121 121 @param other: init param of styledTextControl (can be used as-is)
122 122 '''
123 123 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
124 124
125 125 ####### Scintilla configuration ##################################################
126 126
127 127 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside the widget
128 128 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
129 129 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
130 130
131 131 #we define platform specific fonts
132 132 if wx.Platform == '__WXMSW__':
133 133 faces = { 'times': 'Times New Roman',
134 134 'mono' : 'Courier New',
135 135 'helv' : 'Arial',
136 136 'other': 'Comic Sans MS',
137 137 'size' : 10,
138 138 'size2': 8,
139 139 }
140 140 elif wx.Platform == '__WXMAC__':
141 141 faces = { 'times': 'Times New Roman',
142 142 'mono' : 'Monaco',
143 143 'helv' : 'Arial',
144 144 'other': 'Comic Sans MS',
145 145 'size' : 10,
146 146 'size2': 8,
147 147 }
148 148 else:
149 149 faces = { 'times': 'Times',
150 150 'mono' : 'Courier',
151 151 'helv' : 'Helvetica',
152 152 'other': 'new century schoolbook',
153 153 'size' : 10,
154 154 'size2': 8,
155 155 }
156 156
157 157 #We draw a line at position 80
158 158 self.SetEdgeMode(stc.STC_EDGE_LINE)
159 159 self.SetEdgeColumn(80)
160 160 self.SetEdgeColour(wx.LIGHT_GREY)
161 161
162 162 #self.SetViewWhiteSpace(True)
163 163 #self.SetViewEOL(True)
164 164 self.SetEOLMode(stc.STC_EOL_CRLF)
165 165 #self.SetWrapMode(stc.STC_WRAP_CHAR)
166 166 #self.SetWrapMode(stc.STC_WRAP_WORD)
167 167 self.SetBufferedDraw(True)
168 168 #self.SetUseAntiAliasing(True)
169 169 self.SetLayoutCache(stc.STC_CACHE_PAGE)
170 170
171 171 self.EnsureCaretVisible()
172 172
173 173 self.SetMargins(3,3) #text is moved away from border with 3px
174 174 # Suppressing Scintilla margins
175 175 self.SetMarginWidth(0,0)
176 176 self.SetMarginWidth(1,0)
177 177 self.SetMarginWidth(2,0)
178 178
179 179 # make some styles
180 180 if background_color != "BLACK":
181 181 self.background_color = "WHITE"
182 182 self.SetCaretForeground("BLACK")
183 183 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
184 184 else:
185 185 self.background_color = background_color
186 186 self.SetCaretForeground("WHITE")
187 187 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
188 188
189 189 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s" % (self.ANSI_STYLES['0;30'][1],
190 190 self.background_color,
191 191 faces['size'], faces['mono']))
192 192 self.StyleClearAll()
193 193 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FF0000,back:#0000FF,bold")
194 194 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
195 195
196 196 for style in self.ANSI_STYLES.values():
197 197 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
198 198
199 199 #######################################################################
200 200
201 201 self.indent = 0
202 202 self.prompt_count = 0
203 203 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
204 204
205 205 self.write(intro)
206 206 self.setPrompt(prompt)
207 207 self.showPrompt()
208 208
209 209 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
210 210 #self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
211 211
212 212 def write(self, text):
213 213 '''
214 214 Write given text to buffer.
215 215
216 216 @param text: Text to append.
217 217 @type text: string
218 218 '''
219 219 segments = self.color_pat.split(text)
220 220 segment = segments.pop(0)
221 221 self.StartStyling(self.getCurrentLineEnd(),0xFF)
222 222 self.AppendText(segment)
223 223
224 224 if segments:
225 225 ansi_tags = self.color_pat.findall(text)
226 226
227 227 for tag in ansi_tags:
228 228 i = segments.index(tag)
229 229 self.StartStyling(self.getCurrentLineEnd(),0xFF)
230 230 self.AppendText(segments[i+1])
231 231
232 232 if tag != '0':
233 233 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
234 234
235 235 segments.pop(i)
236 236
237 237 self.moveCursor(self.getCurrentLineEnd())
238 238
239 239 def getPromptLen(self):
240 240 '''
241 241 Return the length of current prompt
242 242 '''
243 243 return len(str(self.prompt_count)) + 7
244 244
245 245 def setPrompt(self,prompt):
246 246 self.prompt = prompt
247 247
248 248 def setIndentation(self,indentation):
249 249 self.indent = indentation
250 250
251 251 def setPromptCount(self,count):
252 252 self.prompt_count = count
253 253
254 254 def showPrompt(self):
255 255 '''
256 256 Prints prompt at start of line.
257 257
258 258 @param prompt: Prompt to print.
259 259 @type prompt: string
260 260 '''
261 261 self.write(self.prompt)
262 262 #now we update the position of end of prompt
263 263 self.current_start = self.getCurrentLineEnd()
264 264
265 265 autoindent = self.indent*' '
266 266 autoindent = autoindent.replace(' ','\t')
267 267 self.write(autoindent)
268 268
269 269 def changeLine(self, text):
270 270 '''
271 271 Replace currently entered command line with given text.
272 272
273 273 @param text: Text to use as replacement.
274 274 @type text: string
275 275 '''
276 276 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
277 277 self.ReplaceSelection(text)
278 278 self.moveCursor(self.getCurrentLineEnd())
279 279
280 280 def getCurrentPromptStart(self):
281 281 return self.current_start
282 282
283 283 def getCurrentLineStart(self):
284 284 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
285 285
286 286 def getCurrentLineEnd(self):
287 287 return self.GetLength()
288 288
289 289 def getCurrentLine(self):
290 290 '''
291 291 Get text in current command line.
292 292
293 293 @return: Text of current command line.
294 294 @rtype: string
295 295 '''
296 296 return self.GetTextRange(self.getCurrentPromptStart(),
297 297 self.getCurrentLineEnd())
298 298
299 299 def showReturned(self, text):
300 300 '''
301 301 Show returned text from last command and print new prompt.
302 302
303 303 @param text: Text to show.
304 304 @type text: string
305 305 '''
306 306 self.write('\n'+text)
307 307 if text:
308 308 self.write('\n')
309 309 self.showPrompt()
310 310
311 311 def moveCursorOnNewValidKey(self):
312 312 #If cursor is at wrong position put it at last line...
313 313 if self.GetCurrentPos() < self.getCurrentPromptStart():
314 314 self.GotoPos(self.getCurrentPromptStart())
315 315
316 316 def removeFromTo(self,from_pos,to_pos):
317 317 if from_pos < to_pos:
318 318 self.SetSelection(from_pos,to_pos)
319 319 self.DeleteBack()
320 320
321 321 def removeCurrentLine(self):
322 322 self.LineDelete()
323 323
324 324 def moveCursor(self,position):
325 325 self.GotoPos(position)
326 326
327 327 def getCursorPos(self):
328 328 return self.GetCurrentPos()
329 329
330 330 def selectFromTo(self,from_pos,to_pos):
331 331 self.SetSelectionStart(from_pos)
332 332 self.SetSelectionEnd(to_pos)
333 333
334 334 def writeHistory(self,history):
335 335 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
336 336 self.changeLine(history)
337 337
338 338 def writeCompletion(self, possibilities):
339 339 max_len = len(max(possibilities,key=len))
340 340 max_symbol =' '*max_len
341 341
342 342 #now we check how much symbol we can put on a line...
343 343 cursor_pos = self.getCursorPos()
344 344 test_buffer = max_symbol + ' '*4
345 345 current_lines = self.GetLineCount()
346 346
347 347 allowed_symbols = 80/len(test_buffer)
348 348 if allowed_symbols == 0:
349 349 allowed_symbols = 1
350 350
351 351 pos = 1
352 352 buf = ''
353 353 for symbol in possibilities:
354 354 #buf += symbol+'\n'#*spaces)
355 355 if pos<allowed_symbols:
356 356 spaces = max_len - len(symbol) + 4
357 357 buf += symbol+' '*spaces
358 358 pos += 1
359 359 else:
360 360 buf+=symbol+'\n'
361 361 pos = 1
362 362 self.write(buf)
363 363
364 364 def _onKeypress(self, event, skip=True):
365 365 '''
366 366 Key press callback used for correcting behavior for console-like
367 367 interfaces. For example 'home' should go to prompt, not to begining of
368 368 line.
369 369
370 370 @param widget: Widget that key press accored in.
371 371 @type widget: gtk.Widget
372 372 @param event: Event object
373 373 @type event: gtk.gdk.Event
374 374
375 375 @return: Return True if event as been catched.
376 376 @rtype: boolean
377 377 '''
378
378
379 379 if event.GetKeyCode() == wx.WXK_HOME:
380 380 if event.Modifiers == wx.MOD_NONE:
381 381 self.moveCursorOnNewValidKey()
382 382 self.moveCursor(self.getCurrentPromptStart())
383 383 return True
384 384 elif event.Modifiers == wx.MOD_SHIFT:
385 385 self.moveCursorOnNewValidKey()
386 386 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
387 387 return True
388 388 else:
389 389 return False
390 390
391 391 elif event.GetKeyCode() == wx.WXK_LEFT:
392 392 if event.Modifiers == wx.MOD_NONE:
393 393 self.moveCursorOnNewValidKey()
394 394
395 395 self.moveCursor(self.getCursorPos()-1)
396 396 if self.getCursorPos() < self.getCurrentPromptStart():
397 397 self.moveCursor(self.getCurrentPromptStart())
398 398 return True
399 399
400 400 elif event.GetKeyCode() == wx.WXK_BACK:
401 401 self.moveCursorOnNewValidKey()
402 if self.getCursorPos() > self.getCurrentPromptStart():
402 if self.getCursorPos() > self.getCurrentPromptStart():
403 403 self.removeFromTo(self.getCursorPos()-1,self.getCursorPos())
404 return True
404 return True
405 405
406 406 if skip:
407 407 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
408 408 self.moveCursorOnNewValidKey()
409 409
410 410 event.Skip()
411 411 return True
412 412 return False
413 413
414 414 def OnUpdateUI(self, evt):
415 415 # check for matching braces
416 416 braceAtCaret = -1
417 417 braceOpposite = -1
418 418 charBefore = None
419 419 caretPos = self.GetCurrentPos()
420 420
421 421 if caretPos > 0:
422 422 charBefore = self.GetCharAt(caretPos - 1)
423 423 styleBefore = self.GetStyleAt(caretPos - 1)
424 424
425 425 # check before
426 426 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
427 427 braceAtCaret = caretPos - 1
428 428
429 429 # check after
430 430 if braceAtCaret < 0:
431 431 charAfter = self.GetCharAt(caretPos)
432 432 styleAfter = self.GetStyleAt(caretPos)
433 433
434 434 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
435 435 braceAtCaret = caretPos
436 436
437 437 if braceAtCaret >= 0:
438 438 braceOpposite = self.BraceMatch(braceAtCaret)
439 439
440 440 if braceAtCaret != -1 and braceOpposite == -1:
441 441 self.BraceBadLight(braceAtCaret)
442 442 else:
443 443 self.BraceHighlight(braceAtCaret, braceOpposite)
444 444 #pt = self.PointFromPosition(braceOpposite)
445 445 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
446 446 #print pt
447 447 #self.Refresh(False)
448 448
449 449 class WxIPythonViewPanel(wx.Panel):
450 450 '''
451 451 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
452 452 If you want to port this to any other GUI toolkit, just replace the WxConsoleView
453 453 by YOURGUIConsoleView and make YOURGUIIPythonView derivate from whatever container you want.
454 454 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
455 455 Any idea to make it more 'genric' welcomed.
456 456 '''
457 457
458 458 def __init__(self, parent, ask_exit_handler=None, intro=None,
459 459 background_color="BLACK", add_button_handler=None,
460 460 wx_ip_shell=None,
461 461 ):
462 462 '''
463 463 Initialize.
464 464 Instanciate an IPython thread.
465 465 Instanciate a WxConsoleView.
466 466 Redirect I/O to console.
467 467 '''
468 468 wx.Panel.__init__(self,parent,-1)
469 469
470 470 ### IPython thread instanciation ###
471 471 self.cout = StringIO()
472 472
473 473 self.add_button_handler = add_button_handler
474 474 self.ask_exit_handler = ask_exit_handler
475 475
476 476 if wx_ip_shell is not None:
477 477 self.IP = wx_ip_shell
478 478 else:
479 479 self.IP = WxNonBlockingIPShell(self,
480 480 cout=self.cout,cerr=self.cout,
481 481 ask_exit_handler = ask_exit_handler)
482 482 ### IPython wx console view instanciation ###
483 483 #If user didn't defined an intro text, we create one for him
484 484 #If you really wnat an empty intrp just call wxIPythonViewPanel with intro=''
485 485 if intro == None:
486 486 welcome_text = "Welcome to WxIPython Shell.\n\n"
487 487 welcome_text+= self.IP.getBanner()
488 488 welcome_text+= "!command -> Execute command in shell\n"
489 489 welcome_text+= "TAB -> Autocompletion\n"
490 490
491 491 self.text_ctrl = WxConsoleView(self,
492 492 self.IP.getPrompt(),
493 493 intro=welcome_text,
494 494 background_color=background_color)
495 495
496 496 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
497 497
498 498 ### making the layout of the panel ###
499 499 sizer = wx.BoxSizer(wx.VERTICAL)
500 500 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
501 501 self.SetAutoLayout(True)
502 502 sizer.Fit(self)
503 503 sizer.SetSizeHints(self)
504 504 self.SetSizer(sizer)
505 505 #and we focus on the widget :)
506 506 self.SetFocus()
507 507
508 508 ### below are the thread communication variable ###
509 509 # the IPython thread is managed via unidirectional communication.
510 510 # It's a thread slave that can't interact by itself with the GUI.
511 511 # When the GUI event loop is done runStateMachine() is called and the thread sate is then
512 512 # managed.
513 513
514 514 #Initialize the state machine #kept for information
515 515 #self.states = ['IDLE',
516 516 # 'DO_EXECUTE_LINE',
517 517 # 'WAIT_END_OF_EXECUTION',
518 518 # 'SHOW_DOC',
519 519 # 'SHOW_PROMPT']
520 520
521 521 self.cur_state = 'IDLE'
522 522 self.pager_state = 'DONE'
523 523 #wx.CallAfter(self.runStateMachine)
524 524
525 525 # This creates a new Event class and a EVT binder function
526 526 #(self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
527 527 #(self.AddButtonEvent, EVT_ADDBUTTON_EXIT) = wx.lib.newevent.NewEvent()
528 528
529 529
530 530 #self.Bind(wx.EVT_IDLE, self.runStateMachine)
531 531
532 532 def __del__(self):
533 533 WxConsoleView.__del__()
534 534
535 535 #---------------------------- IPython Thread Management ---------------------------------------
536 536 def stateDoExecuteLine(self):
537 537 #print >>sys.__stdout__,"command:",self.getCurrentLine()
538 538 self.doExecuteLine(self.text_ctrl.getCurrentLine())
539 539
540 540 def doExecuteLine(self,line):
541 541 #print >>sys.__stdout__,"command:",line
542 542 self.IP.doExecute(line.replace('\t',' '*4))
543 543 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
544 544 self.cur_state = 'WAIT_END_OF_EXECUTION'
545 545
546 546
547 547 def evtStateExecuteDone(self,evt):
548 548 self.doc = self.IP.getDocText()
549 549 self.help = self.IP.getHelpText()
550 550 if self.doc:
551 551 self.pager_state = 'INIT'
552 552 self.cur_state = 'SHOW_DOC'
553 553 self.pager(self.doc)
554 554 #if self.pager_state == 'DONE':
555 555 if self.help:
556 556 self.pager_state = 'INIT_HELP'
557 557 self.cur_state = 'SHOW_DOC'
558 558 self.pager(self.help)
559 559
560 560 else:
561 561 self.stateShowPrompt()
562 562
563 563 def stateShowPrompt(self):
564 564 self.cur_state = 'SHOW_PROMPT'
565 565 self.text_ctrl.setPrompt(self.IP.getPrompt())
566 566 self.text_ctrl.setIndentation(self.IP.getIndentation())
567 567 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
568 568 rv = self.cout.getvalue()
569 569 if rv: rv = rv.strip('\n')
570 570 self.text_ctrl.showReturned(rv)
571 571 self.cout.truncate(0)
572 572 self.IP.initHistoryIndex()
573 573 self.cur_state = 'IDLE'
574 574
575 575 ## def runStateMachine(self,event):
576 576 ## #print >>sys.__stdout__,"state:",self.cur_state
577 577 ## self.updateStatusTracker(self.cur_state)
578 578 ##
579 579 ## #if self.cur_state == 'DO_EXECUTE_LINE':
580 580 ## # self.doExecuteLine()
581 581 ##
582 582 ## if self.cur_state == 'WAIT_END_OF_EXECUTION':
583 583 ## if self.IP.isExecuteDone():
584 584 ## #self.button = self.IP.getAddButton()
585 585 ## #if self.IP.getAskExit():
586 586 ## # evt = self.AskExitEvent()
587 587 ## # wx.PostEvent(self, evt)
588 588 ## # self.IP.clearAskExit()
589 589 ## self.doc = self.IP.getDocText()
590 590 ## if self.doc:
591 591 ## self.pager_state = 'INIT'
592 592 ## self.cur_state = 'SHOW_DOC'
593 593 ## #if self.button:
594 594 ## #self.IP.doExecute('print "cool"')#self.button['func'])
595 595 ## #self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
596 596 ##
597 597 ## # self.button['func']='print "cool!"'
598 598 ## # self.add_button_handler(self.button)
599 599 ## # self.IP.shortcutProcessed()
600 600 ##
601 601 ## else:
602 602 ## self.cur_state = 'SHOW_PROMPT'
603 603 ##
604 604 ## if self.cur_state == 'SHOW_PROMPT':
605 605 ## self.text_ctrl.setPrompt(self.IP.getPrompt())
606 606 ## self.text_ctrl.setIndentation(self.IP.getIndentation())
607 607 ## self.text_ctrl.setPromptCount(self.IP.getPromptCount())
608 608 ## rv = self.cout.getvalue()
609 609 ## if rv: rv = rv.strip('\n')
610 610 ## self.text_ctrl.showReturned(rv)
611 611 ## self.cout.truncate(0)
612 ## self.IP.initHistoryIndex()
612 ## self.IP.initHistoryIndex()
613 613 ## self.cur_state = 'IDLE'
614 614 ##
615 615 ## if self.cur_state == 'SHOW_DOC':
616 616 ## self.pager(self.doc)
617 617 ## if self.pager_state == 'DONE':
618 618 ## self.cur_state = 'SHOW_PROMPT'
619 619 ##
620 620 ## event.Skip()
621 621
622 622 #---------------------------- IPython pager ---------------------------------------
623 623 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
624 624 if self.pager_state == 'WAITING':
625 #print >>sys.__stdout__,"PAGER waiting"
626 return
627
628 if self.pager_state == 'INIT':
629 #print >>sys.__stdout__,"PAGER state:",self.pager_state
630 self.pager_lines = text[7:].split('\n')
631 self.pager_nb_lines = len(self.pager_lines)
632 self.pager_index = 0
633 self.pager_do_remove = False
634 self.text_ctrl.write('\n')
635 self.pager_state = 'PROCESS_LINES'
636
637 if self.pager_state == 'INIT_HELP':
638 #print >>sys.__stdout__,"HELP PAGER state:",self.pager_state
639 self.pager_lines = text[:].split('\n')
640 self.pager_nb_lines = len(self.pager_lines)
641 self.pager_index = 0
642 self.pager_do_remove = False
643 self.text_ctrl.write('\n')
644 self.pager_state = 'PROCESS_LINES'
645
646 if self.pager_state == 'PROCESS_LINES':
647 #print >>sys.__stdout__,"PAGER state:",self.pager_state
648 if self.pager_do_remove == True:
649 self.text_ctrl.removeCurrentLine()
650 self.pager_do_remove = False
651
652 if self.pager_nb_lines > 10:
653 #print >>sys.__stdout__,"PAGER processing 10 lines"
654 if self.pager_index > 0:
655 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
656 else:
657 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
658
659 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
660 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
661 self.pager_index += 10
662 self.pager_nb_lines -= 10
663 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
664 self.pager_do_remove = True
665 self.pager_state = 'WAITING'
666 return
667 else:
668 #print >>sys.__stdout__,"PAGER processing last lines"
669 if self.pager_nb_lines > 0:
670 if self.pager_index > 0:
671 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
672 else:
673 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
674
675 self.pager_index += 1
625 #print >>sys.__stdout__,"PAGER waiting"
626 return
627
628 if self.pager_state == 'INIT':
629 #print >>sys.__stdout__,"PAGER state:",self.pager_state
630 self.pager_lines = text[7:].split('\n')
631 self.pager_nb_lines = len(self.pager_lines)
632 self.pager_index = 0
633 self.pager_do_remove = False
634 self.text_ctrl.write('\n')
635 self.pager_state = 'PROCESS_LINES'
636
637 if self.pager_state == 'INIT_HELP':
638 #print >>sys.__stdout__,"HELP PAGER state:",self.pager_state
639 self.pager_lines = text[:].split('\n')
640 self.pager_nb_lines = len(self.pager_lines)
641 self.pager_index = 0
642 self.pager_do_remove = False
643 self.text_ctrl.write('\n')
644 self.pager_state = 'PROCESS_LINES'
645
646 if self.pager_state == 'PROCESS_LINES':
647 #print >>sys.__stdout__,"PAGER state:",self.pager_state
648 if self.pager_do_remove == True:
649 self.text_ctrl.removeCurrentLine()
650 self.pager_do_remove = False
651
652 if self.pager_nb_lines > 10:
653 #print >>sys.__stdout__,"PAGER processing 10 lines"
654 if self.pager_index > 0:
655 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
656 else:
657 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
658
659 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
660 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
661 self.pager_index += 10
662 self.pager_nb_lines -= 10
663 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
664 self.pager_do_remove = True
665 self.pager_state = 'WAITING'
666 return
667 else:
668 #print >>sys.__stdout__,"PAGER processing last lines"
669 if self.pager_nb_lines > 0:
670 if self.pager_index > 0:
671 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
672 else:
673 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
674
675 self.pager_index += 1
676 676 self.pager_nb_lines -= 1
677 if self.pager_nb_lines > 0:
678 for line in self.pager_lines[self.pager_index:]:
679 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
680 self.pager_nb_lines = 0
681 self.pager_state = 'DONE'
682 self.stateShowPrompt()
677 if self.pager_nb_lines > 0:
678 for line in self.pager_lines[self.pager_index:]:
679 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
680 self.pager_nb_lines = 0
681 self.pager_state = 'DONE'
682 self.stateShowPrompt()
683 683
684 684 #---------------------------- Key Handler --------------------------------------------
685 685 def keyPress(self, event):
686 686 '''
687 687 Key press callback with plenty of shell goodness, like history,
688 688 autocompletions, etc.
689 689 '''
690
690
691 691 if event.GetKeyCode() == ord('C'):
692 692 if event.Modifiers == wx.MOD_CONTROL:
693 693 if self.cur_state == 'WAIT_END_OF_EXECUTION':
694 694 #we raise an exception inside the IPython thread container
695 695 self.IP.ce.raise_exc(KeyboardInterrupt)
696 696 return
697 697
698 698 if event.KeyCode == wx.WXK_RETURN:
699 699 if self.cur_state == 'IDLE':
700 700 #we change the state ot the state machine
701 701 self.cur_state = 'DO_EXECUTE_LINE'
702 702 self.stateDoExecuteLine()
703 703 return
704 704 if self.pager_state == 'WAITING':
705 705 self.pager_state = 'PROCESS_LINES'
706 706 self.pager(self.doc)
707 707 return
708 708
709 709 if event.GetKeyCode() in [ord('q'),ord('Q')]:
710 710 if self.pager_state == 'WAITING':
711 711 self.pager_state = 'DONE'
712 712 self.stateShowPrompt()
713 713 return
714 714
715 715 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
716 if self.cur_state == 'IDLE':
716 if self.cur_state == 'IDLE':
717 717 if event.KeyCode == wx.WXK_UP:
718 718 history = self.IP.historyBack()
719 719 self.text_ctrl.writeHistory(history)
720 720 return
721 721 if event.KeyCode == wx.WXK_DOWN:
722 722 history = self.IP.historyForward()
723 723 self.text_ctrl.writeHistory(history)
724 724 return
725 725 if event.KeyCode == wx.WXK_TAB:
726 726 #if line empty we disable tab completion
727 727 if not self.text_ctrl.getCurrentLine().strip():
728 728 self.text_ctrl.write('\t')
729 729 return
730 730 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
731 731 if len(possibilities) > 1:
732 732 cur_slice = self.text_ctrl.getCurrentLine()
733 733 self.text_ctrl.write('\n')
734 734 self.text_ctrl.writeCompletion(possibilities)
735 735 self.text_ctrl.write('\n')
736 736
737 737 self.text_ctrl.showPrompt()
738 738 self.text_ctrl.write(cur_slice)
739 739 self.text_ctrl.changeLine(completed or cur_slice)
740 740
741 741 return
742 742 event.Skip()
743
743
744 744 #---------------------------- Hook Section --------------------------------------------
745 745 def updateHistoryTracker(self,command_line):
746 746 '''
747 747 Default history tracker (does nothing)
748 748 '''
749 749 pass
750 750
751 751 def setHistoryTrackerHook(self,func):
752 752 '''
753 753 Define a new history tracker
754 754 '''
755 755 self.updateHistoryTracker = func
756 756 def updateStatusTracker(self,status):
757 757 '''
758 758 Default status tracker (does nothing)
759 759 '''
760 760 pass
761 761
762 762 def setStatusTrackerHook(self,func):
763 763 '''
764 764 Define a new status tracker
765 765 '''
766 766 self.updateStatusTracker = func
767 767
@@ -1,473 +1,473 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3 '''
4 4 Provides IPython remote instance.
5 5
6 6 @author: Laurent Dufrechou
7 7 laurent.dufrechou _at_ gmail.com
8 8 @license: BSD
9 9
10 10 All rights reserved. This program and the accompanying materials are made
11 11 available under the terms of the BSD which accompanies this distribution, and
12 12 is available at U{http://www.opensource.org/licenses/bsd-license.php}
13 13 '''
14 14
15 15 __version__ = 0.9
16 16 __author__ = "Laurent Dufrechou"
17 17 __email__ = "laurent.dufrechou _at_ gmail.com"
18 18 __license__ = "BSD"
19 19
20 20 import re
21 21 import sys
22 22 import os
23 23 import locale
24 24 import time
25 25 import pydoc,__builtin__,site
26 26 from thread_ex import ThreadEx
27 27 from StringIO import StringIO
28 28
29 29 try:
30 30 import IPython
31 31 except Exception,e:
32 32 raise "Error importing IPython (%s)" % str(e)
33 33
34 34 ##############################################################################
35 35 class _Helper(object):
36 36 """Redefine the built-in 'help'.
37 37 This is a wrapper around pydoc.help (with a twist).
38 38 """
39 39
40 40 def __init__(self,pager):
41 41 self._pager = pager
42 42
43 43 def __repr__(self):
44 44 return "Type help() for interactive help, " \
45 45 "or help(object) for help about object."
46 46
47 47 def __call__(self, *args, **kwds):
48 48 class DummyWriter(object):
49 49 def __init__(self,pager):
50 50 self._pager = pager
51 51
52 52 def write(self,data):
53 53 self._pager(data)
54 54
55 55 import pydoc
56 56 pydoc.help.output = DummyWriter(self._pager)
57 57 pydoc.help.interact = lambda :1
58 58
59 59 #helper.output.write = self.doc.append
60 60 return pydoc.help(*args, **kwds)
61 61
62 62
63 63 ##############################################################################
64 64 class _CodeExecutor(ThreadEx):
65 65
66 66 def __init__(self, instance, after):
67 67 ThreadEx.__init__(self)
68 68 self.instance = instance
69 69 self._afterExecute=after
70 70
71 71 def run(self):
72 72 try:
73 73 self.instance._doc_text = None
74 74 self.instance._help_text = None
75 75 self.instance._execute()
76 76 # used for uper class to generate event after execution
77 77 self._afterExecute()
78 78
79 79 except KeyboardInterrupt:
80 80 pass
81 81
82 82
83 83 ##############################################################################
84 84 class NonBlockingIPShell(object):
85 85 '''
86 86 Create an IPython instance, running the commands in a separate,
87 87 non-blocking thread.
88 88 This allows embedding in any GUI without blockage.
89 89
90 90 Note: The ThreadEx class supports asynchroneous function call
91 91 via raise_exc()
92 92 '''
93 93
94 94 def __init__(self,argv
95 95 =[],user_ns={},user_global_ns=None,
96 96 cin=None, cout=None, cerr=None,
97 97 ask_exit_handler=None):
98 98 '''
99 99 @param argv: Command line options for IPython
100 100 @type argv: list
101 101 @param user_ns: User namespace.
102 102 @type user_ns: dictionary
103 103 @param user_global_ns: User global namespace.
104 104 @type user_global_ns: dictionary.
105 105 @param cin: Console standard input.
106 106 @type cin: IO stream
107 107 @param cout: Console standard output.
108 108 @type cout: IO stream
109 109 @param cerr: Console standard error.
110 110 @type cerr: IO stream
111 111 @param exit_handler: Replacement for builtin exit() function
112 112 @type exit_handler: function
113 113 @param time_loop: Define the sleep time between two thread's loop
114 114 @type int
115 115 '''
116 116 #first we redefine in/out/error functions of IPython
117 117 if cin:
118 118 IPython.Shell.Term.cin = cin
119 119 if cout:
120 120 IPython.Shell.Term.cout = cout
121 121 if cerr:
122 122 IPython.Shell.Term.cerr = cerr
123 123
124 124 # This is to get rid of the blockage that accurs during
125 125 # IPython.Shell.InteractiveShell.user_setup()
126 126 IPython.iplib.raw_input = lambda x: None
127 127
128 128 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
129 129
130 130 excepthook = sys.excepthook
131 131
132 132 self._IP = IPython.Shell.make_IPython(
133 133 argv,user_ns=user_ns,
134 134 user_global_ns=user_global_ns,
135 135 embedded=True,
136 136 shell_class=IPython.Shell.InteractiveShell)
137 137
138 138 #we replace IPython default encoding by wx locale encoding
139 loc = locale.getpreferredencoding()
140 if loc:
141 self._IP.stdin_encoding = loc
139 loc = locale.getpreferredencoding()
140 if loc:
141 self._IP.stdin_encoding = loc
142 142 #we replace the ipython default pager by our pager
143 143 self._IP.set_hook('show_in_pager',self._pager)
144 144
145 145 #we replace the ipython default shell command caller by our shell handler
146 self._IP.set_hook('shell_hook',self._shell)
146 self._IP.set_hook('shell_hook',self._shell)
147 147
148 148 #we replace the ipython default input command caller by our method
149 149 IPython.iplib.raw_input_original = self._raw_input
150 150 #we replace the ipython default exit command by our method
151 151 self._IP.exit = self._setAskExit
152 152 #we modify Exit and Quit Magic
153 153 ip = IPython.ipapi.get()
154 154 ip.expose_magic('Exit', self._setDoExit)
155 155 ip.expose_magic('Quit', self._setDoExit)
156 156
157 157 sys.excepthook = excepthook
158 158
159 159 self._iter_more = 0
160 160 self._history_level = 0
161 161 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
162 162 self._prompt = str(self._IP.outputcache.prompt1).strip()
163 163
164 164 #thread working vars
165 165 self._line_to_execute = ''
166 166
167 167 #vars that will be checked by GUI loop to handle thread states...
168 168 #will be replaced later by PostEvent GUI funtions...
169 169 self._doc_text = None
170 170 self._help_text = None
171 171 self._ask_exit = False
172 172 self._add_button = None
173 173
174 174 #we replace the help command
175 175 self._IP.user_ns['help'] = _Helper(self._pager_help)
176 176
177 177 #----------------------- Thread management section ----------------------
178 178 def doExecute(self,line):
179 179 """
180 180 Tell the thread to process the 'line' command
181 181 """
182 182
183 183 self._line_to_execute = line
184 184
185 185 self.ce = _CodeExecutor(self,self._afterExecute)
186 186 self.ce.start()
187 187
188 188 #----------------------- IPython management section ----------------------
189 189 def getAskExit(self):
190 190 '''
191 191 returns the _ask_exit variable that can be checked by GUI to see if
192 192 IPython request an exit handling
193 193 '''
194 194 return self._ask_exit
195 195
196 196 def clearAskExit(self):
197 197 '''
198 198 clear the _ask_exit var when GUI as handled the request.
199 199 '''
200 200 self._ask_exit = False
201 201
202 202 def getDocText(self):
203 203 """
204 204 Returns the output of the processing that need to be paged (if any)
205 205
206 206 @return: The std output string.
207 207 @rtype: string
208 208 """
209 209 return self._doc_text
210 210
211 211 def getHelpText(self):
212 212 """
213 213 Returns the output of the processing that need to be paged via help pager(if any)
214 214
215 215 @return: The std output string.
216 216 @rtype: string
217 217 """
218 218 return self._help_text
219 219
220 220 def getBanner(self):
221 221 """
222 222 Returns the IPython banner for useful info on IPython instance
223 223
224 224 @return: The banner string.
225 225 @rtype: string
226 226 """
227 227 return self._IP.BANNER
228 228
229 229 def getPromptCount(self):
230 230 """
231 231 Returns the prompt number.
232 232 Each time a user execute a line in the IPython shell the prompt count is increased
233 233
234 234 @return: The prompt number
235 235 @rtype: int
236 236 """
237 237 return self._IP.outputcache.prompt_count
238 238
239 239 def getPrompt(self):
240 240 """
241 241 Returns current prompt inside IPython instance
242 242 (Can be In [...]: ot ...:)
243 243
244 244 @return: The current prompt.
245 245 @rtype: string
246 246 """
247 247 return self._prompt
248 248
249 249 def getIndentation(self):
250 250 """
251 251 Returns the current indentation level
252 252 Usefull to put the caret at the good start position if we want to do autoindentation.
253 253
254 254 @return: The indentation level.
255 255 @rtype: int
256 256 """
257 257 return self._IP.indent_current_nsp
258 258
259 259 def updateNamespace(self, ns_dict):
260 260 '''
261 261 Add the current dictionary to the shell namespace.
262 262
263 263 @param ns_dict: A dictionary of symbol-values.
264 264 @type ns_dict: dictionary
265 265 '''
266 266 self._IP.user_ns.update(ns_dict)
267 267
268 268 def complete(self, line):
269 269 '''
270 270 Returns an auto completed line and/or posibilities for completion.
271 271
272 272 @param line: Given line so far.
273 273 @type line: string
274 274
275 275 @return: Line completed as for as possible,
276 276 and possible further completions.
277 277 @rtype: tuple
278 278 '''
279 279 split_line = self._complete_sep.split(line)
280 280 possibilities = self._IP.complete(split_line[-1])
281 281 if possibilities:
282 282
283 283 def _commonPrefix(str1, str2):
284 284 '''
285 285 Reduction function. returns common prefix of two given strings.
286 286
287 287 @param str1: First string.
288 288 @type str1: string
289 289 @param str2: Second string
290 290 @type str2: string
291 291
292 292 @return: Common prefix to both strings.
293 293 @rtype: string
294 294 '''
295 295 for i in range(len(str1)):
296 296 if not str2.startswith(str1[:i+1]):
297 297 return str1[:i]
298 298 return str1
299 299 common_prefix = reduce(_commonPrefix, possibilities)
300 300 completed = line[:-len(split_line[-1])]+common_prefix
301 301 else:
302 302 completed = line
303 303 return completed, possibilities
304 304
305 305 def historyBack(self):
306 306 '''
307 307 Provides one history command back.
308 308
309 309 @return: The command string.
310 310 @rtype: string
311 311 '''
312 312 history = ''
313 313 #the below while loop is used to suppress empty history lines
314 while((history == '' or history == '\n') and self._history_level >0):
315 if self._history_level>=1:
316 self._history_level -= 1
317 history = self._getHistory()
314 while((history == '' or history == '\n') and self._history_level >0):
315 if self._history_level>=1:
316 self._history_level -= 1
317 history = self._getHistory()
318 318 return history
319 319
320 320 def historyForward(self):
321 321 '''
322 322 Provides one history command forward.
323 323
324 324 @return: The command string.
325 325 @rtype: string
326 326 '''
327 history = ''
328 #the below while loop is used to suppress empty history lines
329 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
330 if self._history_level < self._getHistoryMaxIndex():
331 self._history_level += 1
332 history = self._getHistory()
333 else:
334 if self._history_level == self._getHistoryMaxIndex():
335 history = self._getHistory()
336 self._history_level += 1
337 else:
338 history = ''
327 history = ''
328 #the below while loop is used to suppress empty history lines
329 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
330 if self._history_level < self._getHistoryMaxIndex():
331 self._history_level += 1
332 history = self._getHistory()
333 else:
334 if self._history_level == self._getHistoryMaxIndex():
335 history = self._getHistory()
336 self._history_level += 1
337 else:
338 history = ''
339 339 return history
340 340
341 341 def initHistoryIndex(self):
342 342 '''
343 343 set history to last command entered
344 344 '''
345 345 self._history_level = self._getHistoryMaxIndex()+1
346 346
347 347 #----------------------- IPython PRIVATE management section --------------
348 348 def _afterExecute(self):
349 349 '''
350 350 Can be redefined to generate post event after excution is done
351 351 '''
352 352 pass
353 353
354 354 def _setAskExit(self):
355 355 '''
356 356 set the _ask_exit variable that can be checked by GUI to see if
357 357 IPython request an exit handling
358 358 '''
359 359 self._ask_exit = True
360 360
361 361 def _setDoExit(self, toto, arg):
362 362 '''
363 363 set the _do_exit variable that can be checked by GUI to see if
364 364 IPython do a direct exit of the app
365 365 '''
366 366 self._do_exit = True
367 367
368 368 def _getHistoryMaxIndex(self):
369 369 '''
370 370 returns the max length of the history buffer
371 371
372 372 @return: history length
373 373 @rtype: int
374 374 '''
375 375 return len(self._IP.input_hist_raw)-1
376 376
377 377 def _getHistory(self):
378 378 '''
379 379 Get's the command string of the current history level.
380 380
381 381 @return: Historic command stri
382 382 @rtype: string
383 383 '''
384 384 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
385 385 return rv
386 386
387 387 def _pager_help(self,text):
388 388 '''
389 389 This function is used as a callback replacment to IPython help pager function
390 390
391 391 It puts the 'text' value inside the self._help_text string that can be retrived via getHelpText
392 392 function.
393 393 '''
394 394 if self._help_text == None:
395 395 self._help_text = text
396 396 else:
397 397 self._help_text += text
398 398
399 399 def _pager(self,IP,text):
400 400 '''
401 401 This function is used as a callback replacment to IPython pager function
402 402
403 403 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
404 404 function.
405 405 '''
406 406 self._doc_text = text
407 407
408 408 def _raw_input(self, prompt=''):
409 409 '''
410 410 Custom raw_input() replacement. Get's current line from console buffer.
411 411
412 412 @param prompt: Prompt to print. Here for compatability as replacement.
413 413 @type prompt: string
414 414
415 415 @return: The current command line text.
416 416 @rtype: string
417 417 '''
418 418 return self._line_to_execute
419 419
420 420 def _execute(self):
421 421 '''
422 422 Executes the current line provided by the shell object.
423 423 '''
424 424 orig_stdout = sys.stdout
425 425 sys.stdout = IPython.Shell.Term.cout
426 426
427 427 try:
428 428 line = self._IP.raw_input(None, self._iter_more)
429 429 if self._IP.autoindent:
430 430 self._IP.readline_startup_hook(None)
431 431
432 432 except KeyboardInterrupt:
433 433 self._IP.write('\nKeyboardInterrupt\n')
434 434 self._IP.resetbuffer()
435 435 # keep cache in sync with the prompt counter:
436 436 self._IP.outputcache.prompt_count -= 1
437 437
438 438 if self._IP.autoindent:
439 439 self._IP.indent_current_nsp = 0
440 440 self._iter_more = 0
441 441 except:
442 442 self._IP.showtraceback()
443 443 else:
444 444 self._iter_more = self._IP.push(line)
445 445 if (self._IP.SyntaxTB.last_syntax_error and
446 446 self._IP.rc.autoedit_syntax):
447 447 self._IP.edit_syntax_error()
448 448 if self._iter_more:
449 449 self._prompt = str(self._IP.outputcache.prompt2).strip()
450 450 if self._IP.autoindent:
451 451 self._IP.readline_startup_hook(self._IP.pre_readline)
452 452 else:
453 453 self._prompt = str(self._IP.outputcache.prompt1).strip()
454 454 self._IP.indent_current_nsp = 0 #we set indentation to 0
455 455 sys.stdout = orig_stdout
456 456
457 457 def _shell(self, ip, cmd):
458 458 '''
459 459 Replacement method to allow shell commands without them blocking.
460 460
461 461 @param ip: Ipython instance, same as self._IP
462 462 @type cmd: Ipython instance
463 463 @param cmd: Shell command to execute.
464 464 @type cmd: string
465 465 '''
466 466 stdin, stdout = os.popen4(cmd)
467 467 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
468 468 #we use print command because the shell command is called inside IPython instance and thus is
469 469 #redirected to thread cout
470 470 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
471 471 print "\x01\x1b[1;36m\x02"+result
472 472 stdout.close()
473 473 stdin.close()
@@ -1,199 +1,199 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3
4 4 import wx.aui
5 5 import wx.py
6 6 from wx.lib.wordwrap import wordwrap
7 7
8 8 from ipython_view import *
9 9 from ipython_history import *
10 10
11 11 __version__ = 0.8
12 12 __author__ = "Laurent Dufrechou"
13 13 __email__ = "laurent.dufrechou _at_ gmail.com"
14 14 __license__ = "BSD"
15 15
16 16 #-----------------------------------------
17 17 # Creating one main frame for our
18 18 # application with movables windows
19 19 #-----------------------------------------
20 20 class MyFrame(wx.Frame):
21 21 """Creating one main frame for our
22 22 application with movables windows"""
23 23 def __init__(self, parent=None, id=-1, title="WxIPython", pos=wx.DefaultPosition,
24 24 size=(800, 600), style=wx.DEFAULT_FRAME_STYLE):
25 25 wx.Frame.__init__(self, parent, id, title, pos, size, style)
26 26 self._mgr = wx.aui.AuiManager()
27 27
28 28 # notify PyAUI which frame to use
29 29 self._mgr.SetManagedWindow(self)
30 30
31 31 #create differents panels and make them persistant
32 32 self.history_panel = IPythonHistoryPanel(self)
33 33
34 34 self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg,
35 35 background_color = "BLACK")
36 36
37 37 #self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg,
38 38 # background_color = "WHITE")
39 39
40 40 self.ipython_panel.setHistoryTrackerHook(self.history_panel.write)
41 41 self.ipython_panel.setStatusTrackerHook(self.updateStatus)
42 42
43 43 self.statusbar = self.createStatus()
44 44 self.createMenu()
45 45
46 46 ########################################################################
47 47 ### add the panes to the manager
48 48 # main panels
49 49 self._mgr.AddPane(self.ipython_panel , wx.CENTER, "IPython Shell")
50 50 self._mgr.AddPane(self.history_panel , wx.RIGHT, "IPython history")
51
51
52 52 # now we specify some panel characteristics
53 53 self._mgr.GetPane(self.ipython_panel).CaptionVisible(True);
54 54 self._mgr.GetPane(self.history_panel).CaptionVisible(True);
55 55 self._mgr.GetPane(self.history_panel).MinSize((200,400));
56 56
57 57 # tell the manager to "commit" all the changes just made
58 58 self._mgr.Update()
59 59
60 60 #global event handling
61 61 self.Bind(wx.EVT_CLOSE, self.OnClose)
62 62 self.Bind(wx.EVT_MENU, self.OnClose,id=wx.ID_EXIT)
63 63 self.Bind(wx.EVT_MENU, self.OnShowIPythonPanel,id=wx.ID_HIGHEST+1)
64 64 self.Bind(wx.EVT_MENU, self.OnShowHistoryPanel,id=wx.ID_HIGHEST+2)
65 65 self.Bind(wx.EVT_MENU, self.OnShowAbout, id=wx.ID_HIGHEST+3)
66 66 self.Bind(wx.EVT_MENU, self.OnShowAllPanel,id=wx.ID_HIGHEST+6)
67 67
68 68 warn_text = 'Hello from IPython and wxPython.\n'
69 69 warn_text +='Please Note that this work is still EXPERIMENTAL\n'
70 70 warn_text +='It does NOT emulate currently all the IPython functions.\n'
71 71
72 72 dlg = wx.MessageDialog(self,
73 73 warn_text,
74 74 'Warning Box',
75 75 wx.OK | wx.ICON_INFORMATION
76 76 )
77 77 dlg.ShowModal()
78 78 dlg.Destroy()
79 79
80 80 def createMenu(self):
81 81 """local method used to create one menu bar"""
82 82
83 83 mb = wx.MenuBar()
84 84
85 85 file_menu = wx.Menu()
86 86 file_menu.Append(wx.ID_EXIT, "Exit")
87 87
88 88 view_menu = wx.Menu()
89 89 view_menu.Append(wx.ID_HIGHEST+1, "Show IPython Panel")
90 90 view_menu.Append(wx.ID_HIGHEST+2, "Show History Panel")
91 91 view_menu.AppendSeparator()
92 92 view_menu.Append(wx.ID_HIGHEST+6, "Show All")
93 93
94 94 about_menu = wx.Menu()
95 95 about_menu.Append(wx.ID_HIGHEST+3, "About")
96 96
97 97 #view_menu.AppendSeparator()
98 98 #options_menu = wx.Menu()
99 99 #options_menu.AppendCheckItem(wx.ID_HIGHEST+7, "Allow Floating")
100 100 #options_menu.AppendCheckItem(wx.ID_HIGHEST+8, "Transparent Hint")
101 101 #options_menu.AppendCheckItem(wx.ID_HIGHEST+9, "Transparent Hint Fade-in")
102 102
103 103
104 104 mb.Append(file_menu, "File")
105 105 mb.Append(view_menu, "View")
106 106 mb.Append(about_menu, "About")
107 107 #mb.Append(options_menu, "Options")
108 108
109 109 self.SetMenuBar(mb)
110 110
111 111 def createStatus(self):
112 112 statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
113 113 statusbar.SetStatusWidths([-2, -3])
114 114 statusbar.SetStatusText("Ready", 0)
115 115 statusbar.SetStatusText("WxIPython "+str(__version__), 1)
116 116 return statusbar
117 117
118 118 def updateStatus(self,text):
119 119 states = {'IDLE':'Idle',
120 120 'DO_EXECUTE_LINE':'Send command',
121 121 'WAIT_END_OF_EXECUTION':'Running command',
122 122 'SHOW_DOC':'Showing doc',
123 123 'SHOW_PROMPT':'Showing prompt'}
124 124 self.statusbar.SetStatusText(states[text], 0)
125 125
126 126 def OnClose(self, event):
127 127 """#event used to close program """
128 128 # deinitialize the frame manager
129 129 self._mgr.UnInit()
130 130 self.Destroy()
131 131 event.Skip()
132 132
133 133 def OnExitDlg(self, event):
134 134 dlg = wx.MessageDialog(self, 'Are you sure you want to quit WxIPython',
135 135 'WxIPython exit',
136 136 wx.ICON_QUESTION |
137 137 wx.YES_NO | wx.NO_DEFAULT
138 138 )
139 139 if dlg.ShowModal() == wx.ID_YES:
140 140 dlg.Destroy()
141 141 self._mgr.UnInit()
142 142 self.Destroy()
143 143 dlg.Destroy()
144 144
145 145 #event to display IPython pannel
146 146 def OnShowIPythonPanel(self,event):
147 147 """ #event to display Boxpannel """
148 148 self._mgr.GetPane(self.ipython_panel).Show(True)
149 149 self._mgr.Update()
150 150 #event to display History pannel
151 151 def OnShowHistoryPanel(self,event):
152 152 self._mgr.GetPane(self.history_panel).Show(True)
153 153 self._mgr.Update()
154 154
155 155 def OnShowAllPanel(self,event):
156 156 """#event to display all Pannels"""
157 157 self._mgr.GetPane(self.ipython_panel).Show(True)
158 158 self._mgr.GetPane(self.history_panel).Show(True)
159 159 self._mgr.Update()
160 160
161 161 def OnShowAbout(self, event):
162 162 # First we create and fill the info object
163 163 info = wx.AboutDialogInfo()
164 164 info.Name = "WxIPython"
165 165 info.Version = str(__version__)
166 166 info.Copyright = "(C) 2007 Laurent Dufrechou"
167 167 info.Description = wordwrap(
168 168 "A Gui that embbed a multithreaded IPython Shell",
169 169 350, wx.ClientDC(self))
170 170 info.WebSite = ("http://ipython.scipy.org/", "IPython home page")
171 171 info.Developers = [ "Laurent Dufrechou" ]
172 172 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"
173 173 info.License = wordwrap(licenseText, 500, wx.ClientDC(self))
174 174
175 175 # Then we call wx.AboutBox giving it that info object
176 176 wx.AboutBox(info)
177 177
178 178 #-----------------------------------------
179 179 #Creating our application
180 180 #-----------------------------------------
181 181 class MyApp(wx.PySimpleApp):
182 182 """Creating our application"""
183 183 def __init__(self):
184 184 wx.PySimpleApp.__init__(self)
185 185
186 186 self.frame = MyFrame()
187 187 self.frame.Show()
188 188
189 189 #-----------------------------------------
190 190 #Main loop
191 191 #-----------------------------------------
192 192 def main():
193 193 app = MyApp()
194 194 app.SetTopWindow(app.frame)
195 195 app.MainLoop()
196 196
197 197 #if launched as main program run this
198 198 if __name__ == '__main__':
199 199 main()
General Comments 0
You need to be logged in to leave comments. Login now