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