[pygtk] Problem with pygtk, threads,
an interactive interpreter and os.system()
Fernando.Perez at colorado.edu
Wed Aug 18 01:06:34 WST 2004
I'm new to this list, but some of you in the python community may know me from
ipython (http://ipython.scipy.org). I'm trying to add gtk support to ipython,
so that any gtk-based code can be executed and played with from an interactive
shell. Based on:
I've written the attached mini-interpreter pyint-gtk.py, which runs without
needing ipython to ease independent testing. One of my main goals with this
effort is to support the gtk and wx backends for matplotlib, but this should
be useful to other users of pygtk as well.
The attached code works fairly well for most purposes, but I'm having problems
with certain calls made by os.system(), specifically to gv (under Linux,
Fedora Core 2 at the moment). If I call os.system('gv foo.eps &'), gv seems
to open fine, but after about 30 seconds of fiddling with it, especially if I
open zoom sub-windows, the gv GUI just hangs. The process becomes completely
unresponsive, and the only way to kill it (and the gs children it spawns) is
via a manual kill -9. The weird thing is that I've tried opening other
programs, including ggv on the same eps file (and xcalc, konqueror, etc), and
nothing else seems to show this behavior.
But I'm very leery of releasing this code as production stuff (the version in
ipython is different, since it integrates with the whole system, but the
threading code is identical and shows the same problem). I don't know for a
fact that gv is the only culprit, and since I don't understand the root cause
of the problem, I'm afraid of having ipython randomly lock up on people for no
I am a complete ignoramus on threading issues, and this code was blindly
copied from the above recipe, so I may well be just doing something very
stupid. I would be very appreciative of any help on this.
Best to all,
-------------- next part --------------
"""Multithreaded interactive interpreter with GTK and Matplotlib support.
pyint-gtk.py -> starts shell with gtk thread running separately
pyint-gtk.py -mplot [filename] -> initializes matplotlib, optionally running
the named file. The shell starts after the file is executed.
Threading code inspired by:
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 at colorado.edu>"
has_readline = False
has_readline = True
"""Simple multi-threaded shell"""
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
try: # this form only works with python 2.3
self.completer = rlcompleter.Completer(self.locals)
except: # simpler for py2.2
self.completer = rlcompleter.Completer()
# Use tab for completions
# 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.
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
code = self.compile(source, filename, symbol)
except (OverflowError, SyntaxError, ValueError):
# Case 1
if code is None:
# Case 2
# Case 3
# Store code in self, so the execution thread can handle it
self.code_to_run = code
self.ready.wait() # Wait until processed in timeout interval
"""Execute a code object.
When an exception occurs, self.showtraceback() is called to display a
print 'Closing threads...',
for tokill in self.on_kill:
if self.code_to_run is not None:
self.code_to_run = None
def kill (self):
"""Kill the thread, returning when it has been shut down."""
self._kill = True
"""Run a gtk mainloop() 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 # Milisecond interval between timeouts.
self.banner = banner
self.shell = MTConsole(on_kill=[gtk.mainquit])
if gtk.gtk_version >= 2:
"""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"""
"""Threaded interpreter with matplotlib support."""
banner = """\nWelcome to matplotlib, a matlab-like python environment.
help(matlab) -> help on matlab compatible commands from matplotlib.
help(plotting) -> help on plotting commands.
"""Initialize matplotlib before user interaction begins"""
push = self.shell.push
# Code to execute in user's namespace
lines = ["import matplotlib",
"import matplotlib.matlab as matlab",
"from matplotlib.matlab import *"]
# turn off rendering until end of script
matplotlib.matlab.interactive = 0
# Execute file if given.
fname = sys.argv
inFile = file(fname, 'r')
print '*** ERROR *** Could not read file <%s>' % fname
print '*** Executing file <%s>:' % fname
for line in inFile:
if line.lstrip().find('show()')==0: continue
print '>>', line,
if __name__ == '__main__':
# 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=='-mplot':
sys.argv = [sys.argv]+sys.argv[2:]
More information about the pygtk