Ryan May wrote:
On Wed, May 6, 2009 at 8:53 AM, Robert Cimrman <cimrm...@ntc.zcu.cz> wrote:
Ryan May wrote:
On Wed, May 6, 2009 at 7:57 AM, Robert Cimrman <cimrm...@ntc.zcu.cz>
wrote:
Just for the record: Ryan May's example in this thread, that uses pipes,
inspired me to try pipes as well, instead of queues
(multiprocessing.Pipe instead of Queue) and the "hanging problem", i.e.
the problem that Ctrl-C interrupted the program, but it had to be killed
to stop, disappeared. I can fix the script that I sent in message [1]
and provide it, if there is interest. (Currently I have fixed only the
version that is within sfepy).
I know I'd be interested. With your permission, it might make a nice
example as well.
Permission granted :) I have sent the script in response to William.
Done. I like the fact that with your example, everything is self-contained
in a single script.
Exactly, the details of starting another python process are hidden, the
multiprocessing module is really nice.
You might want to add
import matplotlib
matplotlib.use('GtkAgg')
to the script, and remove "from Queue import Empty".
FYI: I am sending also a more complex example - a Log class used in
sfepy, which supports multiple subplots, labels, logarithmic plots etc.
The file contains some other support classes too, so that it works
standalone. It is not very polished, but it serves its purpose.
r.
import matplotlib
matplotlib.use('GtkAgg')
import numpy as nm
from multiprocessing import Process, Pipe
from matplotlib.ticker import LogLocator, AutoLocator
import pylab
import gobject
def get_default( arg, default, msg_if_none = None ):
if arg is None:
out = default
else:
out = arg
if (out is None) and (msg_if_none is not None):
raise ValueError( msg_if_none )
return out
class Struct( object ):
def __init__( self, **kwargs ):
if kwargs:
self.__dict__.update( kwargs )
def __str__( self ):
"""Print instance class, name and items in alphabetical order."""
ss = "%s" % self.__class__.__name__
if hasattr( self, 'name' ):
ss += ":%s" % self.name
ss += '\n'
keys, vals = self.__dict__.keys(), self.__dict__.values()
order = nm.argsort(keys)
for ii in order:
key, val = keys[ii], vals[ii]
if issubclass( val.__class__, Struct ):
ss += " %s:\n %s" % (key, val.__class__.__name__)
if hasattr( val, 'name' ):
ss += ":%s" % val.name
ss += '\n'
else:
aux = "\n" + str( val )
aux = aux.replace( "\n", "\n " );
ss += " %s:\n%s\n" % (key, aux[1:])
return( ss.rstrip() )
def __repr__( self ):
ss = "%s" % self.__class__.__name__
if hasattr( self, 'name' ):
ss += ":%s" % self.name
return ss
class Output( Struct ):
"""Factory class providing output (print) functions.
Example:
>>> output = Output( 'sfepy:' )
>>> output( 1, 2, 3, 'hello' )
>>> output.prefix = 'my_cool_app:'
>>> output( 1, 2, 3, 'hello' )
"""
def __init__(self, prefix, filename=None, combined=False, **kwargs):
Struct.__init__(self, **kwargs)
self.prefix = prefix
self.set_output(filename, combined)
def __call__(self, *argc, **argv):
self.output_function(*argc, **argv)
def set_output(self, filename=None, combined=False, append=False):
"""Set the output function - all SfePy printing is accomplished by
it. If filename is None, output is to screen only, otherwise it is to
the specified file, moreover, if combined is True, both the ways are
used.
Arguments:
filename - print into this file
combined - print both on screen and into a file
append - append to an existing file instead of overwriting it
"""
self.level = 0
def output_screen( *argc, **argv ):
format = '%s' + ' %s' * (len( argc ) - 1)
msg = format % argc
if msg.startswith( '...' ):
self.level -= 1
print self._prefix + (' ' * self.level) + msg
if msg.endswith( '...' ):
self.level += 1
def output_file( *argc, **argv ):
format = '%s' + ' %s' * (len( argc ) - 1)
msg = format % argc
if msg.startswith( '...' ):
self.level -= 1
fd = open( filename, 'a' )
print >>fd, self._prefix + (' ' * self.level) + msg
fd.close()
if msg.endswith( '...' ):
self.level += 1
def output_combined( *argc, **argv ):
output_screen( *argc, **argv )
output_file( *argc, **argv )
if filename is None:
self.output_function = output_screen
else:
if not append:
fd = open( filename, 'w' )
fd.close()
if combined:
self.output_function = output_combined
else:
self.output_function = output_file
def get_output_function(self):
return self.output_function
def set_output_prefix( self, prefix ):
if len( prefix ) > 0:
prefix += ' '
self._prefix = prefix
def get_output_prefix( self ):
return self._prefix[:-1]
prefix = property( get_output_prefix, set_output_prefix )
output = Output( 'main:' )
class ProcessPlotter( Struct ):
output = Output( 'plotter:', filename='plotter.log' )
output = staticmethod( output )
def __init__( self, aggregate = 100 ):
Struct.__init__( self, aggregate = aggregate )
def process_command( self, command ):
self.output( command[0] )
if command[0] == 'iseq':
self.iseq = command[1]
elif command[0] == 'plot':
ii = self.iseq
name = self.seq_data_names[ii]
try:
ig = self.igs[ii]
ax = self.ax[ig]
ax.set_yscale( self.yscales[ig] )
ax.yaxis.grid( True )
ax.plot( command[1], command[2] )
if self.yscales[ig] == 'log':
ymajor_formatter = ax.yaxis.get_major_formatter()
ymajor_formatter.label_minor( True )
yminor_locator = LogLocator()
else:
yminor_locator = AutoLocator()
self.ax[ig].yaxis.set_minor_locator( yminor_locator )
except:
print ii, name
raise
elif command[0] == 'clear':
for ig in range( self.n_gr ):
self.ax[ig].cla()
elif command[0] == 'legends':
for ig in range( self.n_gr ):
self.ax[ig].legend( self.data_names[ig] )
if self.xaxes[ig]:
self.ax[ig].set_xlabel( self.xaxes[ig] )
if self.yaxes[ig]:
self.ax[ig].set_ylabel( self.yaxes[ig] )
elif command[0] == 'save':
self.fig.savefig( command[1] )
def terminate( self ):
if self.ii:
self.output( 'processed %d commands' % self.ii )
self.output( 'ended.' )
pylab.close( 'all' )
def poll_draw( self ):
def call_back():
self.ii = 0
while 1:
if not self.pipe.poll():
break
command = self.pipe.recv()
can_break = False
if command is None:
self.terminate()
return False
elif command[0] == 'continue':
can_break = True
else:
self.process_command( command )
if (self.ii >= self.aggregate) and can_break:
break
self.ii += 1
if self.ii:
self.fig.canvas.draw()
self.output( 'processed %d commands' % self.ii )
return True
return call_back
def __call__( self, pipe, data_names, igs, seq_data_names, yscales,
xaxes, yaxes ):
"""Sets-up the plotting window, sets GTK event loop timer callback to
callback() returned by self.poll_draw(). The callback does the actual
plotting, taking commands out of `pipe`, and is called every second."""
self.output( 'starting plotter...' )
# atexit.register( self.terminate )
self.pipe = pipe
self.data_names = data_names
self.igs = igs
self.seq_data_names = seq_data_names
self.yscales = yscales
self.xaxes = xaxes
self.yaxes = yaxes
self.n_gr = len( data_names )
self.fig = pylab.figure()
self.ax = []
for ig in range( self.n_gr ):
isub = 100 * self.n_gr + 11 + ig
self.ax.append( self.fig.add_subplot( isub ) )
self.gid = gobject.timeout_add( 1000, self.poll_draw() )
self.output( '...done' )
pylab.show()
def name_to_key( name, ii ):
return name + (':%d' % ii)
class Log( Struct ):
"""Log data and (optionally) plot them in the second process via
ProcessPlotter."""
def from_conf( conf, data_names ):
"""`data_names` ... tuple of names grouped by subplots:
([name1, name2, ...], [name3, name4, ...], ...)
where name<n> are strings to display in (sub)plot legends."""
if not isinstance( data_names, tuple ):
data_names = (data_names,)
obj = Log(data_names, **conf)
return obj
from_conf = staticmethod( from_conf )
def __init__(self, data_names, is_plot=True, aggregate=200, yscales=None,
xaxes=None, yaxes=None):
"""`data_names` ... tuple of names grouped by subplots:
([name1, name2, ...], [name3, name4, ...], ...)
where name<n> are strings to display in (sub)plot legends."""
Struct.__init__(self, data_names = data_names, seq_data_names = [],
igs = [], data = {}, x_values = {}, n_calls = 0,
plot_pipe = None)
ii = 0
for ig, names in enumerate( self.data_names ):
self.x_values[ig] = []
for name in names:
key = name_to_key( name, ii )
self.data[key] = []
self.igs.append( ig )
self.seq_data_names.append( name )
ii += 1
self.n_arg = len( self.igs )
self.n_gr = len( self.data_names )
self.is_plot = get_default( is_plot, True )
self.yscales = get_default( yscales, ['linear'] * self.n_arg )
self.xaxes = get_default( xaxes, ['iteration'] * self.n_arg )
self.yaxes = get_default( yaxes, [''] * self.n_arg )
self.aggregate = get_default( aggregate, 100 )
self.can_plot = (pylab is not None) and (Process is not None)
if self.is_plot and (not self.can_plot):
output( 'warning: log plot is disabled, install pylab (GTKAgg)' )
output( ' and multiprocessing' )
def __call__( self, *args, **kwargs ):
finished = False
save_figure = ''
x_values = None
if kwargs:
if 'finished' in kwargs:
finished = kwargs['finished']
if 'save_figure' in kwargs:
save_figure = kwargs['save_figure']
if 'x' in kwargs:
x_values = kwargs['x']
if save_figure and (self.plot_pipe is not None):
self.plot_pipe.send( ['save', save_figure] )
if finished:
self.terminate()
return
ls = len( args ), self.n_arg
if ls[0] != ls[1]:
msg = 'log called with wrong number of arguments! (%d == %d)' % ls
raise IndexError( msg )
for ii, name in enumerate( self.seq_data_names ):
aux = args[ii]
if isinstance( aux, nm.ndarray ):
aux = nm.array( aux, ndmin = 1 )
if len( aux ) == 1:
aux = aux[0]
else:
raise ValueError, 'can log only scalars (%s)' % aux
key = name_to_key( name, ii )
self.data[key].append( aux )
for ig in range( self.n_gr ):
if (x_values is not None) and x_values[ig]:
self.x_values[ig].append( x_values[ig] )
else:
self.x_values[ig].append( self.n_calls )
if self.is_plot and self.can_plot:
if self.n_calls == 0:
# atexit.register( self.terminate )
self.plot_pipe, plotter_pipe = Pipe()
self.plotter = ProcessPlotter( self.aggregate )
self.plot_process = Process( target = self.plotter,
args = (plotter_pipe,
self.data_names,
self.igs,
self.seq_data_names,
self.yscales,
self.xaxes, self.yaxes) )
self.plot_process.daemon = True
self.plot_process.start()
self.plot_data()
self.n_calls += 1
def terminate( self ):
if self.is_plot and self.can_plot:
self.plot_pipe.send( None )
self.plot_process.join()
self.n_calls = 0
output( 'terminated' )
def plot_data( self ):
send = self.plot_pipe.send
send( ['clear'] )
for ii, name in enumerate( self.seq_data_names ):
key = name_to_key( name, ii )
try:
send( ['iseq', ii] )
send( ['plot',
nm.array( self.x_values[self.igs[ii]] ),
nm.array( self.data[key] )] )
except:
print ii, name, self.data[key]
raise
send( ['legends'] )
send( ['continue'] )
def main():
log = Log((['sin( x )', 'cos( x )'], ['exp( x )']),
aggregate=100, yscales=['linear', 'log'],
xaxes=['angle', None], yaxes=[None, 'a function'])
for x in nm.linspace( 0, 4.0 * nm.pi, 200 ):
output( 'x: ', x )
log( nm.sin( x ), nm.cos( x ), nm.exp( x ), x = [x, None] )
print log
raw_input('press Enter...')
log( finished = True )
if __name__ == '__main__':
main()
------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users