##// END OF EJS Templates
wx namespace patch by fernando
vivainio -
Show More
@@ -1,1039 +1,1039 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Shell classes.
3 3
4 4 All the matplotlib support code was co-developed with John Hunter,
5 5 matplotlib's author.
6 6
7 $Id: Shell.py 1384 2006-06-29 20:04:37Z vivainio $"""
7 $Id: Shell.py 1988 2006-12-13 16:49:41Z vivainio $"""
8 8
9 9 #*****************************************************************************
10 10 # Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #*****************************************************************************
15 15
16 16 from IPython import Release
17 17 __author__ = '%s <%s>' % Release.authors['Fernando']
18 18 __license__ = Release.license
19 19
20 20 # Code begins
21 21 import __builtin__
22 22 import __main__
23 23 import Queue
24 24 import os
25 25 import signal
26 26 import sys
27 27 import threading
28 28 import time
29 29
30 30 import IPython
31 31 from IPython import ultraTB
32 32 from IPython.genutils import Term,warn,error,flag_calls
33 33 from IPython.iplib import InteractiveShell
34 34 from IPython.ipmaker import make_IPython
35 35 from IPython.Magic import Magic
36 36 from IPython.ipstruct import Struct
37 37
38 38 # global flag to pass around information about Ctrl-C without exceptions
39 39 KBINT = False
40 40
41 41 # global flag to turn on/off Tk support.
42 42 USE_TK = False
43 43
44 44 #-----------------------------------------------------------------------------
45 45 # This class is trivial now, but I want to have it in to publish a clean
46 46 # interface. Later when the internals are reorganized, code that uses this
47 47 # shouldn't have to change.
48 48
49 49 class IPShell:
50 50 """Create an IPython instance."""
51 51
52 52 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
53 53 debug=1,shell_class=InteractiveShell):
54 54 self.IP = make_IPython(argv,user_ns=user_ns,
55 55 user_global_ns=user_global_ns,
56 56 debug=debug,shell_class=shell_class)
57 57
58 58 def mainloop(self,sys_exit=0,banner=None):
59 59 self.IP.mainloop(banner)
60 60 if sys_exit:
61 61 sys.exit()
62 62
63 63 #-----------------------------------------------------------------------------
64 64 class IPShellEmbed:
65 65 """Allow embedding an IPython shell into a running program.
66 66
67 67 Instances of this class are callable, with the __call__ method being an
68 68 alias to the embed() method of an InteractiveShell instance.
69 69
70 70 Usage (see also the example-embed.py file for a running example):
71 71
72 72 ipshell = IPShellEmbed([argv,banner,exit_msg,rc_override])
73 73
74 74 - argv: list containing valid command-line options for IPython, as they
75 75 would appear in sys.argv[1:].
76 76
77 77 For example, the following command-line options:
78 78
79 79 $ ipython -prompt_in1 'Input <\\#>' -colors LightBG
80 80
81 81 would be passed in the argv list as:
82 82
83 83 ['-prompt_in1','Input <\\#>','-colors','LightBG']
84 84
85 85 - banner: string which gets printed every time the interpreter starts.
86 86
87 87 - exit_msg: string which gets printed every time the interpreter exits.
88 88
89 89 - rc_override: a dict or Struct of configuration options such as those
90 90 used by IPython. These options are read from your ~/.ipython/ipythonrc
91 91 file when the Shell object is created. Passing an explicit rc_override
92 92 dict with any options you want allows you to override those values at
93 93 creation time without having to modify the file. This way you can create
94 94 embeddable instances configured in any way you want without editing any
95 95 global files (thus keeping your interactive IPython configuration
96 96 unchanged).
97 97
98 98 Then the ipshell instance can be called anywhere inside your code:
99 99
100 100 ipshell(header='') -> Opens up an IPython shell.
101 101
102 102 - header: string printed by the IPython shell upon startup. This can let
103 103 you know where in your code you are when dropping into the shell. Note
104 104 that 'banner' gets prepended to all calls, so header is used for
105 105 location-specific information.
106 106
107 107 For more details, see the __call__ method below.
108 108
109 109 When the IPython shell is exited with Ctrl-D, normal program execution
110 110 resumes.
111 111
112 112 This functionality was inspired by a posting on comp.lang.python by cmkl
113 113 <cmkleffner@gmx.de> on Dec. 06/01 concerning similar uses of pyrepl, and
114 114 by the IDL stop/continue commands."""
115 115
116 116 def __init__(self,argv=None,banner='',exit_msg=None,rc_override=None,
117 117 user_ns=None):
118 118 """Note that argv here is a string, NOT a list."""
119 119 self.set_banner(banner)
120 120 self.set_exit_msg(exit_msg)
121 121 self.set_dummy_mode(0)
122 122
123 123 # sys.displayhook is a global, we need to save the user's original
124 124 # Don't rely on __displayhook__, as the user may have changed that.
125 125 self.sys_displayhook_ori = sys.displayhook
126 126
127 127 # save readline completer status
128 128 try:
129 129 #print 'Save completer',sys.ipcompleter # dbg
130 130 self.sys_ipcompleter_ori = sys.ipcompleter
131 131 except:
132 132 pass # not nested with IPython
133 133
134 134 self.IP = make_IPython(argv,rc_override=rc_override,
135 135 embedded=True,
136 136 user_ns=user_ns)
137 137
138 138 # copy our own displayhook also
139 139 self.sys_displayhook_embed = sys.displayhook
140 140 # and leave the system's display hook clean
141 141 sys.displayhook = self.sys_displayhook_ori
142 142 # don't use the ipython crash handler so that user exceptions aren't
143 143 # trapped
144 144 sys.excepthook = ultraTB.FormattedTB(color_scheme = self.IP.rc.colors,
145 145 mode = self.IP.rc.xmode,
146 146 call_pdb = self.IP.rc.pdb)
147 147 self.restore_system_completer()
148 148
149 149 def restore_system_completer(self):
150 150 """Restores the readline completer which was in place.
151 151
152 152 This allows embedded IPython within IPython not to disrupt the
153 153 parent's completion.
154 154 """
155 155
156 156 try:
157 157 self.IP.readline.set_completer(self.sys_ipcompleter_ori)
158 158 sys.ipcompleter = self.sys_ipcompleter_ori
159 159 except:
160 160 pass
161 161
162 162 def __call__(self,header='',local_ns=None,global_ns=None,dummy=None):
163 163 """Activate the interactive interpreter.
164 164
165 165 __call__(self,header='',local_ns=None,global_ns,dummy=None) -> Start
166 166 the interpreter shell with the given local and global namespaces, and
167 167 optionally print a header string at startup.
168 168
169 169 The shell can be globally activated/deactivated using the
170 170 set/get_dummy_mode methods. This allows you to turn off a shell used
171 171 for debugging globally.
172 172
173 173 However, *each* time you call the shell you can override the current
174 174 state of dummy_mode with the optional keyword parameter 'dummy'. For
175 175 example, if you set dummy mode on with IPShell.set_dummy_mode(1), you
176 176 can still have a specific call work by making it as IPShell(dummy=0).
177 177
178 178 The optional keyword parameter dummy controls whether the call
179 179 actually does anything. """
180 180
181 181 # Allow the dummy parameter to override the global __dummy_mode
182 182 if dummy or (dummy != 0 and self.__dummy_mode):
183 183 return
184 184
185 185 # Set global subsystems (display,completions) to our values
186 186 sys.displayhook = self.sys_displayhook_embed
187 187 if self.IP.has_readline:
188 188 self.IP.readline.set_completer(self.IP.Completer.complete)
189 189
190 190 if self.banner and header:
191 191 format = '%s\n%s\n'
192 192 else:
193 193 format = '%s%s\n'
194 194 banner = format % (self.banner,header)
195 195
196 196 # Call the embedding code with a stack depth of 1 so it can skip over
197 197 # our call and get the original caller's namespaces.
198 198 self.IP.embed_mainloop(banner,local_ns,global_ns,stack_depth=1)
199 199
200 200 if self.exit_msg:
201 201 print self.exit_msg
202 202
203 203 # Restore global systems (display, completion)
204 204 sys.displayhook = self.sys_displayhook_ori
205 205 self.restore_system_completer()
206 206
207 207 def set_dummy_mode(self,dummy):
208 208 """Sets the embeddable shell's dummy mode parameter.
209 209
210 210 set_dummy_mode(dummy): dummy = 0 or 1.
211 211
212 212 This parameter is persistent and makes calls to the embeddable shell
213 213 silently return without performing any action. This allows you to
214 214 globally activate or deactivate a shell you're using with a single call.
215 215
216 216 If you need to manually"""
217 217
218 218 if dummy not in [0,1,False,True]:
219 219 raise ValueError,'dummy parameter must be boolean'
220 220 self.__dummy_mode = dummy
221 221
222 222 def get_dummy_mode(self):
223 223 """Return the current value of the dummy mode parameter.
224 224 """
225 225 return self.__dummy_mode
226 226
227 227 def set_banner(self,banner):
228 228 """Sets the global banner.
229 229
230 230 This banner gets prepended to every header printed when the shell
231 231 instance is called."""
232 232
233 233 self.banner = banner
234 234
235 235 def set_exit_msg(self,exit_msg):
236 236 """Sets the global exit_msg.
237 237
238 238 This exit message gets printed upon exiting every time the embedded
239 239 shell is called. It is None by default. """
240 240
241 241 self.exit_msg = exit_msg
242 242
243 243 #-----------------------------------------------------------------------------
244 244 def sigint_handler (signum,stack_frame):
245 245 """Sigint handler for threaded apps.
246 246
247 247 This is a horrible hack to pass information about SIGINT _without_ using
248 248 exceptions, since I haven't been able to properly manage cross-thread
249 249 exceptions in GTK/WX. In fact, I don't think it can be done (or at least
250 250 that's my understanding from a c.l.py thread where this was discussed)."""
251 251
252 252 global KBINT
253 253
254 254 print '\nKeyboardInterrupt - Press <Enter> to continue.',
255 255 Term.cout.flush()
256 256 # Set global flag so that runsource can know that Ctrl-C was hit
257 257 KBINT = True
258 258
259 259 class MTInteractiveShell(InteractiveShell):
260 260 """Simple multi-threaded shell."""
261 261
262 262 # Threading strategy taken from:
263 263 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian
264 264 # McErlean and John Finlay. Modified with corrections by Antoon Pardon,
265 265 # from the pygtk mailing list, to avoid lockups with system calls.
266 266
267 267 # class attribute to indicate whether the class supports threads or not.
268 268 # Subclasses with thread support should override this as needed.
269 269 isthreaded = True
270 270
271 271 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
272 272 user_ns=None,user_global_ns=None,banner2='',**kw):
273 273 """Similar to the normal InteractiveShell, but with threading control"""
274 274
275 275 InteractiveShell.__init__(self,name,usage,rc,user_ns,
276 276 user_global_ns,banner2)
277 277
278 278 # Locking control variable. We need to use a norma lock, not an RLock
279 279 # here. I'm not exactly sure why, it seems to me like it should be
280 280 # the opposite, but we deadlock with an RLock. Puzzled...
281 281 self.thread_ready = threading.Condition(threading.Lock())
282 282
283 283 # A queue to hold the code to be executed. A scalar variable is NOT
284 284 # enough, because uses like macros cause reentrancy.
285 285 self.code_queue = Queue.Queue()
286 286
287 287 # Stuff to do at closing time
288 288 self._kill = False
289 289 on_kill = kw.get('on_kill')
290 290 if on_kill is None:
291 291 on_kill = []
292 292 # Check that all things to kill are callable:
293 293 for t in on_kill:
294 294 if not callable(t):
295 295 raise TypeError,'on_kill must be a list of callables'
296 296 self.on_kill = on_kill
297 297
298 298 def runsource(self, source, filename="<input>", symbol="single"):
299 299 """Compile and run some source in the interpreter.
300 300
301 301 Modified version of code.py's runsource(), to handle threading issues.
302 302 See the original for full docstring details."""
303 303
304 304 global KBINT
305 305
306 306 # If Ctrl-C was typed, we reset the flag and return right away
307 307 if KBINT:
308 308 KBINT = False
309 309 return False
310 310
311 311 try:
312 312 code = self.compile(source, filename, symbol)
313 313 except (OverflowError, SyntaxError, ValueError):
314 314 # Case 1
315 315 self.showsyntaxerror(filename)
316 316 return False
317 317
318 318 if code is None:
319 319 # Case 2
320 320 return True
321 321
322 322 # Case 3
323 323 # Store code in queue, so the execution thread can handle it.
324 324
325 325 # Note that with macros and other applications, we MAY re-enter this
326 326 # section, so we have to acquire the lock with non-blocking semantics,
327 327 # else we deadlock.
328 328 got_lock = self.thread_ready.acquire(False)
329 329 self.code_queue.put(code)
330 330 if got_lock:
331 331 self.thread_ready.wait() # Wait until processed in timeout interval
332 332 self.thread_ready.release()
333 333
334 334 return False
335 335
336 336 def runcode(self):
337 337 """Execute a code object.
338 338
339 339 Multithreaded wrapper around IPython's runcode()."""
340 340
341 341 # lock thread-protected stuff
342 342 got_lock = self.thread_ready.acquire(False)
343 343
344 344 # Install sigint handler
345 345 try:
346 346 signal.signal(signal.SIGINT, sigint_handler)
347 347 except SystemError:
348 348 # This happens under Windows, which seems to have all sorts
349 349 # of problems with signal handling. Oh well...
350 350 pass
351 351
352 352 if self._kill:
353 353 print >>Term.cout, 'Closing threads...',
354 354 Term.cout.flush()
355 355 for tokill in self.on_kill:
356 356 tokill()
357 357 print >>Term.cout, 'Done.'
358 358
359 359 # Flush queue of pending code by calling the run methood of the parent
360 360 # class with all items which may be in the queue.
361 361 while 1:
362 362 try:
363 363 code_to_run = self.code_queue.get_nowait()
364 364 except Queue.Empty:
365 365 break
366 366 if got_lock:
367 367 self.thread_ready.notify()
368 368 InteractiveShell.runcode(self,code_to_run)
369 369 else:
370 370 break
371 371
372 372 # We're done with thread-protected variables
373 373 if got_lock:
374 374 self.thread_ready.release()
375 375 # This MUST return true for gtk threading to work
376 376 return True
377 377
378 378 def kill(self):
379 379 """Kill the thread, returning when it has been shut down."""
380 380 got_lock = self.thread_ready.acquire(False)
381 381 self._kill = True
382 382 if got_lock:
383 383 self.thread_ready.release()
384 384
385 385 class MatplotlibShellBase:
386 386 """Mixin class to provide the necessary modifications to regular IPython
387 387 shell classes for matplotlib support.
388 388
389 389 Given Python's MRO, this should be used as the FIRST class in the
390 390 inheritance hierarchy, so that it overrides the relevant methods."""
391 391
392 392 def _matplotlib_config(self,name,user_ns):
393 393 """Return items needed to setup the user's shell with matplotlib"""
394 394
395 395 # Initialize matplotlib to interactive mode always
396 396 import matplotlib
397 397 from matplotlib import backends
398 398 matplotlib.interactive(True)
399 399
400 400 def use(arg):
401 401 """IPython wrapper for matplotlib's backend switcher.
402 402
403 403 In interactive use, we can not allow switching to a different
404 404 interactive backend, since thread conflicts will most likely crash
405 405 the python interpreter. This routine does a safety check first,
406 406 and refuses to perform a dangerous switch. It still allows
407 407 switching to non-interactive backends."""
408 408
409 409 if arg in backends.interactive_bk and arg != self.mpl_backend:
410 410 m=('invalid matplotlib backend switch.\n'
411 411 'This script attempted to switch to the interactive '
412 412 'backend: `%s`\n'
413 413 'Your current choice of interactive backend is: `%s`\n\n'
414 414 'Switching interactive matplotlib backends at runtime\n'
415 415 'would crash the python interpreter, '
416 416 'and IPython has blocked it.\n\n'
417 417 'You need to either change your choice of matplotlib backend\n'
418 418 'by editing your .matplotlibrc file, or run this script as a \n'
419 419 'standalone file from the command line, not using IPython.\n' %
420 420 (arg,self.mpl_backend) )
421 421 raise RuntimeError, m
422 422 else:
423 423 self.mpl_use(arg)
424 424 self.mpl_use._called = True
425 425
426 426 self.matplotlib = matplotlib
427 427 self.mpl_backend = matplotlib.rcParams['backend']
428 428
429 429 # we also need to block switching of interactive backends by use()
430 430 self.mpl_use = matplotlib.use
431 431 self.mpl_use._called = False
432 432 # overwrite the original matplotlib.use with our wrapper
433 433 matplotlib.use = use
434 434
435 435 # This must be imported last in the matplotlib series, after
436 436 # backend/interactivity choices have been made
437 437 import matplotlib.pylab as pylab
438 438 self.pylab = pylab
439 439
440 440 self.pylab.show._needmain = False
441 441 # We need to detect at runtime whether show() is called by the user.
442 442 # For this, we wrap it into a decorator which adds a 'called' flag.
443 443 self.pylab.draw_if_interactive = flag_calls(self.pylab.draw_if_interactive)
444 444
445 445 # Build a user namespace initialized with matplotlib/matlab features.
446 446 user_ns = IPython.ipapi.make_user_ns(user_ns)
447 447
448 448 exec ("import matplotlib\n"
449 449 "import matplotlib.pylab as pylab\n"
450 450 "from matplotlib.pylab import *") in user_ns
451 451
452 452 # Build matplotlib info banner
453 453 b="""
454 454 Welcome to pylab, a matplotlib-based Python environment.
455 455 For more information, type 'help(pylab)'.
456 456 """
457 457 return user_ns,b
458 458
459 459 def mplot_exec(self,fname,*where,**kw):
460 460 """Execute a matplotlib script.
461 461
462 462 This is a call to execfile(), but wrapped in safeties to properly
463 463 handle interactive rendering and backend switching."""
464 464
465 465 #print '*** Matplotlib runner ***' # dbg
466 466 # turn off rendering until end of script
467 467 isInteractive = self.matplotlib.rcParams['interactive']
468 468 self.matplotlib.interactive(False)
469 469 self.safe_execfile(fname,*where,**kw)
470 470 self.matplotlib.interactive(isInteractive)
471 471 # make rendering call now, if the user tried to do it
472 472 if self.pylab.draw_if_interactive.called:
473 473 self.pylab.draw()
474 474 self.pylab.draw_if_interactive.called = False
475 475
476 476 # if a backend switch was performed, reverse it now
477 477 if self.mpl_use._called:
478 478 self.matplotlib.rcParams['backend'] = self.mpl_backend
479 479
480 480 def magic_run(self,parameter_s=''):
481 481 Magic.magic_run(self,parameter_s,runner=self.mplot_exec)
482 482
483 483 # Fix the docstring so users see the original as well
484 484 magic_run.__doc__ = "%s\n%s" % (Magic.magic_run.__doc__,
485 485 "\n *** Modified %run for Matplotlib,"
486 486 " with proper interactive handling ***")
487 487
488 488 # Now we provide 2 versions of a matplotlib-aware IPython base shells, single
489 489 # and multithreaded. Note that these are meant for internal use, the IPShell*
490 490 # classes below are the ones meant for public consumption.
491 491
492 492 class MatplotlibShell(MatplotlibShellBase,InteractiveShell):
493 493 """Single-threaded shell with matplotlib support."""
494 494
495 495 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
496 496 user_ns=None,user_global_ns=None,**kw):
497 497 user_ns,b2 = self._matplotlib_config(name,user_ns)
498 498 InteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
499 499 banner2=b2,**kw)
500 500
501 501 class MatplotlibMTShell(MatplotlibShellBase,MTInteractiveShell):
502 502 """Multi-threaded shell with matplotlib support."""
503 503
504 504 def __init__(self,name,usage=None,rc=Struct(opts=None,args=None),
505 505 user_ns=None,user_global_ns=None, **kw):
506 506 user_ns,b2 = self._matplotlib_config(name,user_ns)
507 507 MTInteractiveShell.__init__(self,name,usage,rc,user_ns,user_global_ns,
508 508 banner2=b2,**kw)
509 509
510 510 #-----------------------------------------------------------------------------
511 511 # Utility functions for the different GUI enabled IPShell* classes.
512 512
513 513 def get_tk():
514 514 """Tries to import Tkinter and returns a withdrawn Tkinter root
515 515 window. If Tkinter is already imported or not available, this
516 516 returns None. This function calls `hijack_tk` underneath.
517 517 """
518 518 if not USE_TK or sys.modules.has_key('Tkinter'):
519 519 return None
520 520 else:
521 521 try:
522 522 import Tkinter
523 523 except ImportError:
524 524 return None
525 525 else:
526 526 hijack_tk()
527 527 r = Tkinter.Tk()
528 528 r.withdraw()
529 529 return r
530 530
531 531 def hijack_tk():
532 532 """Modifies Tkinter's mainloop with a dummy so when a module calls
533 533 mainloop, it does not block.
534 534
535 535 """
536 536 def misc_mainloop(self, n=0):
537 537 pass
538 538 def tkinter_mainloop(n=0):
539 539 pass
540 540
541 541 import Tkinter
542 542 Tkinter.Misc.mainloop = misc_mainloop
543 543 Tkinter.mainloop = tkinter_mainloop
544 544
545 545 def update_tk(tk):
546 546 """Updates the Tkinter event loop. This is typically called from
547 547 the respective WX or GTK mainloops.
548 548 """
549 549 if tk:
550 550 tk.update()
551 551
552 552 def hijack_wx():
553 553 """Modifies wxPython's MainLoop with a dummy so user code does not
554 554 block IPython. The hijacked mainloop function is returned.
555 555 """
556 556 def dummy_mainloop(*args, **kw):
557 557 pass
558 558 import wxPython
559 559 ver = wxPython.__version__
560 560 orig_mainloop = None
561 561 if ver[:3] >= '2.5':
562 562 import wx
563 563 if hasattr(wx, '_core_'): core = getattr(wx, '_core_')
564 564 elif hasattr(wx, '_core'): core = getattr(wx, '_core')
565 565 else: raise AttributeError('Could not find wx core module')
566 566 orig_mainloop = core.PyApp_MainLoop
567 567 core.PyApp_MainLoop = dummy_mainloop
568 568 elif ver[:3] == '2.4':
569 569 orig_mainloop = wxPython.wxc.wxPyApp_MainLoop
570 570 wxPython.wxc.wxPyApp_MainLoop = dummy_mainloop
571 571 else:
572 572 warn("Unable to find either wxPython version 2.4 or >= 2.5.")
573 573 return orig_mainloop
574 574
575 575 def hijack_gtk():
576 576 """Modifies pyGTK's mainloop with a dummy so user code does not
577 577 block IPython. This function returns the original `gtk.mainloop`
578 578 function that has been hijacked.
579 579 """
580 580 def dummy_mainloop(*args, **kw):
581 581 pass
582 582 import gtk
583 583 if gtk.pygtk_version >= (2,4,0): orig_mainloop = gtk.main
584 584 else: orig_mainloop = gtk.mainloop
585 585 gtk.mainloop = dummy_mainloop
586 586 gtk.main = dummy_mainloop
587 587 return orig_mainloop
588 588
589 589 #-----------------------------------------------------------------------------
590 590 # The IPShell* classes below are the ones meant to be run by external code as
591 591 # IPython instances. Note that unless a specific threading strategy is
592 592 # desired, the factory function start() below should be used instead (it
593 593 # selects the proper threaded class).
594 594
595 595 class IPShellGTK(threading.Thread):
596 596 """Run a gtk mainloop() in a separate thread.
597 597
598 598 Python commands can be passed to the thread where they will be executed.
599 599 This is implemented by periodically checking for passed code using a
600 600 GTK timeout callback."""
601 601
602 602 TIMEOUT = 100 # Millisecond interval between timeouts.
603 603
604 604 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
605 605 debug=1,shell_class=MTInteractiveShell):
606 606
607 607 import gtk
608 608
609 609 self.gtk = gtk
610 610 self.gtk_mainloop = hijack_gtk()
611 611
612 612 # Allows us to use both Tk and GTK.
613 613 self.tk = get_tk()
614 614
615 615 if gtk.pygtk_version >= (2,4,0): mainquit = self.gtk.main_quit
616 616 else: mainquit = self.gtk.mainquit
617 617
618 618 self.IP = make_IPython(argv,user_ns=user_ns,
619 619 user_global_ns=user_global_ns,
620 620 debug=debug,
621 621 shell_class=shell_class,
622 622 on_kill=[mainquit])
623 623
624 624 # HACK: slot for banner in self; it will be passed to the mainloop
625 625 # method only and .run() needs it. The actual value will be set by
626 626 # .mainloop().
627 627 self._banner = None
628 628
629 629 threading.Thread.__init__(self)
630 630
631 631 def run(self):
632 632 self.IP.mainloop(self._banner)
633 633 self.IP.kill()
634 634
635 635 def mainloop(self,sys_exit=0,banner=None):
636 636
637 637 self._banner = banner
638 638
639 639 if self.gtk.pygtk_version >= (2,4,0):
640 640 import gobject
641 641 gobject.idle_add(self.on_timer)
642 642 else:
643 643 self.gtk.idle_add(self.on_timer)
644 644
645 645 if sys.platform != 'win32':
646 646 try:
647 647 if self.gtk.gtk_version[0] >= 2:
648 648 self.gtk.threads_init()
649 649 except AttributeError:
650 650 pass
651 651 except RuntimeError:
652 652 error('Your pyGTK likely has not been compiled with '
653 653 'threading support.\n'
654 654 'The exception printout is below.\n'
655 655 'You can either rebuild pyGTK with threads, or '
656 656 'try using \n'
657 657 'matplotlib with a different backend (like Tk or WX).\n'
658 658 'Note that matplotlib will most likely not work in its '
659 659 'current state!')
660 660 self.IP.InteractiveTB()
661 661 self.start()
662 662 self.gtk.threads_enter()
663 663 self.gtk_mainloop()
664 664 self.gtk.threads_leave()
665 665 self.join()
666 666
667 667 def on_timer(self):
668 668 """Called when GTK is idle.
669 669
670 670 Must return True always, otherwise GTK stops calling it"""
671 671
672 672 update_tk(self.tk)
673 673 self.IP.runcode()
674 674 time.sleep(0.01)
675 675 return True
676 676
677 677 class IPShellWX(threading.Thread):
678 678 """Run a wx mainloop() in a separate thread.
679 679
680 680 Python commands can be passed to the thread where they will be executed.
681 681 This is implemented by periodically checking for passed code using a
682 682 GTK timeout callback."""
683 683
684 684 TIMEOUT = 100 # Millisecond interval between timeouts.
685 685
686 686 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
687 687 debug=1,shell_class=MTInteractiveShell):
688 688
689 689 self.IP = make_IPython(argv,user_ns=user_ns,
690 690 user_global_ns=user_global_ns,
691 691 debug=debug,
692 692 shell_class=shell_class,
693 693 on_kill=[self.wxexit])
694 694
695 695 wantedwxversion=self.IP.rc.wxversion
696 696 if wantedwxversion!="0":
697 697 try:
698 698 import wxversion
699 699 except ImportError:
700 700 error('The wxversion module is needed for WX version selection')
701 701 else:
702 702 try:
703 703 wxversion.select(wantedwxversion)
704 704 except:
705 705 self.IP.InteractiveTB()
706 706 error('Requested wxPython version %s could not be loaded' %
707 707 wantedwxversion)
708 708
709 import wxPython.wx as wx
709 import wx
710 710
711 711 threading.Thread.__init__(self)
712 712 self.wx = wx
713 713 self.wx_mainloop = hijack_wx()
714 714
715 715 # Allows us to use both Tk and GTK.
716 716 self.tk = get_tk()
717 717
718 718
719 719 # HACK: slot for banner in self; it will be passed to the mainloop
720 720 # method only and .run() needs it. The actual value will be set by
721 721 # .mainloop().
722 722 self._banner = None
723 723
724 724 self.app = None
725 725
726 726 def wxexit(self, *args):
727 727 if self.app is not None:
728 728 self.app.agent.timer.Stop()
729 729 self.app.ExitMainLoop()
730 730
731 731 def run(self):
732 732 self.IP.mainloop(self._banner)
733 733 self.IP.kill()
734 734
735 735 def mainloop(self,sys_exit=0,banner=None):
736 736
737 737 self._banner = banner
738 738
739 739 self.start()
740 740
741 class TimerAgent(self.wx.wxMiniFrame):
741 class TimerAgent(self.wx.MiniFrame):
742 742 wx = self.wx
743 743 IP = self.IP
744 744 tk = self.tk
745 745 def __init__(self, parent, interval):
746 style = self.wx.wxDEFAULT_FRAME_STYLE | self.wx.wxTINY_CAPTION_HORIZ
747 self.wx.wxMiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200),
746 style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ
747 self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200),
748 748 size=(100, 100),style=style)
749 749 self.Show(False)
750 750 self.interval = interval
751 self.timerId = self.wx.wxNewId()
751 self.timerId = self.wx.NewId()
752 752
753 753 def StartWork(self):
754 self.timer = self.wx.wxTimer(self, self.timerId)
754 self.timer = self.wx.Timer(self, self.timerId)
755 755 self.wx.EVT_TIMER(self, self.timerId, self.OnTimer)
756 756 self.timer.Start(self.interval)
757 757
758 758 def OnTimer(self, event):
759 759 update_tk(self.tk)
760 760 self.IP.runcode()
761 761
762 class App(self.wx.wxApp):
762 class App(self.wx.App):
763 763 wx = self.wx
764 764 TIMEOUT = self.TIMEOUT
765 765 def OnInit(self):
766 766 'Create the main window and insert the custom frame'
767 767 self.agent = TimerAgent(None, self.TIMEOUT)
768 self.agent.Show(self.wx.false)
768 self.agent.Show(False)
769 769 self.agent.StartWork()
770 return self.wx.true
770 return True
771 771
772 772 self.app = App(redirect=False)
773 773 self.wx_mainloop(self.app)
774 774 self.join()
775 775
776 776
777 777 class IPShellQt(threading.Thread):
778 778 """Run a Qt event loop in a separate thread.
779 779
780 780 Python commands can be passed to the thread where they will be executed.
781 781 This is implemented by periodically checking for passed code using a
782 782 Qt timer / slot."""
783 783
784 784 TIMEOUT = 100 # Millisecond interval between timeouts.
785 785
786 786 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
787 787 debug=0,shell_class=MTInteractiveShell):
788 788
789 789 import qt
790 790
791 791 class newQApplication:
792 792 def __init__( self ):
793 793 self.QApplication = qt.QApplication
794 794
795 795 def __call__( *args, **kwargs ):
796 796 return qt.qApp
797 797
798 798 def exec_loop( *args, **kwargs ):
799 799 pass
800 800
801 801 def __getattr__( self, name ):
802 802 return getattr( self.QApplication, name )
803 803
804 804 qt.QApplication = newQApplication()
805 805
806 806 # Allows us to use both Tk and QT.
807 807 self.tk = get_tk()
808 808
809 809 self.IP = make_IPython(argv,user_ns=user_ns,
810 810 user_global_ns=user_global_ns,
811 811 debug=debug,
812 812 shell_class=shell_class,
813 813 on_kill=[qt.qApp.exit])
814 814
815 815 # HACK: slot for banner in self; it will be passed to the mainloop
816 816 # method only and .run() needs it. The actual value will be set by
817 817 # .mainloop().
818 818 self._banner = None
819 819
820 820 threading.Thread.__init__(self)
821 821
822 822 def run(self):
823 823 self.IP.mainloop(self._banner)
824 824 self.IP.kill()
825 825
826 826 def mainloop(self,sys_exit=0,banner=None):
827 827
828 828 import qt
829 829
830 830 self._banner = banner
831 831
832 832 if qt.QApplication.startingUp():
833 833 a = qt.QApplication.QApplication(sys.argv)
834 834 self.timer = qt.QTimer()
835 835 qt.QObject.connect( self.timer, qt.SIGNAL( 'timeout()' ), self.on_timer )
836 836
837 837 self.start()
838 838 self.timer.start( self.TIMEOUT, True )
839 839 while True:
840 840 if self.IP._kill: break
841 841 qt.qApp.exec_loop()
842 842 self.join()
843 843
844 844 def on_timer(self):
845 845 update_tk(self.tk)
846 846 result = self.IP.runcode()
847 847 self.timer.start( self.TIMEOUT, True )
848 848 return result
849 849
850 850
851 851 class IPShellQt4(threading.Thread):
852 852 """Run a Qt event loop in a separate thread.
853 853
854 854 Python commands can be passed to the thread where they will be executed.
855 855 This is implemented by periodically checking for passed code using a
856 856 Qt timer / slot."""
857 857
858 858 TIMEOUT = 100 # Millisecond interval between timeouts.
859 859
860 860 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
861 861 debug=0,shell_class=MTInteractiveShell):
862 862
863 863 from PyQt4 import QtCore, QtGui
864 864
865 865 class newQApplication:
866 866 def __init__( self ):
867 867 self.QApplication = QtGui.QApplication
868 868
869 869 def __call__( *args, **kwargs ):
870 870 return QtGui.qApp
871 871
872 872 def exec_loop( *args, **kwargs ):
873 873 pass
874 874
875 875 def __getattr__( self, name ):
876 876 return getattr( self.QApplication, name )
877 877
878 878 QtGui.QApplication = newQApplication()
879 879
880 880 # Allows us to use both Tk and QT.
881 881 self.tk = get_tk()
882 882
883 883 self.IP = make_IPython(argv,user_ns=user_ns,
884 884 user_global_ns=user_global_ns,
885 885 debug=debug,
886 886 shell_class=shell_class,
887 887 on_kill=[QtGui.qApp.exit])
888 888
889 889 # HACK: slot for banner in self; it will be passed to the mainloop
890 890 # method only and .run() needs it. The actual value will be set by
891 891 # .mainloop().
892 892 self._banner = None
893 893
894 894 threading.Thread.__init__(self)
895 895
896 896 def run(self):
897 897 self.IP.mainloop(self._banner)
898 898 self.IP.kill()
899 899
900 900 def mainloop(self,sys_exit=0,banner=None):
901 901
902 902 from PyQt4 import QtCore, QtGui
903 903
904 904 self._banner = banner
905 905
906 906 if QtGui.QApplication.startingUp():
907 907 a = QtGui.QApplication.QApplication(sys.argv)
908 908 self.timer = QtCore.QTimer()
909 909 QtCore.QObject.connect( self.timer, QtCore.SIGNAL( 'timeout()' ), self.on_timer )
910 910
911 911 self.start()
912 912 self.timer.start( self.TIMEOUT )
913 913 while True:
914 914 if self.IP._kill: break
915 915 QtGui.qApp.exec_()
916 916 self.join()
917 917
918 918 def on_timer(self):
919 919 update_tk(self.tk)
920 920 result = self.IP.runcode()
921 921 self.timer.start( self.TIMEOUT )
922 922 return result
923 923
924 924
925 925 # A set of matplotlib public IPython shell classes, for single-threaded
926 926 # (Tk* and FLTK* backends) and multithreaded (GTK* and WX* backends) use.
927 927 class IPShellMatplotlib(IPShell):
928 928 """Subclass IPShell with MatplotlibShell as the internal shell.
929 929
930 930 Single-threaded class, meant for the Tk* and FLTK* backends.
931 931
932 932 Having this on a separate class simplifies the external driver code."""
933 933
934 934 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
935 935 IPShell.__init__(self,argv,user_ns,user_global_ns,debug,
936 936 shell_class=MatplotlibShell)
937 937
938 938 class IPShellMatplotlibGTK(IPShellGTK):
939 939 """Subclass IPShellGTK with MatplotlibMTShell as the internal shell.
940 940
941 941 Multi-threaded class, meant for the GTK* backends."""
942 942
943 943 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
944 944 IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug,
945 945 shell_class=MatplotlibMTShell)
946 946
947 947 class IPShellMatplotlibWX(IPShellWX):
948 948 """Subclass IPShellWX with MatplotlibMTShell as the internal shell.
949 949
950 950 Multi-threaded class, meant for the WX* backends."""
951 951
952 952 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
953 953 IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug,
954 954 shell_class=MatplotlibMTShell)
955 955
956 956 class IPShellMatplotlibQt(IPShellQt):
957 957 """Subclass IPShellQt with MatplotlibMTShell as the internal shell.
958 958
959 959 Multi-threaded class, meant for the Qt* backends."""
960 960
961 961 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
962 962 IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug,
963 963 shell_class=MatplotlibMTShell)
964 964
965 965 class IPShellMatplotlibQt4(IPShellQt4):
966 966 """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell.
967 967
968 968 Multi-threaded class, meant for the Qt4* backends."""
969 969
970 970 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
971 971 IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug,
972 972 shell_class=MatplotlibMTShell)
973 973
974 974 #-----------------------------------------------------------------------------
975 975 # Factory functions to actually start the proper thread-aware shell
976 976
977 977 def _matplotlib_shell_class():
978 978 """Factory function to handle shell class selection for matplotlib.
979 979
980 980 The proper shell class to use depends on the matplotlib backend, since
981 981 each backend requires a different threading strategy."""
982 982
983 983 try:
984 984 import matplotlib
985 985 except ImportError:
986 986 error('matplotlib could NOT be imported! Starting normal IPython.')
987 987 sh_class = IPShell
988 988 else:
989 989 backend = matplotlib.rcParams['backend']
990 990 if backend.startswith('GTK'):
991 991 sh_class = IPShellMatplotlibGTK
992 992 elif backend.startswith('WX'):
993 993 sh_class = IPShellMatplotlibWX
994 994 elif backend.startswith('Qt4'):
995 995 sh_class = IPShellMatplotlibQt4
996 996 elif backend.startswith('Qt'):
997 997 sh_class = IPShellMatplotlibQt
998 998 else:
999 999 sh_class = IPShellMatplotlib
1000 1000 #print 'Using %s with the %s backend.' % (sh_class,backend) # dbg
1001 1001 return sh_class
1002 1002
1003 1003 # This is the one which should be called by external code.
1004 1004 def start(user_ns = None):
1005 1005 """Return a running shell instance, dealing with threading options.
1006 1006
1007 1007 This is a factory function which will instantiate the proper IPython shell
1008 1008 based on the user's threading choice. Such a selector is needed because
1009 1009 different GUI toolkits require different thread handling details."""
1010 1010
1011 1011 global USE_TK
1012 1012 # Crude sys.argv hack to extract the threading options.
1013 1013 argv = sys.argv
1014 1014 if len(argv) > 1:
1015 1015 if len(argv) > 2:
1016 1016 arg2 = argv[2]
1017 1017 if arg2.endswith('-tk'):
1018 1018 USE_TK = True
1019 1019 arg1 = argv[1]
1020 1020 if arg1.endswith('-gthread'):
1021 1021 shell = IPShellGTK
1022 1022 elif arg1.endswith( '-qthread' ):
1023 1023 shell = IPShellQt
1024 1024 elif arg1.endswith( '-q4thread' ):
1025 1025 shell = IPShellQt4
1026 1026 elif arg1.endswith('-wthread'):
1027 1027 shell = IPShellWX
1028 1028 elif arg1.endswith('-pylab'):
1029 1029 shell = _matplotlib_shell_class()
1030 1030 else:
1031 1031 shell = IPShell
1032 1032 else:
1033 1033 shell = IPShell
1034 1034 return shell(user_ns = user_ns)
1035 1035
1036 1036 # Some aliases for backwards compatibility
1037 1037 IPythonShell = IPShell
1038 1038 IPythonShellEmbed = IPShellEmbed
1039 1039 #************************ End of file <Shell.py> ***************************
General Comments 0
You need to be logged in to leave comments. Login now