python-damaris/src/gui/DamarisGUI.py
2014-12-15 20:45:44 +00:00

2945 lines
127 KiB
Python

# -*- coding: utf-8 -*-
# import native python modules
import time
import math
import sys
import platform
import cStringIO
import codecs
import os.path
import traceback
import tables
import types
import xml.parsers.expat
import threading
import webbrowser
import xdg.BaseDirectory
# import 3rd party modules
# gui graphics
import gobject
gobject.threads_init( )
import pygtk
pygtk.require( "2.0" )
import gtk
gtk_version_missmatch = gtk.check_version( 2, 8, 0 )
if gtk_version_missmatch:
raise Exception( "insufficient gtk version: " + gtk_version_missmatch )
import gtk.gdk
gtk.gdk.threads_init( )
import gtk.glade
import pango
import cairo
# array math
import numpy
# math graphics
import matplotlib
# force noninteractive and use of numpy
# matplotlib.rcParams["numerix"]="numpy"
matplotlib.rcParams[ "interactive" ] = "False"
matplotlib.rcParams[ "text.usetex" ] = "False"
matplotlib.rcParams[ "axes.formatter.limits" ] = "-3,3"
if matplotlib.rcParams[ "backend" ] == "GTK":
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
max_points_to_display = 0 # no limit
elif matplotlib.rcParams[ "backend" ] == "GTKCairo":
from matplotlib.backends.backend_gtkcairo import FigureCanvasGTKCairo as FigureCanvas
max_points_to_display = 1 << 14 # cairo cannot render longer paths than 18???
else:
# default
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
max_points_to_display = 0
import matplotlib.axes
import matplotlib.figure
#for printing issues
if hasattr( gtk, "PrintOperation" ):
import matplotlib.backends.backend_cairo
# import our own stuff
from damaris.gui import ExperimentWriter, ExperimentHandling
from damaris.gui import ResultReader, ResultHandling
from damaris.gui import BackendDriver
from damaris.gui.gtkcodebuffer import CodeBuffer, SyntaxLoader
#from damaris.data import Drawable # this is a base class, it should be used...
from damaris.data import DataPool, Accumulation, ADC_Result, MeasurementResult
# default, can be set to true from start script
debug = False
# version info
__version__ = "0.15-0-$Revision$"
class logstream:
gui_log = None
text_log = sys.__stdout__
def write( self, message ):
# default for stdout and stderr
if self.gui_log is not None:
self.gui_log( message )
if debug or self.gui_log is None:
self.text_log.write( message )
self.text_log.flush( )
def __call__( self, message ):
self.write( message )
def __del__( self ):
pass
global log
log = logstream( )
sys.stdout = log
sys.stderr = log
ExperimentHandling.log = log
ResultHandling.log = log
ResultReader.log = log
ExperimentWriter.log = log
BackendDriver.log = log
DataPool.log = log
class DamarisGUI:
ExpScript_Display = 0
ResScript_Display = 1
Monitor_Display = 2
Log_Display = 3
Config_Display = 4
Edit_State = 0
Run_State = 1
Pause_State = 2
Stop_State = 3
Quit_State = 4
def __init__( self, exp_script_filename=None, res_script_filename=None ):
# state: edit, run, stop, quit
# state transitions:
# edit -> run|quit
# run -> pause|stop
# pause -> run|stop
# stop -> edit
self.state = DamarisGUI.Edit_State
# script execution engines and backend driver
self.si = None
# produced and displayed data
self.data = None
self.glade_layout_init( )
# my notebook
self.main_notebook = self.xml_gui.get_widget( "main_notebook" )
self.log = LogWindow( self.xml_gui )
self.sw = ScriptWidgets( self.xml_gui )
self.toolbar_init( )
self.documentation_init( )
self.monitor = MonitorWidgets( self.xml_gui )
self.config = ConfigTab( self.xml_gui )
exp_script = u""
if exp_script_filename is not None and exp_script_filename != "":
self.sw.exp_script_filename = exp_script_filename[ : ]
if os.path.isfile( exp_script_filename ) and os.access( exp_script_filename, os.R_OK ):
exp_script = self.sw.load_file_as_unicode( exp_script_filename )
res_script = u""
if res_script_filename is not None and res_script_filename != "":
self.sw.res_script_filename = res_script_filename[ : ]
if os.path.isfile( res_script_filename ) and os.access( res_script_filename, os.R_OK ):
res_script = self.sw.load_file_as_unicode( res_script_filename )
self.sw.set_scripts( exp_script, res_script )
self.statusbar_init( )
self.main_window.show_all( )
self.main_window.present( )
def glade_layout_init( self ):
glade_file = os.path.join( os.path.dirname( __file__ ), "damaris.glade" )
self.xml_gui = gtk.glade.XML( glade_file )
self.main_window = self.xml_gui.get_widget( "main_window" )
self.main_window.connect( "delete-event", self.quit_event )
self.main_window.set_icon_from_file( os.path.join( os.path.dirname( __file__ ), "DAMARIS.png" ) )
self.main_window.set_title( u"DAMARIS-%s" % __version__ )
def statusbar_init( self ):
"""
experiment and result thread status, backend state
"""
self.experiment_script_statusbar_label = self.xml_gui.get_widget( "statusbar_experiment_script_label" )
self.data_handling_statusbar_label = self.xml_gui.get_widget( "statusbar_data_handling_label" )
self.backend_statusbar_label = self.xml_gui.get_widget( "statusbar_core_label" )
def toolbar_init( self ):
"""
buttons like save and run...
"""
self.toolbar_stop_button = self.xml_gui.get_widget( "toolbar_stop_button" )
self.toolbar_run_button = self.xml_gui.get_widget( "toolbar_run_button" )
self.toolbar_pause_button = self.xml_gui.get_widget( "toolbar_pause_button" )
# print button
self.toolbar_print_button = self.xml_gui.get_widget( "toolbar_print_button" )
if not hasattr( gtk, "PrintOperation" ):
self.toolbar_print_button.set_sensitive( False )
print "Printing is not supported by GTK+ version in use"
else:
self.toolbar_print_button.set_sensitive( True )
self.xml_gui.signal_connect( "on_toolbar_print_button_clicked", self.print_button_switch )
# prepare for edit state
self.toolbar_run_button.set_sensitive( True )
self.toolbar_stop_button.set_sensitive( False )
self.toolbar_pause_button.set_sensitive( False )
# and their events
self.xml_gui.signal_connect( "on_toolbar_run_button_clicked", self.start_experiment )
self.xml_gui.signal_connect( "on_toolbar_pause_button_toggled", self.pause_experiment )
self.xml_gui.signal_connect( "on_toolbar_stop_button_clicked", self.stop_experiment )
self.xml_gui.signal_connect( "on_doc_menu_activate", self.show_doc_menu )
self.xml_gui.signal_connect( "on_toolbar_manual_button_clicked", self.show_manual )
def run( self ):
# prolong lifetime of clipboard till the very end (avoid error message)
self.main_clipboard = self.sw.main_clipboard
gtk.gdk.threads_enter( )
gtk.main( )
gtk.gdk.threads_leave( )
self.si = None
self.sw = None
self.config = None
self.xml_gui = None
# event handling: the real acitons in gui programming
# first global events
def quit_event( self, widget, data=None ):
"""
expecting quit event for main application
"""
if self.state in [ DamarisGUI.Edit_State, DamarisGUI.Quit_State ]:
self.state = DamarisGUI.Quit_State
# do a cleanup...
print "ToDo: Cleanup, Save Dialogs ..."
self.config = None
self.sw = None
self.monitor = None
self.log = None
# and quit
gtk.main_quit( )
return True
else:
print "Stop Experiment please! (ToDo: Dialog)"
return True
# toolbar related events:
def start_experiment( self, widget, data=None ):
# something running?
if self.si is not None:
print "Last Experiment is not clearly stopped!"
self.si = None
# get config values:
actual_config = self.config.get( )
# get scripts and start script interface
self.sw.disable_editing( )
exp_script, res_script = self.sw.get_scripts( )
if not actual_config[ "start_result_script" ]:
res_script = ""
if not actual_config[ "start_experiment_script" ]:
exp_script = ""
backend = actual_config[ "backend_executable" ]
if not actual_config[ "start_backend" ]:
backend = ""
if (backend == "" and exp_script == "" and res_script == ""):
print "nothing to do...so doing nothing!"
self.sw.enable_editing( )
return
# check whether scripts are syntacticaly valid
# should be merged with check script function
exp_code = None
if exp_script != "":
try:
exp_code = compile( exp_script, "Experiment Script", "exec" )
except SyntaxError, e:
ln = e.lineno
lo = e.offset
if type( ln ) is not types.IntType:
ln = 0
if type( lo ) is not types.IntType:
lo = 0
print "Experiment Script: %s at line %d, col %d:" % (e.__class__.__name__, ln, lo)
if e.text != "":
print "\"%s\"" % e.text
# print " "*(e.offset+1)+"^" # nice idea, but needs monospaced fonts
pass
print e
res_code = None
if res_script != "":
try:
res_code = compile( res_script, "Result Script", "exec" )
except SyntaxError, e:
ln = e.lineno
lo = e.offset
if type( ln ) is not types.IntType:
ln = 0
if type( lo ) is not types.IntType:
lo = 0
print "Result script: %s at line %d, col %d:" % (e.__class__.__name__, ln, lo)
if e.text != "":
print "\"%s\"" % e.text
# print " "*(e.offset+1)+"^" # nice idea, but needs monospaced fonts
pass
print e
# detect error
if (exp_script != "" and exp_code is None) or \
(res_script != "" and res_code is None):
self.main_notebook.set_current_page( DamarisGUI.Log_Display )
self.sw.enable_editing( )
return
# prepare to run
self.state = DamarisGUI.Run_State
self.toolbar_run_button.set_sensitive( False )
self.toolbar_stop_button.set_sensitive( True )
self.toolbar_pause_button.set_sensitive( True )
self.toolbar_pause_button.set_active( False )
# delete old data
self.data = None
self.monitor.observe_data_pool( self.data )
# set the text mark for hdf logging
if self.log.textbuffer.get_mark( "lastdumped" ) is None:
self.log.textbuffer.create_mark( "lastdumped", self.log.textbuffer.get_end_iter( ), left_gravity=True )
# start experiment
try:
self.spool_dir = os.path.abspath( actual_config[ "spool_dir" ] )
# setup script engines
self.si = ScriptInterface( exp_code,
res_code,
backend,
self.spool_dir,
clear_jobs=actual_config[ "del_jobs_after_execution" ],
clear_results=actual_config[ "del_results_after_processing" ] )
self.data = self.si.data
# run frontend and script engines
self.monitor.observe_data_pool( self.data )
self.si.runScripts( )
except Exception, e:
#print "ToDo evaluate exception",str(e), "at",traceback.extract_tb(sys.exc_info()[2])[-1][1:3]
#print "Full traceback:"
traceback_file = cStringIO.StringIO( )
traceback.print_tb( sys.exc_info( )[ 2 ], None, traceback_file )
self.main_notebook.set_current_page( DamarisGUI.Log_Display )
print "Error while executing scripts: %s\n" % str( e ) + traceback_file.getvalue( )
traceback_file = None
self.data = None
if self.si is not None:
still_running = filter( None, [ self.si.exp_handling, self.si.res_handling, self.si.back_driver ] )
for r in still_running:
r.quit_flag.set( )
print "waiting for threads stoping...",
still_running = filter( lambda x: x is not None and x.isAlive( ),
[ self.si.exp_handling, self.si.res_handling, self.si.back_driver ] )
for t in still_running:
t.join( )
print "done"
# cleanup
self.si = None
self.state = DamarisGUI.Edit_State
self.sw.enable_editing( )
self.toolbar_run_button.set_sensitive( True )
self.toolbar_stop_button.set_sensitive( False )
self.toolbar_pause_button.set_sensitive( False )
self.toolbar_pause_button.set_active( False )
return
# switch to grapics
self.main_notebook.set_current_page( DamarisGUI.Monitor_Display )
# set running
if self.si.exp_handling is not None:
self.experiment_script_statusbar_label.set_text( "Experiment Script Running (0)" )
else:
self.experiment_script_statusbar_label.set_text( "Experiment Script Idle" )
if self.si.res_handling is not None:
self.data_handling_statusbar_label.set_text( "Result Script Running (0)" )
else:
self.data_handling_statusbar_label.set_text( "Result Script Idle" )
if self.si.back_driver is not None:
self.backend_statusbar_label.set_text( "Backend Running" )
else:
self.backend_statusbar_label.set_text( "Backend Idle" )
# start data dump
self.dump_thread = None
self.save_thread = None
self.dump_filename = ""
if actual_config[ "data_pool_name" ] != "":
self.dump_states( init=True )
gobject.timeout_add( 200, self.observe_running_experiment )
def observe_running_experiment( self ):
"""
periodically look at running threads
"""
# look at components and update them
# test whether backend and scripts are done
r = self.si.data.get( "__recentresult", -1 ) + 1
b = self.si.data.get( "__resultsinadvance", -1 ) + 1
e = self.si.data.get( "__recentexperiment", -1 ) + 1
e_text = None
r_text = None
b_text = None
if self.si.exp_handling is not None:
if not self.si.exp_handling.isAlive( ):
self.si.exp_handling.join( )
if self.si.exp_handling.raised_exception:
print "experiment script failed at line %d (function %s): %s" % (self.si.exp_handling.location[ 0 ],
self.si.exp_handling.location[ 1 ],
self.si.exp_handling.raised_exception)
print "Full traceback", self.si.exp_handling.traceback
e_text = "Experiment Script Failed (%d)" % e
else:
e_text = "Experiment Script Finished (%d)" % e
print "experiment script finished"
self.si.exp_handling = None
else:
e_text = "Experiment Script Running (%d)" % e
if self.si.res_handling is not None:
if not self.si.res_handling.isAlive( ):
self.si.res_handling.join( )
if self.si.res_handling.raised_exception:
print "result script failed at line %d (function %s): %s" % (self.si.res_handling.location[ 0 ],
self.si.res_handling.location[ 1 ],
self.si.res_handling.raised_exception)
print "Full traceback", self.si.res_handling.traceback
r_text = "Result Script Failed (%d)" % r
else:
r_text = "Result Script Finished (%d)" % r
self.si.res_handling = None
else:
r_text = "Result Script Running (%d)" % r
if self.si.back_driver is not None:
if not self.si.back_driver.isAlive( ):
if self.si.back_driver.raised_exception:
b_text = "Backend Failed"
else:
b_text = "Backend Finished"
self.si.back_driver.join( )
self.si.back_driver = None
else:
b_text = "Backend Running"
if b != 0:
b_text += " (%d)" % b
if self.dump_thread is not None:
if self.dump_thread.isAlive( ):
sys.stdout.write( "." )
self.dump_dots += 1
if self.dump_dots > 80:
print
self.dump_dots = 0
else:
self.dump_thread.join( )
self.dump_thread = None
dump_size = os.stat( self.dump_filename ).st_size / 1e6
print "done (%.1f s, %.1f MB)" % (time.time( ) - self.dump_start_time, dump_size)
gtk.gdk.threads_enter( )
if e_text:
self.experiment_script_statusbar_label.set_text( e_text )
if r_text:
self.data_handling_statusbar_label.set_text( r_text )
if b_text:
self.backend_statusbar_label.set_text( b_text )
gtk.gdk.threads_leave( )
still_running = filter( None,
[ self.si.exp_handling, self.si.res_handling, self.si.back_driver, self.dump_thread ] )
if len( still_running ) == 0:
if self.save_thread is None and self.dump_filename != "":
print "all subprocesses ended, saving data pool"
# thread to save data...
self.save_thread = threading.Thread( target=self.dump_states, name="dump states" )
self.save_thread.start( )
self.dump_start_time = time.time( )
self.dump_dots = 0
self.state = DamarisGUI.Stop_State
if self.state == DamarisGUI.Stop_State:
gtk.gdk.threads_enter( )
self.toolbar_pause_button.set_sensitive( False )
self.toolbar_stop_button.set_sensitive( False )
gtk.gdk.threads_leave( )
if len( still_running ) != 0:
print "subprocess(es) still running: " + ', '.join( map( lambda s: s.getName( ), still_running ) )
return True
else:
if self.save_thread is not None:
if self.save_thread.isAlive( ):
sys.stdout.write( "." )
self.dump_dots += 1
if self.dump_dots > 80:
print
self.dump_dots = 0
return True
self.save_thread.join( )
self.save_thread = None
dump_size = os.stat( self.dump_filename ).st_size / 1e6
print "done (%.1f s, %.1f MB)" % (time.time( ) - self.dump_start_time, dump_size)
# now everything is stopped
self.state = DamarisGUI.Edit_State
gtk.gdk.threads_enter( )
self.sw.enable_editing( )
self.toolbar_run_button.set_sensitive( True )
self.toolbar_stop_button.set_sensitive( False )
self.toolbar_pause_button.set_sensitive( False )
gtk.gdk.threads_leave( )
# keep data to display but throw away everything else
self.si = None
return False
# dump states?
if self.dump_thread is None and self.dump_filename != "" and \
self.dump_timeinterval != 0 and self.last_dumped + self.dump_timeinterval < time.time( ):
print "dumping data pool"
self.dump_start_time = time.time( )
self.dump_dots = 0
# thread to save data...
self.dump_thread = threading.Thread( target=self.dump_states, name="dump states" )
self.dump_thread.start( )
# or look at them again
return True
def dump_states( self, init=False ):
"""
init: constructs basic structure of this file
compress: optional argument for zlib compression 0-9
"""
if init:
# calculate new settings for hdf files
actual_config = self.config.get( )
# provide name tags for dump file name
extensions_dict = { "date": time.strftime( "%Y-%m-%d" ),
"datetime": time.strftime( "%Y-%m-%d_%H%M" )
}
if self.sw.exp_script_filename is None:
extensions_dict[ "exp" ] = "unnamed"
else:
extensions_dict[ "exp" ] = os.path.splitext( os.path.basename( self.sw.exp_script_filename ) )[ 0 ]
if self.sw.res_script_filename is None:
extensions_dict[ "res" ] = "unnamed"
else:
extensions_dict[ "res" ] = os.path.splitext( os.path.basename( self.sw.res_script_filename ) )[ 0 ]
# create new dump file name
try:
self.dump_filename = actual_config.get( "data_pool_name" ) % extensions_dict
except ValueError, e:
print "invalid formating character in '" + actual_config.get( "data_pool_name" ) + "'"
self.dump_filename = "DAMARIS_data_pool.h5"
print "reseting dumpfile to " + self.dump_filename
del extensions_dict
if os.path.split( self.dump_filename )[ 1 ] == "" or os.path.isdir( self.dump_filename ):
print "The dump filename is a directory, using filename 'DAMARIS_data_pool.h5'"
self.dump_filename += os.sep + "DAMARIS_data_pool.h5"
# compression
self.dump_complib = actual_config.get( "data_pool_complib", None )
if self.dump_complib == "None":
self.dump_complib = None
if self.dump_complib is not None:
self.dump_complevel = int( actual_config.get( "data_pool_comprate", 0 ) )
else:
self.dump_complevel = 0
# time interval
self.dump_timeinterval = 60 * 60 * 10
try:
self.dump_timeinterval = int( 60 * float( actual_config[ "data_pool_write_interval" ] ) )
except ValueError, e:
print "configuration provides non-number for dump interval: " + str( e )
# if existent, move away old files
if os.path.isfile( self.dump_filename ):
# create bakup name pattern
dump_filename_pattern = None
(filename, ext) = os.path.splitext( self.dump_filename )
if ext in [ ".h5", ".hdf", ".hdf5" ]:
dump_filename_pattern = filename.replace( "%", "%%" ) + "_%d" + ext
else:
dump_filename_pattern = self.dump_filename.replace( "%", "%%" ) + "_%d"
last_backup = 0
cummulated_size = os.stat( self.dump_filename ).st_size
while os.path.isfile( dump_filename_pattern % last_backup ):
cummulated_size += os.stat( dump_filename_pattern % last_backup ).st_size
last_backup += 1
while last_backup > 0:
os.rename( dump_filename_pattern % (last_backup - 1), dump_filename_pattern % last_backup )
last_backup -= 1
os.rename( self.dump_filename, dump_filename_pattern % 0 )
if cummulated_size > (1 << 30):
print "Warning: the cummulated backups size of '%s' is %d MByte" % (self.dump_filename,
cummulated_size / (1 << 20))
# init is finnished now
# now it's time to create the hdf file
dump_file = None
if not os.path.isfile( self.dump_filename ):
if not init:
print "dump file \"%s\" vanished unexpectedly, creating new one" % self.dump_filename
# have a look to the path and create necessary directories
dir_stack = [ ]
dir_trunk = os.path.dirname( os.path.abspath( self.dump_filename ) )
while dir_trunk != "" and not os.path.isdir( dir_trunk ):
dir_stack.append( os.path.basename( dir_trunk ) )
dir_trunk = os.path.dirname( dir_trunk )
try:
while len( dir_stack ):
dir_trunk = os.path.join( dir_trunk, dir_stack.pop( ) )
if os.path.isdir( dir_trunk ):
continue
os.mkdir( dir_trunk )
except OSError, e:
print e
print "coud not create dump directory '%s', so hdf5 dumps disabled" % dir_trunk
self.dump_filename = ""
self.dump_timeinterval = 0
return True
# create new dump file
dump_file = tables.openFile( self.dump_filename, mode="w", title="DAMARIS experiment data" )
# write scripts and other useful information
scriptgroup = dump_file.createGroup( "/", "scripts", "Used Scripts" )
exp_text, res_text = self.sw.get_scripts( )
if self.si.exp_script:
dump_file.createArray( scriptgroup, "experiment_script", exp_text )
if self.si.res_script:
dump_file.createArray( scriptgroup, "result_script", res_text )
if self.si.backend_executable:
dump_file.createArray( scriptgroup, "backend_executable", self.si.backend_executable )
if self.spool_dir:
dump_file.createArray( scriptgroup, "spool_directory", self.spool_dir )
timeline_tablecols = numpy.recarray( 0, dtype=([ ("time", "S17"),
("experiments", "int64"),
("results", "int64") ]) )
timeline_table = dump_file.createTable( "/", "timeline", timeline_tablecols,
title="Timeline of Experiment" )
if tables.__version__[ 0 ] == "1":
logarray = dump_file.createVLArray( where=dump_file.root,
name="log",
atom=tables.StringAtom( length=120 ),
title="log messages",
filters=tables.Filters( complevel=9, complib='zlib' ) )
else:
logarray = dump_file.createEArray( where=dump_file.root,
name="log",
atom=tables.StringAtom( itemsize=120 ),
shape=(0,),
title="log messages",
filters=tables.Filters( complevel=9, complib='zlib' ) )
if dump_file is None and os.path.isfile( self.dump_filename ) and tables.isPyTablesFile( self.dump_filename ):
# take some data from dump file and repack
os.rename( self.dump_filename, self.dump_filename + ".bak" )
old_dump_file = tables.openFile( self.dump_filename + ".bak", mode="r+" )
if "data_pool" in old_dump_file.root:
old_dump_file.removeNode( where="/", name="data_pool", recursive=True )
old_dump_file.copyFile( self.dump_filename )
old_dump_file.close( )
del old_dump_file
os.remove( self.dump_filename + ".bak" )
# prepare for update
dump_file = tables.openFile( self.dump_filename, mode="r+" )
if dump_file is None:
# exit!
print "coud not create dump directory '%s', so hdf5 dumps disabled" % dir_trunk
self.dump_filename = ""
self.dump_timeinterval = 0
return True
# no undo please!
if dump_file.isUndoEnabled( ):
dump_file.disableUndo( )
# save the data!
self.data.write_hdf5( dump_file, where="/", name="data_pool",
complib=self.dump_complib, complevel=self.dump_complevel )
# now save additional information
e = self.si.data.get( "__recentexperiment", -1 ) + 1
r = self.si.data.get( "__recentresult", -1 ) + 1
timeline_table = dump_file.root.timeline
timeline_row = timeline_table.row
timeline_row[ "time" ] = time.strftime( "%Y%m%d %H:%M:%S" )
timeline_row[ "experiments" ] = e
timeline_row[ "results" ] = r
timeline_row.append( )
timeline_table.flush( )
# save log window information:
# also save backend's logfile information?
logtextbuffer = self.log.textbuffer
last_end = logtextbuffer.get_mark( "lastdumped" )
if last_end is None:
last_end = logtextbuffer.create_mark( "lastdumped", logtextbuffer.get_start_iter( ), left_gravity=True )
logtext_start = logtextbuffer.get_iter_at_mark( last_end )
logtext_end = logtextbuffer.get_end_iter( )
logtextbuffer.move_mark( last_end, logtext_end )
# recode from unicode
logtext = codecs.getencoder( "iso-8859-15" )( logtextbuffer.get_text( logtext_start, logtext_end ), "replace" )[
0 ]
# avoid circular references (seems to be necessary with gtk-2.12)
del logtextbuffer, logtext_start, logtext_end, last_end
for l in logtext.splitlines( ):
dump_file.root.log.append( numpy.array( [ l ], dtype="S120" ) )
dump_file.flush( )
dump_file.close( )
self.last_dumped = time.time( )
del dump_file
return True
def pause_experiment( self, widget, data=None ):
"""
pause experiment execution (that means delay backend and let others run)
"""
if self.si is None:
return False
pause_state = self.toolbar_pause_button.get_active( )
if pause_state:
if self.state != DamarisGUI.Run_State:
return False
if self.spool_dir is None:
return False
no = self.si.data.get( "__recentresult", -1 ) + 1
result_pattern = os.path.join( self.spool_dir, "job.%09d.result" )
job_pattern = os.path.join( self.spool_dir, "job.%09d" )
while os.path.isfile( result_pattern % no ):
no += 1
i = 0
self.pause_files = [ ]
while i < 3 and os.path.isfile( job_pattern % (no + i) ):
pause_file = (job_pattern % (no + i)) + ".pause"
os.rename( job_pattern % (no + i), pause_file )
self.pause_files.append( pause_file )
i += 1
self.state = DamarisGUI.Pause_State
self.backend_statusbar_label.set_text( "Backend Paused" )
else:
if self.state != DamarisGUI.Pause_State:
return False
self.state = DamarisGUI.Run_State
for f in self.pause_files:
os.rename( f, f[ :-6 ] )
self.pause_files = None
self.backend_statusbar_label.set_text( "Backend Running" )
def stop_experiment( self, widget, data=None ):
if self.state in [ DamarisGUI.Run_State, DamarisGUI.Pause_State ]:
if self.si is None:
return
still_running = filter( None, [ self.si.exp_handling, self.si.res_handling, self.si.back_driver ] )
for r in still_running:
r.quit_flag.set( )
self.state = DamarisGUI.Stop_State
def print_button_switch( self, widget ):
"""
decides what to print... and prints, layout is done by responsible class
"""
if not hasattr( gtk, "PrintOperation" ):
return
# copied and modified from pygtk-2.10.1/examples/pygtk-demo/demos/print_editor.py
print_ = gtk.PrintOperation( )
# will come from config
settings = None
if settings is not None:
print_.set_print_settings( settings )
page_setup = None
if page_setup is not None:
print_.set_default_page_setup( page_setup )
#print_.set_property("allow_async",True)
current_page = self.main_notebook.get_current_page( )
print_data = { }
if current_page in [ 0, 1 ]:
print_.connect( "begin_print", self.sw.begin_print, print_data )
print_.connect( "draw_page", self.sw.draw_page, print_data )
elif current_page == 2:
print_.connect( "begin_print", self.monitor.begin_print, print_data )
print_.connect( "draw_page", self.monitor.draw_page, print_data )
else:
return
try:
res = print_.run( gtk.PRINT_OPERATION_ACTION_PRINT_DIALOG, self.main_window )
except gobject.GError, ex:
error_dialog = gtk.MessageDialog( self.main_window,
gtk.DIALOG_DESTROY_WITH_PARENT,
gtk._MESSAGE_ERROR,
gtk.BUTTONS_CLOSE,
("Error printing file:\n%s" % str( ex )) )
error_dialog.connect( "response", gtk.Widget.destroy )
error_dialog.show( )
else:
if res == gtk.PRINT_OPERATION_RESULT_APPLY:
settings = print_.get_print_settings( )
def documentation_init( self ):
self.doc_urls = {
"Python DAMARIS": None,
"DAMARIS Homepage": "http://damaris.berlios.de/",
"Python": "http://www.python.org/doc/%d.%d/" % (sys.version_info[ :2 ]),
"numpy/scipy": "http://docs.scipy.org/",
"pytables": "http://www.pytables.org/docs/manual/",
"DAMARIS backends": None,
"DAMARIS Repository": "http://svn.berlios.de/viewvc/damaris"
}
if os.path.isdir( "/usr/share/doc/python%d.%d-doc/html" % (sys.version_info[ :2 ]) ):
self.doc_urls[ "Python" ] = "file:///usr/share/doc/python%d.%d-doc/html/index.html" % (
sys.version_info[ :2 ])
if os.path.isdir( "/usr/share/doc/python-tables-doc/html" ):
self.doc_urls[ "pytables" ] = "file:///usr/share/doc/python-tables-doc/html/index.html"
doc_index_url = None
# local installation
installation_base = __file__
for i in xrange( 5 ):
installation_base = os.path.dirname( installation_base )
if os.path.isfile( os.path.join( installation_base, "share", "python-damaris", "doc", "index.html" ) ):
self.doc_urls[ "Python DAMARIS" ] = os.path.join( installation_base, "share", "python-damaris", "doc",
"index.html" )
elif os.path.isfile( "/usr/share/doc/python-damaris/html/index.html" ):
# check generic debian location
self.doc_urls[ "Python DAMARIS" ] = "file:///usr/share/doc/python-damaris/html/index.html"
else:
self.doc_urls[ "Python DAMARIS" ] = "http://damaris.berlios.de/wiki/index.php/Tutorial"
self.doc_browser = None
def show_doc_menu( self, widget, data=None ):
"""
offer a wide variety of docs, prefer local installations
"""
if type( widget ) is gtk.MenuItem:
requested_doc = widget.get_child( ).get_text( )
else:
requested_doc = "Python DAMARIS"
if requested_doc in self.doc_urls and self.doc_urls[ requested_doc ] is not None:
if self.doc_browser is not None:
if not self.doc_browser.isAlive( ):
self.doc_browser.join( )
if self.doc_browser.my_webbrowser is not None:
print "new browser tab"
self.doc_browser.my_webbrowser.open_new_tab( self.doc_urls[ requested_doc ] )
else:
del self.doc_browser
self.doc_browser = start_browser( self.doc_urls[ requested_doc ] )
self.doc_browser.start( )
else:
self.doc_browser = start_browser( self.doc_urls[ requested_doc ] )
self.doc_browser.start( )
else:
print "missing docs for '%s'" % (requested_doc)
show_manual = show_doc_menu
class start_browser( threading.Thread ):
def __init__( self, url ):
threading.Thread.__init__( self, name="manual browser" )
self.my_webbrowser = None
self.my_webbrowser_process = None
self.start_url = url
def run( self ):
"""
start a webbrowser
"""
if sys.hexversion >= 0x02050000:
if sys.platform == "linux2" and self.my_webbrowser is None:
# try for debian linux
self.my_webbrowser = webbrowser.get( "x-www-browser" )
if self.my_webbrowser is None:
# this is what it should be everywhere!
self.my_webbrowser = webbrowser.get( )
if self.my_webbrowser is not None:
print "starting web browser (module webbrowser)"
self.my_webbrowser.open( self.start_url )
return True
# last resort
print "starting web browser (webbrowser.py)"
self.my_webbrowser_process = os.spawnl( os.P_NOWAIT,
sys.executable,
os.path.basename( sys.executable ),
"-c",
"import webbrowser\nwebbrowser.open('%s')" % self.start_url )
return True
class LogWindow:
"""
writes messages to the log window
"""
def __init__( self, xml_gui ):
self.xml_gui = xml_gui
self.textview = self.xml_gui.get_widget( "messages_textview" )
self.textbuffer = self.textview.get_buffer( )
self.logstream = log
self.logstream.gui_log = self
self.last_timetag = None
self( "Started in directory %s\n" % os.getcwd( ) )
def __call__( self, message ):
timetag = time.time( )
gobject.idle_add( self.add_message_callback, timetag, message, priority=gobject.PRIORITY_LOW )
def add_message_callback( self, timetag, message ):
date_tag = u""
if self.last_timetag is None or (message != "\n" and self.last_timetag + 60 < timetag):
self.last_timetag = timetag
timetag_int = math.floor( timetag )
timetag_ms = int( (timetag - timetag_int) * 1000 )
date_tag = time.strftime( u"%Y%m%d %H:%M:%S.%%03d:\n", time.localtime( timetag_int ) ) % timetag_ms
gtk.gdk.threads_enter( )
self.textbuffer.place_cursor( self.textbuffer.get_end_iter( ) )
self.textbuffer.insert_at_cursor( date_tag + unicode( message ) )
self.textview.scroll_to_mark( self.textbuffer.get_insert( ), 0.1 )
gtk.gdk.threads_leave( )
def __del__( self ):
self.logstream.gui_log = None
self.logstream = None
class ScriptWidgets:
def __init__( self, xml_gui ):
"""
initialize text widgets with text
"""
self.xml_gui = xml_gui
# my states
# editing enabled/disabled
self.editing_state = True
# keep in mind which filename was used for experiment script
self.exp_script_filename = None
# keep in mind which filename was used for result script
self.res_script_filename = None
# load syntax description for syntax highlighting (defined in python.xml)
sl = SyntaxLoader( "python" )
# script buffers:
self.experiment_script_textview = self.xml_gui.get_widget( "experiment_script_textview" )
self.data_handling_textview = self.xml_gui.get_widget( "data_handling_textview" )
# create and set syntax-highlighting text-buffers as textview backends
self.experiment_script_textbuffer = CodeBuffer( lang=sl )
self.data_handling_textbuffer = CodeBuffer( lang=sl )
self.experiment_script_textview.set_buffer( self.experiment_script_textbuffer )
self.data_handling_textview.set_buffer( self.data_handling_textbuffer )
# script fonts are atlered by configuration
# clipboard
self.main_clipboard = gtk.Clipboard( selection="CLIPBOARD" )
# statusbar
self.experiment_script_statusbar_label = self.xml_gui.get_widget( "statusbar_experiment_script_label" )
self.data_handling_statusbar_label = self.xml_gui.get_widget( "statusbar_data_handling_label" )
# footline with line and col indicators
self.experiment_script_line_indicator = self.xml_gui.get_widget( "experiment_script_line_textfield" )
self.experiment_script_column_indicator = self.xml_gui.get_widget( "experiment_script_column_textfield" )
self.data_handling_line_indicator = self.xml_gui.get_widget( "data_handling_line_textfield" )
self.data_handling_column_indicator = self.xml_gui.get_widget( "data_handling_column_textfield" )
# also listen to location values changed
self.xml_gui.signal_connect( "on_data_handling_column_textfield_value_changed",
self.column_line_widgets_changed_event )
self.xml_gui.signal_connect( "on_data_handling_line_textfield_value_changed",
self.column_line_widgets_changed_event )
self.xml_gui.signal_connect( "on_experiment_script_line_textfield_value_changed",
self.column_line_widgets_changed_event )
self.xml_gui.signal_connect( "on_experiment_script_column_textfield_value_changed",
self.column_line_widgets_changed_event )
# some event handlers
self.experiment_script_textbuffer.connect( "modified-changed", self.textviews_modified )
self.experiment_script_textview.connect_after( "move-cursor", self.textviews_moved )
self.experiment_script_textview.connect( "key-press-event", self.textviews_keypress )
self.experiment_script_textview.connect( "button-release-event", self.textviews_clicked )
self.data_handling_textbuffer.connect( "modified-changed", self.textviews_modified )
self.data_handling_textview.connect_after( "move-cursor", self.textviews_moved )
self.data_handling_textview.connect( "key-press-event", self.textviews_keypress )
self.data_handling_textview.connect( "button-release-event", self.textviews_clicked )
# and the editing toolbar part
# buttons
self.toolbar_new_button = self.xml_gui.get_widget( "toolbar_new_button" )
self.toolbar_open_button = self.xml_gui.get_widget( "toolbar_open_file_button" )
self.toolbar_save_button = self.xml_gui.get_widget( "toolbar_save_file_button" )
self.toolbar_save_as_button = self.xml_gui.get_widget( "toolbar_save_as_button" )
self.toolbar_save_all_button = self.xml_gui.get_widget( "toolbar_save_all_button" )
self.toolbar_check_scripts_button = self.xml_gui.get_widget( "toolbar_check_scripts_button" )
# events
self.xml_gui.signal_connect( "on_toolbar_open_file_button_clicked", self.open_file )
self.xml_gui.signal_connect( "on_toolbar_new_button_clicked", self.new_file )
self.xml_gui.signal_connect( "on_toolbar_save_as_button_clicked", self.save_file_as )
self.xml_gui.signal_connect( "on_toolbar_save_file_button_clicked", self.save_file )
self.xml_gui.signal_connect( "on_toolbar_save_all_button_clicked", self.save_all_files )
self.xml_gui.signal_connect( "on_toolbar_check_scripts_button_clicked", self.check_script )
# my notebook
self.main_notebook = self.xml_gui.get_widget( "main_notebook" )
# config toolbar
self.main_notebook.connect_after( "switch_page", self.notebook_page_switched )
# start with empty scripts
self.set_scripts( "", "" )
self.enable_editing( )
# public methods
def set_scripts( self, exp_script=None, res_script=None ):
# load buffers and set cursor to front
if exp_script is not None:
self.experiment_script_textbuffer.set_text( unicode( exp_script ) )
self.experiment_script_textbuffer.place_cursor( self.experiment_script_textbuffer.get_start_iter( ) )
self.experiment_script_textbuffer.set_modified( False )
self.textviews_moved( self.experiment_script_textview )
if res_script is not None:
self.data_handling_textbuffer.set_text( unicode( res_script ) )
self.data_handling_textbuffer.place_cursor( self.data_handling_textbuffer.get_start_iter( ) )
self.data_handling_textbuffer.set_modified( False )
self.textviews_moved( self.data_handling_textview )
self.set_toolbuttons_status( )
def get_scripts( self ):
"""
returns scripts
"""
exp_script = self.experiment_script_textbuffer.get_text( self.experiment_script_textbuffer.get_start_iter( ),
self.experiment_script_textbuffer.get_end_iter( ) ).rstrip( )
res_script = self.data_handling_textbuffer.get_text( self.data_handling_textbuffer.get_start_iter( ),
self.data_handling_textbuffer.get_end_iter( ) ).rstrip( )
return (exp_script, res_script)
def disable_editing( self ):
"""
disable editing (for running experiments)
"""
# disable buffers
self.editing_state = False
self.experiment_script_textview.set_sensitive( False )
self.data_handling_textview.set_sensitive( False )
self.set_toolbuttons_status( )
def enable_editing( self ):
"""
returns to editable state
"""
self.editing_state = True
self.experiment_script_textview.set_sensitive( True )
self.data_handling_textview.set_sensitive( True )
self.set_toolbuttons_status( )
# methods to update status and appearance
def set_toolbuttons_status( self ):
"""
ToDo: care about associated file names
"""
current_page = self.main_notebook.get_current_page( )
if self.editing_state and current_page in [ 0, 1 ]:
self.toolbar_new_button.set_sensitive( True )
self.toolbar_open_button.set_sensitive( True )
# find visible tab
exp_modified = self.experiment_script_textbuffer.get_modified( )
res_modified = self.data_handling_textbuffer.get_modified( )
enable_save = True
if current_page == 0:
enable_save = exp_modified and self.exp_script_filename is not None
elif current_page == 1:
enable_save = res_modified and self.res_script_filename is not None
self.toolbar_save_button.set_sensitive( enable_save )
self.toolbar_save_as_button.set_sensitive( True )
self.toolbar_save_all_button.set_sensitive( exp_modified or res_modified )
self.toolbar_check_scripts_button.set_sensitive( True )
else:
# disable toolbar
self.toolbar_new_button.set_sensitive( False )
self.toolbar_open_button.set_sensitive( False )
self.toolbar_save_button.set_sensitive( False )
self.toolbar_save_as_button.set_sensitive( False )
self.toolbar_save_all_button.set_sensitive( False )
self.toolbar_check_scripts_button.set_sensitive( False )
if self.exp_script_filename:
exp_titlename = unicode( os.path.splitext( os.path.basename( self.exp_script_filename ) )[ 0 ] )
else:
exp_titlename = u"unnamed"
if self.res_script_filename:
res_titlename = unicode( os.path.splitext( os.path.basename( self.res_script_filename ) )[ 0 ] )
else:
res_titlename = u"unnamed"
window_title = u"DAMARIS-%s %s,%s" % (__version__, exp_titlename, res_titlename)
self.xml_gui.get_widget( "main_window" ).set_title( window_title )
# text widget related events
def check_script( self, widget, data=None ):
if not self.editing_state:
return 0
current_page = self.main_notebook.get_current_page( )
if not current_page in [ 0, 1 ]:
return 0
script = self.get_scripts( )[ current_page ]
try:
compile( script, ("Experiment Script", "Result Script")[ current_page ], "exec" )
except SyntaxError, se:
if current_page == 0:
tb = self.experiment_script_textbuffer
tv = self.experiment_script_textview
elif current_page == 1:
tb = self.data_handling_script_textbuffer
tv = self.data_handling_script_textview
ln = se.lineno
lo = se.offset
# reguard http://bugs.python.org/issue1778
if type( ln ) is not types.IntType:
ln = 0
if type( lo ) is not types.IntType:
lo = 0
print "Syntax Error:\n%s in %s at line %d, offset %d" % (
str( se ), se.filename, ln, lo) + "\n(ToDo: Dialog)"
if ln > 0 and ln <= tb.get_line_count( ):
new_place = tb.get_iter_at_line_offset( ln - 1, 0 )
if lo > 0 and lo <= new_place.get_chars_in_line( ):
new_place.set_line_offset( lo )
tb.place_cursor( new_place )
tv.scroll_to_iter( new_place, 0.2, False, 0, 0 )
except Exception, e:
print "Compilation Error:\n" + str( e ) + "\n(ToDo: Dialog)"
def notebook_page_switched( self, notebook, page, pagenumber ):
self.set_toolbuttons_status( )
def column_line_widgets_changed_event( self, data=None ):
widget_name = data.name
text_name = None
if widget_name.startswith( "data_handling" ):
text_name = "data_handling"
elif widget_name.startswith( "experiment_script" ):
text_name = "experiment_script"
else:
print "unknown line/column selector"
return
textview = self.__dict__[ text_name + "_textview" ]
textbuffer = textview.get_buffer( )
newline = self.__dict__[ text_name + "_line_indicator" ].get_value_as_int( ) - 1
newcol = self.__dict__[ text_name + "_column_indicator" ].get_value_as_int( ) - 1
#if newline>textbuffer.get_end_iter().get_line():
# return
new_place = textbuffer.get_iter_at_line( newline )
if not newcol > new_place.get_chars_in_line( ):
new_place.set_line_offset( newcol )
else:
self.__dict__[ text_name + "_column_indicator" ].set_value( 1 )
if len( textbuffer.get_selection_bounds( ) ) != 0:
textbuffer.move_mark_by_name( "insert", new_place )
else:
textbuffer.place_cursor( new_place )
textview.scroll_mark_onscreen( textbuffer.get_insert( ) )
textview.grab_focus( )
return True
def textviews_modified( self, data=None ):
# mix into toolbar affairs
self.set_toolbuttons_status( )
def textviews_clicked( self, widget, event ):
return self.textviews_moved( widget )
def textviews_moved( self, widget, text=None, count=None, ext_selection=None, data=None ):
textbuffer = widget.get_buffer( )
cursor_mark = textbuffer.get_insert( )
cursor_iter = textbuffer.get_iter_at_mark( cursor_mark )
if textbuffer is self.experiment_script_textbuffer:
line_indicator = self.experiment_script_line_indicator
column_indicator = self.experiment_script_column_indicator
if textbuffer is self.data_handling_textbuffer:
line_indicator = self.data_handling_line_indicator
column_indicator = self.data_handling_column_indicator
# do only necessary updates!
li_range_new = textbuffer.get_end_iter( ).get_line( ) + 1
if line_indicator.get_range( )[ 1 ] != li_range_new:
line_indicator.set_range( 1, li_range_new )
ci_range_new = cursor_iter.get_chars_in_line( ) + 1
if column_indicator.get_range( )[ 1 ] != ci_range_new:
column_indicator.set_range( 1, ci_range_new )
cursor_line = cursor_iter.get_line( ) + 1
cursor_lineoffset = cursor_iter.get_line_offset( ) + 1
if line_indicator.get_value( ) != cursor_line:
line_indicator.set_value( cursor_line )
if column_indicator.get_value( ) != cursor_lineoffset:
column_indicator.set_value( cursor_lineoffset )
return False
def textviews_keypress( self, widget, event, data=None ):
"""
helpful tab and return key functions
"""
#print "keypress", event.state, event.keyval
if event.state & gtk.gdk.CONTROL_MASK != 0:
if event.keyval == gtk.gdk.keyval_from_name( "c" ):
if self.main_notebook.get_current_page( ) == 0:
self.experiment_script_textbuffer.copy_clipboard( self.main_clipboard )
elif self.main_notebook.get_current_page( ) == 1:
self.data_handling_textbuffer.copy_clipboard( self.main_clipboard )
return True
elif event.keyval == gtk.gdk.keyval_from_name( "x" ):
# cut_clipboard(clipboard, textview editable?)
if self.main_notebook.get_current_page( ) == 0:
self.experiment_script_textbuffer.cut_clipboard( self.main_clipboard, True )
elif self.main_notebook.get_current_page( ) == 1:
self.data_handling_textbuffer.cut_clipboard( self.main_clipboard, True )
return True
elif event.keyval == gtk.gdk.keyval_from_name( "v" ):
# paste_clipboard(clipboard, textpos (None = Cursor), textview editable?)
if self.main_notebook.get_current_page( ) == 0:
self.experiment_script_textbuffer.paste_clipboard( self.main_clipboard, None, True )
elif self.main_notebook.get_current_page( ) == 1:
self.data_handling_textbuffer.paste_clipboard( self.main_clipboard, None, True )
return True
elif event.keyval == gtk.gdk.keyval_from_name( "s" ):
# save buffer
page = self.main_notebook.get_current_page( )
if (self.exp_script_filename, self.res_script_filename)[ page ] is None:
self.save_file_as( )
else:
self.save_file( )
return True
elif event.keyval == gtk.gdk.keyval_from_name( "S" ):
# save both buffers
print "ToDo: save both buffers"
self.save_all_files( None, None )
return True
return 0
# indent helpers
# tab keyval 0xff09
# backspace keyval 0xff08
# to do check if modified event is called after all
if (event.keyval == 0xFF09 or event.keyval == 0xFF08):
textbuffer = widget.get_buffer( )
# do not do things during selection
if (textbuffer.get_selection_bounds( )):
return 0
cursor_mark = textbuffer.get_insert( )
cursor_iter = textbuffer.get_iter_at_mark( cursor_mark )
if (cursor_iter.starts_line( )):
# backspace with normal function at line start
if (event.keyval == 0xFF08):
self.textviews_moved( widget )
return 0
# now get iterator at line start
linestart_iter = cursor_iter.copy( )
linestart_iter.set_line_offset( 0 )
linebegin = textbuffer.get_text( linestart_iter, cursor_iter ).expandtabs( )
if (len( linebegin ) != 0 and not linebegin.isspace( )):
# just make the spaces go away
textbuffer.delete( linestart_iter, cursor_iter )
textbuffer.insert( linestart_iter, linebegin )
self.textviews_moved( widget )
return 0
# find all space at the begin
while (not cursor_iter.ends_line( )
and not cursor_iter.is_end( )
and cursor_iter.get_char( ).isspace( )):
cursor_iter.forward_char( )
linebegin = textbuffer.get_text( linestart_iter, cursor_iter )
if (event.keyval == 0xFF08):
# backspace shortens space
linebegin = u' ' * ((len( linebegin ) - 1) / 4) * 4
elif (event.keyval == 0xFF09):
# tab widens space
linebegin = u' ' * ((len( linebegin ) + 4) / 4) * 4
textbuffer.delete( linestart_iter, cursor_iter )
textbuffer.insert( linestart_iter, linebegin )
self.textviews_moved( widget )
return 1
# implement convenience function for enter key
elif (event.keyval == 0xFF0D):
textbuffer = widget.get_buffer( )
# do not do things during selection
if (textbuffer.get_selection_bounds( )):
return 0
cursor_mark = textbuffer.get_insert( )
cursor_iter = textbuffer.get_iter_at_mark( cursor_mark )
# determine this line's indent count
linestart_iter = cursor_iter.copy( )
linestart_iter.set_line_offset( 0 )
spaceend_iter = linestart_iter.copy( )
while (not spaceend_iter.ends_line( )
and not spaceend_iter.is_end( )
and spaceend_iter.get_char( ).isspace( )):
spaceend_iter.forward_char( )
linebegin = textbuffer.get_text( linestart_iter, spaceend_iter ).expandtabs( )
indent_length = len( linebegin )
textbuffer.delete( linestart_iter, spaceend_iter )
textbuffer.insert( linestart_iter, u' ' * indent_length )
# start with the real work
cursor_iter = textbuffer.get_iter_at_mark( cursor_mark )
if (not cursor_iter.starts_line( )):
# find last char before cursor
lastchar_iter = cursor_iter.copy( )
lastchar_iter.backward_char( )
if (lastchar_iter.get_char( ) == u":"):
indent_length += 4
# now find indent of next line...
textbuffer.insert( cursor_iter, u'\n' + (u' ' * indent_length) )
widget.scroll_to_mark( cursor_mark, 0.0, 0 )
self.textviews_moved( widget )
return 1
#self.textviews_moved(widget)
return 0
def load_file_as_unicode( self, script_filename ):
script_file = file( script_filename, "rU" )
script_string = u""
for line in script_file:
script_string += unicode( line, encoding="iso-8859-15", errors="replace" )
script_file.close( )
return script_string
def open_file( self, widget, Data=None ):
"""
do the open file dialog, if necessary ask for save
"""
# ignore
if not self.editing_state:
return 0
# Determining the tab which is currently open
current_page = self.main_notebook.get_current_page( )
if current_page == 0:
open_dialog_title = "Open Experiment Script..."
modified = self.experiment_script_textbuffer.get_modified( )
elif current_page == 1:
open_dialog_title = "Open Result Script..."
modified = self.data_handling_textbuffer.get_modified( )
else:
return 0
if modified:
print "ToDo: Save First Dialog"
def response( self, response_id, script_widget ):
if response_id == gtk.RESPONSE_OK:
file_name = dialog.get_filename( )
if file_name is None:
return
script_filename = os.path.abspath( file_name )
if not os.access( script_filename, os.R_OK ):
outer_space.show_error_dialog( "File I/O Error", "Cannot read from file %s" % script_filename )
return True
script_string = script_widget.load_file_as_unicode( script_filename )
if script_widget.main_notebook.get_current_page( ) == 0:
script_widget.exp_script_filename = script_filename
script_widget.set_scripts( script_string, None )
elif script_widget.main_notebook.get_current_page( ) == 1:
script_widget.res_script_filename = script_filename
script_widget.set_scripts( None, script_string )
return True
parent_window = self.xml_gui.get_widget( "main_window" )
dialog = gtk.FileChooserDialog( title=open_dialog_title,
parent=parent_window,
action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_OPEN, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
)
dialog.set_default_response( gtk.RESPONSE_OK )
dialog.set_select_multiple( False )
# Event-Handler for responce-signal (when one of the button is pressed)
dialog.connect( "response", response, self )
dialog.run( )
dialog.destroy( )
# update title and so on...
return True
def save_file( self, widget=None, Data=None ):
"""
save file to associated filename
"""
# ignore
if not self.editing_state:
return 0
# Determining the tab which is currently open
current_page = self.main_notebook.get_current_page( )
if current_page == 0:
filename = self.exp_script_filename
elif current_page == 1:
filename = self.res_script_filename
else:
return 0
if filename is None:
return 0
# save file
if current_page == 0:
script = self.get_scripts( )[ 0 ]
elif current_page == 1:
script = self.get_scripts( )[ 1 ]
else:
return 0
# encode from unicode to iso-8859-15
filecontents = codecs.getencoder( "iso-8859-15" )( script, "replace" )[ 0 ]
file( filename, "w" ).write( filecontents )
if current_page == 0:
self.experiment_script_textbuffer.set_modified( False )
elif current_page == 1:
self.data_handling_textbuffer.set_modified( False )
self.set_toolbuttons_status( )
def save_file_as( self, widget=None, Data=None ):
def response( self, response_id, script_widget ):
if response_id == gtk.RESPONSE_OK:
file_name = dialog.get_filename( )
if file_name is None:
return True
absfilename = os.path.abspath( file_name )
if os.access( file_name, os.F_OK ):
print "ToDo: Overwrite file question"
current_page = script_widget.main_notebook.get_current_page( )
if current_page == 0:
script_widget.exp_script_filename = absfilename
elif current_page == 1:
script_widget.res_script_filename = absfilename
script_widget.save_file( )
return True
# Determining the tab which is currently open
current_page = self.main_notebook.get_current_page( )
if current_page == 0:
dialog_title = "Save Experiment Script As..."
elif current_page == 1:
dialog_title = "Save Result Script As..."
else:
return
parent_window = self.xml_gui.get_widget( "main_window" )
dialog = gtk.FileChooserDialog( title=dialog_title,
parent=parent_window,
action=gtk.FILE_CHOOSER_ACTION_SAVE,
buttons=(
gtk.STOCK_SAVE, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) )
dialog.set_default_response( gtk.RESPONSE_OK )
dialog.set_select_multiple( False )
# Event-Handler for responce-signal (when one of the button is pressed)
dialog.connect( "response", response, self )
dialog.run( )
dialog.destroy( )
return True
def save_all_files( self, widget, Data=None ):
current_page = self.main_notebook.get_current_page( )
# change page and call save dialog
self.main_notebook.set_current_page( 0 )
if self.exp_script_filename is None:
self.save_file_as( )
else:
self.save_file( )
self.main_notebook.set_current_page( 1 )
if self.res_script_filename is None:
self.save_file_as( )
else:
self.save_file( )
self.main_notebook.set_current_page( current_page )
def new_file( self, widget, Data=None ):
if not self.editing_state:
return 0
current_page = self.main_notebook.get_current_page( )
if current_page == 0:
if self.experiment_script_textbuffer.get_modified( ):
print "ToDo: Save before Clear Dialog"
self.set_scripts( "", None )
self.exp_script_filename = None
elif current_page == 1:
if self.data_handling_textbuffer.get_modified( ):
print "ToDo: Save before Clear Dialog"
self.set_scripts( None, "" )
self.res_script_filename = None
self.set_toolbuttons_status( )
def begin_print( self, operation, context, print_data ):
"""
layout of all pages
"""
# copied and modified from pygtk-2.10.1/examples/pygtk-demo/demos/print_editor.py
# Determining the tab which is currently open
current_page = self.main_notebook.get_current_page( )
script = ""
# get script text
if current_page == 0:
script = self.get_scripts( )[ 0 ]
elif current_page == 1:
script = self.get_scripts( )[ 1 ]
width = context.get_width( )
height = context.get_height( )
layout = context.create_pango_layout( )
layout.set_font_description( pango.FontDescription( "Monospace 12" ) )
layout.set_width( int( width * pango.SCALE ) )
layout.set_text( script )
num_lines = layout.get_line_count( )
page_breaks = [ ]
page_height = 0
for line in xrange( num_lines ):
layout_line = layout.get_line( line )
ink_rect, logical_rect = layout_line.get_extents( )
lx, ly, lwidth, lheight = logical_rect
line_height = lheight / 1024.0
if page_height + line_height > height:
page_breaks.append( line )
page_height = 0
page_height += line_height
operation.set_n_pages( len( page_breaks ) + 1 )
print_data[ "page_breaks" ] = page_breaks
print_data[ "layout" ] = layout
def draw_page( self, operation, context, page_nr, print_data ):
"""
render a single page
"""
# copied and modified from pygtk-2.10.1/examples/pygtk-demo/demos/print_editor.py
assert isinstance( print_data[ "page_breaks" ], list )
if page_nr == 0:
start = 0
else:
start = print_data[ "page_breaks" ][ page_nr - 1 ]
try:
end = print_data[ "page_breaks" ][ page_nr ]
except IndexError:
end = print_data[ "layout" ].get_line_count( )
cr = context.get_cairo_context( )
cr.set_source_rgb( 0, 0, 0 )
i = 0
start_pos = 0
iter = print_data[ "layout" ].get_iter( )
while 1:
if i >= start:
line = iter.get_line( )
_, logical_rect = iter.get_line_extents( )
lx, ly, lwidth, lheight = logical_rect
baseline = iter.get_baseline( )
if i == start:
start_pos = ly / 1024.0;
cr.move_to( lx / 1024.0, baseline / 1024.0 - start_pos )
cr.show_layout_line( line )
i += 1
if not (i < end and iter.next_line( )):
break
class ConfigTab:
"""
by now all values are saved in the GUI widgets
"""
def __init__( self, xml_gui ):
self.xml_gui = xml_gui
self.configname = "damaris/python-damaris.xml"
self.system_default_filename = None
self.system_backend_folder = "/usr/lib/damaris/backends/"
if sys.platform[ :5 ] == "linux" or "darwin":
xdg_dirs = xdg.BaseDirectory.xdg_config_dirs
xdg_dirs.remove( xdg.BaseDirectory.xdg_config_home )
self.user_default_filename = os.path.join( xdg.BaseDirectory.xdg_config_home, self.configname )
self.system_default_filenames = [ os.path.join( syscfg, self.configname ) for syscfg in xdg_dirs ]
self.config_start_backend_checkbutton = self.xml_gui.get_widget( "start_backend_checkbutton" )
self.config_backend_executable_entry = self.xml_gui.get_widget( "backend_executable_entry" )
self.config_spool_dir_entry = self.xml_gui.get_widget( "spool_dir_entry" )
self.config_start_experiment_script_checkbutton = self.xml_gui.get_widget(
"start_experiment_script_checkbutton" )
self.config_start_result_script_checkbutton = self.xml_gui.get_widget( "start_result_script_checkbutton" )
self.config_del_results_after_processing_checkbutton = self.xml_gui.get_widget(
"del_results_after_processing_checkbutton" )
self.config_del_jobs_after_execution_checkbutton = self.xml_gui.get_widget(
"del_jobs_after_execution_checkbutton" )
self.config_data_pool_name_entry = self.xml_gui.get_widget( "data_pool_name_entry" )
self.config_data_pool_write_interval_entry = self.xml_gui.get_widget( "data_pool_write_interval_entry" )
self.config_data_pool_complib = self.xml_gui.get_widget( "CompLibs" )
self.config_data_pool_comprate = self.xml_gui.get_widget( "CompRatio" )
self.config_info_textview = self.xml_gui.get_widget( "info_textview" )
self.config_script_font_button = self.xml_gui.get_widget( "script_fontbutton" )
self.config_printer_setup_button = self.xml_gui.get_widget( "printer_setup_button" )
if not hasattr( gtk, "print_run_page_setup_dialog" ):
self.config_printer_setup_button.set_sensitive( False )
# insert version informations
components_text = u"""
operating system %(os)s
gtk version %(gtk)s
glib version %(glib)s
python version %(python)s
matplotlib version %(matplotlib)s, %(matplotlib_backend)s
numpy version %(numpy)s
pytables version %(pytables)s, using %(pytables_libs)s
pygtk version %(pygtk)s
pygobject version %(pygobject)s
"""
if hasattr( gobject, "glib_version" ):
glib_version = "%d.%d.%d" % gobject.glib_version
else:
glib_version = "? (no pygobject module)"
if hasattr( gobject, "pygobject_version" ):
pygobject_version = "%d.%d.%d" % gobject.pygobject_version
else:
pygobject_version = "? (no gobject version number)"
numpy_version = "none"
try:
import numpy
except ImportError:
pass
else:
numpy_version = numpy.__version__
components_versions = {
"os": platform.platform( ),
"gtk": "%d.%d.%d" % gtk.gtk_version,
"glib": glib_version,
"python": sys.version,
"matplotlib": matplotlib.__version__,
"matplotlib_backend": FigureCanvas.__name__[ 12: ],
"numpy": numpy_version,
"pytables": tables.getPyTablesVersion( ),
"pytables_libs": "",
"pygtk": "%d.%d.%d" % gtk.pygtk_version,
"pygobject": pygobject_version
}
# pytables modules:
# find compression extensions for combo box and write version numbers
# list is taken from ValueError output of tables.whichLibVersion("")
model = self.config_data_pool_complib.get_model( )
for libname in ('hdf5', 'zlib', 'lzo', 'ucl', 'bzip2'):
version_info = None
try:
version_info = tables.whichLibVersion( libname )
except ValueError:
continue
if version_info:
components_versions[ "pytables_libs" ] += "\n %s: %s" % (libname, str( version_info ))
if libname != "hdf5":
# a compression library, add it to combo box
if isinstance( model, gtk.ListStore ):
model.append( [ libname ] )
elif isinstance( model, gtk.TreeStore ):
model.append( None, [ libname ] )
else:
print "cannot append compression lib name to %s" % model.__class__.__name__
# debug message
if debug:
print "DAMARIS", __version__
print components_text % components_versions
# set no compression as default...
self.config_data_pool_complib.set_active( 0 )
info_textbuffer = self.config_info_textview.get_buffer( )
info_text = info_textbuffer.get_text( info_textbuffer.get_start_iter( ), info_textbuffer.get_end_iter( ) )
info_text %= { "moduleversions": components_text % components_versions, "damarisversion": __version__ }
info_textbuffer.set_text( info_text )
del info_textbuffer, info_text, components_text, components_versions
self.xml_gui.signal_connect( "on_config_save_button_clicked", self.save_config_handler )
self.xml_gui.signal_connect( "on_config_load_button_clicked", self.load_config_handler )
self.xml_gui.signal_connect( "on_backend_executable_browse_button_clicked",
self.browse_backend_executable_dialog )
self.xml_gui.signal_connect( "on_fontbutton_font_set", self.set_script_font_handler )
self.xml_gui.signal_connect( "on_printer_setup_button_clicked", self.printer_setup_handler )
for self.system_default_filename in self.system_default_filenames:
if self.system_default_filename:
if os.access( self.system_default_filename, os.R_OK ):
self.load_config( self.system_default_filename )
else:
print "can not read system defaults from %s, ask your instrument responsible if required" % self.system_default_filename
self.config_from_system = self.get( )
self.load_config( )
def get( self ):
"""
returns a dictionary of actual config values
"""
complib_iter = self.config_data_pool_complib.get_active_iter( )
complib = self.config_data_pool_complib.get_model( ).get_value( complib_iter, 0 )
return {
"start_backend": self.config_start_backend_checkbutton.get_active( ),
"start_result_script": self.config_start_result_script_checkbutton.get_active( ),
"start_experiment_script": self.config_start_experiment_script_checkbutton.get_active( ),
"spool_dir": self.config_spool_dir_entry.get_text( ),
"backend_executable": self.config_backend_executable_entry.get_text( ),
"data_pool_name": self.config_data_pool_name_entry.get_text( ),
"del_results_after_processing": self.config_del_results_after_processing_checkbutton.get_active( ),
"del_jobs_after_execution": self.config_del_jobs_after_execution_checkbutton.get_active( ),
"data_pool_write_interval": self.config_data_pool_write_interval_entry.get_text( ),
"data_pool_complib": complib,
"data_pool_comprate": self.config_data_pool_comprate.get_value_as_int( ),
"script_font": self.config_script_font_button.get_font_name( )
}
def set( self, config ):
if "start_backend" in config:
self.config_start_backend_checkbutton.set_active( config[ "start_backend" ] )
if "start_experiment_script" in config:
self.config_start_experiment_script_checkbutton.set_active( config[ "start_experiment_script" ] )
if "start_result_script" in config:
self.config_start_result_script_checkbutton.set_active( config[ "start_result_script" ] )
if "spool_dir" in config:
self.config_spool_dir_entry.set_text( config[ "spool_dir" ] )
if "backend_executable" in config:
self.config_backend_executable_entry.set_text( config[ "backend_executable" ] )
if "del_results_after_processing" in config:
self.config_del_results_after_processing_checkbutton.set_active( config[ "del_results_after_processing" ] )
if "del_jobs_after_execution" in config:
self.config_del_jobs_after_execution_checkbutton.set_active( config[ "del_jobs_after_execution" ] )
if "data_pool_write_interval" in config:
self.config_data_pool_write_interval_entry.set_text( config[ "data_pool_write_interval" ] )
if "data_pool_name" in config:
self.config_data_pool_name_entry.set_text( config[ "data_pool_name" ] )
if "data_pool_comprate" in config:
self.config_data_pool_comprate.set_value( float( config[ "data_pool_comprate" ] ) )
if "script_font" in config:
self.config_script_font_button.set_font_name( config[ "script_font" ] )
self.set_script_font_handler( None )
if "data_pool_complib" in config:
# find combo-box entry and make it active...
model = self.config_data_pool_complib.get_model( )
iter = model.get_iter_first( )
while iter is not None:
if model.get( iter, 0 )[ 0 ] == config[ "data_pool_complib" ]:
self.config_data_pool_complib.set_active_iter( iter )
break
iter = model.iter_next( iter )
# if this compression method is not supported, warn and do nothing
if iter is None:
print "compression method %s is not supported" % config[ "data_pool_complib" ]
def load_config_handler( self, widget ):
if self.system_default_filename:
self.load_config( self.system_default_filename )
self.load_config( )
def save_config_handler( self, widget ):
self.save_config( )
def set_script_font_handler( self, widget ):
"""
handles changes in font name
also sets the fonts to the text views (fast implementation: breaking encapsulation)
"""
font = self.config_script_font_button.get_font_name( )
experiment_script_textview = self.xml_gui.get_widget( "experiment_script_textview" )
if experiment_script_textview:
experiment_script_textview.modify_font( pango.FontDescription( font ) )
data_handling_textview = self.xml_gui.get_widget( "data_handling_textview" )
if data_handling_textview:
data_handling_textview.modify_font( pango.FontDescription( font ) )
def printer_setup_handler( self, widget ):
"""
changes to printer setup
"""
if not (hasattr( gtk, "PrintSettings" ) and hasattr( gtk, "print_run_page_setup_dialog" )):
return
if not hasattr( self, "printer_setup" ):
self.printer_setup = gtk.PrintSettings( )
if not hasattr( self, "page_setup" ):
self.page_setup = None
self.page_setup = gtk.print_run_page_setup_dialog( self.xml_gui.get_widget( "main_window" ),
self.page_setup, self.printer_setup )
def browse_backend_executable_dialog( self, widget ):
"""
do the open file dialog
"""
backend_filename_dialog_title = "find backend"
def response( self, response_id, script_widget ):
if response_id == gtk.RESPONSE_OK:
file_name = self.get_filename( )
if file_name is None:
return
script_widget.config_backend_executable_entry.set_text( file_name )
return True
parent_window = self.xml_gui.get_widget( "main_window" )
dialog = gtk.FileChooserDialog( title=backend_filename_dialog_title,
parent=parent_window,
action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_OPEN,
gtk.RESPONSE_OK,
gtk.STOCK_CANCEL,
gtk.RESPONSE_CANCEL) )
dialog.set_default_response( gtk.RESPONSE_OK )
dialog.set_select_multiple( False )
dialog.set_filename( os.path.abspath( self.config_backend_executable_entry.get_text( ) ) )
if os.path.isdir( self.system_backend_folder ) \
and os.access( self.system_backend_folder, os.R_OK ):
dialog.add_shortcut_folder( self.system_backend_folder )
# Event-Handler for responce-signal (when one of the button is pressed)
dialog.connect( "response", response, self )
f = gtk.FileFilter( )
f.add_custom( gtk.FILE_FILTER_FILENAME, lambda x: os.access( x[ 0 ], os.X_OK ) )
dialog.set_filter( f )
dialog.run( )
dialog.destroy( )
return True
def load_config( self, filename=None ):
"""
set config from an xml file
"""
if filename is None:
filename = self.user_default_filename
try:
readfile = file( filename, "r" )
except Exception, e:
if debug:
print "Could not open %s: %s" % (filename, str( e ))
return
# parser functions
def start_element( name, attrs, config ):
if name == "config" and "key" in attrs:
config[ "__this_key__" ] = attrs[ "key" ]
if "type" in attrs:
config[ "__this_type__" ] = attrs[ "type" ]
config[ attrs[ "key" ] ] = ""
def end_element( name, config ):
if "__this_type__" in config and "__this_key__" in config:
if config[ "__this_type__" ] == "Boolean":
if config[ config[ "__this_key__" ] ] == "True":
config[ config[ "__this_key__" ] ] = True
else:
config[ config[ "__this_key__" ] ] = False
elif config[ "__this_type__" ] == "Integer":
config[ config[ "__this_key__" ] ] = int( config[ config[ "__this_key__" ] ] )
if "__this_type__" in config:
del config[ "__this_type__" ]
if "__this_key__" in config:
del config[ "__this_key__" ]
def char_data( data, config ):
if "__this_key__" in config:
config[ config[ "__this_key__" ] ] += data
# parse file contents to dictionary
config = { }
p = xml.parsers.expat.ParserCreate( )
p.StartElementHandler = lambda n, a: start_element( n, a, config )
p.EndElementHandler = lambda n: end_element( n, config )
p.CharacterDataHandler = lambda d: char_data( d, config )
p.ParseFile( readfile )
self.set( config )
def save_config( self, filename=None ):
"""
write config as an xml file
"""
config = self.get( )
if filename is None:
filename = self.user_default_filename
dirs = os.path.dirname( filename )
if not os.path.isdir( dirs ):
os.makedirs( dirs )
configfile = file( filename, "w" )
configfile.write( "<?xml version='1.0'?>\n" )
configfile.write( "<damaris>\n" )
for k, v in config.iteritems( ):
if k in self.config_from_system \
and self.config_from_system[ k ] == v:
if debug:
print "Ignoring for write, because system value for %r is %r equal to %r" % \
(k, self.config_from_system[ k ], v)
continue
val = ""
typename = ""
if type( v ) is types.BooleanType:
typename = "Boolean"
if v:
val = "True"
else:
val = "False"
elif type( v ) is types.StringType:
typename = "String"
val = v
elif type( v ) is types.IntType:
typename = "Integer"
val = str( v )
configfile.write( " <config key='%s' type='%s'>%s</config>\n" % (k, typename, val) )
configfile.write( "</damaris>\n" )
class MonitorWidgets:
def __init__( self, xml_gui ):
"""
initialize matplotlib widgets and stuff around
"""
self.xml_gui = xml_gui
self.main_window = self.xml_gui.get_widget( "main_window" )
self.display_settings_frame = self.xml_gui.get_widget( "display_settings_frame" )
# Display footer:
self.display_x_scaling_combobox = self.xml_gui.get_widget( "display_x_scaling_combobox" )
self.display_x_scaling_combobox.remove_text( 1 ) # remove base-e log
self.display_x_scaling_combobox.set_sensitive( False )
self.display_y_scaling_combobox = self.xml_gui.get_widget( "display_y_scaling_combobox" )
self.display_y_scaling_combobox.remove_text( 1 ) # remove base-e log
self.display_y_scaling_combobox.set_sensitive( False )
self.display_autoscaling_checkbutton = self.xml_gui.get_widget( "display_autoscaling_checkbutton" )
self.display_statistics_checkbutton = self.xml_gui.get_widget( "display_statistics_checkbutton" )
# insert monitor
# Matplot (Display_Table, 1st Row) --------------------------------------------------------
# Neue Abbildung erstellen
self.matplot_figure = matplotlib.figure.Figure( )
# the plot area surrounded by axes
self.matplot_axes = self.matplot_figure.add_axes( [ 0.1, 0.15, 0.8, 0.7 ] )
# Achsen beschriften & Gitternetzlinien sichtbar machen
self.matplot_axes.grid( True )
# Ersten Plot erstellen und Referenz des ersten Eintrags im zurueckgegebenen Tupel speichern
# Voerst: graphen[0,1] = Real und Img-Kanal; [2,3] = Real-Fehler, [4,5] = Img-Fehler
#self.graphen = []
#self.measurementresultgraph=None
self.matplot_axes.set_ylim( [ 0.0, 1.0 ] )
self.matplot_axes.set_xlim( [ 0.0, 1.0 ] )
self.matplot_axes.set_autoscale_on( self.display_autoscaling_checkbutton.get_active( ) )
# Lineare y-/x-Skalierung
self.matplot_axes.set_yscale( "linear" )
self.matplot_axes.set_xscale( "linear" )
# Matplot in einen GTK-Rahmen stopfen
self.matplot_canvas = FigureCanvas( self.matplot_figure )
self.display_table = self.xml_gui.get_widget( "display_table" )
self.display_table.attach( self.matplot_canvas, 0, 6, 0, 1, gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL, 0, 0 )
self.matplot_canvas.show( )
# Matplot Toolbar hinzufuegen (Display_Table, 2. Zeile)
self.matplot_toolbar = matplotlib.backends.backend_gtk.NavigationToolbar2GTK( self.matplot_canvas,
self.main_window )
self.display_table.attach( self.matplot_toolbar, 0, 1, 1, 2, gtk.FILL | gtk.EXPAND, 0, 0, 0 )
self.matplot_toolbar.show( )
# /Mathplot --------------------------------------------------------------------------------
# display source
self.display_source_combobox = self.xml_gui.get_widget( "display_source_combobox" )
self.display_source_treestore = gtk.TreeStore( gobject.TYPE_STRING )
self.display_source_combobox.set_model( self.display_source_treestore )
display_source_cell = gtk.CellRendererText( )
self.display_source_combobox.pack_start( display_source_cell, True )
self.display_source_combobox.add_attribute( display_source_cell, 'text', 0 )
self.source_list_reset( )
self.display_source_path_label = self.xml_gui.get_widget( "display_source_path_label" )
# display scaling: ToDo enable scaling
self.display_x_scaling_combobox.set_active( 0 )
self.display_y_scaling_combobox.set_active( 0 )
self.display_x_scaling_combobox.set_sensitive( True )
self.display_y_scaling_combobox.set_sensitive( True )
# and events...
self.display_source_combobox.connect( "changed", self.display_source_changed_event )
self.xml_gui.signal_connect( "on_display_autoscaling_checkbutton_toggled", self.display_autoscaling_toggled )
self.xml_gui.signal_connect( "on_display_statistics_checkbutton_toggled", self.display_statistics_toggled )
self.xml_gui.signal_connect( "on_display_x_scaling_combobox_changed", self.display_scaling_changed )
self.xml_gui.signal_connect( "on_display_y_scaling_combobox_changed", self.display_scaling_changed )
self.xml_gui.signal_connect( "on_display_save_data_as_text_button_clicked", self.save_display_data_as_text )
self.xml_gui.signal_connect( "on_display_copy_data_to_clipboard_button_clicked",
self.copy_display_data_to_clipboard )
# data to observe
self.data_pool = None
# name of displayed data and reference to data
self.displayed_data = [ None, None ]
self.__rescale = True
self.update_counter = 0
self.update_counter_lock = threading.Lock( )
def source_list_reset( self ):
self.display_source_treestore.clear( )
self.source_list_add( u'None' )
none_iter = self.source_list_find( [ u'None' ] )
if none_iter is not None:
self.display_source_combobox.set_active_iter( none_iter )
def source_list_find_one( self, model, iter, what ):
"""find node in subcategory"""
while iter is not None:
if model.get( iter, 0 )[ 0 ] == what:
return iter
iter = model.iter_next( iter )
return iter
def source_list_find( self, namelist ):
"""
namelist sequence of names, e.g. ["a", "b", "c" ] for "a/b/c"
"""
model = self.display_source_treestore
retval = None
iter = model.get_iter_root( )
while iter is not None and len( namelist ) > 0:
name = namelist[ 0 ]
iter = self.source_list_find_one( model, iter, name )
if iter is not None:
retval = iter
namelist.pop( 0 )
iter = model.iter_children( retval )
return retval
def source_list_add( self, source_name, parent=None ):
namelist = source_name.split( "/" )
found = self.source_list_find( namelist )
if parent is None:
parent = found
for rest_name in namelist:
# append() returns iter for the new row
parent = self.display_source_treestore.append( parent, [ rest_name ] )
def source_list_remove( self, source_name ):
namelist = source_name.split( "/" )
pwd = namelist[ : ]
iter = self.source_list_find( namelist )
if iter is None or len( namelist ) > 0:
print "source_list_remove: WARNING: Not found"
return
model = self.display_source_treestore
if model.iter_has_child( iter ):
print "source_list_remove: WARNING: Request to delete a tree"
return
while True:
parent = model.iter_parent( iter )
model.remove( iter )
pwd.pop( )
# We now test, if we want to remove parent too
if parent is None:
break
if model.iter_has_child( parent ):
# The parent has other children
break
if "/".join( pwd ) in self.data_pool:
# The parent has data connected to it
break
iter = parent
def source_list_current( self ):
ai = self.display_source_combobox.get_active_iter( )
namelist = [ ]
while ai is not None:
namelist.insert( 0, str( self.display_source_treestore.get( ai, 0 )[ 0 ] ) )
ai = self.display_source_treestore.iter_parent( ai )
cur_source_name = "/".join( namelist )
return cur_source_name
def observe_data_pool( self, data_pool ):
"""
register a listener and save reference to data
assume to be in gtk/gdk lock
"""
if not self.data_pool is None:
# maybe some extra cleanup needed
print "ToDo: cleanup widgets"
if self.displayed_data[ 1 ] is not None and hasattr( self.displayed_data[ 1 ], "unregister_listener" ):
self.displayed_data[ 1 ].unregister_listener( self.datastructures_listener )
self.displayed_data[ 1 ] = None
self.displayed_data = [ None, None ]
self.data_pool.unregister_listener( self.datapool_listener )
self.data_pool = None
self.source_list_reset( )
self.update_counter_lock.acquire( )
self.update_counter = 0
self.update_counter_lock.release( )
# display states
self.__rescale = True
self.displayed_data = [ None, None ]
self.display_source_path_label.set_label( u"" )
self.clear_display( )
if data_pool is not None:
# keep track of data
self.data_pool = data_pool
self.data_pool.register_listener( self.datapool_listener )
#################### observing data structures and produce idle events
def datapool_listener( self, event ):
"""
sort data as fast as possible and get rid of non-interesting data
"""
if event.subject.startswith( "__" ):
return
if debug and self.update_counter < 0:
print "negative event count!", self.update_counter
if self.update_counter > 5:
if debug:
print "sleeping to find time for grapics updates"
threading.Event( ).wait( 0.05 )
while self.update_counter > 15:
threading.Event( ).wait( 0.05 )
if event.what == DataPool.Event.updated_value:
if self.displayed_data[ 0 ] is None or event.subject != self.displayed_data[ 0 ]:
# do nothing, forget it
return
displayed_object = self.displayed_data[ 1 ]
object_to_display = self.data_pool.get( event.subject )
if displayed_object is None or object_to_display is None:
self.update_counter_lock.acquire( )
self.update_counter += 1
self.update_counter_lock.release( )
gobject.idle_add( self.datapool_idle_listener, event, priority=gobject.PRIORITY_DEFAULT_IDLE )
else:
if event.what == DataPool.Event.updated_value and \
(
displayed_object is object_to_display or displayed_object.__class__ is object_to_display.__class__):
# oh, another category
self.update_counter += 1
gobject.idle_add( self.update_display_idle_event, self.displayed_data[ 0 ][ : ],
priority=gobject.PRIORITY_DEFAULT_IDLE )
else:
self.update_counter_lock.acquire( )
self.update_counter += 1
self.update_counter_lock.release( )
gobject.idle_add( self.datapool_idle_listener, event, priority=gobject.PRIORITY_DEFAULT_IDLE )
if event.what in [ DataPool.Event.updated_value, DataPool.Event.new_key ]:
#print "Update hdf5 file ..."
# TODO: incremental hdf5 update
pass
del displayed_object
del object_to_display
def datastructures_listener( self, event ):
"""
do fast work selecting important events
"""
if debug and self.update_counter < 0:
print "negative event count!", self.update_counter
if self.update_counter > 5:
if debug:
print "sleeping to find time for graphics updates"
threading.Event( ).wait( 0.05 )
while self.update_counter > 15:
threading.Event( ).wait( 0.05 )
if event.origin is not self.displayed_data[ 1 ]:
return
self.update_counter_lock.acquire( )
self.update_counter += 1
self.update_counter_lock.release( )
gobject.idle_add( self.update_display_idle_event, self.displayed_data[ 0 ][ : ],
priority=gobject.PRIORITY_DEFAULT_IDLE )
################### consume idle events
def datapool_idle_listener( self, event ):
"""
here dictionary changes are done
"""
self.update_counter_lock.acquire( )
self.update_counter -= 1
self.update_counter_lock.release( )
if event.what == DataPool.Event.updated_value:
if (self.displayed_data[ 0 ] is not None and
self.displayed_data[ 0 ] == event.subject):
new_data_struct = self.data_pool[ self.displayed_data[ 0 ] ]
if self.displayed_data[ 1 ] is new_data_struct:
# update display only
if self.update_counter > 10:
print "update queue too long (%d>10): skipping one update" % self.update_counter
else:
gtk.gdk.threads_enter( )
try:
self.update_display( )
finally:
gtk.gdk.threads_leave( )
else:
# unregister old one
if self.displayed_data[ 1 ] is not None and hasattr( self.displayed_data[ 1 ],
"unregister_listener" ):
self.displayed_data[ 1 ].unregister_listener( self.datastructures_listener )
self.displayed_data[ 1 ] = None
# register new one
if hasattr( new_data_struct, "register_listener" ):
new_data_struct.register_listener( self.datastructures_listener )
self.displayed_data[ 1 ] = new_data_struct
if self.update_counter > 10:
print "update queue too long (%d>10): skipping one update" % self.update_counter
else:
gtk.gdk.threads_enter( )
try:
self.renew_display( )
finally:
gtk.gdk.threads_leave( )
new_data_struct = None
elif event.what == DataPool.Event.new_key:
# update combo-box by inserting and rely on consistent information
gtk.gdk.threads_enter( )
self.source_list_add( event.subject )
gtk.gdk.threads_leave( )
elif event.what == DataPool.Event.deleted_key:
# update combo-box by removing and rely on consistent information
gtk.gdk.threads_enter( )
if (not self.displayed_data[ 0 ] is None and
self.displayed_data[ 0 ] == event.subject):
self.displayed_data = [ None, None ]
none_iter = self.source_list_find( [ u'None' ] )
if none_iter is not None:
self.display_source_combobox.set_active_iter( none_iter )
# not necessary, because event will be submitted
#self.clear_display()
self.source_list_remove( event.subject )
gtk.gdk.threads_leave( )
elif event.what == DataPool.Event.destroy:
gtk.gdk.threads_enter( )
self.source_list_reset( 'None' )
self.displayed_data = [ None, None ]
self.clear_display( )
gtk.gdk.threads_leave( )
return
def update_display_idle_event( self, subject=None ):
self.update_counter_lock.acquire( )
self.update_counter -= 1
self.update_counter_lock.release( )
# print "update display", self.update_counter
if self.update_counter > 10:
print "update queue too long (%d>10): skipping one update" % self.update_counter
return
if self.displayed_data[ 0 ] is None or subject != self.displayed_data[ 0 ]:
return
gtk.gdk.threads_enter( )
try:
self.update_display( )
finally:
gtk.gdk.threads_leave( )
######################## events from buttons
def display_source_changed_event( self, widget, data=None ):
new_data_name = self.source_list_current( )
if (self.displayed_data[ 0 ] is None and new_data_name == u"None"):
return
if (self.displayed_data[ 0 ] == new_data_name):
return
if self.displayed_data[ 1 ] is not None and hasattr( self.displayed_data[ 1 ], "unregister_listener" ):
self.displayed_data[ 1 ].unregister_listener( self.datastructures_listener )
self.displayed_data[ 1 ] = None
# register new one
if new_data_name == u"None":
self.display_source_path_label.set_label( u"" )
self.displayed_data = [ None, None ]
self.clear_display( )
elif self.data_pool is None or new_data_name not in self.data_pool:
none_iter = self.source_list_find( [ u'None' ] )
if none_iter is not None:
self.display_source_combobox.set_active_iter( none_iter )
self.display_source_path_label.set_label( u"" )
else:
new_data_struct = self.data_pool[ new_data_name ]
if hasattr( new_data_struct, "register_listener" ):
new_data_struct.register_listener( self.datastructures_listener )
self.displayed_data = [ new_data_name, new_data_struct ]
dirpart = new_data_name.rfind( "/" )
if dirpart >= 0:
self.display_source_path_label.set_label( u"in " + new_data_name[ :dirpart ] )
else:
self.display_source_path_label.set_label( u"" )
self.clear_display( )
# renew display via idle event
self.update_counter_lock.acquire( )
self.update_counter += 1
self.update_counter_lock.release( )
event = DataPool.Event( DataPool.Event.updated_value, new_data_name )
gobject.idle_add( self.datapool_idle_listener, event, priority=gobject.PRIORITY_DEFAULT_IDLE )
def display_autoscaling_toggled( self, widget, data=None ):
self.matplot_axes.set_autoscale_on( self.display_autoscaling_checkbutton.get_active( ) )
if self.displayed_data[ 0 ] is not None:
self.update_display( self.displayed_data[ 0 ][ : ] )
def display_scaling_changed( self, widget, data=None ):
self.__rescale = True
if self.displayed_data[ 0 ] is not None:
self.update_display( self.displayed_data[ 0 ][ : ] )
def display_statistics_toggled( self, widget, data=None ):
if self.displayed_data[ 0 ] is not None:
self.update_display( self.displayed_data[ 0 ][ : ] )
def save_display_data_as_text( self, widget, data=None ):
"""
copy data to tmp file and show save dialog
"""
data_to_save = self.displayed_data[ : ]
if self.displayed_data[ 1 ] is None:
# nothing to save
return
if not hasattr( data_to_save[ 1 ], "write_to_csv" ):
log( "do not know how to save %s of class/type %s" % (data_to_save[ 0 ], type( data_to_save[ 1 ] )) )
return
# save them to a temporary file (in memory)
tmpdata = os.tmpfile( )
tmpdata.write( "# saved from monitor as %s\n" % data_to_save[ 0 ] )
data_to_save[ 1 ].write_to_csv( tmpdata )
# show save dialog
def response( self, response_id, tmpfile ):
if response_id == gtk.RESPONSE_OK:
file_name = dialog.get_filename( )
if file_name is None:
return True
absfilename = os.path.abspath( file_name )
if os.access( file_name, os.F_OK ):
log( "ToDo: Overwrite file question" )
textfile = file( absfilename, "w" )
tmpfile.seek( 0 )
for l in tmpfile:
textfile.write( l )
textfile.close( )
textfile = None
tmpfile = None
return True
# Determining the tab which is currently open
dialog_title = "Save %s in file" % data_to_save[ 0 ]
dialog = gtk.FileChooserDialog( title=dialog_title,
parent=self.main_window,
action=gtk.FILE_CHOOSER_ACTION_SAVE,
buttons=(
gtk.STOCK_SAVE, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) )
dialog.set_default_response( gtk.RESPONSE_OK )
dialog.set_current_name( data_to_save[ 0 ] )
dialog.set_select_multiple( False )
# Event-Handler for responce-signal (when one of the button is pressed)
dialog.connect( "response", response, tmpdata )
del tmpdata, data_to_save
dialog.run( )
dialog.destroy( )
return True
def copy_display_data_to_clipboard( self, widget, data=None ):
data_to_save = self.displayed_data[ : ]
if self.displayed_data[ 1 ] is None:
# nothing to save
return
if not hasattr( data_to_save[ 1 ], "write_to_csv" ):
log( "do not know how to save %s of class/type %s" % (data_to_save[ 0 ], type( data_to_save[ 1 ] )) )
return
# tested with qtiplot, labplot, openoffice
# tab delimiters necessary
# comments are not welcome :-(
tmpdata = cStringIO.StringIO( )
data_to_save[ 1 ].write_to_csv( tmpdata, delimiter=u"\t" )
# cut away comments
tmpstring = u""
tmpdata.seek( 0 )
for line in tmpdata:
if line[ 0 ] == "#":
continue
tmpstring += line
del tmpdata
clipboard = gtk.clipboard_get( )
clipboard.set_text( tmpstring )
del tmpstring, clipboard
##################### functions to feed display
def clear_display( self ):
"""
unconditionally throw away everything
we are inside gtk/gdk lock
"""
self.display_x_scaling_combobox.set_sensitive( False )
self.display_y_scaling_combobox.set_sensitive( False )
if not hasattr( self, "__rescale" ):
self.__rescale = True
if not hasattr( self, "measurementresultline" ):
self.measurementresultline = None
elif self.measurementresultline is not None:
# clear line plot
self.matplot_axes.lines.remove( self.measurementresultline[ 0 ] )
self.measurementresultline = None
if not hasattr( self, "measurementresultgraph" ):
self.measurementresultgraph = None
elif self.measurementresultgraph is not None:
# clear errorbars
self.matplot_axes.lines.remove( self.measurementresultgraph[ 0 ] )
for l in self.measurementresultgraph[ 1 ]:
self.matplot_axes.lines.remove( l )
for l in self.measurementresultgraph[ 2 ]:
self.matplot_axes.collections.remove( l )
self.measurementresultgraph = None
self.matplot_axes.clear( )
self.matplot_axes.grid( True )
if not hasattr( self, "graphen" ):
self.graphen = [ ]
elif self.graphen:
for l in self.graphen:
self.matplot_axes.lines.remove( l )
self.graphen = [ ]
self.matplot_axes.clear( )
self.matplot_axes.grid( True )
self.matplot_canvas.draw_idle( )
def update_display( self, subject=None ):
"""
try to recycle labels, data, lines....
assume, object is not changed
we are inside gtk/gdk lock
"""
in_result = self.data_pool.get( self.displayed_data[ 0 ] )
if in_result is None:
self.clear_display( )
return
if isinstance( in_result, Accumulation ) or isinstance( in_result, ADC_Result ):
# directly taken from bluedamaris
xmin = in_result.get_xmin( )
xmax = in_result.get_xmax( )
ymin = in_result.get_ymin( )
ymax = in_result.get_ymax( )
# Check for log-scale
if xmin <= 0 or xmax <= 0:
self.display_x_scaling_combobox.set_sensitive( False )
self.display_x_scaling_combobox.set_active( 0 )
else:
self.display_x_scaling_combobox.set_sensitive( True )
if ymin <= 0 or ymax <= 0:
self.display_y_scaling_combobox.set_sensitive( False )
self.display_y_scaling_combobox.set_active( 0 )
else:
self.display_y_scaling_combobox.set_sensitive( False )
# Initial rescaling needed?
if self.__rescale:
x_scale = self.display_x_scaling_combobox.get_active_text( )
y_scale = self.display_y_scaling_combobox.get_active_text( )
if xmin <= 0 or x_scale == "lin":
self.matplot_axes.set_xscale( "linear" )
if ymin <= 0 or y_scale == "lin":
self.matplot_axes.set_yscale( "linear" )
self.matplot_axes.set_xlim( xmin, xmax )
self.matplot_axes.set_ylim( ymin, ymax )
self.__rescale = False
# Autoscaling activated?
elif self.display_autoscaling_checkbutton.get_active( ):
xlim_min, xlim_max = self.matplot_axes.get_xlim( )
if xlim_min != xmin or xlim_max != xmax:
self.matplot_axes.set_xlim( xmin, xmax )
# Rescale if new max is much larger than old ymax, simialar rules apply to ymin
ylim_min, ylim_max = self.matplot_axes.get_ylim( )
ydiff = ymax - ymin
if (ylim_max < ymax or ylim_min > ymin or
ylim_max > ymax + 0.2 * ydiff or ylim_min < ymin - 0.2 * ydiff):
self.matplot_axes.set_ylim( ymin, ymax )
xdata = in_result.get_xdata( )
chans = in_result.get_number_of_channels( )
data = [ ]
colors = [ (0, 0, 0.8, 1), (0.7, 0, 0, 1), (0, 0.7, 0, 1), (0.7, 0.5, 0, 1),
(0, 0, 0, 1) ] # rgba tuples: blue, red, green, yellow
for i in xrange( chans ):
data.append( in_result.get_ydata( i ) )
moving_average = data_slice = None
if max_points_to_display > 0 and len( xdata ) > max_points_to_display:
print "decimating data to %d points by moving average (prevent crash of matplotlib)" % max_points_to_display
n = numpy.ceil( len( xdata ) / max_points_to_display )
moving_average = numpy.ones( n, dtype="float" ) / n
data_slice = numpy.array( numpy.floor( numpy.arange( max_points_to_display, dtype="float" ) \
/ max_points_to_display * len( xdata ) ),
dtype="int" )
xdata = xdata.take( data_slice ) # no average !?
for i in xrange( chans ):
data[ i ] = numpy.convolve( data[ i ], moving_average, "same" ).take( data_slice )
if len( self.graphen ) == 0:
for i in xrange( chans ):
self.graphen.extend(
self.matplot_axes.plot( xdata, data[ i ], linestyle="-", color=colors[ i ], linewidth=2 ) )
for i in xrange( chans ):
# initialize error bars
self.graphen.extend(
self.matplot_axes.plot( [ 0.0 ], [ 0.0 ], linestyle="-", color=colors[ i ], linewidth=0.5 ) )
self.graphen.extend(
self.matplot_axes.plot( [ 0.0 ], [ 0.0 ], linestyle="-", color=colors[ i ], linewidth=0.5 ) )
else:
for i in xrange( chans ):
self.graphen[ i ].set_data( xdata, data[ i ] )
# Statistics activated?
if (self.display_statistics_checkbutton.get_active( ) and
in_result.uses_statistics( ) and in_result.ready_for_drawing_error( )):
for i in xrange( chans ):
err = in_result.get_yerr( i )
if moving_average is not None:
err = numpy.convolve( err, moving_average, "same" ).take( data_slice )
self.graphen[ chans + 2 * i ].set_data( xdata, data[ i ] + err )
self.graphen[ chans + 2 * i + 1 ].set_data( xdata, data[ i ] - err )
else:
for i in xrange( chans ):
self.graphen[ chans + 2 * i ].set_data( [ 0.0 ], [ 0.0 ] )
self.graphen[ chans + 2 * i + 1 ].set_data( [ 0.0 ], [ 0.0 ] )
moving_average = data_mask = None
data = None
# Any title to be set?
in_result_title = in_result.get_title( )
if in_result_title is not None:
col = 101
while len( in_result_title ) - col > 10:
in_result_title = in_result_title[ :col ] + "\n" + in_result_title[ col: ]
col += 101
self.matplot_axes.set_title( in_result_title )
else:
self.matplot_axes.set_title( "" )
# Any labels to be set?
if in_result.get_xlabel( ) is not None:
self.matplot_axes.set_xlabel( in_result.get_xlabel( ) )
else:
self.matplot_axes.set_xlabel( "" )
if in_result.get_ylabel( ) is not None:
self.matplot_axes.set_ylabel( in_result.get_ylabel( ) )
else:
self.matplot_axes.set_ylabel( "" )
# Any variables to be set?
# if False:
# if isinstance(in_result, Accumulation):
# descriptions = in_result.common_descriptions
# elif isinstance(in_result, ADC_Result):
# descriptions = in_result.description
# else: pass
# actual_config = self.config.get()
# if (descriptions is not None) : #--markusro
# print actual_config['pretty_descriptions']
# pass
# description_string = ""
# for key in descriptions.keys():
# description_string += "%s = %s\n" % (key,descriptions[key])
# self.matplot_axes.text(0.7,0.95, description_string[:-1],
# size=8,
# transform=self.matplot_axes.transAxes,
# va='top',
# backgroundcolor='white')
#
# Draw it!
self.matplot_canvas.draw_idle( )
del in_result
elif isinstance( in_result, MeasurementResult ):
# remove lines and error bars
if self.measurementresultgraph is not None:
self.matplot_axes.lines.remove( self.measurementresultgraph[ 0 ] )
# remove caps
for l in self.measurementresultgraph[ 1 ]:
self.matplot_axes.lines.remove( l )
# and columns
for l in self.measurementresultgraph[ 2 ]:
self.matplot_axes.collections.remove( l )
self.measurementresultgraph = None
if self.measurementresultline is not None:
self.matplot_axes.lines.remove( self.measurementresultline[ 0 ] )
self.measurementresultline = None
[ k, v, e ] = in_result.get_errorplotdata( )
if k.shape[ 0 ] != 0:
xmin = k.min( )
ymin = (v - e).min( )
if xmin > 0:
self.display_x_scaling_combobox.set_sensitive( True )
else:
# force switch to lin scale
self.display_x_scaling_combobox.set_sensitive( False )
if self.display_x_scaling_combobox.get_active_text( ) != "lin":
self.__rescale = True
# and reset to linear
self.display_x_scaling_combobox.set_active( 0 )
if ymin > 0:
self.display_y_scaling_combobox.set_sensitive( True )
else:
# force switch to lin scale
self.display_y_scaling_combobox.set_sensitive( False )
if self.display_y_scaling_combobox.get_active_text( ) != "lin":
self.__rescale = True
# and reset to linear
self.display_y_scaling_combobox.set_active( 0 )
x_scale = self.display_x_scaling_combobox.get_active_text( )
y_scale = self.display_y_scaling_combobox.get_active_text( )
# Initial rescaling needed?
if self.__rescale: # or self.display_autoscaling_checkbutton.get_active():
xmax = k.max( )
ymax = (v + e).max( )
# is there a range problem?
if xmin == xmax or ymin == ymax:
# fix range and scaling problems
if xmin == xmax:
if xmin != 0:
(xmin, xmax) = (xmin - abs( xmin / 10.0 ), xmin + abs( xmin / 10.0 ))
else:
(xmin, xmax) = (-1, 1)
if ymin == ymax:
if ymin == 0:
(ymin, ymax) = (ymin - abs( ymin / 10.0 ), ymin + abs( ymin / 10.0 ))
else:
(ymin, ymax) = (-1, 1)
if xmin <= 0 or x_scale == "lin":
self.matplot_axes.set_xscale( "linear" )
if ymin <= 0 or y_scale == "lin":
self.matplot_axes.set_yscale( "linear" )
self.matplot_axes.set_xlim( xmin, xmax )
self.matplot_axes.set_ylim( ymin, ymax )
# finally decide about x log plot
if x_scale == "log10" and xmin > 0:
self.matplot_axes.set_xscale( "log", basex=10.0 )
self.matplot_axes.fmt_xdata = lambda x: "%g" % x
#elif x_scale=="log" and xmin>0:
# e scaling implementation not really useful
# self.matplot_axes.set_xscale("log", basex=numpy.e)
if y_scale == "log10" and ymin > 0:
self.matplot_axes.set_yscale( "log", basex=10.0 )
self.matplot_axes.fmt_ydata = lambda x: "%g" % x
self.__rescale = False
self.measurementresultgraph = self.matplot_axes.errorbar( x=k, y=v, yerr=e, fmt="bx" )
[ k, v ] = in_result.get_lineplotdata( )
if k.shape[ 0 ] != 0 and v.shape == k.shape:
self.measurementresultline = self.matplot_axes.plot( k, v, 'r-' )
# Any title to be set?
title = in_result.get_title( ) + ""
if title is not None:
self.matplot_axes.set_title( title )
else:
self.matplot_axes.set_title( "" )
self.matplot_canvas.draw_idle( )
del k, v, e
del in_result
def renew_display( self ):
"""
set all properties of display
we are inside gtk/gdk lock
"""
self.clear_display( )
to_draw = self.data_pool[ self.displayed_data[ 0 ] ]
if to_draw is None:
return
self.update_display( )
def begin_print( self, operation, context, print_data ):
"""
layout of one page with matplotlib graph
"""
operation.set_n_pages( 1 )
def draw_page( self, operation, context, page_nr, print_data ):
"""
render a single page
"""
# copied and modified from pygtk-2.10.1/examples/pygtk-demo/demos/print_editor.py
if page_nr != 0:
return
# check page dimensions
# all lengths in inch: name *_in
page_setup = context.get_page_setup( )
dpi = context.get_dpi_x( )
if dpi != context.get_dpi_y( ):
print "draw_page: dpi_x!=dpi_y, I am not prepared for that"
freewidth_in = float( context.get_width( ) ) / dpi
freeheight_in = float( context.get_height( ) ) / dpi
fc = self.matplot_canvas.switch_backends( matplotlib.backends.backend_cairo.FigureCanvasCairo )
fc.figure.dpi.set( dpi )
orig_w_in, orig_h_in = fc.figure.get_size_inches( )
orig_f_color = fc.figure.get_facecolor( )
orig_e_color = fc.figure.get_edgecolor( )
# scale to maximum
fc.figure.set_facecolor( "w" )
fc.figure.set_edgecolor( "w" )
# maximum scale with constant aspect
scale = min( freewidth_in / orig_w_in, freeheight_in / orig_h_in )
fc.figure.set_size_inches( orig_w_in * scale, orig_h_in * scale )
width_in_points, height_in_points = orig_w_in * dpi * scale, orig_h_in * dpi * scale
renderer = matplotlib.backends.backend_cairo.RendererCairo( fc.figure.dpi )
renderer.width = width_in_points
renderer.height = height_in_points
# centered picture
renderer.matrix_flipy = cairo.Matrix( yy=-1, xx=1,
y0=page_setup.get_top_margin( gtk.UNIT_POINTS ) + (
height_in_points + freeheight_in * dpi) / 2.0,
x0=page_setup.get_left_margin( gtk.UNIT_POINTS ) + (
freewidth_in * dpi - width_in_points) / 2.0 )
renderer.set_ctx_from_surface( context.get_cairo_context( ).get_target( ) )
# unfortunateley there is need for extra treatment of text
renderer.ctx.translate(
page_setup.get_left_margin( gtk.UNIT_POINTS ) + (freewidth_in * dpi - width_in_points) / 2.0,
page_setup.get_top_margin( gtk.UNIT_POINTS ) - height_in_points / 2.0 + freeheight_in * dpi / 2.0 )
renderer.ctx.save( ) # important! there will be no effect of previous statement without save
fc.figure.draw( renderer )
# restore the figure's settings
fc.figure.set_size_inches( orig_w_in, orig_h_in )
fc.figure.set_facecolor( orig_f_color )
fc.figure.set_edgecolor( orig_e_color )
class ScriptInterface:
"""
texts or code objects are executed as experiment and result script the backend is started with sufficient arguments
"""
def __init__( self, exp_script=None, res_script=None, backend_executable=None, spool_dir="spool", clear_jobs=True,
clear_results=True ):
"""
run experiment scripts and result scripts
"""
self.exp_script = exp_script
self.res_script = res_script
self.backend_executable = str( backend_executable )
self.spool_dir = os.path.abspath( spool_dir )
self.clear_jobs = clear_jobs
self.clear_results = clear_results
self.exp_handling = self.res_handling = None
self.exp_writer = self.res_reader = self.back_driver = None
if self.backend_executable is not None and self.backend_executable != "":
self.back_driver = BackendDriver.BackendDriver( self.backend_executable, spool_dir, clear_jobs,
clear_results )
if self.exp_script:
self.exp_writer = self.back_driver.get_exp_writer( )
if self.res_script:
self.res_reader = self.back_driver.get_res_reader( )
elif self.exp_script and self.res_script:
self.back_driver = None
self.res_reader = ResultReader.BlockingResultReader( spool_dir, clear_jobs=self.clear_jobs,
clear_results=self.clear_results )
self.exp_writer = ExperimentWriter.ExperimentWriter( spool_dir, inform_last_job=self.res_reader )
else:
self.back_driver = None
if self.exp_script:
self.exp_writer = ExperimentWriter.ExperimentWriter( spool_dir )
if self.res_script:
self.res_reader = ResultReader.ResultReader( spool_dir, clear_jobs=self.clear_jobs,
clear_results=self.clear_results )
self.data = DataPool( )
def runScripts( self ):
try:
# get script engines
self.exp_handling = self.res_handling = None
if self.exp_script and self.exp_writer:
self.exp_handling = ExperimentHandling.ExperimentHandling( self.exp_script, self.exp_writer, self.data )
if self.res_script and self.res_reader:
self.res_handling = ResultHandling.ResultHandling( self.res_script, self.res_reader, self.data )
# start them
if self.back_driver is not None:
self.back_driver.start( )
while (not self.back_driver.quit_flag.isSet( ) and \
self.back_driver.core_pid is None and self.back_driver.core_pid <= 0):
self.back_driver.quit_flag.wait( 0.1 )
if self.exp_handling:
self.exp_handling.start( )
if self.res_handling:
self.res_handling.start( )
finally:
self.exp_writer = self.res_reader = None
def __del__( self ):
self.exp_writer = None
self.res_reader = None
self.back_driver = None
self.data = None
self.exp_handling = None
self.res_handling = None