##// END OF EJS Templates
merge, fix crlf
Ville M. Vainio -
r1112:bc7ad023 merge
parent child Browse files
Show More
@@ -0,0 +1,453 b''
1 #!/usr/bin/python
2 # -*- coding: iso-8859-15 -*-
3 '''
4 Provides IPython remote instance.
5
6 @author: Laurent Dufrechou
7 laurent.dufrechou _at_ gmail.com
8 @license: BSD
9
10 All rights reserved. This program and the accompanying materials are made
11 available under the terms of the BSD which accompanies this distribution, and
12 is available at U{http://www.opensource.org/licenses/bsd-license.php}
13 '''
14
15 __version__ = 0.9
16 __author__ = "Laurent Dufrechou"
17 __email__ = "laurent.dufrechou _at_ gmail.com"
18 __license__ = "BSD"
19
20 import re
21 import sys
22 import os
23 import locale
24 import time
25 import pydoc,__builtin__,site
26 from thread_ex import ThreadEx
27 from StringIO import StringIO
28
29 try:
30 import IPython
31 except Exception,e:
32 raise "Error importing IPython (%s)" % str(e)
33
34 ##############################################################################
35 class _Helper(object):
36 """Redefine the built-in 'help'.
37 This is a wrapper around pydoc.help (with a twist).
38 """
39
40 def __init__(self,pager):
41 self._pager = pager
42
43 def __repr__(self):
44 return "Type help() for interactive help, " \
45 "or help(object) for help about object."
46
47 def __call__(self, *args, **kwds):
48 class DummyWriter(object):
49 def __init__(self,pager):
50 self._pager = pager
51
52 def write(self,data):
53 self._pager(data)
54
55 import pydoc
56 pydoc.help.output = DummyWriter(self._pager)
57 pydoc.help.interact = lambda :1
58
59 return pydoc.help(*args, **kwds)
60
61
62 ##############################################################################
63 class _CodeExecutor(ThreadEx):
64
65 def __init__(self, instance, after):
66 ThreadEx.__init__(self)
67 self.instance = instance
68 self._afterExecute=after
69
70 def run(self):
71 try:
72 self.instance._doc_text = None
73 self.instance._help_text = None
74 self.instance._execute()
75 # used for uper class to generate event after execution
76 self._afterExecute()
77
78 except KeyboardInterrupt:
79 pass
80
81
82 ##############################################################################
83 class NonBlockingIPShell(object):
84 '''
85 Create an IPython instance, running the commands in a separate,
86 non-blocking thread.
87 This allows embedding in any GUI without blockage.
88
89 Note: The ThreadEx class supports asynchroneous function call
90 via raise_exc()
91 '''
92
93 def __init__(self,argv=[],user_ns={},user_global_ns=None,
94 cin=None, cout=None, cerr=None,
95 ask_exit_handler=None):
96 '''
97 @param argv: Command line options for IPython
98 @type argv: list
99 @param user_ns: User namespace.
100 @type user_ns: dictionary
101 @param user_global_ns: User global namespace.
102 @type user_global_ns: dictionary.
103 @param cin: Console standard input.
104 @type cin: IO stream
105 @param cout: Console standard output.
106 @type cout: IO stream
107 @param cerr: Console standard error.
108 @type cerr: IO stream
109 @param exit_handler: Replacement for builtin exit() function
110 @type exit_handler: function
111 @param time_loop: Define the sleep time between two thread's loop
112 @type int
113 '''
114 #ipython0 initialisation
115 self.initIpython0(argv, user_ns, user_global_ns,
116 cin, cout, cerr,
117 ask_exit_handler)
118
119 #vars used by _execute
120 self._iter_more = 0
121 self._history_level = 0
122 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
123 self._prompt = str(self._IP.outputcache.prompt1).strip()
124
125 #thread working vars
126 self._line_to_execute = ''
127
128 #vars that will be checked by GUI loop to handle thread states...
129 #will be replaced later by PostEvent GUI funtions...
130 self._doc_text = None
131 self._help_text = None
132 self._add_button = None
133
134 def initIpython0(self, argv=[], user_ns={}, user_global_ns=None,
135 cin=None, cout=None, cerr=None,
136 ask_exit_handler=None):
137 #first we redefine in/out/error functions of IPython
138 if cin:
139 IPython.Shell.Term.cin = cin
140 if cout:
141 IPython.Shell.Term.cout = cout
142 if cerr:
143 IPython.Shell.Term.cerr = cerr
144
145 # This is to get rid of the blockage that accurs during
146 # IPython.Shell.InteractiveShell.user_setup()
147 IPython.iplib.raw_input = lambda x: None
148
149 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
150
151 excepthook = sys.excepthook
152
153 self._IP = IPython.Shell.make_IPython(
154 argv,user_ns=user_ns,
155 user_global_ns=user_global_ns,
156 embedded=True,
157 shell_class=IPython.Shell.InteractiveShell)
158
159 #we replace IPython default encoding by wx locale encoding
160 loc = locale.getpreferredencoding()
161 if loc:
162 self._IP.stdin_encoding = loc
163 #we replace the ipython default pager by our pager
164 self._IP.set_hook('show_in_pager',self._pager)
165
166 #we replace the ipython default shell command caller by our shell handler
167 self._IP.set_hook('shell_hook',self._shell)
168
169 #we replace the ipython default input command caller by our method
170 IPython.iplib.raw_input_original = self._raw_input
171 #we replace the ipython default exit command by our method
172 self._IP.exit = ask_exit_handler
173 #we replace the help command
174 self._IP.user_ns['help'] = _Helper(self._pager_help)
175
176 sys.excepthook = excepthook
177
178 #----------------------- Thread management section ----------------------
179 def doExecute(self,line):
180 """
181 Tell the thread to process the 'line' command
182 """
183
184 self._line_to_execute = line
185 #we launch the ipython line execution in a thread to make it interruptible
186 self.ce = _CodeExecutor(self,self._afterExecute)
187 self.ce.start()
188
189 #----------------------- IPython management section ----------------------
190 def getDocText(self):
191 """
192 Returns the output of the processing that need to be paged (if any)
193
194 @return: The std output string.
195 @rtype: string
196 """
197 return self._doc_text
198
199 def getHelpText(self):
200 """
201 Returns the output of the processing that need to be paged via help pager(if any)
202
203 @return: The std output string.
204 @rtype: string
205 """
206 return self._help_text
207
208 def getBanner(self):
209 """
210 Returns the IPython banner for useful info on IPython instance
211
212 @return: The banner string.
213 @rtype: string
214 """
215 return self._IP.BANNER
216
217 def getPromptCount(self):
218 """
219 Returns the prompt number.
220 Each time a user execute a line in the IPython shell the prompt count is increased
221
222 @return: The prompt number
223 @rtype: int
224 """
225 return self._IP.outputcache.prompt_count
226
227 def getPrompt(self):
228 """
229 Returns current prompt inside IPython instance
230 (Can be In [...]: ot ...:)
231
232 @return: The current prompt.
233 @rtype: string
234 """
235 return self._prompt
236
237 def getIndentation(self):
238 """
239 Returns the current indentation level
240 Usefull to put the caret at the good start position if we want to do autoindentation.
241
242 @return: The indentation level.
243 @rtype: int
244 """
245 return self._IP.indent_current_nsp
246
247 def updateNamespace(self, ns_dict):
248 '''
249 Add the current dictionary to the shell namespace.
250
251 @param ns_dict: A dictionary of symbol-values.
252 @type ns_dict: dictionary
253 '''
254 self._IP.user_ns.update(ns_dict)
255
256 def complete(self, line):
257 '''
258 Returns an auto completed line and/or posibilities for completion.
259
260 @param line: Given line so far.
261 @type line: string
262
263 @return: Line completed as for as possible,
264 and possible further completions.
265 @rtype: tuple
266 '''
267 split_line = self._complete_sep.split(line)
268 possibilities = self._IP.complete(split_line[-1])
269 if possibilities:
270
271 def _commonPrefix(str1, str2):
272 '''
273 Reduction function. returns common prefix of two given strings.
274
275 @param str1: First string.
276 @type str1: string
277 @param str2: Second string
278 @type str2: string
279
280 @return: Common prefix to both strings.
281 @rtype: string
282 '''
283 for i in range(len(str1)):
284 if not str2.startswith(str1[:i+1]):
285 return str1[:i]
286 return str1
287 common_prefix = reduce(_commonPrefix, possibilities)
288 completed = line[:-len(split_line[-1])]+common_prefix
289 else:
290 completed = line
291 return completed, possibilities
292
293 def historyBack(self):
294 '''
295 Provides one history command back.
296
297 @return: The command string.
298 @rtype: string
299 '''
300 history = ''
301 #the below while loop is used to suppress empty history lines
302 while((history == '' or history == '\n') and self._history_level >0):
303 if self._history_level>=1:
304 self._history_level -= 1
305 history = self._getHistory()
306 return history
307
308 def historyForward(self):
309 '''
310 Provides one history command forward.
311
312 @return: The command string.
313 @rtype: string
314 '''
315 history = ''
316 #the below while loop is used to suppress empty history lines
317 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
318 if self._history_level < self._getHistoryMaxIndex():
319 self._history_level += 1
320 history = self._getHistory()
321 else:
322 if self._history_level == self._getHistoryMaxIndex():
323 history = self._getHistory()
324 self._history_level += 1
325 else:
326 history = ''
327 return history
328
329 def initHistoryIndex(self):
330 '''
331 set history to last command entered
332 '''
333 self._history_level = self._getHistoryMaxIndex()+1
334
335 #----------------------- IPython PRIVATE management section --------------
336 def _afterExecute(self):
337 '''
338 Can be redefined to generate post event after excution is done
339 '''
340 pass
341
342 #def _askExit(self):
343 # '''
344 # Can be redefined to generate post event to exit the Ipython shell
345 # '''
346 # pass
347
348 def _getHistoryMaxIndex(self):
349 '''
350 returns the max length of the history buffer
351
352 @return: history length
353 @rtype: int
354 '''
355 return len(self._IP.input_hist_raw)-1
356
357 def _getHistory(self):
358 '''
359 Get's the command string of the current history level.
360
361 @return: Historic command stri
362 @rtype: string
363 '''
364 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
365 return rv
366
367 def _pager_help(self,text):
368 '''
369 This function is used as a callback replacment to IPython help pager function
370
371 It puts the 'text' value inside the self._help_text string that can be retrived via getHelpText
372 function.
373 '''
374 if self._help_text == None:
375 self._help_text = text
376 else:
377 self._help_text += text
378
379 def _pager(self,IP,text):
380 '''
381 This function is used as a callback replacment to IPython pager function
382
383 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
384 function.
385 '''
386 self._doc_text = text
387
388 def _raw_input(self, prompt=''):
389 '''
390 Custom raw_input() replacement. Get's current line from console buffer.
391
392 @param prompt: Prompt to print. Here for compatability as replacement.
393 @type prompt: string
394
395 @return: The current command line text.
396 @rtype: string
397 '''
398 return self._line_to_execute
399
400 def _execute(self):
401 '''
402 Executes the current line provided by the shell object.
403 '''
404 orig_stdout = sys.stdout
405 sys.stdout = IPython.Shell.Term.cout
406
407 try:
408 line = self._IP.raw_input(None, self._iter_more)
409 if self._IP.autoindent:
410 self._IP.readline_startup_hook(None)
411
412 except KeyboardInterrupt:
413 self._IP.write('\nKeyboardInterrupt\n')
414 self._IP.resetbuffer()
415 # keep cache in sync with the prompt counter:
416 self._IP.outputcache.prompt_count -= 1
417
418 if self._IP.autoindent:
419 self._IP.indent_current_nsp = 0
420 self._iter_more = 0
421 except:
422 self._IP.showtraceback()
423 else:
424 self._iter_more = self._IP.push(line)
425 if (self._IP.SyntaxTB.last_syntax_error and
426 self._IP.rc.autoedit_syntax):
427 self._IP.edit_syntax_error()
428 if self._iter_more:
429 self._prompt = str(self._IP.outputcache.prompt2).strip()
430 if self._IP.autoindent:
431 self._IP.readline_startup_hook(self._IP.pre_readline)
432 else:
433 self._prompt = str(self._IP.outputcache.prompt1).strip()
434 self._IP.indent_current_nsp = 0 #we set indentation to 0
435 sys.stdout = orig_stdout
436
437 def _shell(self, ip, cmd):
438 '''
439 Replacement method to allow shell commands without them blocking.
440
441 @param ip: Ipython instance, same as self._IP
442 @type cmd: Ipython instance
443 @param cmd: Shell command to execute.
444 @type cmd: string
445 '''
446 stdin, stdout = os.popen4(cmd)
447 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
448 #we use print command because the shell command is called inside IPython instance and thus is
449 #redirected to thread cout
450 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
451 print "\x01\x1b[1;36m\x02"+result
452 stdout.close()
453 stdin.close()
@@ -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 #----------------------------------------------------------------------
This diff has been collapsed as it changes many lines, (691 lines changed) Show them Hide them
@@ -1,1046 +1,715 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3 '''
4 Provides IPython WX console widget.
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 import os
34 33 import locale
35 import time
36 from ThreadEx import Thread
37 34 from StringIO import StringIO
38
39 35 try:
40 36 import IPython
41 37 except Exception,e:
42 38 raise "Error importing IPython (%s)" % str(e)
43 39
44 class IterableIPShell(Thread):
40 from ipshell_nonblocking import NonBlockingIPShell
41
42
43 class WxNonBlockingIPShell(NonBlockingIPShell):
45 44 '''
46 Create an IPython instance inside a dedicated thread.
47 Does not start a blocking event loop, instead allow single iterations.
48 This allows embedding in any GUI without blockage.
49 The thread is a slave one, in that it doesn't interact directly with the GUI.
50 Note Thread class comes from ThreadEx that supports asynchroneous function call
51 via raise_exc()
45 An NonBlockingIPShell Thread that is WX dependent.
52 46 '''
53
54 def __init__(self,argv=[],user_ns=None,user_global_ns=None,
47 def __init__(self, parent,
48 argv=[],user_ns={},user_global_ns=None,
55 49 cin=None, cout=None, cerr=None,
56 exit_handler=None,time_loop = 0.1):
57 '''
58 @param argv: Command line options for IPython
59 @type argv: list
60 @param user_ns: User namespace.
61 @type user_ns: dictionary
62 @param user_global_ns: User global namespace.
63 @type user_global_ns: dictionary.
64 @param cin: Console standard input.
65 @type cin: IO stream
66 @param cout: Console standard output.
67 @type cout: IO stream
68 @param cerr: Console standard error.
69 @type cerr: IO stream
70 @param exit_handler: Replacement for builtin exit() function
71 @type exit_handler: function
72 @param time_loop: Define the sleep time between two thread's loop
73 @type int
74 '''
75 Thread.__init__(self)
76
77 #first we redefine in/out/error functions of IPython
78 if cin:
79 IPython.Shell.Term.cin = cin
80 if cout:
81 IPython.Shell.Term.cout = cout
82 if cerr:
83 IPython.Shell.Term.cerr = cerr
84
85 # This is to get rid of the blockage that accurs during
86 # IPython.Shell.InteractiveShell.user_setup()
87 IPython.iplib.raw_input = lambda x: None
88
89 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
90
91 excepthook = sys.excepthook
92 self._IP = IPython.Shell.make_IPython(
93 argv,user_ns=user_ns,
94 user_global_ns=user_global_ns,
95 embedded=True,
96 shell_class=IPython.Shell.InteractiveShell)
97
98 #we replace IPython default encoding by wx locale encoding
99 loc = locale.getpreferredencoding()
100 if loc:
101 self._IP.stdin_encoding = loc
102 #we replace the ipython default pager by our pager
103 self._IP.set_hook('show_in_pager',self._pager)
104
105 #we replace the ipython default shell command caller by our shell handler
106 self._IP.set_hook('shell_hook',self._shell)
107
108 #we replace the ipython default input command caller by our method
109 IPython.iplib.raw_input_original = self._raw_input
110 #we replace the ipython default exit command by our method
111 self._IP.exit = self._setAskExit
112
113 sys.excepthook = excepthook
114
115 self._iter_more = 0
116 self._history_level = 0
117 self._complete_sep = re.compile('[\s\{\}\[\]\(\)]')
118 self._prompt = str(self._IP.outputcache.prompt1).strip()
119
120 #thread working vars
121 self._terminate = False
122 self._time_loop = time_loop
123 self._has_doc = False
124 self._do_execute = False
125 self._line_to_execute = ''
126 self._doc_text = None
127 self._ask_exit = False
128
129 #----------------------- Thread management section ----------------------
130 def run (self):
131 """
132 Thread main loop
133 The thread will run until self._terminate will be set to True via shutdown() function
134 Command processing can be interrupted with Instance.raise_exc(KeyboardInterrupt) call in the
135 GUI thread.
136 """
137 while(not self._terminate):
138 try:
139 if self._do_execute:
140 self._doc_text = None
141 self._execute()
142 self._do_execute = False
143
144 except KeyboardInterrupt:
145 pass
146
147 time.sleep(self._time_loop)
148
149 def shutdown(self):
150 """
151 Shutdown the tread
152 """
153 self._terminate = True
154
155 def doExecute(self,line):
156 """
157 Tell the thread to process the 'line' command
158 """
159 self._do_execute = True
160 self._line_to_execute = line
161
162 def isExecuteDone(self):
163 """
164 Returns the processing state
165 """
166 return not self._do_execute
167
168 #----------------------- IPython management section ----------------------
169 def getAskExit(self):
170 '''
171 returns the _ask_exit variable that can be checked by GUI to see if
172 IPython request an exit handling
173 '''
174 return self._ask_exit
175
176 def clearAskExit(self):
177 '''
178 clear the _ask_exit var when GUI as handled the request.
179 '''
180 self._ask_exit = False
181
182 def getDocText(self):
183 """
184 Returns the output of the processing that need to be paged (if any)
185
186 @return: The std output string.
187 @rtype: string
188 """
189 return self._doc_text
190
191 def getBanner(self):
192 """
193 Returns the IPython banner for useful info on IPython instance
194
195 @return: The banner string.
196 @rtype: string
197 """
198 return self._IP.BANNER
199
200 def getPromptCount(self):
201 """
202 Returns the prompt number.
203 Each time a user execute a line in the IPython shell the prompt count is increased
204
205 @return: The prompt number
206 @rtype: int
207 """
208 return self._IP.outputcache.prompt_count
209
210 def getPrompt(self):
211 """
212 Returns current prompt inside IPython instance
213 (Can be In [...]: ot ...:)
214
215 @return: The current prompt.
216 @rtype: string
217 """
218 return self._prompt
219
220 def getIndentation(self):
221 """
222 Returns the current indentation level
223 Usefull to put the caret at the good start position if we want to do autoindentation.
224
225 @return: The indentation level.
226 @rtype: int
227 """
228 return self._IP.indent_current_nsp
229
230 def updateNamespace(self, ns_dict):
231 '''
232 Add the current dictionary to the shell namespace.
233
234 @param ns_dict: A dictionary of symbol-values.
235 @type ns_dict: dictionary
236 '''
237 self._IP.user_ns.update(ns_dict)
238
239 def complete(self, line):
240 '''
241 Returns an auto completed line and/or posibilities for completion.
242
243 @param line: Given line so far.
244 @type line: string
245
246 @return: Line completed as for as possible,
247 and possible further completions.
248 @rtype: tuple
249 '''
250 split_line = self._complete_sep.split(line)
251 possibilities = self._IP.complete(split_line[-1])
252 if possibilities:
253
254 def _commonPrefix(str1, str2):
255 '''
256 Reduction function. returns common prefix of two given strings.
257
258 @param str1: First string.
259 @type str1: string
260 @param str2: Second string
261 @type str2: string
262
263 @return: Common prefix to both strings.
264 @rtype: string
265 '''
266 for i in range(len(str1)):
267 if not str2.startswith(str1[:i+1]):
268 return str1[:i]
269 return str1
270 common_prefix = reduce(_commonPrefix, possibilities)
271 completed = line[:-len(split_line[-1])]+common_prefix
272 else:
273 completed = line
274 return completed, possibilities
275
276 def historyBack(self):
277 '''
278 Provides one history command back.
279
280 @return: The command string.
281 @rtype: string
282 '''
283 history = ''
284 #the below while loop is used to suppress empty history lines
285 while((history == '' or history == '\n') and self._history_level >0):
286 if self._history_level>=1:
287 self._history_level -= 1
288 history = self._getHistory()
289 return history
290
291 def historyForward(self):
292 '''
293 Provides one history command forward.
294
295 @return: The command string.
296 @rtype: string
297 '''
298 history = ''
299 #the below while loop is used to suppress empty history lines
300 while((history == '' or history == '\n') and self._history_level <= self._getHistoryMaxIndex()):
301 if self._history_level < self._getHistoryMaxIndex():
302 self._history_level += 1
303 history = self._getHistory()
304 else:
305 if self._history_level == self._getHistoryMaxIndex():
306 history = self._getHistory()
307 self._history_level += 1
308 else:
309 history = ''
310 return history
311
312 def initHistoryIndex(self):
313 '''
314 set history to last command entered
315 '''
316 self._history_level = self._getHistoryMaxIndex()+1
317
318 #----------------------- IPython PRIVATE management section ----------------------
319 def _setAskExit(self):
320 '''
321 set the _ask_exit variable that can be cjhecked by GUI to see if
322 IPython request an exit handling
323 '''
324 self._ask_exit = True
325
326 def _getHistoryMaxIndex(self):
327 '''
328 returns the max length of the history buffer
329
330 @return: history length
331 @rtype: int
332 '''
333 return len(self._IP.input_hist_raw)-1
50 ask_exit_handler=None):
334 51
335 def _getHistory(self):
336 '''
337 Get's the command string of the current history level.
52 NonBlockingIPShell.__init__(self,argv,user_ns,user_global_ns,
53 cin, cout, cerr,
54 ask_exit_handler)
338 55
339 @return: Historic command string.
340 @rtype: string
341 '''
342 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
343 return rv
56 self.parent = parent
344 57
345 def _pager(self,IP,text):
346 '''
347 This function is used as a callback replacment to IPython pager function
58 self.ask_exit_callback = ask_exit_handler
59 self._IP.exit = self._askExit
348 60
349 It puts the 'text' value inside the self._doc_text string that can be retrived via getDocText
350 function.
351 '''
352 self._doc_text = text
353
354 def _raw_input(self, prompt=''):
355 '''
356 Custom raw_input() replacement. Get's current line from console buffer.
61 def addGUIShortcut(self,text,func):
62 wx.CallAfter(self.parent.add_button_handler,
63 button_info={ 'text':text,
64 'func':self.parent.doExecuteLine(func)})
357 65
358 @param prompt: Prompt to print. Here for compatability as replacement.
359 @type prompt: string
66 def _askExit(self):
67 wx.CallAfter(self.ask_exit_callback, ())
360 68
361 @return: The current command line text.
362 @rtype: string
363 '''
364 return self._line_to_execute
69 def _afterExecute(self):
70 wx.CallAfter(self.parent.evtStateExecuteDone, ())
365 71
366 def _execute(self):
367 '''
368 Executes the current line provided by the shell object.
369 '''
370 orig_stdout = sys.stdout
371 sys.stdout = IPython.Shell.Term.cout
372 72
373 try:
374 line = self._IP.raw_input(None, self._iter_more)
375 if self._IP.autoindent:
376 self._IP.readline_startup_hook(None)
377
378 except KeyboardInterrupt:
379 self._IP.write('\nKeyboardInterrupt\n')
380 self._IP.resetbuffer()
381 # keep cache in sync with the prompt counter:
382 self._IP.outputcache.prompt_count -= 1
383
384 if self._IP.autoindent:
385 self._IP.indent_current_nsp = 0
386 self._iter_more = 0
387 except:
388 self._IP.showtraceback()
389 else:
390 self._iter_more = self._IP.push(line)
391 if (self._IP.SyntaxTB.last_syntax_error and
392 self._IP.rc.autoedit_syntax):
393 self._IP.edit_syntax_error()
394 if self._iter_more:
395 self._prompt = str(self._IP.outputcache.prompt2).strip()
396 if self._IP.autoindent:
397 self._IP.readline_startup_hook(self._IP.pre_readline)
398 else:
399 self._prompt = str(self._IP.outputcache.prompt1).strip()
400 self._IP.indent_current_nsp = 0 #we set indentation to 0
401 sys.stdout = orig_stdout
402
403 def _shell(self, ip, cmd):
404 '''
405 Replacement method to allow shell commands without them blocking.
406
407 @param ip: Ipython instance, same as self._IP
408 @type cmd: Ipython instance
409 @param cmd: Shell command to execute.
410 @type cmd: string
411 '''
412 stdin, stdout = os.popen4(cmd)
413 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
414 #we use print command because the shell command is called inside IPython instance and thus is
415 #redirected to thread cout
416 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
417 print "\x01\x1b[1;36m\x02"+result
418 stdout.close()
419 stdin.close()
420
421 73 class WxConsoleView(stc.StyledTextCtrl):
422 74 '''
423 75 Specialized styled text control view for console-like workflow.
424 We use here a scintilla frontend thus it can be reused in any GUI taht supports
425 scintilla with less work.
76 We use here a scintilla frontend thus it can be reused in any GUI that
77 supports scintilla with less work.
426 78
427 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.(with Black background)
79 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.
80 (with Black background)
428 81 @type ANSI_COLORS_BLACK: dictionary
429 82
430 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.(with White background)
83 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.
84 (with White background)
431 85 @type ANSI_COLORS_WHITE: dictionary
432 86
433 87 @ivar color_pat: Regex of terminal color pattern
434 88 @type color_pat: _sre.SRE_Pattern
435 89 '''
436 ANSI_STYLES_BLACK ={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
437 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
438 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
439 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
440 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
441 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
442 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
443 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
444
445 ANSI_STYLES_WHITE ={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
446 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
447 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
448 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
449 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
450 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
451 '1;34': [12,'LIGHT BLUE'], '1;35': [13,'MEDIUM VIOLET RED'],
452 '1;36': [14,'LIGHT STEEL BLUE'], '1;37': [15,'YELLOW']}
453
454 def __init__(self,parent,prompt,intro="",background_color="BLACK",pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
90 ANSI_STYLES_BLACK={'0;30': [0,'WHITE'], '0;31': [1,'RED'],
91 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
92 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
93 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
94 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
95 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
96 '1;34': [12,'LIGHT BLUE'], '1;35':
97 [13,'MEDIUM VIOLET RED'],
98 '1;36': [14,'LIGHT STEEL BLUE'],'1;37': [15,'YELLOW']}
99
100 ANSI_STYLES_WHITE={'0;30': [0,'BLACK'], '0;31': [1,'RED'],
101 '0;32': [2,'GREEN'], '0;33': [3,'BROWN'],
102 '0;34': [4,'BLUE'], '0;35': [5,'PURPLE'],
103 '0;36': [6,'CYAN'], '0;37': [7,'LIGHT GREY'],
104 '1;30': [8,'DARK GREY'], '1;31': [9,'RED'],
105 '1;32': [10,'SEA GREEN'], '1;33': [11,'YELLOW'],
106 '1;34': [12,'LIGHT BLUE'], '1;35':
107 [13,'MEDIUM VIOLET RED'],
108 '1;36': [14,'LIGHT STEEL BLUE'],'1;37': [15,'YELLOW']}
109
110 def __init__(self,parent,prompt,intro="",background_color="BLACK",
111 pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
455 112 style=0):
456 113 '''
457 114 Initialize console view.
458 115
459 116 @param parent: Parent widget
460 117 @param prompt: User specified prompt
461 118 @type intro: string
462 119 @param intro: User specified startup introduction string
463 120 @type intro: string
464 121 @param background_color: Can be BLACK or WHITE
465 122 @type background_color: string
466 123 @param other: init param of styledTextControl (can be used as-is)
467 124 '''
468 125 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
469 126
470 ####### Scintilla configuration ##################################################
127 ####### Scintilla configuration ###################################
471 128
472 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside the widget
129 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside
130 # the widget
473 131 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
474 132 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
475 133
476 134 #we define platform specific fonts
477 135 if wx.Platform == '__WXMSW__':
478 136 faces = { 'times': 'Times New Roman',
479 137 'mono' : 'Courier New',
480 138 'helv' : 'Arial',
481 139 'other': 'Comic Sans MS',
482 140 'size' : 10,
483 141 'size2': 8,
484 142 }
485 143 elif wx.Platform == '__WXMAC__':
486 144 faces = { 'times': 'Times New Roman',
487 145 'mono' : 'Monaco',
488 146 'helv' : 'Arial',
489 147 'other': 'Comic Sans MS',
490 148 'size' : 10,
491 149 'size2': 8,
492 150 }
493 151 else:
494 152 faces = { 'times': 'Times',
495 153 'mono' : 'Courier',
496 154 'helv' : 'Helvetica',
497 155 'other': 'new century schoolbook',
498 156 'size' : 10,
499 157 'size2': 8,
500 158 }
501 159
502 160 #We draw a line at position 80
503 161 self.SetEdgeMode(stc.STC_EDGE_LINE)
504 162 self.SetEdgeColumn(80)
505 163 self.SetEdgeColour(wx.LIGHT_GREY)
506 164
507 165 #self.SetViewWhiteSpace(True)
508 166 #self.SetViewEOL(True)
509 167 self.SetEOLMode(stc.STC_EOL_CRLF)
510 168 #self.SetWrapMode(stc.STC_WRAP_CHAR)
511 169 #self.SetWrapMode(stc.STC_WRAP_WORD)
512 170 self.SetBufferedDraw(True)
513 171 #self.SetUseAntiAliasing(True)
514 172 self.SetLayoutCache(stc.STC_CACHE_PAGE)
515 173
516 174 self.EnsureCaretVisible()
517 175
518 176 self.SetMargins(3,3) #text is moved away from border with 3px
519 177 # Suppressing Scintilla margins
520 178 self.SetMarginWidth(0,0)
521 179 self.SetMarginWidth(1,0)
522 180 self.SetMarginWidth(2,0)
523 181
524 182 # make some styles
525 183 if background_color != "BLACK":
526 184 self.background_color = "WHITE"
527 185 self.SetCaretForeground("BLACK")
528 186 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
529 187 else:
530 188 self.background_color = background_color
531 189 self.SetCaretForeground("WHITE")
532 190 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
533 191
534 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "fore:%s,back:%s,size:%d,face:%s" % (self.ANSI_STYLES['0;30'][1],
535 self.background_color,
536 faces['size'], faces['mono']))
192 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
193 "fore:%s,back:%s,size:%d,face:%s"
194 % (self.ANSI_STYLES['0;30'][1],
195 self.background_color,
196 faces['size'], faces['mono']))
537 197 self.StyleClearAll()
538 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FF0000,back:#0000FF,bold")
539 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
198 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
199 "fore:#FF0000,back:#0000FF,bold")
200 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
201 "fore:#000000,back:#FF0000,bold")
540 202
541 203 for style in self.ANSI_STYLES.values():
542 204 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
543 205
544 206 #######################################################################
545 207
546 208 self.indent = 0
547 209 self.prompt_count = 0
548 210 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
549 211
550 212 self.write(intro)
551 213 self.setPrompt(prompt)
552 214 self.showPrompt()
553 215
554 216 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress, self)
555 #self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
556 217
557 218 def write(self, text):
558 219 '''
559 220 Write given text to buffer.
560 221
561 222 @param text: Text to append.
562 223 @type text: string
563 224 '''
564 225 segments = self.color_pat.split(text)
565 226 segment = segments.pop(0)
566 227 self.StartStyling(self.getCurrentLineEnd(),0xFF)
567 228 self.AppendText(segment)
568 229
569 230 if segments:
570 231 ansi_tags = self.color_pat.findall(text)
571 232
572 233 for tag in ansi_tags:
573 234 i = segments.index(tag)
574 235 self.StartStyling(self.getCurrentLineEnd(),0xFF)
575 236 self.AppendText(segments[i+1])
576 237
577 238 if tag != '0':
578 239 self.SetStyling(len(segments[i+1]),self.ANSI_STYLES[tag][0])
579 240
580 241 segments.pop(i)
581 242
582 243 self.moveCursor(self.getCurrentLineEnd())
583 244
584 245 def getPromptLen(self):
585 246 '''
586 247 Return the length of current prompt
587 248 '''
588 249 return len(str(self.prompt_count)) + 7
589 250
590 251 def setPrompt(self,prompt):
591 252 self.prompt = prompt
592 253
593 254 def setIndentation(self,indentation):
594 255 self.indent = indentation
595 256
596 257 def setPromptCount(self,count):
597 258 self.prompt_count = count
598 259
599 260 def showPrompt(self):
600 261 '''
601 262 Prints prompt at start of line.
602 263
603 264 @param prompt: Prompt to print.
604 265 @type prompt: string
605 266 '''
606 267 self.write(self.prompt)
607 268 #now we update the position of end of prompt
608 269 self.current_start = self.getCurrentLineEnd()
609 270
610 271 autoindent = self.indent*' '
611 272 autoindent = autoindent.replace(' ','\t')
612 273 self.write(autoindent)
613 274
614 275 def changeLine(self, text):
615 276 '''
616 277 Replace currently entered command line with given text.
617 278
618 279 @param text: Text to use as replacement.
619 280 @type text: string
620 281 '''
621 282 self.SetSelection(self.getCurrentPromptStart(),self.getCurrentLineEnd())
622 283 self.ReplaceSelection(text)
623 284 self.moveCursor(self.getCurrentLineEnd())
624 285
625 286 def getCurrentPromptStart(self):
626 287 return self.current_start
627 288
628 289 def getCurrentLineStart(self):
629 290 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
630 291
631 292 def getCurrentLineEnd(self):
632 293 return self.GetLength()
633 294
634 295 def getCurrentLine(self):
635 296 '''
636 297 Get text in current command line.
637 298
638 299 @return: Text of current command line.
639 300 @rtype: string
640 301 '''
641 302 return self.GetTextRange(self.getCurrentPromptStart(),
642 303 self.getCurrentLineEnd())
643 304
644 305 def showReturned(self, text):
645 306 '''
646 307 Show returned text from last command and print new prompt.
647 308
648 309 @param text: Text to show.
649 310 @type text: string
650 311 '''
651 312 self.write('\n'+text)
652 313 if text:
653 314 self.write('\n')
654 315 self.showPrompt()
655 316
656 317 def moveCursorOnNewValidKey(self):
657 318 #If cursor is at wrong position put it at last line...
658 319 if self.GetCurrentPos() < self.getCurrentPromptStart():
659 320 self.GotoPos(self.getCurrentPromptStart())
660 321
661 322 def removeFromTo(self,from_pos,to_pos):
662 323 if from_pos < to_pos:
663 324 self.SetSelection(from_pos,to_pos)
664 325 self.DeleteBack()
665 326
666 327 def removeCurrentLine(self):
667 328 self.LineDelete()
668 329
669 330 def moveCursor(self,position):
670 331 self.GotoPos(position)
671 332
672 333 def getCursorPos(self):
673 334 return self.GetCurrentPos()
674 335
675 336 def selectFromTo(self,from_pos,to_pos):
676 337 self.SetSelectionStart(from_pos)
677 338 self.SetSelectionEnd(to_pos)
678 339
679 340 def writeHistory(self,history):
680 341 self.removeFromTo(self.getCurrentPromptStart(),self.getCurrentLineEnd())
681 342 self.changeLine(history)
682 343
683 344 def writeCompletion(self, possibilities):
684 345 max_len = len(max(possibilities,key=len))
685 346 max_symbol =' '*max_len
686 347
687 348 #now we check how much symbol we can put on a line...
688 349 cursor_pos = self.getCursorPos()
689 350 test_buffer = max_symbol + ' '*4
690 351 current_lines = self.GetLineCount()
691 352
692 353 allowed_symbols = 80/len(test_buffer)
693 354 if allowed_symbols == 0:
694 355 allowed_symbols = 1
695 356
696 357 pos = 1
697 358 buf = ''
698 359 for symbol in possibilities:
699 360 #buf += symbol+'\n'#*spaces)
700 361 if pos<allowed_symbols:
701 362 spaces = max_len - len(symbol) + 4
702 363 buf += symbol+' '*spaces
703 364 pos += 1
704 365 else:
705 366 buf+=symbol+'\n'
706 367 pos = 1
707 368 self.write(buf)
708 369
709 370 def _onKeypress(self, event, skip=True):
710 371 '''
711 372 Key press callback used for correcting behavior for console-like
712 373 interfaces. For example 'home' should go to prompt, not to begining of
713 374 line.
714 375
715 376 @param widget: Widget that key press accored in.
716 377 @type widget: gtk.Widget
717 378 @param event: Event object
718 379 @type event: gtk.gdk.Event
719 380
720 381 @return: Return True if event as been catched.
721 382 @rtype: boolean
722 383 '''
723
384
724 385 if event.GetKeyCode() == wx.WXK_HOME:
725 386 if event.Modifiers == wx.MOD_NONE:
726 387 self.moveCursorOnNewValidKey()
727 388 self.moveCursor(self.getCurrentPromptStart())
728 389 return True
729 390 elif event.Modifiers == wx.MOD_SHIFT:
730 391 self.moveCursorOnNewValidKey()
731 392 self.selectFromTo(self.getCurrentPromptStart(),self.getCursorPos())
732 393 return True
733 394 else:
734 395 return False
735 396
736 397 elif event.GetKeyCode() == wx.WXK_LEFT:
737 398 if event.Modifiers == wx.MOD_NONE:
738 399 self.moveCursorOnNewValidKey()
739 400
740 401 self.moveCursor(self.getCursorPos()-1)
741 402 if self.getCursorPos() < self.getCurrentPromptStart():
742 403 self.moveCursor(self.getCurrentPromptStart())
743 404 return True
744 405
745 406 elif event.GetKeyCode() == wx.WXK_BACK:
746 407 self.moveCursorOnNewValidKey()
747 if self.getCursorPos() > self.getCurrentPromptStart():
408 if self.getCursorPos() > self.getCurrentPromptStart():
748 409 self.removeFromTo(self.getCursorPos()-1,self.getCursorPos())
749 return True
410 return True
750 411
751 412 if skip:
752 413 if event.GetKeyCode() not in [wx.WXK_PAGEUP,wx.WXK_PAGEDOWN] and event.Modifiers == wx.MOD_NONE:
753 414 self.moveCursorOnNewValidKey()
754 415
755 416 event.Skip()
756 417 return True
757 418 return False
758 419
759 420 def OnUpdateUI(self, evt):
760 421 # check for matching braces
761 422 braceAtCaret = -1
762 423 braceOpposite = -1
763 424 charBefore = None
764 425 caretPos = self.GetCurrentPos()
765 426
766 427 if caretPos > 0:
767 428 charBefore = self.GetCharAt(caretPos - 1)
768 429 styleBefore = self.GetStyleAt(caretPos - 1)
769 430
770 431 # check before
771 432 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
772 433 braceAtCaret = caretPos - 1
773 434
774 435 # check after
775 436 if braceAtCaret < 0:
776 437 charAfter = self.GetCharAt(caretPos)
777 438 styleAfter = self.GetStyleAt(caretPos)
778 439
779 440 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
780 441 braceAtCaret = caretPos
781 442
782 443 if braceAtCaret >= 0:
783 444 braceOpposite = self.BraceMatch(braceAtCaret)
784 445
785 446 if braceAtCaret != -1 and braceOpposite == -1:
786 447 self.BraceBadLight(braceAtCaret)
787 448 else:
788 449 self.BraceHighlight(braceAtCaret, braceOpposite)
789 450 #pt = self.PointFromPosition(braceOpposite)
790 451 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
791 452 #print pt
792 453 #self.Refresh(False)
793 454
794 class WxIPythonViewPanel(wx.Panel):
455 class IPShellWidget(wx.Panel):
795 456 '''
796 457 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
797 If you want to port this to any other GUI toolkit, just replace the WxConsoleView
798 by YOURGUIConsoleView and make YOURGUIIPythonView derivate from whatever container you want.
799 I've choosed to derivate from a wx.Panel because it seems to be ore usefull
800 Any idea to make it more 'genric' welcomed.
458 If you want to port this to any other GUI toolkit, just replace the
459 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
460 from whatever container you want. I've choosed to derivate from a wx.Panel
461 because it seems to be more useful
462 Any idea to make it more 'generic' welcomed.
801 463 '''
802 def __init__(self,parent,exit_handler=None,intro=None,background_color="BLACK"):
464
465 def __init__(self, parent, ask_exit_handler=None, intro=None,
466 background_color="BLACK", add_button_handler=None,
467 wx_ip_shell=None,
468 ):
803 469 '''
804 470 Initialize.
805 471 Instanciate an IPython thread.
806 472 Instanciate a WxConsoleView.
807 473 Redirect I/O to console.
808 474 '''
809 475 wx.Panel.__init__(self,parent,-1)
810 476
811 ### IPython thread instanciation ###
477 ### IPython non blocking shell instanciation ###
812 478 self.cout = StringIO()
813 self.IP = IterableIPShell(cout=self.cout,cerr=self.cout,
814 exit_handler = exit_handler,
815 time_loop = 0.1)
816 self.IP.start()
817
479
480 self.add_button_handler = add_button_handler
481 self.ask_exit_handler = ask_exit_handler
482
483 if wx_ip_shell is not None:
484 self.IP = wx_ip_shell
485 else:
486 self.IP = WxNonBlockingIPShell(self,
487 cout=self.cout,cerr=self.cout,
488 ask_exit_handler = ask_exit_handler)
489
818 490 ### IPython wx console view instanciation ###
819 491 #If user didn't defined an intro text, we create one for him
820 #If you really wnat an empty intrp just call wxIPythonViewPanel with intro=''
492 #If you really wnat an empty intrp just call wxIPythonViewPanel
493 #with intro=''
821 494 if intro == None:
822 495 welcome_text = "Welcome to WxIPython Shell.\n\n"
823 496 welcome_text+= self.IP.getBanner()
824 497 welcome_text+= "!command -> Execute command in shell\n"
825 498 welcome_text+= "TAB -> Autocompletion\n"
826 499
827 500 self.text_ctrl = WxConsoleView(self,
828 501 self.IP.getPrompt(),
829 502 intro=welcome_text,
830 503 background_color=background_color)
831 504
832 505 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress, self.text_ctrl)
833 506
834 507 ### making the layout of the panel ###
835 508 sizer = wx.BoxSizer(wx.VERTICAL)
836 509 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
837 510 self.SetAutoLayout(True)
838 511 sizer.Fit(self)
839 512 sizer.SetSizeHints(self)
840 513 self.SetSizer(sizer)
841 514 #and we focus on the widget :)
842 515 self.SetFocus()
843 516
844 ### below are the thread communication variable ###
845 # the IPython thread is managed via unidirectional communication.
846 # It's a thread slave that can't interact by itself with the GUI.
847 # When the GUI event loop is done runStateMachine() is called and the thread sate is then
848 # managed.
849
850 #Initialize the state machine #kept for information
851 #self.states = ['IDLE',
852 # 'DO_EXECUTE_LINE',
853 # 'WAIT_END_OF_EXECUTION',
854 # 'SHOW_DOC',
855 # 'SHOW_PROMPT']
856
857 self.cur_state = 'IDLE'
858 self.pager_state = 'DONE'
859 #wx.CallAfter(self.runStateMachine)
860
861 # This creates a new Event class and a EVT binder function
862 (self.AskExitEvent, EVT_ASK_EXIT) = wx.lib.newevent.NewEvent()
863
864 self.Bind(wx.EVT_IDLE, self.runStateMachine)
865 self.Bind(EVT_ASK_EXIT, exit_handler)
866
867 def __del__(self):
868 self.IP.shutdown()
869 self.IP.join()
870 WxConsoleView.__del__()
871
872 #---------------------------- IPython Thread Management ---------------------------------------
873 def runStateMachine(self,event):
874 #print >>self.sys_stdout,"state:",self.cur_state
517 #widget state management (for key handling different cases)
518 self.setCurrentState('IDLE')
519 self.pager_state = 'DONE'
520
521 #---------------------- IPython Thread Management ------------------------
522 def stateDoExecuteLine(self):
523 #print >>sys.__stdout__,"command:",self.getCurrentLine()
524 line=self.text_ctrl.getCurrentLine()
525 self.IP.doExecute(line.replace('\t',' '*4))
526 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
527 self.setCurrentState('WAIT_END_OF_EXECUTION')
528
529 def evtStateExecuteDone(self,evt):
530 self.doc = self.IP.getDocText()
531 self.help = self.IP.getHelpText()
532 if self.doc:
533 self.pager_lines = self.doc[7:].split('\n')
534 self.pager_state = 'INIT'
535 self.setCurrentState('SHOW_DOC')
536 self.pager(self.doc)
537 elif self.help:
538 self.pager_lines = self.help.split('\n')
539 self.pager_state = 'INIT'
540 self.setCurrentState('SHOW_DOC')
541 self.pager(self.help)
542 else:
543 self.stateShowPrompt()
544
545 def stateShowPrompt(self):
546 self.setCurrentState('SHOW_PROMPT')
547 self.text_ctrl.setPrompt(self.IP.getPrompt())
548 self.text_ctrl.setIndentation(self.IP.getIndentation())
549 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
550 rv = self.cout.getvalue()
551 if rv: rv = rv.strip('\n')
552 self.text_ctrl.showReturned(rv)
553 self.cout.truncate(0)
554 self.IP.initHistoryIndex()
555 self.setCurrentState('IDLE')
556
557 def setCurrentState(self, state):
558 self.cur_state = state
875 559 self.updateStatusTracker(self.cur_state)
876 560
877 if self.cur_state == 'DO_EXECUTE_LINE':
878 #print >>self.sys_stdout,"command:",self.getCurrentLine()
879 self.IP.doExecute(self.text_ctrl.getCurrentLine().replace('\t',' '*4))
880 self.updateHistoryTracker(self.text_ctrl.getCurrentLine())
881 self.cur_state = 'WAIT_END_OF_EXECUTION'
882
883 if self.cur_state == 'WAIT_END_OF_EXECUTION':
884 if self.IP.isExecuteDone():
885 self.doc = self.IP.getDocText()
886 if self.IP.getAskExit():
887 evt = self.AskExitEvent()
888 wx.PostEvent(self, evt)
889 self.IP.clearAskExit()
890 if self.doc:
891 self.pager_state = 'INIT'
892 self.cur_state = 'SHOW_DOC'
893 else:
894 self.cur_state = 'SHOW_PROMPT'
895
896 if self.cur_state == 'SHOW_PROMPT':
897 self.text_ctrl.setPrompt(self.IP.getPrompt())
898 self.text_ctrl.setIndentation(self.IP.getIndentation())
899 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
900 rv = self.cout.getvalue()
901 if rv: rv = rv.strip('\n')
902 self.text_ctrl.showReturned(rv)
903 self.cout.truncate(0)
904 self.IP.initHistoryIndex()
905 self.cur_state = 'IDLE'
906
907 if self.cur_state == 'SHOW_DOC':
908 self.pager(self.doc)
909 if self.pager_state == 'DONE':
910 self.cur_state = 'SHOW_PROMPT'
911
912 event.Skip()
913
914 561 #---------------------------- IPython pager ---------------------------------------
915 562 def pager(self,text):#,start=0,screen_lines=0,pager_cmd = None):
916 if self.pager_state == 'WAITING':
917 #print >>self.sys_stdout,"PAGER waiting"
918 return
919
920 if self.pager_state == 'INIT':
921 #print >>self.sys_stdout,"PAGER state:",self.pager_state
922 self.pager_lines = text[7:].split('\n')
923 self.pager_nb_lines = len(self.pager_lines)
563
564 if self.pager_state == 'INIT':
565 #print >>sys.__stdout__,"PAGER state:",self.pager_state
566 self.pager_nb_lines = len(self.pager_lines)
924 567 self.pager_index = 0
925 568 self.pager_do_remove = False
926 569 self.text_ctrl.write('\n')
927 570 self.pager_state = 'PROCESS_LINES'
928
571
929 572 if self.pager_state == 'PROCESS_LINES':
930 #print >>self.sys_stdout,"PAGER state:",self.pager_state
573 #print >>sys.__stdout__,"PAGER state:",self.pager_state
931 574 if self.pager_do_remove == True:
932 575 self.text_ctrl.removeCurrentLine()
933 576 self.pager_do_remove = False
934 577
935 578 if self.pager_nb_lines > 10:
936 #print >>self.sys_stdout,"PAGER processing 10 lines"
579 #print >>sys.__stdout__,"PAGER processing 10 lines"
937 580 if self.pager_index > 0:
938 581 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
939 582 else:
940 583 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
941 584
942 585 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
943 586 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
944 587 self.pager_index += 10
945 588 self.pager_nb_lines -= 10
946 589 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
947 590 self.pager_do_remove = True
948 591 self.pager_state = 'WAITING'
949 592 return
950 593 else:
951 #print >>self.sys_stdout,"PAGER processing last lines"
594 #print >>sys.__stdout__,"PAGER processing last lines"
952 595 if self.pager_nb_lines > 0:
953 596 if self.pager_index > 0:
954 597 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
955 598 else:
956 599 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
957 600
958 601 self.pager_index += 1
959 602 self.pager_nb_lines -= 1
960 if self.pager_nb_lines > 0:
961 for line in self.pager_lines[self.pager_index:]:
962 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
963 self.pager_nb_lines = 0
964 self.pager_state = 'DONE'
603 if self.pager_nb_lines > 0:
604 for line in self.pager_lines[self.pager_index:]:
605 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
606 self.pager_nb_lines = 0
607 self.pager_state = 'DONE'
608 self.stateShowPrompt()
965 609
966 #---------------------------- Key Handler --------------------------------------------
610 #------------------------ Key Handler ------------------------------------
967 611 def keyPress(self, event):
968 612 '''
969 613 Key press callback with plenty of shell goodness, like history,
970 614 autocompletions, etc.
971 615 '''
972
616
973 617 if event.GetKeyCode() == ord('C'):
974 618 if event.Modifiers == wx.MOD_CONTROL:
975 619 if self.cur_state == 'WAIT_END_OF_EXECUTION':
976 620 #we raise an exception inside the IPython thread container
977 self.IP.raise_exc(KeyboardInterrupt)
621 self.IP.ce.raise_exc(KeyboardInterrupt)
978 622 return
979 623
980 624 if event.KeyCode == wx.WXK_RETURN:
981 625 if self.cur_state == 'IDLE':
982 626 #we change the state ot the state machine
983 self.cur_state = 'DO_EXECUTE_LINE'
627 self.setCurrentState('DO_EXECUTE_LINE')
628 self.stateDoExecuteLine()
984 629 return
985 630 if self.pager_state == 'WAITING':
986 631 self.pager_state = 'PROCESS_LINES'
632 self.pager(self.doc)
987 633 return
988 634
989 635 if event.GetKeyCode() in [ord('q'),ord('Q')]:
990 636 if self.pager_state == 'WAITING':
991 637 self.pager_state = 'DONE'
638 self.stateShowPrompt()
992 639 return
993 640
994 641 #scroll_position = self.text_ctrl.GetScrollPos(wx.VERTICAL)
995 if self.cur_state == 'IDLE':
642 if self.cur_state == 'IDLE':
996 643 if event.KeyCode == wx.WXK_UP:
997 644 history = self.IP.historyBack()
998 645 self.text_ctrl.writeHistory(history)
999 646 return
1000 647 if event.KeyCode == wx.WXK_DOWN:
1001 648 history = self.IP.historyForward()
1002 649 self.text_ctrl.writeHistory(history)
1003 650 return
1004 651 if event.KeyCode == wx.WXK_TAB:
1005 652 #if line empty we disable tab completion
1006 653 if not self.text_ctrl.getCurrentLine().strip():
1007 654 self.text_ctrl.write('\t')
1008 655 return
1009 656 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
1010 657 if len(possibilities) > 1:
1011 658 cur_slice = self.text_ctrl.getCurrentLine()
1012 659 self.text_ctrl.write('\n')
1013 660 self.text_ctrl.writeCompletion(possibilities)
1014 661 self.text_ctrl.write('\n')
1015 662
1016 663 self.text_ctrl.showPrompt()
1017 664 self.text_ctrl.write(cur_slice)
1018 665 self.text_ctrl.changeLine(completed or cur_slice)
1019 666
1020 667 return
1021 668 event.Skip()
1022
1023 #---------------------------- Hook Section --------------------------------------------
669
670 #------------------------ Hook Section -----------------------------------
1024 671 def updateHistoryTracker(self,command_line):
1025 672 '''
1026 673 Default history tracker (does nothing)
1027 674 '''
1028 675 pass
1029 676
1030 677 def setHistoryTrackerHook(self,func):
1031 678 '''
1032 679 Define a new history tracker
1033 680 '''
1034 681 self.updateHistoryTracker = func
682
1035 683 def updateStatusTracker(self,status):
1036 684 '''
1037 685 Default status tracker (does nothing)
1038 686 '''
1039 687 pass
1040 688
1041 689 def setStatusTrackerHook(self,func):
1042 690 '''
1043 691 Define a new status tracker
1044 692 '''
1045 693 self.updateStatusTracker = func
1046
694
695
696 if __name__ == '__main__':
697 # Some simple code to test the shell widget.
698 class MainWindow(wx.Frame):
699 def __init__(self, parent, id, title):
700 wx.Frame.__init__(self, parent, id, title, size=(300,250))
701 self._sizer = wx.BoxSizer(wx.VERTICAL)
702 self.shell = IPShellWidget(self)
703 self._sizer.Add(self.shell, 1, wx.EXPAND)
704 self.SetSizer(self._sizer)
705 self.SetAutoLayout(1)
706 self.Show(True)
707
708 app = wx.PySimpleApp()
709 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
710 frame.SetSize((780, 460))
711 shell = frame.shell
712
713 app.MainLoop()
714
715
@@ -1,45 +1,50 b''
1 """
2 Thread subclass that can deal with asynchronously function calls via
3 raise_exc.
4 """
5
1 6 import threading
2 7 import inspect
3 8 import ctypes
4 9
5 10
6 11 def _async_raise(tid, exctype):
7 12 """raises the exception, performs cleanup if needed"""
8 13 if not inspect.isclass(exctype):
9 14 raise TypeError("Only types can be raised (not instances)")
10 15 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
11 16 if res == 0:
12 17 raise ValueError("invalid thread id")
13 18 elif res != 1:
14 19 # """if it returns a number greater than one, you're in trouble,
15 20 # and you should call it again with exc=NULL to revert the effect"""
16 21 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
17 22 raise SystemError("PyThreadState_SetAsyncExc failed")
18 23
19 24
20 class Thread(threading.Thread):
25 class ThreadEx(threading.Thread):
21 26 def _get_my_tid(self):
22 27 """determines this (self's) thread id"""
23 28 if not self.isAlive():
24 29 raise threading.ThreadError("the thread is not active")
25 30
26 31 # do we have it cached?
27 32 if hasattr(self, "_thread_id"):
28 33 return self._thread_id
29 34
30 35 # no, look for it in the _active dict
31 36 for tid, tobj in threading._active.items():
32 37 if tobj is self:
33 38 self._thread_id = tid
34 39 return tid
35 40
36 41 raise AssertionError("could not determine the thread's id")
37 42
38 43 def raise_exc(self, exctype):
39 44 """raises the given exception type in the context of this thread"""
40 45 _async_raise(self._get_my_tid(), exctype)
41 46
42 47 def kill(self):
43 48 """raises SystemExit in the context of the given thread, which should
44 49 cause the thread to exit silently (unless caught)"""
45 50 self.raise_exc(SystemExit)
@@ -1,201 +1,202 b''
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3
4 4 import wx.aui
5 import wx.py
5
6 #used for about dialog
6 7 from wx.lib.wordwrap import wordwrap
7 8
8 from ipython_view import *
9 from ipython_history import *
9 #used for ipython GUI objects
10 from IPython.gui.wx.ipython_view import IPShellWidget
11 from IPython.gui.wx.ipython_history import IPythonHistoryPanel
10 12
11 13 __version__ = 0.8
12 14 __author__ = "Laurent Dufrechou"
13 15 __email__ = "laurent.dufrechou _at_ gmail.com"
14 16 __license__ = "BSD"
15 17
16 18 #-----------------------------------------
17 19 # Creating one main frame for our
18 20 # application with movables windows
19 21 #-----------------------------------------
20 22 class MyFrame(wx.Frame):
21 23 """Creating one main frame for our
22 24 application with movables windows"""
23 def __init__(self, parent=None, id=-1, title="WxIPython", pos=wx.DefaultPosition,
25 def __init__(self, parent=None, id=-1, title="WxIPython",
26 pos=wx.DefaultPosition,
24 27 size=(800, 600), style=wx.DEFAULT_FRAME_STYLE):
25 28 wx.Frame.__init__(self, parent, id, title, pos, size, style)
26 29 self._mgr = wx.aui.AuiManager()
27 30
28 31 # notify PyAUI which frame to use
29 32 self._mgr.SetManagedWindow(self)
30 33
31 34 #create differents panels and make them persistant
32 35 self.history_panel = IPythonHistoryPanel(self)
33 36
34 self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg,
37 self.ipython_panel = IPShellWidget(self,self.OnExitDlg,
35 38 background_color = "BLACK")
36 39
37 40 #self.ipython_panel = WxIPythonViewPanel(self,self.OnExitDlg,
38 41 # background_color = "WHITE")
39
42
40 43 self.ipython_panel.setHistoryTrackerHook(self.history_panel.write)
41 44 self.ipython_panel.setStatusTrackerHook(self.updateStatus)
42
45
43 46 self.statusbar = self.createStatus()
44 47 self.createMenu()
45 48
46 49 ########################################################################
47 50 ### add the panes to the manager
48 51 # main panels
49 52 self._mgr.AddPane(self.ipython_panel , wx.CENTER, "IPython Shell")
50 53 self._mgr.AddPane(self.history_panel , wx.RIGHT, "IPython history")
51
54
52 55 # now we specify some panel characteristics
53 56 self._mgr.GetPane(self.ipython_panel).CaptionVisible(True);
54 57 self._mgr.GetPane(self.history_panel).CaptionVisible(True);
55 58 self._mgr.GetPane(self.history_panel).MinSize((200,400));
56
57
58 59
59 60 # tell the manager to "commit" all the changes just made
60 61 self._mgr.Update()
61 62
62 63 #global event handling
63 64 self.Bind(wx.EVT_CLOSE, self.OnClose)
64 65 self.Bind(wx.EVT_MENU, self.OnClose,id=wx.ID_EXIT)
65 66 self.Bind(wx.EVT_MENU, self.OnShowIPythonPanel,id=wx.ID_HIGHEST+1)
66 67 self.Bind(wx.EVT_MENU, self.OnShowHistoryPanel,id=wx.ID_HIGHEST+2)
67 68 self.Bind(wx.EVT_MENU, self.OnShowAbout, id=wx.ID_HIGHEST+3)
68 69 self.Bind(wx.EVT_MENU, self.OnShowAllPanel,id=wx.ID_HIGHEST+6)
69 70
70 71 warn_text = 'Hello from IPython and wxPython.\n'
71 72 warn_text +='Please Note that this work is still EXPERIMENTAL\n'
72 73 warn_text +='It does NOT emulate currently all the IPython functions.\n'
73 74
74 75 dlg = wx.MessageDialog(self,
75 76 warn_text,
76 77 'Warning Box',
77 78 wx.OK | wx.ICON_INFORMATION
78 79 )
79 80 dlg.ShowModal()
80 81 dlg.Destroy()
81 82
82 83 def createMenu(self):
83 84 """local method used to create one menu bar"""
84 85
85 86 mb = wx.MenuBar()
86 87
87 88 file_menu = wx.Menu()
88 89 file_menu.Append(wx.ID_EXIT, "Exit")
89 90
90 91 view_menu = wx.Menu()
91 92 view_menu.Append(wx.ID_HIGHEST+1, "Show IPython Panel")
92 93 view_menu.Append(wx.ID_HIGHEST+2, "Show History Panel")
93 94 view_menu.AppendSeparator()
94 95 view_menu.Append(wx.ID_HIGHEST+6, "Show All")
95 96
96 97 about_menu = wx.Menu()
97 98 about_menu.Append(wx.ID_HIGHEST+3, "About")
98 99
99 100 #view_menu.AppendSeparator()
100 101 #options_menu = wx.Menu()
101 102 #options_menu.AppendCheckItem(wx.ID_HIGHEST+7, "Allow Floating")
102 103 #options_menu.AppendCheckItem(wx.ID_HIGHEST+8, "Transparent Hint")
103 104 #options_menu.AppendCheckItem(wx.ID_HIGHEST+9, "Transparent Hint Fade-in")
104 105
105 106
106 107 mb.Append(file_menu, "File")
107 108 mb.Append(view_menu, "View")
108 109 mb.Append(about_menu, "About")
109 110 #mb.Append(options_menu, "Options")
110 111
111 112 self.SetMenuBar(mb)
112 113
113 114 def createStatus(self):
114 115 statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
115 116 statusbar.SetStatusWidths([-2, -3])
116 117 statusbar.SetStatusText("Ready", 0)
117 118 statusbar.SetStatusText("WxIPython "+str(__version__), 1)
118 119 return statusbar
119 120
120 121 def updateStatus(self,text):
121 122 states = {'IDLE':'Idle',
122 123 'DO_EXECUTE_LINE':'Send command',
123 124 'WAIT_END_OF_EXECUTION':'Running command',
124 125 'SHOW_DOC':'Showing doc',
125 126 'SHOW_PROMPT':'Showing prompt'}
126 127 self.statusbar.SetStatusText(states[text], 0)
127 128
128 129 def OnClose(self, event):
129 130 """#event used to close program """
130 131 # deinitialize the frame manager
131 132 self._mgr.UnInit()
132 133 self.Destroy()
133 134 event.Skip()
134 135
135 136 def OnExitDlg(self, event):
136 137 dlg = wx.MessageDialog(self, 'Are you sure you want to quit WxIPython',
137 138 'WxIPython exit',
138 139 wx.ICON_QUESTION |
139 140 wx.YES_NO | wx.NO_DEFAULT
140 141 )
141 142 if dlg.ShowModal() == wx.ID_YES:
142 143 dlg.Destroy()
143 144 self._mgr.UnInit()
144 145 self.Destroy()
145 146 dlg.Destroy()
146 147
147 148 #event to display IPython pannel
148 149 def OnShowIPythonPanel(self,event):
149 150 """ #event to display Boxpannel """
150 151 self._mgr.GetPane(self.ipython_panel).Show(True)
151 152 self._mgr.Update()
152 153 #event to display History pannel
153 154 def OnShowHistoryPanel(self,event):
154 155 self._mgr.GetPane(self.history_panel).Show(True)
155 156 self._mgr.Update()
156 157
157 158 def OnShowAllPanel(self,event):
158 159 """#event to display all Pannels"""
159 160 self._mgr.GetPane(self.ipython_panel).Show(True)
160 161 self._mgr.GetPane(self.history_panel).Show(True)
161 162 self._mgr.Update()
162 163
163 164 def OnShowAbout(self, event):
164 165 # First we create and fill the info object
165 166 info = wx.AboutDialogInfo()
166 167 info.Name = "WxIPython"
167 168 info.Version = str(__version__)
168 169 info.Copyright = "(C) 2007 Laurent Dufrechou"
169 170 info.Description = wordwrap(
170 171 "A Gui that embbed a multithreaded IPython Shell",
171 172 350, wx.ClientDC(self))
172 173 info.WebSite = ("http://ipython.scipy.org/", "IPython home page")
173 174 info.Developers = [ "Laurent Dufrechou" ]
174 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"
175 176 info.License = wordwrap(licenseText, 500, wx.ClientDC(self))
176 177
177 178 # Then we call wx.AboutBox giving it that info object
178 179 wx.AboutBox(info)
179 180
180 181 #-----------------------------------------
181 182 #Creating our application
182 183 #-----------------------------------------
183 184 class MyApp(wx.PySimpleApp):
184 185 """Creating our application"""
185 186 def __init__(self):
186 187 wx.PySimpleApp.__init__(self)
187 188
188 189 self.frame = MyFrame()
189 190 self.frame.Show()
190 191
191 192 #-----------------------------------------
192 193 #Main loop
193 194 #-----------------------------------------
194 195 def main():
195 196 app = MyApp()
196 197 app.SetTopWindow(app.frame)
197 198 app.MainLoop()
198 199
199 200 #if launched as main program run this
200 201 if __name__ == '__main__':
201 202 main()
General Comments 0
You need to be logged in to leave comments. Login now