##// END OF EJS Templates
Merging Laurent's WX branch, reviewed by Gael....
Fernando Perez -
r1862:321357e1 merge
parent child Browse files
Show More
@@ -1,471 +1,525
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 from thread_ex import ThreadEx
25 25
26 26 try:
27 27 import IPython
28 28 except Exception,e:
29 29 print "Error importing IPython (%s)" % str(e)
30 30 raise Exception, e
31 31
32 32 ##############################################################################
33 33 class _Helper(object):
34 34 """Redefine the built-in 'help'.
35 35 This is a wrapper around pydoc.help (with a twist).
36 36 """
37 37
38 38 def __init__(self, pager):
39 39 self._pager = pager
40 40
41 41 def __repr__(self):
42 42 return "Type help() for interactive help, " \
43 43 "or help(object) for help about object."
44 44
45 45 def __call__(self, *args, **kwds):
46 46 class DummyWriter(object):
47 47 '''Dumy class to handle help output'''
48 48 def __init__(self, pager):
49 49 self._pager = pager
50 50
51 51 def write(self, data):
52 52 '''hook to fill self._pager'''
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 ''' Thread that execute ipython code '''
65 def __init__(self, instance, after):
65 def __init__(self, instance):
66 66 ThreadEx.__init__(self)
67 67 self.instance = instance
68 self._afterExecute = after
69 68
70 69 def run(self):
71 70 '''Thread main loop'''
72 71 try:
73 72 self.instance._doc_text = None
74 73 self.instance._help_text = None
75 74 self.instance._execute()
76 75 # used for uper class to generate event after execution
77 self._afterExecute()
76 self.instance._after_execute()
78 77
79 78 except KeyboardInterrupt:
80 79 pass
81 80
82 81
83 82 ##############################################################################
84 83 class NonBlockingIPShell(object):
85 84 '''
86 85 Create an IPython instance, running the commands in a separate,
87 86 non-blocking thread.
88 87 This allows embedding in any GUI without blockage.
89 88
90 89 Note: The ThreadEx class supports asynchroneous function call
91 90 via raise_exc()
92 91 '''
93 92
94 93 def __init__(self, argv=[], user_ns={}, user_global_ns=None,
95 94 cin=None, cout=None, cerr=None,
96 95 ask_exit_handler=None):
97 96 '''
98 97 @param argv: Command line options for IPython
99 98 @type argv: list
100 99 @param user_ns: User namespace.
101 100 @type user_ns: dictionary
102 101 @param user_global_ns: User global namespace.
103 102 @type user_global_ns: dictionary.
104 103 @param cin: Console standard input.
105 104 @type cin: IO stream
106 105 @param cout: Console standard output.
107 106 @type cout: IO stream
108 107 @param cerr: Console standard error.
109 108 @type cerr: IO stream
110 109 @param exit_handler: Replacement for builtin exit() function
111 110 @type exit_handler: function
112 111 @param time_loop: Define the sleep time between two thread's loop
113 112 @type int
114 113 '''
115 114 #ipython0 initialisation
116 115 self._IP = None
117 self._term = None
118 self.initIpython0(argv, user_ns, user_global_ns,
116 self.init_ipython0(argv, user_ns, user_global_ns,
119 117 cin, cout, cerr,
120 118 ask_exit_handler)
121 119
122 120 #vars used by _execute
123 121 self._iter_more = 0
124 122 self._history_level = 0
125 123 self._complete_sep = re.compile('[\s\{\}\[\]\(\)\=]')
126 124 self._prompt = str(self._IP.outputcache.prompt1).strip()
127 125
128 126 #thread working vars
129 127 self._line_to_execute = ''
128 self._threading = True
130 129
131 130 #vars that will be checked by GUI loop to handle thread states...
132 131 #will be replaced later by PostEvent GUI funtions...
133 132 self._doc_text = None
134 133 self._help_text = None
135 134 self._add_button = None
136 135
137 def initIpython0(self, argv=[], user_ns={}, user_global_ns=None,
136 def init_ipython0(self, argv=[], user_ns={}, user_global_ns=None,
138 137 cin=None, cout=None, cerr=None,
139 138 ask_exit_handler=None):
140 ''' Initialize an ithon0 instance '''
139 ''' Initialize an ipython0 instance '''
141 140
142 141 #first we redefine in/out/error functions of IPython
142 #BUG: we've got a limitation form ipython0 there
143 #only one instance can be instanciated else tehre will be
144 #cin/cout/cerr clash...
143 145 if cin:
144 IPython.Shell.Term.cin = cin
146 IPython.genutils.Term.cin = cin
145 147 if cout:
146 IPython.Shell.Term.cout = cout
148 IPython.genutils.Term.cout = cout
147 149 if cerr:
148 IPython.Shell.Term.cerr = cerr
149
150 # This is to get rid of the blockage that accurs during
151 # IPython.Shell.InteractiveShell.user_setup()
152 IPython.iplib.raw_input = lambda x: None
153
154 self._term = IPython.genutils.IOTerm(cin=cin, cout=cout, cerr=cerr)
150 IPython.genutils.Term.cerr = cerr
155 151
156 152 excepthook = sys.excepthook
153
157 154 #Hack to save sys.displayhook, because ipython seems to overwrite it...
158 155 self.sys_displayhook_ori = sys.displayhook
159 156
160 157 self._IP = IPython.Shell.make_IPython(
161 158 argv,user_ns=user_ns,
162 159 user_global_ns=user_global_ns,
163 160 embedded=True,
164 161 shell_class=IPython.Shell.InteractiveShell)
165 162
166 #we restore sys.displayhook
163 #we save ipython0 displayhook and we restore sys.displayhook
164 self.displayhook = sys.displayhook
167 165 sys.displayhook = self.sys_displayhook_ori
168 166
169 167 #we replace IPython default encoding by wx locale encoding
170 168 loc = locale.getpreferredencoding()
171 169 if loc:
172 170 self._IP.stdin_encoding = loc
173 171 #we replace the ipython default pager by our pager
174 172 self._IP.set_hook('show_in_pager', self._pager)
175 173
176 #we replace the ipython default shell command caller by our shell handler
174 #we replace the ipython default shell command caller
175 #by our shell handler
177 176 self._IP.set_hook('shell_hook', self._shell)
178 177
179 178 #we replace the ipython default input command caller by our method
180 IPython.iplib.raw_input_original = self._raw_input
179 IPython.iplib.raw_input_original = self._raw_input_original
181 180 #we replace the ipython default exit command by our method
182 181 self._IP.exit = ask_exit_handler
183 182 #we replace the help command
184 183 self._IP.user_ns['help'] = _Helper(self._pager_help)
185 184
186 185 #we disable cpase magic... until we found a way to use it properly.
187 186 #import IPython.ipapi
188 187 ip = IPython.ipapi.get()
189 def bypassMagic(self, arg):
188 def bypass_magic(self, arg):
190 189 print '%this magic is currently disabled.'
191 ip.expose_magic('cpaste', bypassMagic)
190 ip.expose_magic('cpaste', bypass_magic)
191
192 import __builtin__
193 __builtin__.raw_input = self._raw_input
192 194
193 195 sys.excepthook = excepthook
194 196
195 197 #----------------------- Thread management section ----------------------
196 def doExecute(self, line):
198 def do_execute(self, line):
197 199 """
198 200 Tell the thread to process the 'line' command
199 201 """
200 202
201 203 self._line_to_execute = line
202 #we launch the ipython line execution in a thread to make it interruptible
203 #with include it in self namespace to be able to call ce.raise_exc(KeyboardInterrupt)
204 self.ce = _CodeExecutor(self, self._afterExecute)
204
205 if self._threading:
206 #we launch the ipython line execution in a thread to make it
207 #interruptible with include it in self namespace to be able
208 #to call ce.raise_exc(KeyboardInterrupt)
209 self.ce = _CodeExecutor(self)
205 210 self.ce.start()
211 else:
212 try:
213 self._doc_text = None
214 self._help_text = None
215 self._execute()
216 # used for uper class to generate event after execution
217 self._after_execute()
218
219 except KeyboardInterrupt:
220 pass
206 221
207 222 #----------------------- IPython management section ----------------------
208 def getDocText(self):
223 def get_threading(self):
224 """
225 Returns threading status, is set to True, then each command sent to
226 the interpreter will be executed in a separated thread allowing,
227 for example, breaking a long running commands.
228 Disallowing it, permits better compatibilty with instance that is embedding
229 IPython instance.
230
231 @return: Execution method
232 @rtype: bool
233 """
234 return self._threading
235
236 def set_threading(self, state):
237 """
238 Sets threading state, if set to True, then each command sent to
239 the interpreter will be executed in a separated thread allowing,
240 for example, breaking a long running commands.
241 Disallowing it, permits better compatibilty with instance that is embedding
242 IPython instance.
243
244 @param state: Sets threading state
245 @type bool
246 """
247 self._threading = state
248
249 def get_doc_text(self):
209 250 """
210 251 Returns the output of the processing that need to be paged (if any)
211 252
212 253 @return: The std output string.
213 254 @rtype: string
214 255 """
215 256 return self._doc_text
216 257
217 def getHelpText(self):
258 def get_help_text(self):
218 259 """
219 260 Returns the output of the processing that need to be paged via help pager(if any)
220 261
221 262 @return: The std output string.
222 263 @rtype: string
223 264 """
224 265 return self._help_text
225 266
226 def getBanner(self):
267 def get_banner(self):
227 268 """
228 269 Returns the IPython banner for useful info on IPython instance
229 270
230 271 @return: The banner string.
231 272 @rtype: string
232 273 """
233 274 return self._IP.BANNER
234 275
235 def getPromptCount(self):
276 def get_prompt_count(self):
236 277 """
237 278 Returns the prompt number.
238 279 Each time a user execute a line in the IPython shell the prompt count is increased
239 280
240 281 @return: The prompt number
241 282 @rtype: int
242 283 """
243 284 return self._IP.outputcache.prompt_count
244 285
245 def getPrompt(self):
286 def get_prompt(self):
246 287 """
247 288 Returns current prompt inside IPython instance
248 289 (Can be In [...]: ot ...:)
249 290
250 291 @return: The current prompt.
251 292 @rtype: string
252 293 """
253 294 return self._prompt
254 295
255 def getIndentation(self):
296 def get_indentation(self):
256 297 """
257 298 Returns the current indentation level
258 299 Usefull to put the caret at the good start position if we want to do autoindentation.
259 300
260 301 @return: The indentation level.
261 302 @rtype: int
262 303 """
263 304 return self._IP.indent_current_nsp
264 305
265 def updateNamespace(self, ns_dict):
306 def update_namespace(self, ns_dict):
266 307 '''
267 308 Add the current dictionary to the shell namespace.
268 309
269 310 @param ns_dict: A dictionary of symbol-values.
270 311 @type ns_dict: dictionary
271 312 '''
272 313 self._IP.user_ns.update(ns_dict)
273 314
274 315 def complete(self, line):
275 316 '''
276 317 Returns an auto completed line and/or posibilities for completion.
277 318
278 319 @param line: Given line so far.
279 320 @type line: string
280 321
281 322 @return: Line completed as for as possible,
282 323 and possible further completions.
283 324 @rtype: tuple
284 325 '''
285 326 split_line = self._complete_sep.split(line)
286 327 possibilities = self._IP.complete(split_line[-1])
287 328 if possibilities:
288 329
289 def _commonPrefix(str1, str2):
330 def _common_prefix(str1, str2):
290 331 '''
291 332 Reduction function. returns common prefix of two given strings.
292 333
293 334 @param str1: First string.
294 335 @type str1: string
295 336 @param str2: Second string
296 337 @type str2: string
297 338
298 339 @return: Common prefix to both strings.
299 340 @rtype: string
300 341 '''
301 342 for i in range(len(str1)):
302 343 if not str2.startswith(str1[:i+1]):
303 344 return str1[:i]
304 345 return str1
305 common_prefix = reduce(_commonPrefix, possibilities)
346 common_prefix = reduce(_common_prefix, possibilities)
306 347 completed = line[:-len(split_line[-1])]+common_prefix
307 348 else:
308 349 completed = line
309 350 return completed, possibilities
310 351
311 def historyBack(self):
352 def history_back(self):
312 353 '''
313 354 Provides one history command back.
314 355
315 356 @return: The command string.
316 357 @rtype: string
317 358 '''
318 359 history = ''
319 360 #the below while loop is used to suppress empty history lines
320 361 while((history == '' or history == '\n') and self._history_level >0):
321 362 if self._history_level >= 1:
322 363 self._history_level -= 1
323 history = self._getHistory()
364 history = self._get_history()
324 365 return history
325 366
326 def historyForward(self):
367 def history_forward(self):
327 368 '''
328 369 Provides one history command forward.
329 370
330 371 @return: The command string.
331 372 @rtype: string
332 373 '''
333 374 history = ''
334 375 #the below while loop is used to suppress empty history lines
335 376 while((history == '' or history == '\n') \
336 and self._history_level <= self._getHistoryMaxIndex()):
337 if self._history_level < self._getHistoryMaxIndex():
377 and self._history_level <= self._get_history_max_index()):
378 if self._history_level < self._get_history_max_index():
338 379 self._history_level += 1
339 history = self._getHistory()
380 history = self._get_history()
340 381 else:
341 if self._history_level == self._getHistoryMaxIndex():
342 history = self._getHistory()
382 if self._history_level == self._get_history_max_index():
383 history = self._get_history()
343 384 self._history_level += 1
344 385 else:
345 386 history = ''
346 387 return history
347 388
348 def initHistoryIndex(self):
389 def init_history_index(self):
349 390 '''
350 391 set history to last command entered
351 392 '''
352 self._history_level = self._getHistoryMaxIndex()+1
393 self._history_level = self._get_history_max_index()+1
353 394
354 395 #----------------------- IPython PRIVATE management section --------------
355 def _afterExecute(self):
396 def _after_execute(self):
356 397 '''
357 398 Can be redefined to generate post event after excution is done
358 399 '''
359 400 pass
360 401
361 #def _askExit(self):
362 # '''
363 # Can be redefined to generate post event to exit the Ipython shell
364 # '''
365 # pass
402 def _ask_exit(self):
403 '''
404 Can be redefined to generate post event to exit the Ipython shell
405 '''
406 pass
366 407
367 def _getHistoryMaxIndex(self):
408 def _get_history_max_index(self):
368 409 '''
369 410 returns the max length of the history buffer
370 411
371 412 @return: history length
372 413 @rtype: int
373 414 '''
374 415 return len(self._IP.input_hist_raw)-1
375 416
376 def _getHistory(self):
417 def _get_history(self):
377 418 '''
378 419 Get's the command string of the current history level.
379 420
380 421 @return: Historic command stri
381 422 @rtype: string
382 423 '''
383 424 rv = self._IP.input_hist_raw[self._history_level].strip('\n')
384 425 return rv
385 426
386 427 def _pager_help(self, text):
387 428 '''
388 429 This function is used as a callback replacment to IPython help pager function
389 430
390 431 It puts the 'text' value inside the self._help_text string that can be retrived via
391 getHelpText function.
432 get_help_text function.
392 433 '''
393 434 if self._help_text == None:
394 435 self._help_text = text
395 436 else:
396 437 self._help_text += text
397 438
398 439 def _pager(self, IP, text):
399 440 '''
400 441 This function is used as a callback replacment to IPython pager function
401 442
402 443 It puts the 'text' value inside the self._doc_text string that can be retrived via
403 getDocText function.
444 get_doc_text function.
404 445 '''
405 446 self._doc_text = text
406 447
407 def _raw_input(self, prompt=''):
448 def _raw_input_original(self, prompt=''):
408 449 '''
409 450 Custom raw_input() replacement. Get's current line from console buffer.
410 451
411 452 @param prompt: Prompt to print. Here for compatability as replacement.
412 453 @type prompt: string
413 454
414 455 @return: The current command line text.
415 456 @rtype: string
416 457 '''
417 458 return self._line_to_execute
418 459
460 def _raw_input(self, prompt=''):
461 """ A replacement from python's raw_input.
462 """
463 raise NotImplementedError
464
419 465 def _execute(self):
420 466 '''
421 467 Executes the current line provided by the shell object.
422 468 '''
469
423 470 orig_stdout = sys.stdout
424 471 sys.stdout = IPython.Shell.Term.cout
472 #self.sys_displayhook_ori = sys.displayhook
473 #sys.displayhook = self.displayhook
425 474
426 475 try:
427 476 line = self._IP.raw_input(None, self._iter_more)
428 477 if self._IP.autoindent:
429 478 self._IP.readline_startup_hook(None)
430 479
431 480 except KeyboardInterrupt:
432 481 self._IP.write('\nKeyboardInterrupt\n')
433 482 self._IP.resetbuffer()
434 483 # keep cache in sync with the prompt counter:
435 484 self._IP.outputcache.prompt_count -= 1
436 485
437 486 if self._IP.autoindent:
438 487 self._IP.indent_current_nsp = 0
439 488 self._iter_more = 0
440 489 except:
441 490 self._IP.showtraceback()
442 491 else:
492 self._IP.write(str(self._IP.outputcache.prompt_out).strip())
443 493 self._iter_more = self._IP.push(line)
444 if (self._IP.SyntaxTB.last_syntax_error and self._IP.rc.autoedit_syntax):
494 if (self._IP.SyntaxTB.last_syntax_error and \
495 self._IP.rc.autoedit_syntax):
445 496 self._IP.edit_syntax_error()
446 497 if self._iter_more:
447 498 self._prompt = str(self._IP.outputcache.prompt2).strip()
448 499 if self._IP.autoindent:
449 500 self._IP.readline_startup_hook(self._IP.pre_readline)
450 501 else:
451 502 self._prompt = str(self._IP.outputcache.prompt1).strip()
452 503 self._IP.indent_current_nsp = 0 #we set indentation to 0
504
453 505 sys.stdout = orig_stdout
506 #sys.displayhook = self.sys_displayhook_ori
454 507
455 508 def _shell(self, ip, cmd):
456 509 '''
457 510 Replacement method to allow shell commands without them blocking.
458 511
459 512 @param ip: Ipython instance, same as self._IP
460 513 @type cmd: Ipython instance
461 514 @param cmd: Shell command to execute.
462 515 @type cmd: string
463 516 '''
464 517 stdin, stdout = os.popen4(cmd)
465 result = stdout.read().decode('cp437').encode(locale.getpreferredencoding())
518 result = stdout.read().decode('cp437').\
519 encode(locale.getpreferredencoding())
466 520 #we use print command because the shell command is called
467 521 #inside IPython instance and thus is redirected to thread cout
468 522 #"\x01\x1b[1;36m\x02" <-- add colour to the text...
469 523 print "\x01\x1b[1;36m\x02"+result
470 524 stdout.close()
471 525 stdin.close()
@@ -1,475 +1,509
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.options={'filter_empty':{'value':'True',
32 'checkbox':self.filter_empty,'True':True,'False':False,
32 'checkbox':self.filter_empty, \
33 'True':True,'False':False,
33 34 'setfunc':lambda x:None},
34 35 'filter_doc':{'value':'True',
35 'checkbox':self.filter_doc,'True':True,'False':False,
36 'checkbox':self.filter_doc, \
37 'True':True,'False':False,
36 38 'setfunc':lambda x:None},
37 39 'filter_cmd':{'value':'True',
38 'checkbox':self.filter_cmd,'True':True,'False':False,
40 'checkbox':self.filter_cmd, \
41 'True':True,'False':False,
39 42 'setfunc':lambda x:None},
40 43 'filter_magic':{'value':'True',
41 'checkbox':self.filter_magic,'True':True,'False':False,
44 'checkbox':self.filter_magic, \
45 'True':True,'False':False,
42 46 'setfunc':lambda x:None},
43 47 }
44 48 self.reloadOptions(self.options)
45 49
46 50 self.filter_empty.Bind(wx.EVT_CHECKBOX, self.evtCheckEmptyFilter)
47 51 self.filter_doc.Bind(wx.EVT_CHECKBOX, self.evtCheckDocFilter)
48 52 self.filter_cmd.Bind(wx.EVT_CHECKBOX, self.evtCheckCmdFilter)
49 53 self.filter_magic.Bind(wx.EVT_CHECKBOX, self.evtCheckMagicFilter)
50 54
51 55 #self.filter_empty.SetValue(flt_empty)
52 56 #self.filter_doc.SetValue(flt_doc)
53 57 #self.filter_cmd.SetValue(flt_cmd)
54 58 #self.filter_magic.SetValue(flt_magic)
55 59
56 60 sizer = wx.BoxSizer(wx.VERTICAL)
57 61
58 62 sizer.Add(text_ctrl, 1, wx.EXPAND)
59 63 sizer.AddMany( [(5,5),
60 64 st_filt,
61 65 (10,10),
62 66 self.filter_empty,
63 67 self.filter_doc,
64 68 self.filter_cmd,
65 69 self.filter_magic,
66 70 (10,10),
67 71 ])
68 72 self.SetAutoLayout(True)
69 73 sizer.Fit(self)
70 74 sizer.SetSizeHints(self)
71 75 self.SetSizer(sizer)
72 76 self.text_ctrl=text_ctrl
73 77 #text_ctrl.SetText(demoText + open('Main.py').read())
74 78 text_ctrl.EmptyUndoBuffer()
75 79 text_ctrl.Colourise(0, -1)
76 80
77 81 # line numbers in the margin
78 82 text_ctrl.SetMarginType(1, stc.STC_MARGIN_NUMBER)
79 83 text_ctrl.SetMarginWidth(1, 15)
80 84
81 85
82 86 def write(self,history_line):
83 87 add = True
84 88 if self.filter_empty.GetValue() == True and history_line == '':
85 89 add = False
86 90 if len(history_line)>0:
87 91 if self.filter_doc.GetValue() == True and history_line[-1:] == '?':
88 92 add = False
89 93 if self.filter_cmd.GetValue() == True and history_line[0] == '!':
90 94 add = False
91 95 if self.filter_magic.GetValue() == True and history_line[0] == '%':
92 96 add = False
93 97 if add:
94 98 self.text_ctrl.AppendText(history_line+'\n')
95 99
96 100 #------------------------ Option Section -----------------------------------
97 101 def processOptionCheckedEvt(self, event, name):
98 102 if event.IsChecked():
99 103 self.options[name]['value']='True'
100 104 else:
101 105 self.options[name]['value']='False'
102 106 self.updateOptionTracker(name,
103 107 self.options[name]['value'])
104 108
105 109 def evtCheckEmptyFilter(self, event):
106 110 self.processOptionCheckedEvt(event, 'filter_empty')
107 111
108 112 def evtCheckDocFilter(self, event):
109 113 self.processOptionCheckedEvt(event, 'filter_doc')
110 114
111 115 def evtCheckCmdFilter(self, event):
112 116 self.processOptionCheckedEvt(event, 'filter_cmd')
113 117
114 118 def evtCheckMagicFilter(self, event):
115 119 self.processOptionCheckedEvt(event, 'filter_magic')
116 120
117 121 def getOptions(self):
118 122 return self.options
119 123
120 124 def reloadOptions(self,options):
121 125 self.options = options
122 126 for key in self.options.keys():
123 127 value = self.options[key]['value']
124 128 self.options[key]['checkbox'].SetValue(self.options[key][value])
125 129 self.options[key]['setfunc'](value)
126 130
127 131 #------------------------ Hook Section -----------------------------------
128 132 def updateOptionTracker(self,name,value):
129 133 '''
130 134 Default history tracker (does nothing)
131 135 '''
132 136 pass
133 137
134 138 def setOptionTrackerHook(self,func):
135 139 '''
136 140 Define a new history tracker
137 141 '''
138 142 self.updateOptionTracker = func
139 143
140 144
141 145 #----------------------------------------------------------------------
142 146 # Font definition for Styled Text Control
143 147
144 148 if wx.Platform == '__WXMSW__':
145 149 faces = { 'times': 'Times New Roman',
146 150 'mono' : 'Courier New',
147 151 'helv' : 'Arial',
148 152 'other': 'Comic Sans MS',
149 153 'size' : 8,
150 154 'size2': 6,
151 155 }
152 156 elif wx.Platform == '__WXMAC__':
153 157 faces = { 'times': 'Times New Roman',
154 158 'mono' : 'Monaco',
155 159 'helv' : 'Arial',
156 160 'other': 'Comic Sans MS',
157 161 'size' : 8,
158 162 'size2': 6,
159 163 }
160 164 else:
161 165 faces = { 'times': 'Times',
162 166 'mono' : 'Courier',
163 167 'helv' : 'Helvetica',
164 168 'other': 'new century schoolbook',
165 169 'size' : 8,
166 170 'size2': 6,
167 171 }
168 172
169 173
170 174 #----------------------------------------------------------------------
171 175
172 176 class PythonSTC(stc.StyledTextCtrl):
173 177
174 178 fold_symbols = 3
175 179
176 180 def __init__(self, parent, ID,
177 181 pos=wx.DefaultPosition, size=wx.DefaultSize,
178 182 style=0):
179 183 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
180 184 #self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
181 185 #self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
182 186
183 187 self.SetLexer(stc.STC_LEX_PYTHON)
184 188 self.SetKeyWords(0, " ".join(keyword.kwlist))
185 189
186 190 #self.SetProperty("fold", "1")
187 191 #self.SetProperty("tab.timmy.whinge.level", "1")
188 192 #self.SetMargins(0,0)
189 193
190 194 #self.SetViewWhiteSpace(False)
191 195 #self.SetBufferedDraw(False)
192 196 #self.SetViewEOL(True)
193 197 self.SetEOLMode(stc.STC_EOL_CRLF)
194 198 #self.SetUseAntiAliasing(True)
195 199
196 200 self.SetEdgeMode(stc.STC_EDGE_LINE)
197 201 self.SetEdgeColumn(80)
198 202 self.SetEdgeColour(wx.LIGHT_GREY)
199 203 self.SetLayoutCache(stc.STC_CACHE_PAGE)
200 204
201 205 # Setup a margin to hold fold markers
202 #self.SetFoldFlags(16) ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
206 #self.SetFoldFlags(16)
207 ### WHAT IS THIS VALUE? WHAT ARE THE OTHER FLAGS? DOES IT MATTER?
203 208 self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
204 209 self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
205 210 self.SetMarginSensitive(2, True)
206 211 self.SetMarginWidth(2, 12)
207 212
208 213 if self.fold_symbols == 0:
209 # Arrow pointing right for contracted folders, arrow pointing down for expanded
210 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_ARROWDOWN, "black", "black")
211 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_ARROW, "black", "black")
212 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "black", "black")
213 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "black", "black")
214 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
215 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
216 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
214 # Arrow pointing right for contracted folders,
215 # arrow pointing down for expanded
216 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
217 stc.STC_MARK_ARROWDOWN, "black", "black")
218 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, \
219 stc.STC_MARK_ARROW, "black", "black")
220 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, \
221 stc.STC_MARK_EMPTY, "black", "black")
222 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, \
223 stc.STC_MARK_EMPTY, "black", "black")
224 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, \
225 stc.STC_MARK_EMPTY, "white", "black")
226 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, \
227 stc.STC_MARK_EMPTY, "white", "black")
228 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, \
229 stc.STC_MARK_EMPTY, "white", "black")
217 230
218 231 elif self.fold_symbols == 1:
219 232 # Plus for contracted folders, minus for expanded
220 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_MINUS, "white", "black")
221 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_PLUS, "white", "black")
222 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_EMPTY, "white", "black")
223 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_EMPTY, "white", "black")
224 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_EMPTY, "white", "black")
225 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_EMPTY, "white", "black")
226 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_EMPTY, "white", "black")
233 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
234 stc.STC_MARK_MINUS, "white", "black")
235 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, \
236 stc.STC_MARK_PLUS, "white", "black")
237 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, \
238 stc.STC_MARK_EMPTY, "white", "black")
239 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, \
240 stc.STC_MARK_EMPTY, "white", "black")
241 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, \
242 stc.STC_MARK_EMPTY, "white", "black")
243 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, \
244 stc.STC_MARK_EMPTY, "white", "black")
245 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, \
246 stc.STC_MARK_EMPTY, "white", "black")
227 247
228 248 elif self.fold_symbols == 2:
229 249 # Like a flattened tree control using circular headers and curved joins
230 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_CIRCLEMINUS, "white", "#404040")
231 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_CIRCLEPLUS, "white", "#404040")
232 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#404040")
233 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNERCURVE, "white", "#404040")
234 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040")
235 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
236 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNERCURVE, "white", "#404040")
250 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
251 stc.STC_MARK_CIRCLEMINUS, "white", "#404040")
252 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, \
253 stc.STC_MARK_CIRCLEPLUS, "white", "#404040")
254 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, \
255 stc.STC_MARK_VLINE, "white", "#404040")
256 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, \
257 stc.STC_MARK_LCORNERCURVE, "white", "#404040")
258 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, \
259 stc.STC_MARK_CIRCLEPLUSCONNECTED, "white", "#404040")
260 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, \
261 stc.STC_MARK_CIRCLEMINUSCONNECTED, "white", "#404040")
262 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, \
263 stc.STC_MARK_TCORNERCURVE, "white", "#404040")
237 264
238 265 elif self.fold_symbols == 3:
239 266 # Like a flattened tree control using square headers
240 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, "white", "#808080")
241 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, "white", "#808080")
242 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, "white", "#808080")
243 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, "white", "#808080")
244 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080")
245 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
246 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER, "white", "#808080")
267 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, \
268 stc.STC_MARK_BOXMINUS, "white", "#808080")
269 self.MarkerDefine(stc.STC_MARKNUM_FOLDER, \
270 stc.STC_MARK_BOXPLUS, "white", "#808080")
271 self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, \
272 stc.STC_MARK_VLINE, "white", "#808080")
273 self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, \
274 stc.STC_MARK_LCORNER, "white", "#808080")
275 self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, \
276 stc.STC_MARK_BOXPLUSCONNECTED, "white", "#808080")
277 self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, \
278 stc.STC_MARK_BOXMINUSCONNECTED, "white", "#808080")
279 self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, \
280 stc.STC_MARK_TCORNER, "white", "#808080")
247 281
248 282
249 283 self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdateUI)
250 284 self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
251 285 self.Bind(wx.EVT_KEY_DOWN, self.OnKeyPressed)
252 286
253 287 # Make some styles, The lexer defines what each style is used for, we
254 288 # just have to define what each style looks like. This set is adapted from
255 289 # Scintilla sample property files.
256 290
257 291 # Global default styles for all languages
258 292 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
259 293 self.StyleClearAll() # Reset all to be like the default
260 294
261 295 # Global default styles for all languages
262 296 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, "face:%(helv)s,size:%(size)d" % faces)
263 297 self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "back:#C0C0C0,face:%(helv)s,size:%(size2)d" % faces)
264 298 self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, "face:%(other)s" % faces)
265 299 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, "fore:#FFFFFF,back:#0000FF,bold")
266 300 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, "fore:#000000,back:#FF0000,bold")
267 301
268 302 # Python styles
269 303 # Default
270 304 self.StyleSetSpec(stc.STC_P_DEFAULT, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
271 305 # Comments
272 306 self.StyleSetSpec(stc.STC_P_COMMENTLINE, "fore:#007F00,face:%(other)s,size:%(size)d" % faces)
273 307 # Number
274 308 self.StyleSetSpec(stc.STC_P_NUMBER, "fore:#007F7F,size:%(size)d" % faces)
275 309 # String
276 310 self.StyleSetSpec(stc.STC_P_STRING, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
277 311 # Single quoted string
278 312 self.StyleSetSpec(stc.STC_P_CHARACTER, "fore:#7F007F,face:%(helv)s,size:%(size)d" % faces)
279 313 # Keyword
280 314 self.StyleSetSpec(stc.STC_P_WORD, "fore:#00007F,bold,size:%(size)d" % faces)
281 315 # Triple quotes
282 316 self.StyleSetSpec(stc.STC_P_TRIPLE, "fore:#7F0000,size:%(size)d" % faces)
283 317 # Triple double quotes
284 318 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, "fore:#7F0000,size:%(size)d" % faces)
285 319 # Class name definition
286 320 self.StyleSetSpec(stc.STC_P_CLASSNAME, "fore:#0000FF,bold,underline,size:%(size)d" % faces)
287 321 # Function or method name definition
288 322 self.StyleSetSpec(stc.STC_P_DEFNAME, "fore:#007F7F,bold,size:%(size)d" % faces)
289 323 # Operators
290 324 self.StyleSetSpec(stc.STC_P_OPERATOR, "bold,size:%(size)d" % faces)
291 325 # Identifiers
292 326 self.StyleSetSpec(stc.STC_P_IDENTIFIER, "fore:#000000,face:%(helv)s,size:%(size)d" % faces)
293 327 # Comment-blocks
294 328 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, "fore:#7F7F7F,size:%(size)d" % faces)
295 329 # End of line where string is not closed
296 330 self.StyleSetSpec(stc.STC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d" % faces)
297 331
298 332 self.SetCaretForeground("BLUE")
299 333
300 334
301 335 # register some images for use in the AutoComplete box.
302 336 #self.RegisterImage(1, images.getSmilesBitmap())
303 337 #self.RegisterImage(2,
304 338 # wx.ArtProvider.GetBitmap(wx.ART_NEW, size=(16,16)))
305 339 #self.RegisterImage(3,
306 340 # wx.ArtProvider.GetBitmap(wx.ART_COPY, size=(16,16)))
307 341
308 342
309 343 def OnKeyPressed(self, event):
310 344 if self.CallTipActive():
311 345 self.CallTipCancel()
312 346 key = event.GetKeyCode()
313 347
314 348 if key == 32 and event.ControlDown():
315 349 pos = self.GetCurrentPos()
316 350
317 351 # Tips
318 352 if event.ShiftDown():
319 353 self.CallTipSetBackground("yellow")
320 354 self.CallTipShow(pos, 'lots of of text: blah, blah, blah\n\n'
321 355 'show some suff, maybe parameters..\n\n'
322 356 'fubar(param1, param2)')
323 357 # Code completion
324 358 else:
325 359 #lst = []
326 360 #for x in range(50000):
327 361 # lst.append('%05d' % x)
328 362 #st = " ".join(lst)
329 363 #print len(st)
330 364 #self.AutoCompShow(0, st)
331 365
332 366 kw = keyword.kwlist[:]
333 367
334 368 kw.sort() # Python sorts are case sensitive
335 369 self.AutoCompSetIgnoreCase(False) # so this needs to match
336 370
337 371 # Images are specified with a appended "?type"
338 372 for i in range(len(kw)):
339 373 if kw[i] in keyword.kwlist:
340 374 kw[i] = kw[i]# + "?1"
341 375
342 376 self.AutoCompShow(0, " ".join(kw))
343 377 else:
344 378 event.Skip()
345 379
346 380
347 381 def OnUpdateUI(self, evt):
348 382 # check for matching braces
349 383 braceAtCaret = -1
350 384 braceOpposite = -1
351 385 charBefore = None
352 386 caretPos = self.GetCurrentPos()
353 387
354 388 if caretPos > 0:
355 389 charBefore = self.GetCharAt(caretPos - 1)
356 390 styleBefore = self.GetStyleAt(caretPos - 1)
357 391
358 392 # check before
359 393 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
360 394 braceAtCaret = caretPos - 1
361 395
362 396 # check after
363 397 if braceAtCaret < 0:
364 398 charAfter = self.GetCharAt(caretPos)
365 399 styleAfter = self.GetStyleAt(caretPos)
366 400
367 401 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
368 402 braceAtCaret = caretPos
369 403
370 404 if braceAtCaret >= 0:
371 405 braceOpposite = self.BraceMatch(braceAtCaret)
372 406
373 407 if braceAtCaret != -1 and braceOpposite == -1:
374 408 self.BraceBadLight(braceAtCaret)
375 409 else:
376 410 self.BraceHighlight(braceAtCaret, braceOpposite)
377 411 #pt = self.PointFromPosition(braceOpposite)
378 412 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
379 413 #print pt
380 414 #self.Refresh(False)
381 415
382 416
383 417 def OnMarginClick(self, evt):
384 418 # fold and unfold as needed
385 419 if evt.GetMargin() == 2:
386 420 if evt.GetShift() and evt.GetControl():
387 421 self.FoldAll()
388 422 else:
389 423 lineClicked = self.LineFromPosition(evt.GetPosition())
390 424
391 425 if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG:
392 426 if evt.GetShift():
393 427 self.SetFoldExpanded(lineClicked, True)
394 428 self.Expand(lineClicked, True, True, 1)
395 429 elif evt.GetControl():
396 430 if self.GetFoldExpanded(lineClicked):
397 431 self.SetFoldExpanded(lineClicked, False)
398 432 self.Expand(lineClicked, False, True, 0)
399 433 else:
400 434 self.SetFoldExpanded(lineClicked, True)
401 435 self.Expand(lineClicked, True, True, 100)
402 436 else:
403 437 self.ToggleFold(lineClicked)
404 438
405 439
406 440 def FoldAll(self):
407 441 lineCount = self.GetLineCount()
408 442 expanding = True
409 443
410 444 # find out if we are folding or unfolding
411 445 for lineNum in range(lineCount):
412 446 if self.GetFoldLevel(lineNum) & stc.STC_FOLDLEVELHEADERFLAG:
413 447 expanding = not self.GetFoldExpanded(lineNum)
414 448 break
415 449
416 450 lineNum = 0
417 451
418 452 while lineNum < lineCount:
419 453 level = self.GetFoldLevel(lineNum)
420 454 if level & stc.STC_FOLDLEVELHEADERFLAG and \
421 455 (level & stc.STC_FOLDLEVELNUMBERMASK) == stc.STC_FOLDLEVELBASE:
422 456
423 457 if expanding:
424 458 self.SetFoldExpanded(lineNum, True)
425 459 lineNum = self.Expand(lineNum, True)
426 460 lineNum = lineNum - 1
427 461 else:
428 462 lastChild = self.GetLastChild(lineNum, -1)
429 463 self.SetFoldExpanded(lineNum, False)
430 464
431 465 if lastChild > lineNum:
432 466 self.HideLines(lineNum+1, lastChild)
433 467
434 468 lineNum = lineNum + 1
435 469
436 470
437 471
438 472 def Expand(self, line, doExpand, force=False, visLevels=0, level=-1):
439 473 lastChild = self.GetLastChild(line, level)
440 474 line = line + 1
441 475
442 476 while line <= lastChild:
443 477 if force:
444 478 if visLevels > 0:
445 479 self.ShowLines(line, line)
446 480 else:
447 481 self.HideLines(line, line)
448 482 else:
449 483 if doExpand:
450 484 self.ShowLines(line, line)
451 485
452 486 if level == -1:
453 487 level = self.GetFoldLevel(line)
454 488
455 489 if level & stc.STC_FOLDLEVELHEADERFLAG:
456 490 if force:
457 491 if visLevels > 1:
458 492 self.SetFoldExpanded(line, True)
459 493 else:
460 494 self.SetFoldExpanded(line, False)
461 495
462 496 line = self.Expand(line, doExpand, force, visLevels-1)
463 497
464 498 else:
465 499 if doExpand and self.GetFoldExpanded(line):
466 500 line = self.Expand(line, True, force, visLevels-1)
467 501 else:
468 502 line = self.Expand(line, False, force, visLevels-1)
469 503 else:
470 504 line = line + 1
471 505
472 506 return line
473 507
474 508
475 509 #----------------------------------------------------------------------
@@ -1,885 +1,944
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 __version__ = 0.8
22 __version__ = 0.9
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
30 30 import re
31 31 from StringIO import StringIO
32 32
33 33 import sys
34 34 import codecs
35 35 import locale
36 import time
37
36 38 for enc in (locale.getpreferredencoding(),
37 39 sys.getfilesystemencoding(),
38 40 sys.getdefaultencoding()):
39 41 try:
40 42 codecs.lookup(enc)
41 43 ENCODING = enc
42 44 break
43 45 except LookupError:
44 46 pass
45 47 else:
46 48 ENCODING = 'utf-8'
47 49
48 50 from ipshell_nonblocking import NonBlockingIPShell
49 51
50 52 class WxNonBlockingIPShell(NonBlockingIPShell):
51 53 '''
52 54 An NonBlockingIPShell Thread that is WX dependent.
53 55 '''
54 56 def __init__(self, parent,
55 57 argv=[],user_ns={},user_global_ns=None,
56 58 cin=None, cout=None, cerr=None,
57 59 ask_exit_handler=None):
58 60
59 61 NonBlockingIPShell.__init__(self, argv, user_ns, user_global_ns,
60 62 cin, cout, cerr,
61 63 ask_exit_handler)
62 64
63 65 self.parent = parent
64 66
65 67 self.ask_exit_callback = ask_exit_handler
66 self._IP.exit = self._askExit
68 self._IP.exit = self._ask_exit
67 69
68 70 def addGUIShortcut(self, text, func):
69 71 wx.CallAfter(self.parent.add_button_handler,
70 72 button_info={ 'text':text,
71 73 'func':self.parent.doExecuteLine(func)})
72 74
73 def _askExit(self):
75 def _raw_input(self, prompt=''):
76 """ A replacement from python's raw_input.
77 """
78 self.answer = None
79 if(self._threading == True):
80 wx.CallAfter(self._yesNoBox, prompt)
81 while self.answer is None:
82 time.sleep(.1)
83 else:
84 self._yesNoBox(prompt)
85 return self.answer
86
87 def _yesNoBox(self, prompt):
88 """ yes/no box managed with wx.CallAfter jsut in case caler is executed in a thread"""
89 dlg = wx.TextEntryDialog(
90 self.parent, prompt,
91 'Input requested', 'Python')
92 dlg.SetValue("")
93
94 answer = ''
95 if dlg.ShowModal() == wx.ID_OK:
96 answer = dlg.GetValue()
97
98 dlg.Destroy()
99 self.answer = answer
100
101 def _ask_exit(self):
74 102 wx.CallAfter(self.ask_exit_callback, ())
75 103
76 def _afterExecute(self):
104 def _after_execute(self):
77 105 wx.CallAfter(self.parent.evtStateExecuteDone, ())
78 106
79 107
80 108 class WxConsoleView(stc.StyledTextCtrl):
81 109 '''
82 110 Specialized styled text control view for console-like workflow.
83 111 We use here a scintilla frontend thus it can be reused in any GUI that
84 112 supports scintilla with less work.
85 113
86 114 @cvar ANSI_COLORS_BLACK: Mapping of terminal colors to X11 names.
87 115 (with Black background)
88 116 @type ANSI_COLORS_BLACK: dictionary
89 117
90 118 @cvar ANSI_COLORS_WHITE: Mapping of terminal colors to X11 names.
91 119 (with White background)
92 120 @type ANSI_COLORS_WHITE: dictionary
93 121
94 122 @ivar color_pat: Regex of terminal color pattern
95 123 @type color_pat: _sre.SRE_Pattern
96 124 '''
97 125 ANSI_STYLES_BLACK = {'0;30': [0, 'WHITE'], '0;31': [1, 'RED'],
98 126 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
99 127 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
100 128 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
101 129 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
102 130 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
103 131 '1;34': [12, 'LIGHT BLUE'], '1;35':
104 132 [13, 'MEDIUM VIOLET RED'],
105 133 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
106 134
107 135 ANSI_STYLES_WHITE = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
108 136 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
109 137 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
110 138 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
111 139 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
112 140 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
113 141 '1;34': [12, 'LIGHT BLUE'], '1;35':
114 142 [13, 'MEDIUM VIOLET RED'],
115 143 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
116 144
117 145 def __init__(self, parent, prompt, intro="", background_color="BLACK",
118 146 pos=wx.DefaultPosition, ID = -1, size=wx.DefaultSize,
119 147 style=0, autocomplete_mode = 'IPYTHON'):
120 148 '''
121 149 Initialize console view.
122 150
123 151 @param parent: Parent widget
124 152 @param prompt: User specified prompt
125 153 @type intro: string
126 154 @param intro: User specified startup introduction string
127 155 @type intro: string
128 156 @param background_color: Can be BLACK or WHITE
129 157 @type background_color: string
130 158 @param other: init param of styledTextControl (can be used as-is)
131 159 @param autocomplete_mode: Can be 'IPYTHON' or 'STC'
132 160 'IPYTHON' show autocompletion the ipython way
133 161 'STC" show it scintilla text control way
134 162 '''
135 163 stc.StyledTextCtrl.__init__(self, parent, ID, pos, size, style)
136 164
137 165 ####### Scintilla configuration ###################################
138 166
139 167 # Ctrl + B or Ctrl + N can be used to zoomin/zoomout the text inside
140 168 # the widget
141 169 self.CmdKeyAssign(ord('B'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
142 170 self.CmdKeyAssign(ord('N'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
143 171
144 172 #We draw a line at position 80
145 173 self.SetEdgeMode(stc.STC_EDGE_LINE)
146 174 self.SetEdgeColumn(80)
147 175 self.SetEdgeColour(wx.LIGHT_GREY)
148 176
149 177 #self.SetViewWhiteSpace(True)
150 178 #self.SetViewEOL(True)
151 179 self.SetEOLMode(stc.STC_EOL_CRLF)
152 180 #self.SetWrapMode(stc.STC_WRAP_CHAR)
153 181 #self.SetWrapMode(stc.STC_WRAP_WORD)
154 182 self.SetBufferedDraw(True)
155 183 #self.SetUseAntiAliasing(True)
156 184 self.SetLayoutCache(stc.STC_CACHE_PAGE)
157 185 self.SetUndoCollection(False)
158 186 self.SetUseTabs(True)
159 187 self.SetIndent(4)
160 188 self.SetTabWidth(4)
161 189
162 190 self.EnsureCaretVisible()
163 191
164 192 self.SetMargins(3, 3) #text is moved away from border with 3px
165 193 # Suppressing Scintilla margins
166 194 self.SetMarginWidth(0, 0)
167 195 self.SetMarginWidth(1, 0)
168 196 self.SetMarginWidth(2, 0)
169 197
170 198 self.background_color = background_color
171 199 self.buildStyles()
172 200
173 201 self.indent = 0
174 202 self.prompt_count = 0
175 203 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
176 204
177 205 self.write(intro)
178 206 self.setPrompt(prompt)
179 207 self.showPrompt()
180 208
181 209 self.autocomplete_mode = autocomplete_mode
182 210
183 211 self.Bind(wx.EVT_KEY_DOWN, self._onKeypress)
184 212
185 213 def buildStyles(self):
186 214 #we define platform specific fonts
187 215 if wx.Platform == '__WXMSW__':
188 216 faces = { 'times': 'Times New Roman',
189 217 'mono' : 'Courier New',
190 218 'helv' : 'Arial',
191 219 'other': 'Comic Sans MS',
192 220 'size' : 10,
193 221 'size2': 8,
194 222 }
195 223 elif wx.Platform == '__WXMAC__':
196 224 faces = { 'times': 'Times New Roman',
197 225 'mono' : 'Monaco',
198 226 'helv' : 'Arial',
199 227 'other': 'Comic Sans MS',
200 228 'size' : 10,
201 229 'size2': 8,
202 230 }
203 231 else:
204 232 faces = { 'times': 'Times',
205 233 'mono' : 'Courier',
206 234 'helv' : 'Helvetica',
207 235 'other': 'new century schoolbook',
208 236 'size' : 10,
209 237 'size2': 8,
210 238 }
211 239
212 240 # make some styles
213 241 if self.background_color != "BLACK":
214 242 self.background_color = "WHITE"
215 243 self.SetCaretForeground("BLACK")
216 244 self.ANSI_STYLES = self.ANSI_STYLES_WHITE
217 245 else:
218 246 self.SetCaretForeground("WHITE")
219 247 self.ANSI_STYLES = self.ANSI_STYLES_BLACK
220 248
221 249 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
222 250 "fore:%s,back:%s,size:%d,face:%s"
223 251 % (self.ANSI_STYLES['0;30'][1],
224 252 self.background_color,
225 253 faces['size'], faces['mono']))
226 254 self.StyleClearAll()
227 255 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
228 256 "fore:#FF0000,back:#0000FF,bold")
229 257 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
230 258 "fore:#000000,back:#FF0000,bold")
231 259
232 260 for style in self.ANSI_STYLES.values():
233 261 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
234 262
235 263 #######################################################################
236 264
237 265 def setBackgroundColor(self, color):
238 266 self.background_color = color
239 267 self.buildStyles()
240 268
241 269 def getBackgroundColor(self, color):
242 270 return self.background_color
243 271
244 272 def asyncWrite(self, text):
245 273 '''
246 274 Write given text to buffer in an asynchroneous way.
247 275 It is used from another thread to be able to acces the GUI.
248 276 @param text: Text to append
249 277 @type text: string
250 278 '''
251 279 try:
252 #print >>sys.__stdout__,'entering'
253 280 wx.MutexGuiEnter()
254 #print >>sys.__stdout__,'locking the GUI'
255 281
256 282 #be sure not to be interrutpted before the MutexGuiLeave!
257 283 self.write(text)
258 284
259 #print >>sys.__stdout__,'done'
260
261 285 except KeyboardInterrupt:
262 #print >>sys.__stdout__,'got keyboard interrupt'
263 286 wx.MutexGuiLeave()
264 #print >>sys.__stdout__,'interrupt unlock the GUI'
265 287 raise KeyboardInterrupt
266 288 wx.MutexGuiLeave()
267 #print >>sys.__stdout__,'normal unlock the GUI'
268 289
269 290
270 291 def write(self, text):
271 292 '''
272 293 Write given text to buffer.
273 294
274 295 @param text: Text to append.
275 296 @type text: string
276 297 '''
277 298 segments = self.color_pat.split(text)
278 299 segment = segments.pop(0)
279 300 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
280 301 self.AppendText(segment)
281 302
282 303 if segments:
283 304 ansi_tags = self.color_pat.findall(text)
284 305
285 306 for tag in ansi_tags:
286 307 i = segments.index(tag)
287 308 self.StartStyling(self.getCurrentLineEnd(), 0xFF)
288 309 self.AppendText(segments[i+1])
289 310
290 311 if tag != '0':
291 312 self.SetStyling(len(segments[i+1]), self.ANSI_STYLES[tag][0])
292 313
293 314 segments.pop(i)
294 315
295 316 self.moveCursor(self.getCurrentLineEnd())
296 317
297 318 def getPromptLen(self):
298 319 '''
299 320 Return the length of current prompt
300 321 '''
301 322 return len(str(self.prompt_count)) + 7
302 323
303 324 def setPrompt(self, prompt):
304 325 self.prompt = prompt
305 326
306 327 def setIndentation(self, indentation):
307 328 self.indent = indentation
308 329
309 330 def setPromptCount(self, count):
310 331 self.prompt_count = count
311 332
312 333 def showPrompt(self):
313 334 '''
314 335 Prints prompt at start of line.
315 336
316 337 @param prompt: Prompt to print.
317 338 @type prompt: string
318 339 '''
319 340 self.write(self.prompt)
320 341 #now we update the position of end of prompt
321 342 self.current_start = self.getCurrentLineEnd()
322 343
323 344 autoindent = self.indent*' '
324 345 autoindent = autoindent.replace(' ','\t')
325 346 self.write(autoindent)
326 347
327 348 def changeLine(self, text):
328 349 '''
329 350 Replace currently entered command line with given text.
330 351
331 352 @param text: Text to use as replacement.
332 353 @type text: string
333 354 '''
334 355 self.SetSelection(self.getCurrentPromptStart(), self.getCurrentLineEnd())
335 356 self.ReplaceSelection(text)
336 357 self.moveCursor(self.getCurrentLineEnd())
337 358
338 359 def getCurrentPromptStart(self):
339 360 return self.current_start
340 361
341 362 def getCurrentLineStart(self):
342 363 return self.GotoLine(self.LineFromPosition(self.GetCurrentPos()))
343 364
344 365 def getCurrentLineEnd(self):
345 366 return self.GetLength()
346 367
347 368 def getCurrentLine(self):
348 369 '''
349 370 Get text in current command line.
350 371
351 372 @return: Text of current command line.
352 373 @rtype: string
353 374 '''
354 375 return self.GetTextRange(self.getCurrentPromptStart(),
355 376 self.getCurrentLineEnd())
356 377
357 378 def moveCursorOnNewValidKey(self):
358 379 #If cursor is at wrong position put it at last line...
359 380 if self.GetCurrentPos() < self.getCurrentPromptStart():
360 381 self.GotoPos(self.getCurrentPromptStart())
361 382
362 383 def removeFromTo(self, from_pos, to_pos):
363 384 if from_pos < to_pos:
364 385 self.SetSelection(from_pos, to_pos)
365 386 self.DeleteBack()
366 387
367 388 def removeCurrentLine(self):
368 389 self.LineDelete()
369 390
370 391 def moveCursor(self, position):
371 392 self.GotoPos(position)
372 393
373 394 def getCursorPos(self):
374 395 return self.GetCurrentPos()
375 396
376 397 def selectFromTo(self, from_pos, to_pos):
377 398 self.SetSelectionStart(from_pos)
378 399 self.SetSelectionEnd(to_pos)
379 400
380 401 def writeHistory(self, history):
381 402 self.removeFromTo(self.getCurrentPromptStart(), self.getCurrentLineEnd())
382 403 self.changeLine(history)
383 404
384 405 def setCompletionMethod(self, completion):
385 406 if completion in ['IPYTHON', 'STC']:
386 407 self.autocomplete_mode = completion
387 408 else:
388 409 raise AttributeError
389 410
390 411 def getCompletionMethod(self, completion):
391 412 return self.autocomplete_mode
392 413
393 414 def writeCompletion(self, possibilities):
394 415 if self.autocomplete_mode == 'IPYTHON':
395 416 max_len = len(max(possibilities, key=len))
396 417 max_symbol = ' '*max_len
397 418
398 419 #now we check how much symbol we can put on a line...
399 420 test_buffer = max_symbol + ' '*4
400 421
401 422 allowed_symbols = 80/len(test_buffer)
402 423 if allowed_symbols == 0:
403 424 allowed_symbols = 1
404 425
405 426 pos = 1
406 427 buf = ''
407 428 for symbol in possibilities:
408 429 #buf += symbol+'\n'#*spaces)
409 430 if pos < allowed_symbols:
410 431 spaces = max_len - len(symbol) + 4
411 432 buf += symbol+' '*spaces
412 433 pos += 1
413 434 else:
414 435 buf += symbol+'\n'
415 436 pos = 1
416 437 self.write(buf)
417 438 else:
418 439 possibilities.sort() # Python sorts are case sensitive
419 440 self.AutoCompSetIgnoreCase(False)
420 441 self.AutoCompSetAutoHide(False)
421 442 #let compute the length ot last word
422 splitter = [' ', '(', '[', '{']
443 splitter = [' ', '(', '[', '{','=']
423 444 last_word = self.getCurrentLine()
424 445 for breaker in splitter:
425 446 last_word = last_word.split(breaker)[-1]
426 447 self.AutoCompShow(len(last_word), " ".join(possibilities))
427 448
428 449 def _onKeypress(self, event, skip=True):
429 450 '''
430 451 Key press callback used for correcting behavior for console-like
431 452 interfaces. For example 'home' should go to prompt, not to begining of
432 453 line.
433 454
434 455 @param widget: Widget that key press accored in.
435 456 @type widget: gtk.Widget
436 457 @param event: Event object
437 458 @type event: gtk.gdk.Event
438 459
439 460 @return: Return True if event as been catched.
440 461 @rtype: boolean
441 462 '''
442
443 463 if not self.AutoCompActive():
444 464 if event.GetKeyCode() == wx.WXK_HOME:
445 465 if event.Modifiers == wx.MOD_NONE:
446 466 self.moveCursorOnNewValidKey()
447 467 self.moveCursor(self.getCurrentPromptStart())
448 468 return True
449 469 elif event.Modifiers == wx.MOD_SHIFT:
450 470 self.moveCursorOnNewValidKey()
451 471 self.selectFromTo(self.getCurrentPromptStart(), self.getCursorPos())
452 472 return True
453 473 else:
454 474 return False
455 475
456 476 elif event.GetKeyCode() == wx.WXK_LEFT:
457 477 if event.Modifiers == wx.MOD_NONE:
458 478 self.moveCursorOnNewValidKey()
459 479
460 480 self.moveCursor(self.getCursorPos()-1)
461 481 if self.getCursorPos() < self.getCurrentPromptStart():
462 482 self.moveCursor(self.getCurrentPromptStart())
463 483 return True
464 484
465 485 elif event.GetKeyCode() == wx.WXK_BACK:
466 486 self.moveCursorOnNewValidKey()
467 487 if self.getCursorPos() > self.getCurrentPromptStart():
468 488 event.Skip()
469 489 return True
470 490
471 491 if skip:
472 492 if event.GetKeyCode() not in [wx.WXK_PAGEUP, wx.WXK_PAGEDOWN]\
473 493 and event.Modifiers == wx.MOD_NONE:
474 494 self.moveCursorOnNewValidKey()
475 495
476 496 event.Skip()
477 497 return True
478 498 return False
479 499 else:
480 500 event.Skip()
481 501
482 502 def OnUpdateUI(self, evt):
483 503 # check for matching braces
484 504 braceAtCaret = -1
485 505 braceOpposite = -1
486 506 charBefore = None
487 507 caretPos = self.GetCurrentPos()
488 508
489 509 if caretPos > 0:
490 510 charBefore = self.GetCharAt(caretPos - 1)
491 511 styleBefore = self.GetStyleAt(caretPos - 1)
492 512
493 513 # check before
494 514 if charBefore and chr(charBefore) in "[]{}()" and styleBefore == stc.STC_P_OPERATOR:
495 515 braceAtCaret = caretPos - 1
496 516
497 517 # check after
498 518 if braceAtCaret < 0:
499 519 charAfter = self.GetCharAt(caretPos)
500 520 styleAfter = self.GetStyleAt(caretPos)
501 521
502 522 if charAfter and chr(charAfter) in "[]{}()" and styleAfter == stc.STC_P_OPERATOR:
503 523 braceAtCaret = caretPos
504 524
505 525 if braceAtCaret >= 0:
506 526 braceOpposite = self.BraceMatch(braceAtCaret)
507 527
508 528 if braceAtCaret != -1 and braceOpposite == -1:
509 529 self.BraceBadLight(braceAtCaret)
510 530 else:
511 531 self.BraceHighlight(braceAtCaret, braceOpposite)
512 532 #pt = self.PointFromPosition(braceOpposite)
513 533 #self.Refresh(True, wxRect(pt.x, pt.y, 5,5))
514 534 #print pt
515 535 #self.Refresh(False)
516 536
517 537 class IPShellWidget(wx.Panel):
518 538 '''
519 539 This is wx.Panel that embbed the IPython Thread and the wx.StyledTextControl
520 540 If you want to port this to any other GUI toolkit, just replace the
521 541 WxConsoleView by YOURGUIConsoleView and make YOURGUIIPythonView derivate
522 542 from whatever container you want. I've choosed to derivate from a wx.Panel
523 543 because it seems to be more useful
524 544 Any idea to make it more 'generic' welcomed.
525 545 '''
526 546
527 547 def __init__(self, parent, intro=None,
528 548 background_color="BLACK", add_button_handler=None,
529 549 wx_ip_shell=None, user_ns={},user_global_ns=None,
530 550 ):
531 551 '''
532 552 Initialize.
533 553 Instanciate an IPython thread.
534 554 Instanciate a WxConsoleView.
535 555 Redirect I/O to console.
536 556 '''
537 557 wx.Panel.__init__(self,parent,wx.ID_ANY)
538 558
539 559 self.parent = parent
540 560 ### IPython non blocking shell instanciation ###
541 561 self.cout = StringIO()
542 562 self.add_button_handler = add_button_handler
543 563
544 564 if wx_ip_shell is not None:
545 565 self.IP = wx_ip_shell
546 566 else:
547 567 self.IP = WxNonBlockingIPShell(self,
548 568 cout = self.cout, cerr = self.cout,
549 569 ask_exit_handler = self.askExitCallback)
550 570
551 571 ### IPython wx console view instanciation ###
552 572 #If user didn't defined an intro text, we create one for him
553 573 #If you really wnat an empty intro just call wxIPythonViewPanel
554 574 #with intro=''
555 575 if intro is None:
556 576 welcome_text = "Welcome to WxIPython Shell.\n\n"
557 welcome_text+= self.IP.getBanner()
577 welcome_text+= self.IP.get_banner()
558 578 welcome_text+= "!command -> Execute command in shell\n"
559 579 welcome_text+= "TAB -> Autocompletion\n"
560 580 else:
561 581 welcome_text = intro
562 582
563 583 self.text_ctrl = WxConsoleView(self,
564 self.IP.getPrompt(),
584 self.IP.get_prompt(),
565 585 intro=welcome_text,
566 586 background_color=background_color)
567 587
568 self.cout.write = self.text_ctrl.asyncWrite
569
570 588 option_text = wx.StaticText(self, -1, "Options:")
571 589 self.completion_option = wx.CheckBox(self, -1, "Scintilla Completion")
590 self.completion_option.SetToolTip(wx.ToolTip(
591 "Selects the completion type:\nEither Ipython default style or Scintilla one"))
572 592 #self.completion_option.SetValue(False)
573 593 self.background_option = wx.CheckBox(self, -1, "White Background")
594 self.background_option.SetToolTip(wx.ToolTip(
595 "Selects the back ground color: BLACK or WHITE"))
574 596 #self.background_option.SetValue(False)
597 self.threading_option = wx.CheckBox(self, -1, "Execute in thread")
598 self.threading_option.SetToolTip(wx.ToolTip(
599 "Use threading: infinite loop don't freeze the GUI and commands can be breaked\nNo threading: maximum compatibility"))
600 #self.threading_option.SetValue(False)
575 601
576 602 self.options={'completion':{'value':'IPYTHON',
577 603 'checkbox':self.completion_option,'STC':True,'IPYTHON':False,
578 604 'setfunc':self.text_ctrl.setCompletionMethod},
579 605 'background_color':{'value':'BLACK',
580 606 'checkbox':self.background_option,'WHITE':True,'BLACK':False,
581 607 'setfunc':self.text_ctrl.setBackgroundColor},
608 'threading':{'value':'True',
609 'checkbox':self.threading_option,'True':True,'False':False,
610 'setfunc':self.IP.set_threading},
582 611 }
612
613 #self.cout.write dEfault option is asynchroneous because default sate is threading ON
614 self.cout.write = self.text_ctrl.asyncWrite
615 #we reloard options
583 616 self.reloadOptions(self.options)
584 617
585 618 self.text_ctrl.Bind(wx.EVT_KEY_DOWN, self.keyPress)
586 619 self.completion_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionCompletion)
587 620 self.background_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionBackgroundColor)
621 self.threading_option.Bind(wx.EVT_CHECKBOX, self.evtCheckOptionThreading)
588 622
589 623 ### making the layout of the panel ###
590 624 sizer = wx.BoxSizer(wx.VERTICAL)
591 625 sizer.Add(self.text_ctrl, 1, wx.EXPAND)
592 626 option_sizer = wx.BoxSizer(wx.HORIZONTAL)
593 627 sizer.Add(option_sizer, 0)
594 628 option_sizer.AddMany([(10, 20),
595 629 (option_text, 0, wx.ALIGN_CENTER_VERTICAL),
596 630 (5, 5),
597 631 (self.completion_option, 0, wx.ALIGN_CENTER_VERTICAL),
598 632 (8, 8),
599 (self.background_option, 0, wx.ALIGN_CENTER_VERTICAL)
633 (self.background_option, 0, wx.ALIGN_CENTER_VERTICAL),
634 (8, 8),
635 (self.threading_option, 0, wx.ALIGN_CENTER_VERTICAL)
600 636 ])
601 637 self.SetAutoLayout(True)
602 638 sizer.Fit(self)
603 639 sizer.SetSizeHints(self)
604 640 self.SetSizer(sizer)
605 641 #and we focus on the widget :)
606 642 self.SetFocus()
607 643
608 644 #widget state management (for key handling different cases)
609 645 self.setCurrentState('IDLE')
610 646 self.pager_state = 'DONE'
611 647 self.raw_input_current_line = 0
612 648
613 649 def askExitCallback(self, event):
614 650 self.askExitHandler(event)
615 651
616 652 #---------------------- IPython Thread Management ------------------------
617 653 def stateDoExecuteLine(self):
618 654 lines=self.text_ctrl.getCurrentLine()
619 655 self.text_ctrl.write('\n')
620 656 lines_to_execute = lines.replace('\t',' '*4)
621 657 lines_to_execute = lines_to_execute.replace('\r','')
622 self.IP.doExecute(lines_to_execute.encode(ENCODING))
658 self.IP.do_execute(lines_to_execute.encode(ENCODING))
623 659 self.updateHistoryTracker(lines)
660 if(self.text_ctrl.getCursorPos()!=0):
661 self.text_ctrl.removeCurrentLine()
624 662 self.setCurrentState('WAIT_END_OF_EXECUTION')
625 663
626 664 def evtStateExecuteDone(self,evt):
627 self.doc = self.IP.getDocText()
628 self.help = self.IP.getHelpText()
665 self.doc = self.IP.get_doc_text()
666 self.help = self.IP.get_help_text()
629 667 if self.doc:
630 668 self.pager_lines = self.doc[7:].split('\n')
631 669 self.pager_state = 'INIT'
632 670 self.setCurrentState('SHOW_DOC')
633 671 self.pager(self.doc)
634 672 elif self.help:
635 673 self.pager_lines = self.help.split('\n')
636 674 self.pager_state = 'INIT'
637 675 self.setCurrentState('SHOW_DOC')
638 676 self.pager(self.help)
639 677 else:
678 if(self.text_ctrl.getCursorPos()!=0):
679 self.text_ctrl.removeCurrentLine()
640 680 self.stateShowPrompt()
641 681
642 682 def stateShowPrompt(self):
643 683 self.setCurrentState('SHOW_PROMPT')
644 self.text_ctrl.setPrompt(self.IP.getPrompt())
645 self.text_ctrl.setIndentation(self.IP.getIndentation())
646 self.text_ctrl.setPromptCount(self.IP.getPromptCount())
684 self.text_ctrl.setPrompt(self.IP.get_prompt())
685 self.text_ctrl.setIndentation(self.IP.get_indentation())
686 self.text_ctrl.setPromptCount(self.IP.get_prompt_count())
647 687 self.text_ctrl.showPrompt()
648 self.IP.initHistoryIndex()
688 self.IP.init_history_index()
649 689 self.setCurrentState('IDLE')
650 690
651 691 def setCurrentState(self, state):
652 692 self.cur_state = state
653 693 self.updateStatusTracker(self.cur_state)
654 694
655 695 def pager(self,text):
656 696
657 697 if self.pager_state == 'INIT':
658 698 #print >>sys.__stdout__,"PAGER state:",self.pager_state
659 699 self.pager_nb_lines = len(self.pager_lines)
660 700 self.pager_index = 0
661 701 self.pager_do_remove = False
662 702 self.text_ctrl.write('\n')
663 703 self.pager_state = 'PROCESS_LINES'
664 704
665 705 if self.pager_state == 'PROCESS_LINES':
666 706 #print >>sys.__stdout__,"PAGER state:",self.pager_state
667 707 if self.pager_do_remove == True:
668 708 self.text_ctrl.removeCurrentLine()
669 709 self.pager_do_remove = False
670 710
671 711 if self.pager_nb_lines > 10:
672 712 #print >>sys.__stdout__,"PAGER processing 10 lines"
673 713 if self.pager_index > 0:
674 714 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
675 715 else:
676 716 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
677 717
678 718 for line in self.pager_lines[self.pager_index+1:self.pager_index+9]:
679 719 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
680 720 self.pager_index += 10
681 721 self.pager_nb_lines -= 10
682 722 self.text_ctrl.write("--- Push Enter to continue or 'Q' to quit---")
683 723 self.pager_do_remove = True
684 724 self.pager_state = 'WAITING'
685 725 return
686 726 else:
687 727 #print >>sys.__stdout__,"PAGER processing last lines"
688 728 if self.pager_nb_lines > 0:
689 729 if self.pager_index > 0:
690 730 self.text_ctrl.write(">\x01\x1b[1;36m\x02"+self.pager_lines[self.pager_index]+'\n')
691 731 else:
692 732 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+self.pager_lines[self.pager_index]+'\n')
693 733
694 734 self.pager_index += 1
695 735 self.pager_nb_lines -= 1
696 736 if self.pager_nb_lines > 0:
697 737 for line in self.pager_lines[self.pager_index:]:
698 738 self.text_ctrl.write("\x01\x1b[1;36m\x02 "+line+'\n')
699 739 self.pager_nb_lines = 0
700 740 self.pager_state = 'DONE'
701 741 self.stateShowPrompt()
702 742
703 743 #------------------------ Key Handler ------------------------------------
704 744 def keyPress(self, event):
705 745 '''
706 746 Key press callback with plenty of shell goodness, like history,
707 747 autocompletions, etc.
708 748 '''
709 749 if event.GetKeyCode() == ord('C'):
710 750 if event.Modifiers == wx.MOD_CONTROL or event.Modifiers == wx.MOD_ALT:
711 751 if self.cur_state == 'WAIT_END_OF_EXECUTION':
712 752 #we raise an exception inside the IPython thread container
713 753 self.IP.ce.raise_exc(KeyboardInterrupt)
714 754 return
715 755
716 756 #let this before 'wx.WXK_RETURN' because we have to put 'IDLE'
717 757 #mode if AutoComp has been set as inactive
718 758 if self.cur_state == 'COMPLETING':
719 759 if not self.text_ctrl.AutoCompActive():
720 760 self.cur_state = 'IDLE'
721 761 else:
722 762 event.Skip()
723 763
724 764 if event.KeyCode == wx.WXK_RETURN:
725 765 if self.cur_state == 'IDLE':
726 766 #we change the state ot the state machine
727 767 self.setCurrentState('DO_EXECUTE_LINE')
728 768 self.stateDoExecuteLine()
729 769 return
730 770
731 771 if self.pager_state == 'WAITING':
732 772 self.pager_state = 'PROCESS_LINES'
733 773 self.pager(self.doc)
734 774 return
735 775
736 776 if self.cur_state == 'WAITING_USER_INPUT':
737 777 line=self.text_ctrl.getCurrentLine()
738 778 self.text_ctrl.write('\n')
739 779 self.setCurrentState('WAIT_END_OF_EXECUTION')
740 780 return
741 781
742 782 if event.GetKeyCode() in [ord('q'),ord('Q')]:
743 783 if self.pager_state == 'WAITING':
744 784 self.pager_state = 'DONE'
745 785 self.text_ctrl.write('\n')
746 786 self.stateShowPrompt()
747 787 return
748 788
749 789 if self.cur_state == 'WAITING_USER_INPUT':
750 790 event.Skip()
751 791
752 792 if self.cur_state == 'IDLE':
753 793 if event.KeyCode == wx.WXK_UP:
754 history = self.IP.historyBack()
794 history = self.IP.history_back()
755 795 self.text_ctrl.writeHistory(history)
756 796 return
757 797 if event.KeyCode == wx.WXK_DOWN:
758 history = self.IP.historyForward()
798 history = self.IP.history_forward()
759 799 self.text_ctrl.writeHistory(history)
760 800 return
761 801 if event.KeyCode == wx.WXK_TAB:
762 802 #if line empty we disable tab completion
763 803 if not self.text_ctrl.getCurrentLine().strip():
764 804 self.text_ctrl.write('\t')
765 805 return
766 806 completed, possibilities = self.IP.complete(self.text_ctrl.getCurrentLine())
767 807 if len(possibilities) > 1:
768 808 if self.text_ctrl.autocomplete_mode == 'IPYTHON':
769 809 cur_slice = self.text_ctrl.getCurrentLine()
770 810 self.text_ctrl.write('\n')
771 811 self.text_ctrl.writeCompletion(possibilities)
772 812 self.text_ctrl.write('\n')
773 813
774 814 self.text_ctrl.showPrompt()
775 815 self.text_ctrl.write(cur_slice)
776 816 self.text_ctrl.changeLine(completed or cur_slice)
777 817 else:
778 818 self.cur_state = 'COMPLETING'
779 819 self.text_ctrl.writeCompletion(possibilities)
780 820 else:
781 821 self.text_ctrl.changeLine(completed or cur_slice)
782 822 return
783 823 event.Skip()
784 824
785 825 #------------------------ Option Section ---------------------------------
786 826 def evtCheckOptionCompletion(self, event):
787 827 if event.IsChecked():
788 828 self.options['completion']['value']='STC'
789 829 else:
790 830 self.options['completion']['value']='IPYTHON'
791 831 self.text_ctrl.setCompletionMethod(self.options['completion']['value'])
792 832 self.updateOptionTracker('completion',
793 833 self.options['completion']['value'])
794 834 self.text_ctrl.SetFocus()
795 835
796 836 def evtCheckOptionBackgroundColor(self, event):
797 837 if event.IsChecked():
798 838 self.options['background_color']['value']='WHITE'
799 839 else:
800 840 self.options['background_color']['value']='BLACK'
801 841 self.text_ctrl.setBackgroundColor(self.options['background_color']['value'])
802 842 self.updateOptionTracker('background_color',
803 843 self.options['background_color']['value'])
804 844 self.text_ctrl.SetFocus()
805 845
846 def evtCheckOptionThreading(self, event):
847 if event.IsChecked():
848 self.options['threading']['value']='True'
849 self.IP.set_threading(True)
850 self.cout.write = self.text_ctrl.asyncWrite
851 else:
852 self.options['threading']['value']='False'
853 self.IP.set_threading(False)
854 self.cout.write = self.text_ctrl.write
855 self.updateOptionTracker('threading',
856 self.options['threading']['value'])
857 self.text_ctrl.SetFocus()
858
806 859 def getOptions(self):
807 860 return self.options
808 861
809 862 def reloadOptions(self,options):
810 863 self.options = options
811 864 for key in self.options.keys():
812 865 value = self.options[key]['value']
813 866 self.options[key]['checkbox'].SetValue(self.options[key][value])
814 867 self.options[key]['setfunc'](value)
815 868
869 if self.options['threading']['value']=='True':
870 self.IP.set_threading(True)
871 self.cout.write = self.text_ctrl.asyncWrite
872 else:
873 self.IP.set_threading(False)
874 self.cout.write = self.text_ctrl.write
816 875
817 876 #------------------------ Hook Section -----------------------------------
818 877 def updateOptionTracker(self,name,value):
819 878 '''
820 879 Default history tracker (does nothing)
821 880 '''
822 881 pass
823 882
824 883 def setOptionTrackerHook(self,func):
825 884 '''
826 885 Define a new history tracker
827 886 '''
828 887 self.updateOptionTracker = func
829 888
830 889 def updateHistoryTracker(self,command_line):
831 890 '''
832 891 Default history tracker (does nothing)
833 892 '''
834 893 pass
835 894
836 895 def setHistoryTrackerHook(self,func):
837 896 '''
838 897 Define a new history tracker
839 898 '''
840 899 self.updateHistoryTracker = func
841 900
842 901 def updateStatusTracker(self,status):
843 902 '''
844 903 Default status tracker (does nothing)
845 904 '''
846 905 pass
847 906
848 907 def setStatusTrackerHook(self,func):
849 908 '''
850 909 Define a new status tracker
851 910 '''
852 911 self.updateStatusTracker = func
853 912
854 913 def askExitHandler(self, event):
855 914 '''
856 915 Default exit handler
857 916 '''
858 917 self.text_ctrl.write('\nExit callback has not been set.')
859 918
860 919 def setAskExitHandler(self, func):
861 920 '''
862 921 Define an exit handler
863 922 '''
864 923 self.askExitHandler = func
865 924
866 925 if __name__ == '__main__':
867 926 # Some simple code to test the shell widget.
868 927 class MainWindow(wx.Frame):
869 928 def __init__(self, parent, id, title):
870 929 wx.Frame.__init__(self, parent, id, title, size=(300,250))
871 930 self._sizer = wx.BoxSizer(wx.VERTICAL)
872 931 self.shell = IPShellWidget(self)
873 932 self._sizer.Add(self.shell, 1, wx.EXPAND)
874 933 self.SetSizer(self._sizer)
875 934 self.SetAutoLayout(1)
876 935 self.Show(True)
877 936
878 937 app = wx.PySimpleApp()
879 938 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
880 939 frame.SetSize((780, 460))
881 940 shell = frame.shell
882 941
883 942 app.MainLoop()
884 943
885 944
@@ -1,252 +1,266
1 1 #!/usr/bin/python
2 2 # -*- coding: iso-8859-15 -*-
3 3
4 4 import wx.aui
5 5 import sys
6 6 #used for about dialog
7 7 from wx.lib.wordwrap import wordwrap
8 8
9 9 #used for ipython GUI objects
10 10 from IPython.gui.wx.ipython_view import IPShellWidget
11 11 from IPython.gui.wx.ipython_history import IPythonHistoryPanel
12 12
13 #used to invoke ipython1 wx implementation
14 ### FIXME ### temporary disabled due to interference with 'show_in_pager' hook
15 is_sync_frontend_ok = False
16 try:
17 from IPython.frontend.wx.ipythonx import IPythonXController
18 except ImportError:
19 is_sync_frontend_ok = False
20
13 21 #used to create options.conf file in user directory
14 22 from IPython.ipapi import get
15 23
16 __version__ = 0.8
24 __version__ = 0.91
17 25 __author__ = "Laurent Dufrechou"
18 26 __email__ = "laurent.dufrechou _at_ gmail.com"
19 27 __license__ = "BSD"
20 28
21 29 #-----------------------------------------
22 30 # Creating one main frame for our
23 31 # application with movables windows
24 32 #-----------------------------------------
25 33 class MyFrame(wx.Frame):
26 34 """Creating one main frame for our
27 35 application with movables windows"""
28 36 def __init__(self, parent=None, id=-1, title="WxIPython",
29 37 pos=wx.DefaultPosition,
30 size=(800, 600), style=wx.DEFAULT_FRAME_STYLE):
38 size=(800, 600), style=wx.DEFAULT_FRAME_STYLE, sync_ok=False):
31 39 wx.Frame.__init__(self, parent, id, title, pos, size, style)
32 40 self._mgr = wx.aui.AuiManager()
33 41
34 42 # notify PyAUI which frame to use
35 43 self._mgr.SetManagedWindow(self)
36 44
37 45 #create differents panels and make them persistant
38 46 self.history_panel = IPythonHistoryPanel(self)
39 47
40 48 self.history_panel.setOptionTrackerHook(self.optionSave)
41 49
42 50 self.ipython_panel = IPShellWidget(self,background_color = "BLACK")
43 51 #self.ipython_panel = IPShellWidget(self,background_color = "WHITE")
44
52 if(sync_ok):
53 self.ipython_panel2 = IPythonXController(self)
54 else:
55 self.ipython_panel2 = None
45 56 self.ipython_panel.setHistoryTrackerHook(self.history_panel.write)
46 57 self.ipython_panel.setStatusTrackerHook(self.updateStatus)
47 58 self.ipython_panel.setAskExitHandler(self.OnExitDlg)
48 59 self.ipython_panel.setOptionTrackerHook(self.optionSave)
49 60
61 #Create a notebook to display different IPython shell implementations
62 self.nb = wx.aui.AuiNotebook(self)
63
50 64 self.optionLoad()
51 65
52 66 self.statusbar = self.createStatus()
53 67 self.createMenu()
54 68
55 69 ########################################################################
56 70 ### add the panes to the manager
57 71 # main panels
58 self._mgr.AddPane(self.ipython_panel , wx.CENTER, "IPython Shell")
72 self._mgr.AddPane(self.nb , wx.CENTER, "IPython Shells")
73 self.nb.AddPage(self.ipython_panel , "IPython0 Shell")
74 if(sync_ok):
75 self.nb.AddPage(self.ipython_panel2, "IPython1 Synchroneous Shell")
76
59 77 self._mgr.AddPane(self.history_panel , wx.RIGHT, "IPython history")
60 78
61 79 # now we specify some panel characteristics
62 80 self._mgr.GetPane(self.ipython_panel).CaptionVisible(True);
63 81 self._mgr.GetPane(self.history_panel).CaptionVisible(True);
64 82 self._mgr.GetPane(self.history_panel).MinSize((200,400));
65 83
66 84 # tell the manager to "commit" all the changes just made
67 85 self._mgr.Update()
68 86
69 87 #global event handling
70 88 self.Bind(wx.EVT_CLOSE, self.OnClose)
71 89 self.Bind(wx.EVT_MENU, self.OnClose,id=wx.ID_EXIT)
72 90 self.Bind(wx.EVT_MENU, self.OnShowIPythonPanel,id=wx.ID_HIGHEST+1)
73 91 self.Bind(wx.EVT_MENU, self.OnShowHistoryPanel,id=wx.ID_HIGHEST+2)
74 92 self.Bind(wx.EVT_MENU, self.OnShowAbout, id=wx.ID_HIGHEST+3)
75 93 self.Bind(wx.EVT_MENU, self.OnShowAllPanel,id=wx.ID_HIGHEST+6)
76 94
77 95 warn_text = 'Hello from IPython and wxPython.\n'
78 96 warn_text +='Please Note that this work is still EXPERIMENTAL\n'
79 97 warn_text +='It does NOT emulate currently all the IPython functions.\n'
98 warn_text +="\nIf you use MATPLOTLIB with show() you'll need to deactivate the THREADING option.\n"
99 if(not sync_ok):
100 warn_text +="\n->No twisted package detected, IPython1 example deactivated."
80 101
81 102 dlg = wx.MessageDialog(self,
82 103 warn_text,
83 104 'Warning Box',
84 105 wx.OK | wx.ICON_INFORMATION
85 106 )
86 107 dlg.ShowModal()
87 108 dlg.Destroy()
88 109
89 110 def optionSave(self, name, value):
90 111 ip = get()
91 112 path = ip.IP.rc.ipythondir
92 113 opt = open(path + '/options.conf','w')
93 114
94 115 try:
95 116 options_ipython_panel = self.ipython_panel.getOptions()
96 117 options_history_panel = self.history_panel.getOptions()
97 118
98 119 for key in options_ipython_panel.keys():
99 120 opt.write(key + '=' + options_ipython_panel[key]['value']+'\n')
100 121 for key in options_history_panel.keys():
101 122 opt.write(key + '=' + options_history_panel[key]['value']+'\n')
102 123 finally:
103 124 opt.close()
104 125
105 126 def optionLoad(self):
106 127 try:
107 128 ip = get()
108 129 path = ip.IP.rc.ipythondir
109 130 opt = open(path + '/options.conf','r')
110 131 lines = opt.readlines()
111 132 opt.close()
112 133
113 134 options_ipython_panel = self.ipython_panel.getOptions()
114 135 options_history_panel = self.history_panel.getOptions()
115 136
116 137 for line in lines:
117 138 key = line.split('=')[0]
118 139 value = line.split('=')[1].replace('\n','').replace('\r','')
119 140 if key in options_ipython_panel.keys():
120 141 options_ipython_panel[key]['value'] = value
121 142 elif key in options_history_panel.keys():
122 143 options_history_panel[key]['value'] = value
123 144 else:
124 145 print >>sys.__stdout__,"Warning: key ",key,"not found in widget options. Check Options.conf"
125 146 self.ipython_panel.reloadOptions(options_ipython_panel)
126 147 self.history_panel.reloadOptions(options_history_panel)
127 148
128 149 except IOError:
129 150 print >>sys.__stdout__,"Could not open Options.conf, defaulting to default values."
130 151
131 152
132 153 def createMenu(self):
133 154 """local method used to create one menu bar"""
134 155
135 156 mb = wx.MenuBar()
136 157
137 158 file_menu = wx.Menu()
138 159 file_menu.Append(wx.ID_EXIT, "Exit")
139 160
140 161 view_menu = wx.Menu()
141 162 view_menu.Append(wx.ID_HIGHEST+1, "Show IPython Panel")
142 163 view_menu.Append(wx.ID_HIGHEST+2, "Show History Panel")
143 164 view_menu.AppendSeparator()
144 165 view_menu.Append(wx.ID_HIGHEST+6, "Show All")
145 166
146 167 about_menu = wx.Menu()
147 168 about_menu.Append(wx.ID_HIGHEST+3, "About")
148 169
149 #view_menu.AppendSeparator()
150 #options_menu = wx.Menu()
151 #options_menu.AppendCheckItem(wx.ID_HIGHEST+7, "Allow Floating")
152 #options_menu.AppendCheckItem(wx.ID_HIGHEST+8, "Transparent Hint")
153 #options_menu.AppendCheckItem(wx.ID_HIGHEST+9, "Transparent Hint Fade-in")
154
155
156 170 mb.Append(file_menu, "File")
157 171 mb.Append(view_menu, "View")
158 172 mb.Append(about_menu, "About")
159 173 #mb.Append(options_menu, "Options")
160 174
161 175 self.SetMenuBar(mb)
162 176
163 177 def createStatus(self):
164 178 statusbar = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
165 179 statusbar.SetStatusWidths([-2, -3])
166 180 statusbar.SetStatusText("Ready", 0)
167 181 statusbar.SetStatusText("WxIPython "+str(__version__), 1)
168 182 return statusbar
169 183
170 184 def updateStatus(self,text):
171 185 states = {'IDLE':'Idle',
172 186 'DO_EXECUTE_LINE':'Send command',
173 187 'WAIT_END_OF_EXECUTION':'Running command',
174 188 'WAITING_USER_INPUT':'Waiting user input',
175 189 'SHOW_DOC':'Showing doc',
176 190 'SHOW_PROMPT':'Showing prompt'}
177 191 self.statusbar.SetStatusText(states[text], 0)
178 192
179 193 def OnClose(self, event):
180 194 """#event used to close program """
181 195 # deinitialize the frame manager
182 196 self._mgr.UnInit()
183 197 self.Destroy()
184 198 event.Skip()
185 199
186 200 def OnExitDlg(self, event):
187 201 dlg = wx.MessageDialog(self, 'Are you sure you want to quit WxIPython',
188 202 'WxIPython exit',
189 203 wx.ICON_QUESTION |
190 204 wx.YES_NO | wx.NO_DEFAULT
191 205 )
192 206 if dlg.ShowModal() == wx.ID_YES:
193 207 dlg.Destroy()
194 208 self._mgr.UnInit()
195 209 self.Destroy()
196 210 dlg.Destroy()
197 211
198 212 #event to display IPython pannel
199 213 def OnShowIPythonPanel(self,event):
200 214 """ #event to display Boxpannel """
201 215 self._mgr.GetPane(self.ipython_panel).Show(True)
202 216 self._mgr.Update()
203 217 #event to display History pannel
204 218 def OnShowHistoryPanel(self,event):
205 219 self._mgr.GetPane(self.history_panel).Show(True)
206 220 self._mgr.Update()
207 221
208 222 def OnShowAllPanel(self,event):
209 223 """#event to display all Pannels"""
210 224 self._mgr.GetPane(self.ipython_panel).Show(True)
211 225 self._mgr.GetPane(self.history_panel).Show(True)
212 226 self._mgr.Update()
213 227
214 228 def OnShowAbout(self, event):
215 229 # First we create and fill the info object
216 230 info = wx.AboutDialogInfo()
217 231 info.Name = "WxIPython"
218 232 info.Version = str(__version__)
219 233 info.Copyright = "(C) 2007 Laurent Dufrechou"
220 234 info.Description = wordwrap(
221 235 "A Gui that embbed a multithreaded IPython Shell",
222 236 350, wx.ClientDC(self))
223 237 info.WebSite = ("http://ipython.scipy.org/", "IPython home page")
224 238 info.Developers = [ "Laurent Dufrechou" ]
225 239 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"
226 240 info.License = wordwrap(licenseText, 500, wx.ClientDC(self))
227 241
228 242 # Then we call wx.AboutBox giving it that info object
229 243 wx.AboutBox(info)
230 244
231 245 #-----------------------------------------
232 246 #Creating our application
233 247 #-----------------------------------------
234 248 class MyApp(wx.PySimpleApp):
235 249 """Creating our application"""
236 def __init__(self):
250 def __init__(self, sync_ok=False):
237 251 wx.PySimpleApp.__init__(self)
238 252
239 self.frame = MyFrame()
253 self.frame = MyFrame(sync_ok=sync_ok)
240 254 self.frame.Show()
241 255
242 256 #-----------------------------------------
243 257 #Main loop
244 258 #-----------------------------------------
245 259 def main():
246 app = MyApp()
260 app = MyApp(is_sync_frontend_ok)
247 261 app.SetTopWindow(app.frame)
248 262 app.MainLoop()
249 263
250 264 #if launched as main program run this
251 265 if __name__ == '__main__':
252 266 main()
General Comments 0
You need to be logged in to leave comments. Login now