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