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