##// END OF EJS Templates
Update threads calls to avoid a DeprecationWarning from GTK.
fperez -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,1039 +1,1040 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 1988 2006-12-13 16:49:41Z vivainio $"""
7 $Id: Shell.py 2120 2007-02-27 15:48:24Z fperez $"""
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 self.gtk.threads_init()
648 self.gtk.gdk.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 662 self.start()
662 self.gtk.threads_enter()
663 self.gtk.gdk.threads_enter()
663 664 self.gtk_mainloop()
664 self.gtk.threads_leave()
665 self.gtk.gdk.threads_leave()
665 666 self.join()
666 667
667 668 def on_timer(self):
668 669 """Called when GTK is idle.
669 670
670 671 Must return True always, otherwise GTK stops calling it"""
671 672
672 673 update_tk(self.tk)
673 674 self.IP.runcode()
674 675 time.sleep(0.01)
675 676 return True
676 677
677 678 class IPShellWX(threading.Thread):
678 679 """Run a wx mainloop() in a separate thread.
679 680
680 681 Python commands can be passed to the thread where they will be executed.
681 682 This is implemented by periodically checking for passed code using a
682 683 GTK timeout callback."""
683 684
684 685 TIMEOUT = 100 # Millisecond interval between timeouts.
685 686
686 687 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
687 688 debug=1,shell_class=MTInteractiveShell):
688 689
689 690 self.IP = make_IPython(argv,user_ns=user_ns,
690 691 user_global_ns=user_global_ns,
691 692 debug=debug,
692 693 shell_class=shell_class,
693 694 on_kill=[self.wxexit])
694 695
695 696 wantedwxversion=self.IP.rc.wxversion
696 697 if wantedwxversion!="0":
697 698 try:
698 699 import wxversion
699 700 except ImportError:
700 701 error('The wxversion module is needed for WX version selection')
701 702 else:
702 703 try:
703 704 wxversion.select(wantedwxversion)
704 705 except:
705 706 self.IP.InteractiveTB()
706 707 error('Requested wxPython version %s could not be loaded' %
707 708 wantedwxversion)
708 709
709 710 import wx
710 711
711 712 threading.Thread.__init__(self)
712 713 self.wx = wx
713 714 self.wx_mainloop = hijack_wx()
714 715
715 716 # Allows us to use both Tk and GTK.
716 717 self.tk = get_tk()
717 718
718 719
719 720 # HACK: slot for banner in self; it will be passed to the mainloop
720 721 # method only and .run() needs it. The actual value will be set by
721 722 # .mainloop().
722 723 self._banner = None
723 724
724 725 self.app = None
725 726
726 727 def wxexit(self, *args):
727 728 if self.app is not None:
728 729 self.app.agent.timer.Stop()
729 730 self.app.ExitMainLoop()
730 731
731 732 def run(self):
732 733 self.IP.mainloop(self._banner)
733 734 self.IP.kill()
734 735
735 736 def mainloop(self,sys_exit=0,banner=None):
736 737
737 738 self._banner = banner
738 739
739 740 self.start()
740 741
741 742 class TimerAgent(self.wx.MiniFrame):
742 743 wx = self.wx
743 744 IP = self.IP
744 745 tk = self.tk
745 746 def __init__(self, parent, interval):
746 747 style = self.wx.DEFAULT_FRAME_STYLE | self.wx.TINY_CAPTION_HORIZ
747 748 self.wx.MiniFrame.__init__(self, parent, -1, ' ', pos=(200, 200),
748 749 size=(100, 100),style=style)
749 750 self.Show(False)
750 751 self.interval = interval
751 752 self.timerId = self.wx.NewId()
752 753
753 754 def StartWork(self):
754 755 self.timer = self.wx.Timer(self, self.timerId)
755 756 self.wx.EVT_TIMER(self, self.timerId, self.OnTimer)
756 757 self.timer.Start(self.interval)
757 758
758 759 def OnTimer(self, event):
759 760 update_tk(self.tk)
760 761 self.IP.runcode()
761 762
762 763 class App(self.wx.App):
763 764 wx = self.wx
764 765 TIMEOUT = self.TIMEOUT
765 766 def OnInit(self):
766 767 'Create the main window and insert the custom frame'
767 768 self.agent = TimerAgent(None, self.TIMEOUT)
768 769 self.agent.Show(False)
769 770 self.agent.StartWork()
770 771 return True
771 772
772 773 self.app = App(redirect=False)
773 774 self.wx_mainloop(self.app)
774 775 self.join()
775 776
776 777
777 778 class IPShellQt(threading.Thread):
778 779 """Run a Qt event loop in a separate thread.
779 780
780 781 Python commands can be passed to the thread where they will be executed.
781 782 This is implemented by periodically checking for passed code using a
782 783 Qt timer / slot."""
783 784
784 785 TIMEOUT = 100 # Millisecond interval between timeouts.
785 786
786 787 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
787 788 debug=0,shell_class=MTInteractiveShell):
788 789
789 790 import qt
790 791
791 792 class newQApplication:
792 793 def __init__( self ):
793 794 self.QApplication = qt.QApplication
794 795
795 796 def __call__( *args, **kwargs ):
796 797 return qt.qApp
797 798
798 799 def exec_loop( *args, **kwargs ):
799 800 pass
800 801
801 802 def __getattr__( self, name ):
802 803 return getattr( self.QApplication, name )
803 804
804 805 qt.QApplication = newQApplication()
805 806
806 807 # Allows us to use both Tk and QT.
807 808 self.tk = get_tk()
808 809
809 810 self.IP = make_IPython(argv,user_ns=user_ns,
810 811 user_global_ns=user_global_ns,
811 812 debug=debug,
812 813 shell_class=shell_class,
813 814 on_kill=[qt.qApp.exit])
814 815
815 816 # HACK: slot for banner in self; it will be passed to the mainloop
816 817 # method only and .run() needs it. The actual value will be set by
817 818 # .mainloop().
818 819 self._banner = None
819 820
820 821 threading.Thread.__init__(self)
821 822
822 823 def run(self):
823 824 self.IP.mainloop(self._banner)
824 825 self.IP.kill()
825 826
826 827 def mainloop(self,sys_exit=0,banner=None):
827 828
828 829 import qt
829 830
830 831 self._banner = banner
831 832
832 833 if qt.QApplication.startingUp():
833 834 a = qt.QApplication.QApplication(sys.argv)
834 835 self.timer = qt.QTimer()
835 836 qt.QObject.connect( self.timer, qt.SIGNAL( 'timeout()' ), self.on_timer )
836 837
837 838 self.start()
838 839 self.timer.start( self.TIMEOUT, True )
839 840 while True:
840 841 if self.IP._kill: break
841 842 qt.qApp.exec_loop()
842 843 self.join()
843 844
844 845 def on_timer(self):
845 846 update_tk(self.tk)
846 847 result = self.IP.runcode()
847 848 self.timer.start( self.TIMEOUT, True )
848 849 return result
849 850
850 851
851 852 class IPShellQt4(threading.Thread):
852 853 """Run a Qt event loop in a separate thread.
853 854
854 855 Python commands can be passed to the thread where they will be executed.
855 856 This is implemented by periodically checking for passed code using a
856 857 Qt timer / slot."""
857 858
858 859 TIMEOUT = 100 # Millisecond interval between timeouts.
859 860
860 861 def __init__(self,argv=None,user_ns=None,user_global_ns=None,
861 862 debug=0,shell_class=MTInteractiveShell):
862 863
863 864 from PyQt4 import QtCore, QtGui
864 865
865 866 class newQApplication:
866 867 def __init__( self ):
867 868 self.QApplication = QtGui.QApplication
868 869
869 870 def __call__( *args, **kwargs ):
870 871 return QtGui.qApp
871 872
872 873 def exec_loop( *args, **kwargs ):
873 874 pass
874 875
875 876 def __getattr__( self, name ):
876 877 return getattr( self.QApplication, name )
877 878
878 879 QtGui.QApplication = newQApplication()
879 880
880 881 # Allows us to use both Tk and QT.
881 882 self.tk = get_tk()
882 883
883 884 self.IP = make_IPython(argv,user_ns=user_ns,
884 885 user_global_ns=user_global_ns,
885 886 debug=debug,
886 887 shell_class=shell_class,
887 888 on_kill=[QtGui.qApp.exit])
888 889
889 890 # HACK: slot for banner in self; it will be passed to the mainloop
890 891 # method only and .run() needs it. The actual value will be set by
891 892 # .mainloop().
892 893 self._banner = None
893 894
894 895 threading.Thread.__init__(self)
895 896
896 897 def run(self):
897 898 self.IP.mainloop(self._banner)
898 899 self.IP.kill()
899 900
900 901 def mainloop(self,sys_exit=0,banner=None):
901 902
902 903 from PyQt4 import QtCore, QtGui
903 904
904 905 self._banner = banner
905 906
906 907 if QtGui.QApplication.startingUp():
907 908 a = QtGui.QApplication.QApplication(sys.argv)
908 909 self.timer = QtCore.QTimer()
909 910 QtCore.QObject.connect( self.timer, QtCore.SIGNAL( 'timeout()' ), self.on_timer )
910 911
911 912 self.start()
912 913 self.timer.start( self.TIMEOUT )
913 914 while True:
914 915 if self.IP._kill: break
915 916 QtGui.qApp.exec_()
916 917 self.join()
917 918
918 919 def on_timer(self):
919 920 update_tk(self.tk)
920 921 result = self.IP.runcode()
921 922 self.timer.start( self.TIMEOUT )
922 923 return result
923 924
924 925
925 926 # A set of matplotlib public IPython shell classes, for single-threaded
926 927 # (Tk* and FLTK* backends) and multithreaded (GTK* and WX* backends) use.
927 928 class IPShellMatplotlib(IPShell):
928 929 """Subclass IPShell with MatplotlibShell as the internal shell.
929 930
930 931 Single-threaded class, meant for the Tk* and FLTK* backends.
931 932
932 933 Having this on a separate class simplifies the external driver code."""
933 934
934 935 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
935 936 IPShell.__init__(self,argv,user_ns,user_global_ns,debug,
936 937 shell_class=MatplotlibShell)
937 938
938 939 class IPShellMatplotlibGTK(IPShellGTK):
939 940 """Subclass IPShellGTK with MatplotlibMTShell as the internal shell.
940 941
941 942 Multi-threaded class, meant for the GTK* backends."""
942 943
943 944 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
944 945 IPShellGTK.__init__(self,argv,user_ns,user_global_ns,debug,
945 946 shell_class=MatplotlibMTShell)
946 947
947 948 class IPShellMatplotlibWX(IPShellWX):
948 949 """Subclass IPShellWX with MatplotlibMTShell as the internal shell.
949 950
950 951 Multi-threaded class, meant for the WX* backends."""
951 952
952 953 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
953 954 IPShellWX.__init__(self,argv,user_ns,user_global_ns,debug,
954 955 shell_class=MatplotlibMTShell)
955 956
956 957 class IPShellMatplotlibQt(IPShellQt):
957 958 """Subclass IPShellQt with MatplotlibMTShell as the internal shell.
958 959
959 960 Multi-threaded class, meant for the Qt* backends."""
960 961
961 962 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
962 963 IPShellQt.__init__(self,argv,user_ns,user_global_ns,debug,
963 964 shell_class=MatplotlibMTShell)
964 965
965 966 class IPShellMatplotlibQt4(IPShellQt4):
966 967 """Subclass IPShellQt4 with MatplotlibMTShell as the internal shell.
967 968
968 969 Multi-threaded class, meant for the Qt4* backends."""
969 970
970 971 def __init__(self,argv=None,user_ns=None,user_global_ns=None,debug=1):
971 972 IPShellQt4.__init__(self,argv,user_ns,user_global_ns,debug,
972 973 shell_class=MatplotlibMTShell)
973 974
974 975 #-----------------------------------------------------------------------------
975 976 # Factory functions to actually start the proper thread-aware shell
976 977
977 978 def _matplotlib_shell_class():
978 979 """Factory function to handle shell class selection for matplotlib.
979 980
980 981 The proper shell class to use depends on the matplotlib backend, since
981 982 each backend requires a different threading strategy."""
982 983
983 984 try:
984 985 import matplotlib
985 986 except ImportError:
986 987 error('matplotlib could NOT be imported! Starting normal IPython.')
987 988 sh_class = IPShell
988 989 else:
989 990 backend = matplotlib.rcParams['backend']
990 991 if backend.startswith('GTK'):
991 992 sh_class = IPShellMatplotlibGTK
992 993 elif backend.startswith('WX'):
993 994 sh_class = IPShellMatplotlibWX
994 995 elif backend.startswith('Qt4'):
995 996 sh_class = IPShellMatplotlibQt4
996 997 elif backend.startswith('Qt'):
997 998 sh_class = IPShellMatplotlibQt
998 999 else:
999 1000 sh_class = IPShellMatplotlib
1000 1001 #print 'Using %s with the %s backend.' % (sh_class,backend) # dbg
1001 1002 return sh_class
1002 1003
1003 1004 # This is the one which should be called by external code.
1004 1005 def start(user_ns = None):
1005 1006 """Return a running shell instance, dealing with threading options.
1006 1007
1007 1008 This is a factory function which will instantiate the proper IPython shell
1008 1009 based on the user's threading choice. Such a selector is needed because
1009 1010 different GUI toolkits require different thread handling details."""
1010 1011
1011 1012 global USE_TK
1012 1013 # Crude sys.argv hack to extract the threading options.
1013 1014 argv = sys.argv
1014 1015 if len(argv) > 1:
1015 1016 if len(argv) > 2:
1016 1017 arg2 = argv[2]
1017 1018 if arg2.endswith('-tk'):
1018 1019 USE_TK = True
1019 1020 arg1 = argv[1]
1020 1021 if arg1.endswith('-gthread'):
1021 1022 shell = IPShellGTK
1022 1023 elif arg1.endswith( '-qthread' ):
1023 1024 shell = IPShellQt
1024 1025 elif arg1.endswith( '-q4thread' ):
1025 1026 shell = IPShellQt4
1026 1027 elif arg1.endswith('-wthread'):
1027 1028 shell = IPShellWX
1028 1029 elif arg1.endswith('-pylab'):
1029 1030 shell = _matplotlib_shell_class()
1030 1031 else:
1031 1032 shell = IPShell
1032 1033 else:
1033 1034 shell = IPShell
1034 1035 return shell(user_ns = user_ns)
1035 1036
1036 1037 # Some aliases for backwards compatibility
1037 1038 IPythonShell = IPShell
1038 1039 IPythonShellEmbed = IPShellEmbed
1039 1040 #************************ End of file <Shell.py> ***************************
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now