1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
#!/usr/bin/env python
"""Multithreaded interactive interpreter with GTK and Matplotlib support.
WARNING:
As of 2010/06/25, this is not working, at least on Linux.
I have disabled it as a runnable script. - EF
Usage:
pyint-gtk.py -> starts shell with gtk thread running separately
pyint-gtk.py -pylab [filename] -> initializes matplotlib, optionally running
the named file. The shell starts after the file is executed.
Threading code taken from:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65109, by Brian
McErlean and John Finlay.
Matplotlib support taken from interactive.py in the matplotlib distribution.
Also borrows liberally from code.py in the Python standard library."""
__author__ = "Fernando Perez <Fernando.Perez@colorado.edu>"
import sys
import code
import threading
import gobject
import gtk
try:
import readline
except ImportError:
has_readline = False
else:
has_readline = True
class MTConsole(code.InteractiveConsole):
"""Simple multi-threaded shell"""
def __init__(self,on_kill=None,*args,**kw):
code.InteractiveConsole.__init__(self,*args,**kw)
self.code_to_run = None
self.ready = threading.Condition()
self._kill = False
if on_kill is None:
on_kill = []
# Check that all things to kill are callable:
for _ in on_kill:
if not callable(_):
raise TypeError,'on_kill must be a list of callables'
self.on_kill = on_kill
# Set up tab-completer
if has_readline:
import rlcompleter
try: # this form only works with python 2.3
self.completer = rlcompleter.Completer(self.locals)
except: # simpler for py2.2
self.completer = rlcompleter.Completer()
readline.set_completer(self.completer.complete)
# Use tab for completions
readline.parse_and_bind('tab: complete')
# This forces readline to automatically print the above list when tab
# completion is set to 'complete'.
readline.parse_and_bind('set show-all-if-ambiguous on')
# Bindings for incremental searches in the history. These searches
# use the string typed so far on the command line and search
# anything in the previous input history containing them.
readline.parse_and_bind('"\C-r": reverse-search-history')
readline.parse_and_bind('"\C-s": forward-search-history')
def runsource(self, source, filename="<input>", symbol="single"):
"""Compile and run some source in the interpreter.
Arguments are as for compile_command().
One several things can happen:
1) The input is incorrect; compile_command() raised an
exception (SyntaxError or OverflowError). A syntax traceback
will be printed by calling the showsyntaxerror() method.
2) The input is incomplete, and more input is required;
compile_command() returned None. Nothing happens.
3) The input is complete; compile_command() returned a code
object. The code is executed by calling self.runcode() (which
also handles run-time exceptions, except for SystemExit).
The return value is True in case 2, False in the other cases (unless
an exception is raised). The return value can be used to
decide whether to use sys.ps1 or sys.ps2 to prompt the next
line.
"""
try:
code = self.compile(source, filename, symbol)
except (OverflowError, SyntaxError, ValueError):
# Case 1
self.showsyntaxerror(filename)
return False
if code is None:
# Case 2
return True
# Case 3
# Store code in self, so the execution thread can handle it
self.ready.acquire()
self.code_to_run = code
self.ready.wait() # Wait until processed in timeout interval
self.ready.release()
return False
def runcode(self):
"""Execute a code object.
When an exception occurs, self.showtraceback() is called to display a
traceback."""
self.ready.acquire()
if self._kill:
print 'Closing threads...',
sys.stdout.flush()
for tokill in self.on_kill:
tokill()
print 'Done.'
if self.code_to_run is not None:
self.ready.notify()
code.InteractiveConsole.runcode(self,self.code_to_run)
self.code_to_run = None
self.ready.release()
return True
def kill (self):
"""Kill the thread, returning when it has been shut down."""
self.ready.acquire()
self._kill = True
self.ready.release()
class GTKInterpreter(threading.Thread):
"""Run gtk.main in the main thread and a python interpreter in a
separate thread.
Python commands can be passed to the thread where they will be executed.
This is implemented by periodically checking for passed code using a
GTK timeout callback.
"""
TIMEOUT = 100 # Millisecond interval between timeouts.
def __init__(self,banner=None):
threading.Thread.__init__(self)
self.banner = banner
self.shell = MTConsole(on_kill=[gtk.main_quit])
def run(self):
self.pre_interact()
self.shell.interact(self.banner)
self.shell.kill()
def mainloop(self):
self.start()
gobject.timeout_add(self.TIMEOUT, self.shell.runcode)
try:
if gtk.gtk_version[0] >= 2:
gtk.gdk.threads_init()
except AttributeError:
pass
gtk.main()
self.join()
def pre_interact(self):
"""This method should be overridden by subclasses.
It gets called right before interact(), but after the thread starts.
Typically used to push initialization code into the interpreter"""
pass
class MatplotLibInterpreter(GTKInterpreter):
"""Threaded interpreter with matplotlib support.
Note that this explicitly sets GTKAgg as the backend, since it has
specific GTK hooks in it."""
def __init__(self,banner=None):
banner = """\nWelcome to matplotlib, a MATLAB-like python environment.
help(matlab) -> help on matlab compatible commands from matplotlib.
help(plotting) -> help on plotting commands.
"""
GTKInterpreter.__init__(self,banner)
def pre_interact(self):
"""Initialize matplotlib before user interaction begins"""
push = self.shell.push
# Code to execute in user's namespace
lines = ["import matplotlib",
"matplotlib.use('GTKAgg')",
"matplotlib.interactive(1)",
"import matplotlib.pylab as pylab",
"from matplotlib.pylab import *\n"]
map(push,lines)
# Execute file if given.
if len(sys.argv)>1:
import matplotlib
matplotlib.interactive(0) # turn off interaction
fname = sys.argv[1]
try:
inFile = file(fname, 'r')
except IOError:
print '*** ERROR *** Could not read file <%s>' % fname
else:
print '*** Executing file <%s>:' % fname
for line in inFile:
if line.lstrip().find('show()')==0: continue
print '>>', line,
push(line)
inFile.close()
matplotlib.interactive(1) # turn on interaction
if __name__ == '__main__':
print "This demo is not presently functional, so running"
print "it as a script has been disabled."
sys.exit()
# Quick sys.argv hack to extract the option and leave filenames in sys.argv.
# For real option handling, use optparse or getopt.
if len(sys.argv) > 1 and sys.argv[1]=='-pylab':
sys.argv = [sys.argv[0]]+sys.argv[2:]
MatplotLibInterpreter().mainloop()
else:
GTKInterpreter().mainloop()
|